Full Code of stefanhoelzl/vue.py for AI

master 581e764d57e2 cached
158 files
245.3 KB
67.0k tokens
623 symbols
1 requests
Download .txt
Showing preview only (280K chars total). Download the full file or copy to clipboard to get everything.
Repository: stefanhoelzl/vue.py
Branch: master
Commit: 581e764d57e2
Files: 158
Total size: 245.3 KB

Directory structure:
gitextract_pwes50fh/

├── .editorconfig
├── .github/
│   └── workflows/
│       └── push.yaml
├── .gitignore
├── .gitpod.Dockerfile
├── .gitpod.yml
├── CONTRIBUTING.md
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── docs/
│   ├── _config.yml
│   ├── _layouts/
│   │   └── default.html
│   ├── docs/
│   │   ├── index.md
│   │   ├── management/
│   │   │   ├── cli.md
│   │   │   └── configuration.md
│   │   ├── pyjs_bridge.md
│   │   └── vue_concepts/
│   │       ├── computed_properties.md
│   │       ├── custom_directives.md
│   │       ├── custom_vmodel.md
│   │       ├── data_methods.md
│   │       ├── extend.md
│   │       ├── filter.md
│   │       ├── instance_components.md
│   │       ├── lifecycle_hooks.md
│   │       ├── plugins_mixins.md
│   │       ├── props.md
│   │       ├── render_function.md
│   │       ├── vue-router.md
│   │       └── vuex.md
│   └── planning.md
├── examples/
│   ├── elastic_header/
│   │   ├── app.py
│   │   ├── header-view.html
│   │   ├── main.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── element_ui/
│   │   ├── app.py
│   │   ├── components/
│   │   │   └── navigation.py
│   │   ├── navigation.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── github_commits/
│   │   ├── app.py
│   │   ├── commits.html
│   │   ├── data.json
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── grid_component/
│   │   ├── app.py
│   │   ├── form.html
│   │   ├── grid.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── index.md
│   ├── markdown_editor/
│   │   ├── app.py
│   │   ├── editor.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── modal_component/
│   │   ├── app.py
│   │   ├── main.html
│   │   ├── modal-template.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── svg_graph/
│   │   ├── app-template.html
│   │   ├── app.py
│   │   ├── polygraph-template.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── todo_mvc/
│   │   ├── app-template.html
│   │   ├── app.py
│   │   ├── style.css
│   │   └── vuepy.yml
│   └── tree_view/
│       ├── app-template.html
│       ├── app.py
│       ├── style.css
│       ├── tree-template.html
│       └── vuepy.yml
├── pyproject.toml
├── requirements.txt
├── setup.py
├── stubs/
│   ├── browser.py
│   ├── javascript.py
│   └── local_storage.py
├── tests/
│   ├── __init__.py
│   ├── cli/
│   │   └── test_provider.py
│   ├── pytest.ini
│   ├── selenium/
│   │   ├── .gitignore
│   │   ├── chromedriver.py
│   │   ├── conftest.py
│   │   ├── pytest.ini
│   │   ├── test_api.py
│   │   ├── test_examples.py
│   │   ├── test_guide/
│   │   │   ├── test_components/
│   │   │   │   ├── test_custom_events.py
│   │   │   │   └── test_props.py
│   │   │   ├── test_essentials/
│   │   │   │   ├── test_components_basics.py
│   │   │   │   ├── test_computed_properties.py
│   │   │   │   ├── test_event_handler.py
│   │   │   │   ├── test_instance.py
│   │   │   │   ├── test_introduction.py
│   │   │   │   └── test_list_rendering.py
│   │   │   └── test_reusability_composition/
│   │   │       ├── test_filters.py
│   │   │       ├── test_mixins.py
│   │   │       └── test_render_function.py
│   │   ├── test_vuerouter.py
│   │   └── test_vuex.py
│   ├── test_install.py
│   └── unit/
│       ├── test_bridge/
│       │   ├── __init__.py
│       │   ├── mocks.py
│       │   ├── test_dict.py
│       │   ├── test_jsobject.py
│       │   ├── test_list.py
│       │   ├── test_vue.py
│       │   └── test_vuex.py
│       ├── test_transformers/
│       │   ├── conftest.py
│       │   ├── test_component.py
│       │   ├── test_router.py
│       │   └── test_store.py
│       ├── test_utils.py
│       └── test_vue.py
├── vue/
│   ├── __init__.py
│   ├── bridge/
│   │   ├── __init__.py
│   │   ├── dict.py
│   │   ├── list.py
│   │   ├── object.py
│   │   ├── vue_instance.py
│   │   └── vuex_instance.py
│   ├── decorators/
│   │   ├── __init__.py
│   │   ├── action.py
│   │   ├── base.py
│   │   ├── components.py
│   │   ├── computed.py
│   │   ├── custom.py
│   │   ├── data.py
│   │   ├── directive.py
│   │   ├── extends.py
│   │   ├── filters.py
│   │   ├── getter.py
│   │   ├── lifecycle_hook.py
│   │   ├── method.py
│   │   ├── mixins.py
│   │   ├── model.py
│   │   ├── mutation.py
│   │   ├── plugin.py
│   │   ├── prop.py
│   │   ├── render.py
│   │   ├── routes.py
│   │   ├── state.py
│   │   ├── template.py
│   │   └── watcher.py
│   ├── router.py
│   ├── store.py
│   ├── transformers.py
│   ├── utils.py
│   └── vue.py
└── vuecli/
    ├── __init__.py
    ├── cli.py
    ├── index.html
    └── provider/
        ├── __init__.py
        ├── flask.py
        ├── provider.py
        └── static.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8

# 4 space indentation
[*.{py,ini}]
indent_style = space
indent_size = 4

# 2 space indentation
[*.{css,js,html,yml}]
indent_style = space
indent_size = 2

[Makefile]
indent_style = tab


================================================
FILE: .github/workflows/push.yaml
================================================
name: CI

on:
  pull_request:
    branches:
    - master
  push:
    branches:
      - '**'
    tags:
      - 'release-candidate'

defaults:
  run:
    shell: bash

jobs:
  cleanup:
    runs-on: ubuntu-20.04
    steps:
      - name: Clean Up Release Candiate Tag
        if: ${{ github.ref == 'refs/tags/release-candidate' }}
        uses: dev-drprasad/delete-tag-and-release@v0.2.0
        with:
          tag_name: release-candidate
          delete_release: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  all:
    runs-on: ubuntu-20.04
    steps:
      # Setup
      - name: Checkout Repository
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.head.sha }}
      - name: Setup Python
        uses: actions/setup-python@v1
        with:
          python-version: 3.8
      - uses: browser-actions/setup-chrome@latest
      - name: Setup environment
        run: |
          make env.up
      # Build and Test
      - name: Run CI jobs
        run: |
          make ci
      # Publish documentation
      - name: Set default env variables
        run: |
          echo "GH_PAGES_BRANCH=gh-pages-test" >> $GITHUB_ENV
      - name: Update env variables for release
        if: startsWith(github.ref, 'refs/tags/v')
        run: |
          echo "GH_PAGES_BRANCH=gh-pages" >> $GITHUB_ENV
      - name: Deploy to GitHub Pages
        uses: crazy-max/ghaction-github-pages@v2
        if: ${{ github.event_name != 'pull_request' }}
        with:
          target_branch: ${{ env.GH_PAGES_BRANCH }}
          build_dir: gh-pages-build
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      # Release
      - name: Check Commit Messages
        run: |
          release check-commit-messages
      - name: Generate Changelog
        run: |
          release changelog > changelog.md
      - name: Delete Previous Master Github Release
        if: ${{ github.ref == 'refs/heads/master' }}
        uses: dev-drprasad/delete-tag-and-release@v0.2.0
        with:
          tag_name: master
          delete_release: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Publish Master Github Release
        if: ${{ github.ref == 'refs/heads/master' }}
        run: |
          gh release create master ./dist/*.whl -F changelog.md --prerelease --target master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Publish Github Release
        if: ${{ github.ref == 'refs/tags/release-candidate' }}
        run: |
          gh release create v`release version` ./dist/*.whl -F changelog.md
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Publish PyPI
        if: ${{ github.ref == 'refs/tags/release-candidate' }}
        uses: pypa/gh-action-pypi-publish@master
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}
          repository_url: https://upload.pypi.org/legacy/
          skip_existing: false


================================================
FILE: .gitignore
================================================
venv
.idea
.vscode
.pytest_cache
__pycache__
gh-pages-build

debug
examples/*/screenshot.png
vuecli/js
examples_static

vuepy.egg-info
dist
build
vue/__version__.py
changelog.md


================================================
FILE: .gitpod.Dockerfile
================================================
FROM gitpod/workspace-full

USER gitpod

# Install Google key
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
RUN sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'

# Install custom tools, runtime, etc.
RUN sudo apt-get update && sudo apt-get install -y google-chrome-stable && sudo rm -rf /var/lib/apt/lists/*


================================================
FILE: .gitpod.yml
================================================
image:
  file: .gitpod.Dockerfile
tasks:
- init: make env.up
  command: make serve
ports:
  - port: 8000
    onOpen: open-browser
  - port: 8001
    onOpen: ignore
  - port: 5000
    onOpen: ignore


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing a PR
First of: Thanks for contributing a PR to this project!!

There a four main guidelines I try to follow in this projects:
* Write clean code according to Bob Martins book
* Have tests!!
  * Unit tests and selenium tests
  * In general I try to use the examples in the vue.js documentation as test cases to make sure vue.py works as vue.js
* If a new feature is implemented, please also provide documentation
* Each commit needs a certain format `[type] commit message`
  * This allows a automated generation of the changelog
  * PRs get squashed and merged, so commit messages in PRs can be arbitrary
  * type can be one of the following
    * feature: use when adding new features
    * bugfix: use when a bug gets fixed but function stays the same
    * internal: use when refactoring or no user-facing changed are made
    * docs: use when updating documentation
    * tooling: use when changing tooling


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 Stefan Hoelzl

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: MANIFEST.in
================================================
include LICENSE


================================================
FILE: Makefile
================================================
PYTHONPATH=.:stubs

.PHONY: env.pip
env.pip:
	pip install -r requirements.txt
	pip install -e .

.PHONY: env.chrome
env.chrome:
	python tests/selenium/chromedriver.py

.PHONY: env.up
env.up: env.pip env.chrome

.PHONY: env.down
env.down:
	git clean -xdf --exclude .idea --exclude venv --exclude debug
	pip freeze > /tmp/vuepy-delete-requirements.txt
	pip uninstall -y -r /tmp/vuepy-delete-requirements.txt

.PHONY: serve
serve:
	python -m http.server 8000

.PHONY: run
run:
	cd ${APP} && vue-cli deploy flask

.PHONY: tests.selenium
tests.selenium:
	PYTHONPATH=$(PYTHONPATH) pytest tests/selenium

.PHONY: tests.unit
tests.unit:
	PYTHONPATH=$(PYTHONPATH) pytest tests/unit

.PHONY: tests.cli
tests.cli:
	PYTHONPATH=$(PYTHONPATH) pytest tests/cli

.PHONY: tests
tests:
	PYTHONPATH=$(PYTHONPATH) pytest tests/${TEST}

.PHONY: format
format:
	black --target-version py38 .

.PHONY: lint
lint:
	black --target-version py38 --check .

.PHONY: build
build:
	python setup.py sdist bdist_wheel

.PHONY: docs
docs:
	rm -Rf gh-pages-build
	mkdir gh-pages-build

	cp -Rf docs/* README.md vue gh-pages-build
	cp -Rf examples_static gh-pages-build/examples
	cp examples/index.md gh-pages-build/examples

	mkdir gh-pages-build/tests
	cp -R tests/selenium/_html/* gh-pages-build/tests

	mkdir gh-pages-build/js
	vue-cli package gh-pages-build/js

.PHONY: ci
ci: lint tests build docs


================================================
FILE: README.md
================================================
# vue.py
[![Build Status](https://github.com/stefanhoelzl/vue.py/workflows/CI/badge.svg)](https://github.com/stefanhoelzl/vue.py/actions)
[![PyPI](https://img.shields.io/pypi/v/vuepy.svg)](https://pypi.org/project/vuepy/)
[![License](https://img.shields.io/pypi/l/vuepy.svg)](LICENSE)

use [Vue.js](https://www.vuejs.org) with pure Python

vue.py provides Python bindings for [Vue.js](https://www.vuejs.org).
It uses [brython](https://github.com/brython-dev/brython) to run Python in the browser.

Here is a simple example of an vue.py component
```python
from browser import alert
from vue import VueComponent

class HelloVuePy(VueComponent):
    greeting = "Hello vue.py"

    def greet(self, event):
        alert(self.greeting)

    template = """
    <button @click="greet">click me</button>
    """

HelloVuePy("#app")
```

## Installation
```bash
$ pip install vuepy
```


## Development Status
The goal is to provide a solution to write fully-featured Vue applications in pure Python.

To get an overview what currently is supported, have a look at the [Documentation](https://stefanhoelzl.github.io/vue.py/docs/).

Have a look [here](https://stefanhoelzl.github.io/vue.py/planning.html) to see whats planned!

See also the [Limitations](https://stefanhoelzl.github.io/vue.py/docs/pyjs_bridge.html)

## Documentation
Documentation for the last release is available [here](https://stefanhoelzl.github.io/vue.py/docs/).

Documentation fo the current master branch can be found [here](https://github.com/stefanhoelzl/vue.py/blob/master/docs/docs/index.md).

Examples can be found [here](https://stefanhoelzl.github.io/vue.py/examples).
These are vue.py versions of the [Vue.js examples](https://vuejs.org/v2/examples/)

## Performance
Initial loading times of `vue.py` apps can be very long.
Especially when loading a lot of python files.
Still figuring out how to solve this.

Have not done any peformance tests, but havent noticed any issues with performance
as soon as the app was fully loaded.

## Development
### Getting Started
Open in [gitpod.io](https://gitpod.io#github.com/stefanhoelzl/vue.py)

Get the code
```bash
$ git clone https://github.com/stefanhoelzl/vue.py.git
$ cd vue.py
```

Optionally you can create a [venv](https://docs.python.org/3.8/library/venv.html)
```bash
$ python -m venv venv
$ source venv/bin/activate
```

Install required python packages, the chromedriver for selenium and brython
```bash
$ make env.up
```

Format the code
```bash
$ make format
```

Run tests
```bash
$ make tests           # runs all tets
$ make tests.unit      # runs unit tests
$ make tests.selenium  # runs selenium tests
$ make tests.cli       # runs cli tests
$ make tests TEST=cli/test_provider.py::TestRenderIndex::test_defaults # run explicit test
```

Run an example
```bash
$ make run APP=examples/tree_view  # makes example available on port 5000
```

Reset your development environment
_(clean up, reinstall packages and redownload needed files)_
```bash
$ make env.down
$ make env.up
```

Publish a new release
```bash
$ release release-candidate
```

### Contributing
see [CONTRIBUTING](https://github.com/stefanhoelzl/vue.py/blob/master/CONTRIBUTING.md)

## License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/stefanhoelzl/vue.py/blob/master/LICENSE) file for details


================================================
FILE: docs/_config.yml
================================================
theme: jekyll-theme-cayman
title: vue.py
description: Pythonic Vue.js
include:
  - __init__.py
  - __entry_point__.py
navigation:
  - title: Start
    link: /
  - title: Documentation
    link: docs
  - title: Gallery
    link: examples
  - title: Demo
    link: https://stefanhoelzl.github.io/mqtt-dashboard/


================================================
FILE: docs/_layouts/default.html
================================================
<!DOCTYPE html>
<html lang="{{ site.lang | default: "en-US" }}">
  <head>
    <meta charset="UTF-8">

{% seo %}
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#157878">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
  </head>
  <body>
    <a id="skip-to-content" href="#content">Skip to the content.</a>

    <header class="page-header" role="banner">
      <h1 class="project-name">{{ site.title | default: site.github.repository_name }}</h1>
      <h2 class="project-tagline">{{ site.description | default: site.github.project_tagline }}</h2>
        {%- for nav_item in site.navigation -%}
          <a href="{{ nav_item.link | relative_url }}" class="btn">{{ nav_item.title }}</a>
        {%- endfor -%}
        <a href="{{ site.github.repository_url }}" class="btn">View on GitHub</a>
    </header>

    <main id="content" class="main-content" role="main">
      {{ content }}

      <footer class="site-footer">
        <span class="site-footer-owner"><a href="{{ site.github.repository_url }}">{{ site.github.repository_name }}</a> is maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a>.</span>
        <span class="site-footer-credits">This page was generated by <a href="https://pages.github.com">GitHub Pages</a>.</span>
      </footer>
    </main>
  </body>
</html>


================================================
FILE: docs/docs/index.md
================================================
# Documentation
`vue.py` provides bindings for [Vue.js](https://vuejs.org/).
If you are not familiar with [Vue.js](https://vuejs.org/) read the [Vue.js Guide](https://vuejs.org/v2/guide/)
and then get back here to learn how to use [Vue.js](https://vuejs.org/) with pure Python.

## Installation

Install `vue.py` via `pip`
```bash
$ pip install vuepy
```

or with flask include to deploy apps
```bash
$ pip install vuepy[flask]
```

or from current master branch
```bash
$ pip install git+https://github.com/stefanhoelzl/vue.py@master
```

## First Application
Create a folder for your app
```bash
$ mkdir app
$ cd app
```

and as last step create a `app.py` where you create your Vue Component
```python
from vue import *

class App(VueComponent):
    msg = "Hello vue.py!"
    template = "<div>{{msg}}</div>"
App("#app")
```

deploy your app
```bash
$ vue-cli deploy flask
```
Now goto [http://localhost:5000](http://localhost:5000) and see your first vue.py app.

## Demo App
Checkout the [MQTT-Dashboard](https://github.com/stefanhoelzl/mqtt-dashboard/blob/master/app/app.py).
It's a little test project to demonstrate some `vue.py` features:
* uses the Browsers local storage to implement a vue-plugin in python
* uses a vue.js plugin
* uses some vue.js components
* uses a vuex store

## How to use Vue.js concepts
* [Instance and Components](vue_concepts/instance_components.md)
* [Data and Methods](vue_concepts/data_methods.md)
* [Computed Properties and Watchers](vue_concepts/computed_properties.md)
* [Props](vue_concepts/props.md)
* [Lifecycle Hooks](vue_concepts/lifecycle_hooks.md)
* [Customize V-Model](vue_concepts/custom_vmodel.md)
* [Filter](vue_concepts/filter.md)
* [Custom Directives](vue_concepts/custom_directives.md)
* [Plugins and Mixins](vue_concepts/plugins_mixins.md)
* [Extend](vue_concepts/extend.md)
* [Render Function](vue_concepts/render_function.md)
* [Vuex](vue_concepts/vuex.md)
* [Vue Router](vue_concepts/vue-router.md)

## Management
* [Configuration](management/configuration.md)
* [vue-cli](management/cli.md)

## Python/Javascript Bridge
[here](pyjs_bridge.md)


================================================
FILE: docs/docs/management/cli.md
================================================
# Command Line Interface

`vue.py` provides a command line tool `vue-cli` to deploy your application.

## Deployment
A `vue.py` application can be deployed via several provider.

Get help about the available provider and their arguments
```bash
$ vue-cli deploy -h
```

This installs all the required packages for e.g. the flask provider
```bash
pip install vuepy[flask]
```

### Flask
With a flask live deployment your application is accessible on
[http://localhost:5000](http://localhost:5000).
```bash
$ vue-cli deploy flask
```
This is the best deployment method when debugging.

#### Configuration
`vuepy.yml` can be used to set `HOST`, `PORT` and [Flask Builtin Configuration Values](https://flask.palletsprojects.com/en/2.0.x/config/#builtin-configuration-values)
```yaml
provider:
  flask:
    HOST: "0.0.0.0"
    PORT: 5001
```

### Static
With a static deployment everything your application needs,
gets packaged into a single folder,
which can be served by your favorite web server.
```bash
$ vue-cli deploy static <destination> --package
```
* `destination` specifies the path where your application should be deployed to.
* `--package` (optional) packages the python code into the vuepy.js file.


================================================
FILE: docs/docs/management/configuration.md
================================================
# Configuration

Your `vue.py` application can be customized via a `vuepy.yml` file
located in your application folder.

## Stylesheets
If you want to use custom CSS stylsheets, add this section to the configuration file:
```yaml
stylesheets:
  - <path of the stylesheet relative to your application folder>
  - <URL of the stylesheet>
```

## Scripts
### Javascript Libraries
If you want to use custom javascript libraries, add this section to the configuration file:
```yaml
scripts:
  - <path of the script relative to your application folder>
  - <URL of the script>
```
or if combined with [extensions](#Extensions) or [custom versions](#Custom-Versions)
```yaml
scripts:
  "local_lib_name": <path of the script relative to your application folder>
  "lib_name": <URL of the script>
```

### Extensions
`vue.py` comes with some vue.js extensions builtin:
* [vuex](https://vuex.vuejs.org)
* [vue-router](https://router.vuejs.org)
The extensions can be activated as followed:
```yaml
scripts:
  vuex: true
  vue-router: true
```
By default all extensions are deactivated to avoid loading unnecessary files.


### Custom Versions
`vue.py` comes with vue.js and brython built-in.
If different versions can be used as followed:
```yaml
scripts:
  vue: <URL/Path to custom vue.js file>
  brython: <URL/Path to custom brython_dist.js file>
  vuex: <URL/Path to custom vuex.js file>
  vue-router: <URL/Path to custom vue-router.js file>
```

## EntryPoint
By default the `app.py` in your project directory is the entry point for your app.
If you want to point to a custom entry point `custom.py`, add this section:
```yaml
entry_point: custom
```

## Templates
Since writing HTML in python strings can be tedious 
you can write your templates in .html files 
and link them as your template string.
```yaml
templates:
    myhtml: my.html
```

```python
from vue import VueComponent

class MyComponent(VueComponent):
    template = "#myhtml"
```


================================================
FILE: docs/docs/pyjs_bridge.md
================================================
# Python/Javascript Bridge
## Call Javascript Functions
`vue.py` provides some utilities to access Javascript libraris.

You can load Javascript libraries dynamically
```python
from vue.utils import js_load
marked = js_load("https://unpkg.com/marked@0.3.6")
html = marked("# Title")
```
This perfoms an synchrous ajax call and therefore is not recommended for responsive applications.
Furthermore prevent some browser (e.g. Chrome) load from external ressources with ajax.

Therefore a second method is provided to acces a already loaded Javascript library.
```html
<script src="https://unpkg.com/marked@0.3.6"></script>
```
```python
from vue.utils import js_lib
marked = js_lib("marked")
html = marked("# Title")
```
This uses the optimized methods a Browser uses to load all dependencies.
And provides also access to all object in the Javascript namespace.

## Vue Reactivity
To keep the reactivity of Vue.js and at the same time providing a
Pythonic interface, all attribuets of vue.py components are wrapped in
custom types.
These types provide the same interfaes than native python types, but use
the javascript types in the background.

Just making dicts out of Javascript object, method calls, would look
rather unusual.
```python
element['focus']()
```
To avoid this, wrapped dicts can also access items as attributes, this leads to
more readable code
```python
element.focus()
```

By wrapping the javascript types, it is also possible
to improve the original Vue.js behavior. In Vue.js this is forbidden.
```javascript
var vm = new Vue({
  data: {
    reactive: {yes: 0}
  }
})
// `vm.reactive.yes` is now reactive

vm.no = 2
// `vm.reactive.no` is NOT reactive
```

Your have to use `Vue.set()`. vue.py takes care of this under the hood.
```python
class App(VueComponent):
    reactive = {"yes": 0}

app = App("#element")
# `vm.reactive.yes` is now reactive

app.reactive["also"] = 2  # `vm.reactive.also` is now also reactive
```

## Limitations
## Usable Types
For now vue.py only supports basics types (int, float, str, bool, list, dict), since these can be converted fairly simple to their Javascript equivalentive.
Writing own classes and using them for Component properties may not work.

This may change in the future, but for now it is not planned to work on this issue.

## Due To Wrapping Types
Due to restrictions of Brython in combination with the reactivity system in Vue.js are custom wrapper around component data and props neccessary.
This is done mostly in the background, there are some limitations to consider.

### When Native Python Types Are Assumed
The wrapper around lists and dictionaries provide the same interface than native python types but due to restrictions in Brython, they are no subclasses of `list`/`dict`.
This can lead to problems when passing this methods to other native python methods.
Therefore a helper is provided to convert a wrapped Javascript object into a native python type.
```python
import json
from vue import VueComponent, computed
from vue.bridge import Object


class MyComponent(VueComponent):
    template = "<div>{{ content_as_json }}</div>"
    content = [{"a": 1}]

    @computed
    def content_as_json(self):
        # Will break because self.content is not a native python type
        # json.dumps does not know how to serialize this types
        return json.dumps(sef.content)

        # vue.py provides a method to convert the wrapper types
        return json.dumps(Object.to_py(self.content))

```
**When converting to native python types reactivity may get lost!**


### When Native Javascript Types Are Assumed
A similar problem exists when passing wrapper variables to native javascript methods.
Brython can convert native Python types like lists and dicts to their javascript equivalent.
Since the wrapper types are not real lists/dicts Brython cannot convert them.
```python
from vue import VueComponent, computed
from vue.bridge import Object
from vue.utils import js_lib

js_json = js_lib("JSON")

class MyComponent(VueComponent):
    template = "<div>{{ content_as_json }}</div>"
    content = [{"a": 1}]

    @computed
    def content_as_json(self):
        # Will break because self.content is not a native javascript type
        # JSON.stringify does not know how to serialize this types
        return js_json.stringify(self.content)

        # vue.py provides a method to convert the wrapper types
        return js_json.stringify(Object.to_js(self.content))
```

### Are These Limitations Forever?
I hope not!

Currently the main reason for this limitations is [Brython Issue 893](https://github.com/brython-dev/brython/issues/893).
When this one gets fixed, the wrapper classes can be subclasses of native python types and Brython should be able to do the right conversions under the hood.


================================================
FILE: docs/docs/vue_concepts/computed_properties.md
================================================
# Computed Properties
Computed properties can be defined with the `@computed` decorator
```python
from vue import VueComponent, computed

class ComponentWithMethods(VueComponent):
    message = "Hallo vue.py"
    @computed
    def reversed(self):
        return "".join(reversed(self.message))
```

computed setters are defined similar to plain python setters
```python
class ComputedSetter(VueComponent):
    message = "Hallo vue.py"

    @computed
    def reversed_message(self):
        return self.message[::-1]

    @reversed_message.setter
    def reversed_message(self, reversed_message):
        self.message = reversed_message[::-1]
```

# Watchers
Watchers can be defined with the `@watch` decorator.
```python
from vue import VueComponent, watch

class Watch(VueComponent):
    message = ""

    @watch("message")
    def log_message_changes(self, new, old):
        print("'message' changed from '{}' to '{}'".format(old, new)
```


`deep` and `immediate` watchers can be configured via arguments

```python
from vue import VueComponent, watch

class Watch(VueComponent):
    message = ""

    @watch("message", deep=True, immediate=True)
    def log_message_changes(self, new, old):
        print("'message' changed from '{}' to '{}'".format(old, new)
```


================================================
FILE: docs/docs/vue_concepts/custom_directives.md
================================================
# Custom Directives

## Local Registration
For [function directives](https://vuejs.org/v2/guide/custom-directive.html#Function-Shorthand)
is just a decorator necessary
```python
from vue import VueComponent, directive

class CustomDirective(VueComponent):
    @staticmethod
    @directive
    def custom_focus(el, binding, vnode, old_vnode, *args):
        pass
```

To define custom hook functions, add the directive name as argument to
the decorator
```python
from vue import VueComponent, directive

class CustomDirective(VueComponent):
    @staticmethod
    @directive("focus")
    def component_updated(el, binding, vnode, old_vnode, *args):
        # implement 'componentUpdated' hook function here
        pass

    @staticmethod
    @directive("focus")
    def inserted(el, binding, vnode, old_vnode, *args):
        # implement 'inserted' hook function here
        pass
```

To avoid code duplication when adding the same hook function to different hooks,
the hooks can be specified as decorator arguments.
```python
from vue import VueComponent, directive

class CustomDirective(VueComponent):
    @staticmethod
    @directive("focus", "component_updated", "inserted")
    def combined_hook(el, binding, vnode, old_vnode, *args):
        # implement function for 'componentUpdated' and 'inserted' hook here
        pass
```

**The Vue.js hook `componentUpdated` is called `component_updated` to be more pythonic**

The `@staticmethod` decorator is only necessary to avoid IDE checker errors.

Underscores in directive names get replaced by dashes, so `custom_focus` gets `v-custom-focus`.


## Global Registration
Global directives can be created by sub-classing `VueDirective`.
```python
from vue import Vue, VueDirective
class MyDirective(VueDirective):
    def bind(el, binding, vnode, old_vnode):
        pass

    def component_updated(el, binding, vnode, old_vnode):
        pass

Vue.directive("my-directive", MyDirective)
```
and for function directives just pass the function to `Vue.directive`
```python
from vue import Vue
def my_directive(el, binding, vnode, old_vnode):
    pass

Vue.directive("my-directive", my_directive)
```
`vue.py` offeres a shorthand, if you like to take the **lower-cased** name
of the function/directive-class as directive name.
```python
from vue import Vue
def my_directive(el, binding, vnode, old_vnode):
    pass

Vue.directive(my_directive) # directive name is 'my_directive'
```

## Retrieve Global Directives
Getter for global directives works similar to Vue.js
```python
from vue import Vue
directive = Vue.directive('directive-name')
```


================================================
FILE: docs/docs/vue_concepts/custom_vmodel.md
================================================
# Customize V-Model
To customize the event and prop used by `v-model` a class variable of the type `Model()` can be defined.
```python
from vue import VueComponent, Model

class CustomVModel(VueComponent):
    model = Model(prop="checked", event="change")
    checked: bool
    template = """
    <div>
        <p id="component">{{ checked }}</p>
        <input
            id="c"
            type="checkbox"
            :checked="checked"
            @change="$emit('change', $event.target.checked)"
        >
    </div>
    """
```


================================================
FILE: docs/docs/vue_concepts/data_methods.md
================================================
# Data
All class variables of a `vue.py` component are available as data fields in the Vue instance.
```python
class ComponentWithData(VueComponent):
    data_field = "value"
```

to initialize a data field with a prop you can use the `@data` decorator
```python
from vue import VueComponent, data

class ComponentWithData(VueComponent):
    prop: str
    @data
    def initialized_with_prop(self):
        return self.prop
```

# Methods
Similar to data fields all methods of the `vue.py` component are available as methods.
```python
from vue import VueComponent

class ComponentWithMethods(VueComponent):
    counter = 0

    def increase(self):
        self.counter += 1
```

Methods used as event handler, must have a (optional) argument for the event.
```python
from vue import VueComponent

class ComponentWithMethods(VueComponent):
    def handle(self, ev):
        print(ev)
```

## self
All attributes of the Vue instance are available as attributes of `self` (e.g. methods, computed properties, props etc.).



================================================
FILE: docs/docs/vue_concepts/extend.md
================================================
# Extend
Vue.js uses `Vue.extend` or the `extends` Component attribute to extend
components. Per default `vue.py` sticks to the Pythonic way and uses the
python class inheritance behavior.
```python
from vue import VueComponent

class Base(VueComponent):
    def created(self):
        print("Base")

class Sub(Base):
    def created(self):
        super().created()
        print("Sub")
```
which outputs on the console
```
Base
Sub
```

To use the merging strategies Vue.js applies when using `extends`
in `vue.py` just set the `extends` attribute to `True`

```python
from vue import VueComponent

class Base(VueComponent):
    def created(self):
        print("Base")

class Sub(Base):
    extends = True

    def created(self):
        print("Sub")
```
which outputs on the console
```
Base
Sub
```

The `extend` attribute can also be a native Vue.js component to extend from this.
```
from vue import *
from vue.utils import js_lib
NativeVueJsComponent = js_lib("NativeVueJsComponent")

class Sub(VueComponent):
    extends = NativeVueJsComponent
```

## Template Slots
Vue.js does not support extending templates out-of-the-box and it is
[recommended](https://vuejsdevelopers.com/2017/06/11/vue-js-extending-components/)
to use third-party libraries like [pug](https://pugjs.org/api/getting-started.html).

With `vue.py` a feature called `template_slots` is included to extend templates.
A base component can define slots in the template
and a sub component can fill the slots with the attribute `template_slots`.
The base component can also define default values for the slots.

```python
from vue import VueComponent

class Base(VueComponent):
    template_slots = {
        "heading": "Default Heading",
        "footer": "Default Footer",
    }
    template = """
    <div>
        <h1>{heading}</h1>
        {content}
        <small>{footer}</small>
    </div>
    """

class Sub(Base):
    template_slots = {
        "heading": "My Custom Heading",
        "content": "content..."
    }
```
The `Sub` component gets rendered as
```html
<h1>My Custom Heading</h1>
content...
<small>Default Footer</small>
```

If you only have one slot in your component, the `template_slots` attribute
can be the template string

```python
from vue import VueComponent

class Base(VueComponent):
    template = "<h1>{}</h1>"

class Sub(Base):
    template_slots = "heading"
```
The `Sub` component gets rendered as
```html
<h1>heading</h1>
```

Mixing both is also possible
```python
class Base(VueComponent):
    template_slots = {"pre": "DEFAULT", "post": "DEFAULT"}
    template = "<p>{pre} {} {post}</p>"

class WithSlots(Base):
    template_slots = {"pre": "PRE", "default": "SUB"}

class WithDefault(Base):
    template_slots = "SUB"
```

The `WithSlots` component gets rendered as
```html
<p>PRE SUB DEFAULT</p>
```

The `WithDefault` component gets rendered as
```html
<p>DEFAULT SUB DEFAULT</p>
```


================================================
FILE: docs/docs/vue_concepts/filter.md
================================================
# Filter

## Local Registration
Local registration of filters is done with the `@filters` decorator.
```python
from vue import VueComponent, filters

class ComponentWithFilter(VueComponent):
    message = "Message"

    @filters
    def lower_case(value):
        return value.lower()

    template = "<div id='content'>{{ message | lower_case }}</div>"
```

To avoid errors on source code checking errors in modern IDEs, an additional `@staticmethod` decorator can be added
```python
from vue import VueComponent, filters

class ComponentWithFilter(VueComponent):
    @staticmethod
    @filters
    def lower_case(value):
        return value.lower()
```

## Global Registration
Global registration of filters works similar to Vue.js
```python
from vue import Vue

Vue.filter("capitalize", str.capitalize)
```

Additionally in vue.py it is allowd to only pass a function to `Vue.filter`.
In this case the filter gets registered under the function name.
```python
from vue import Vue

def my_filter(val):
    return "filtered({})".format(val)

Vue.filter(my_filter)
```


================================================
FILE: docs/docs/vue_concepts/instance_components.md
================================================
# Components
## Define
A Vue component can be defined by writing a sub-class of `VueComponent`
```python
from vue import VueComponent

class MyComponent(VueComponent):
    pass
```

## Registration
Every component has to be [registered](https://vuejs.org/v2/guide/components-registration.html) to be available in other components.
### Local Registration
```python
from vue import VueComponent

class MyComponent(VueComponent):
    components = [
        MyVuePyComponent,
        AnotherNativeVueJsComponent,
    ]
```
The component to register can be either a `vue.py` component or a native
Vue.js component loaded with `js_lib` or `js_import`
### Global Registration
```python
from vue import Vue

# For vue.py components or native Vue.js component loaded with js_lib or js_import
Vue.component(MyComponent)
Vue.component("my-custom-name", MyComponent)

# Only for vue.py components
MyComponent.register()
MyComponent.register("my-custom-name")
```

## Template
The component html template can be defined with a class variable called `template`
```python
from vue import VueComponent

class MyComponent(VueComponent):
    template = """
    <div>
        Hallo vue.py!
    </div>
    """
```

`vue.py` templates look the same than Vue.js templates. This means inline expressions must be javascript!!.
```python
from vue import VueComponent

class MyComponent(VueComponent):
    message = "Hallo vue.py!"
    template = """
    <div>
        {{ message.split('').reverse().join('') }}
    </div>
    """
```


# Instance
## Start
To start a component as Vue application, just pass a css selector at initialization
```
App("#app")
```

## Prop Data
[propsData](https://vuejs.org/v2/api/#propsData) can be passed in as a dictionary.
```python
App("#app", props_data={"prop": "value"})
```


## API
### Dollar Methods
$-methods like `$emit` can be called by omitting the `$`
```python
from vue import VueComponent

class MyComponent(VueComponent):
    def created(self):
        self.emit("creation", "Arg")
```

In the case your Component has another attribute with the same name, you can use a workaround and directly call `getattr()`
```python
from vue import VueComponent

class MyComponent(VueComponent):
    emit = "already used"
    def created(self):
        getattr(self, "$emit")("creation", "Arg")
```



================================================
FILE: docs/docs/vue_concepts/lifecycle_hooks.md
================================================
# Lifecycle Hooks
Certain names for component methods are reserved, to specify lifecycle hooks.
```python
from vue import VueComponent

class ComponentLifecycleHooks(VueComponent):
    def before_create(self):
        print("on beforeCreate")

    def created(self):
        print("on created")

    def before_mount(self):
        print("on beforeMount")

    def mounted(self):
        print("on mounted")

    def before_update(self):
        print("on beforeUpdate")

    def updated(self):
        print("on updated")

    def before_destroy(self):
        print("on beforeDestroy")

    def destroyed(self):
        print("on destroyed")
```


================================================
FILE: docs/docs/vue_concepts/plugins_mixins.md
================================================
# Mixins

## Write Mixins
Mixins are created by sub-classing from `VueMixin` and from there it is
similar to write a `VueComponent`
```python
from vue import VueMixin, computed

class MyMixin(VueMixin):
    prop: str

    value = "default"

    def created(self):
        pass

    @computed
    def upper_value(self):
        return self.value.upper()
```

## Use Mixins
### Local Registration
Local registration of a Mixin within a Component works just like in Vue.js.
```python
from vue import VueComponent

class MyComponent(VueComponent):
    mixins = [MyPyMixin, AnotherVueJsMixin]
```
Mixins wirtten in Vue.js and `vue.py` can be mixed.

### Global Registration
Global registration of a Mixin works also just like in Vue.js.
```python
from vue import Vue
Vue.mixin(MyMixin)
```

# Plugins
## Write Plugins
A plugin can be written by sub-classing `VuePlugin` and implementing
the function `install` similar to Vue.js.
```python
from vue import Vue, VuePlugin, VueMixin

class MyPlugin(VuePlugin):
    class MyMixin(VueMixin):
        def created(self):
            pass

        # 4) Within a Mixin, new instance methods can be defined
        def my_method(self, args):
            pass

    @staticmethod
    def global_method():
        pass

    @staticmethod
    def install(*args, **kwargs):
        # 1) Add a global method or property
        Vue.my_global_method = MyPlugin.global_method

        # 2) Add a global assed
        Vue.directive(MyDirective)

        # 3) Inject Mixins
        Vue.mixin(MyPlugin.MyMixin)
```

## Use Plugins
Using plugins works like in Vue.js
```python
from vue import Vue

Vue.use(MyPlugin)
```
`vue.py` supports also using native Vue.js plugins.


================================================
FILE: docs/docs/vue_concepts/props.md
================================================
# Props
A prop is defined by adding a type hint to a class variable.
```python
from vue import VueComponent

class ComponentWithData(VueComponent):
    prop: str
```

## Types
Unlike Vue.js, vue.py enforces prop types (if not type hint is provided, it is a data field).

The following types are currently supported:
* `int`
* `float`
* `str`
* `bool`
* `list`
* `dict`

## Default
By assigning a value to a prop, the default value can be defined.
```python
from vue import VueComponent

class ComponentWithData(VueComponent):
    prop: str = "default"
```

## Required
If no default value is given, the prop is automatically required.

## Validator
With the `@validator` decorator are prop validators defined
```python
from vue import VueComponent, validator

class ComponentWithData(VueComponent):
    prop: int

    @validator("prop")
    def prop_must_be_greater_than_100(self, value):
        return value > 100
```


================================================
FILE: docs/docs/vue_concepts/render_function.md
================================================
# Render Function
A render function can be defined by overwriting the `render`  method.
```python
from vue import VueComponent

class ComponentWithData(VueComponent):
    def render(self, create_element):
        return create_element("h1", "Title")
```

## Accessing Slots
Slots can be accessed as a dictionary.
```python
from vue import VueComponent

class ComponentWithSlots(VueComponent):
    def render(self, create_element):
        return create_element(f"div", self.slots.get("default"))
```
It is recommened to access the dictionary via the `get`-method to avoid failures
when no children for the slot are provided.

## Passing Props
```python
from vue import VueComponent

class ComponentWithProps(VueComponent):
    prop: str = "p"
    template = "<div :id='prop'></div>"

ComponentWithProps.register()

class Component(VueComponent):
    def render(self, create_element):
        return create_element(
            "ComponentWithProps", {"props": {"prop": "p"}},
        )
```


================================================
FILE: docs/docs/vue_concepts/vue-router.md
================================================
# Vue Router
## Define
A vue router can be used by writing a sub-class of `VueRouter`, 
setting some `VueRoute`s 
and set the `router` parameter when initializing a app.
```python
from vue import VueComponent, VueRouter, VueRoute

class Foo(VueComponent):
    template = "<div>foo</div>"

class Bar(VueComponent):
    template = "<div>bar</div>"

class Router(VueRouter):
    routes = [
        VueRoute("/foo", Foo),
        VueRoute("/bar", Bar),
    ]

class App(VueComponent):
    template = """
        <div>
            <p>
                <router-link to="/foo">Go to Foo</router-link>
                <router-link to="/bar">Go to Bar</router-link>
            </p>
            <router-view></router-view>
        </div>
    """

App("#app", router=Router())
```

enable the vue-router extension in the `vuepy.yml` [config file](../management/configuration.md):
```yaml
scripts:
  vue-router: true
```


================================================
FILE: docs/docs/vue_concepts/vuex.md
================================================
# Vuex
## Define
A Vuex store can be defined by writing a sub-class of `VueStore`
and used by setting the `store` parameter when initializing a app.
```python
from vue import VueComponent, VueStore

class Store(VueStore):
    pass

class App(VueComponent):
    pass

App("#app", store=Store())
```

enable the vuex extension in the `vuepy.yml` [config file](../management/configuration.md):
```yaml
scripts:
  vuex: true
```

## State Variables
```python
from vue import VueComponent, VueStore

class Store(VueStore):
    greeting = "Hello Store"

class App(VueComponent):
    def created(self):
        print(self.store.greeting)

App("#app", store=Store())
```


## Getter
```python
from vue import VueComponent, VueStore, getter

class Store(VueStore):
    greeting = "Hello"

    @getter
    def get_greeting(self):
        return self.greeting

    @getter
    def personalized_greeting(self, name):
        return "{} {}".format(self.greeting, name)

class App(VueComponent):
    def created(self):
        print(self.store.get_greeting) # "Hello"
        print(self.store.personalized_greeting("Store")) # "Hello Store"

App("#app", store=Store())
```

## Mutations
Unlike `Vue.js` mutations in `vue.py` can have multiple arguments and
even keyword-arguments
```python
from vue import VueComponent, VueStore, mutation

class Store(VueStore):
    greeting = ""

    @mutation
    def set_greeting(self, greeting, name=None):
        self.greeting = greeting
        if name:
            self.greeting += " " + name

class App(VueComponent):
    def created(self):
        self.store.commit("set_greeting", "Hello", name="Store")
        print(self.store.greeting) # "Hello Store"

App("#app", store=Store())
```

## Mutations
Similar to mutations actions in `vue.py` can have multiple arguments and
even keyword-arguments.
```python
from vue import VueComponent, VueStore, mutation

class Store(VueStore):
    @action
    def greet(self, greeting, name=None):
        if name:
            greeting += " " + name
        print(greeting)

class App(VueComponent):
    def created(self):
        self.store.dispatch("greet", "Hello", name="Store")

App("#app", store=Store())
```

## Plugins
```python
class Plugin(VueStorePlugin):
    def initialize(self, store):
        store.message = "Message"

    def subscribe(self, mut, *args, **kwargs):
        print(mut, args, kwargs)

class Store(VueStore):
    plugins = [Plugin().install] # list can also contain native vuex plugins

    message = ""

    @mutation
    def msg(self, prefix, postfix=""):
        pass

class ComponentUsingGetter(VueComponent):
    @computed
    def message(self):
        return self.store.message

    def created(self):
        self.store.commit("msg", "Hallo", postfix="!")

    template = "<div id='content'>{{ message }}</div>"
```


================================================
FILE: docs/planning.md
================================================
# Future Plans
## Performance
* How to improve loading times?
* create benchmarks

## Tools
* Docker deployment

## Vue.py Universe
* store synchronization
  * with local/session storage
  * over WebSockets with python backend
* desktop toolkit
  * based on [pywebview](https://github.com/r0x0r/pywebview) ??

## Vue.js Features
* full access to Vue object (global configuration etc.)
* ...

## Internals
* write tests for decorators

## Docs
* embed examples in gallery


================================================
FILE: examples/elastic_header/app.py
================================================
from vue import VueComponent, data, computed
from vue.bridge import Object
from vue.utils import js_lib


dynamics = js_lib("dynamics")


class DraggableHeaderView(VueComponent):
    template = "#header-view"

    dragging = False

    @data
    def c(self):
        return {"x": 160, "y": 160}

    @data
    def start(self):
        return {"x": 0, "y": 0}

    @computed
    def header_path(self):
        return f'M0,0 L320,0 320,160Q{self.c["x"]},{self.c["y"]} 0,160'

    @computed
    def content_position(self):
        dy = self.c["y"] - 160
        dampen = 2 if dy > 0 else 4
        return {"transform": f"translate3d(0,{dy / dampen}px,0)"}

    def start_drag(self, e):
        e = e["changedTouches"][0] if "changedTouches" in e else e
        self.dragging = True
        self.start["x"] = e.pageX
        self.start["y"] = e.pageY

    def on_drag(self, e):
        e = e["changedTouches"][0] if "changedTouches" in e else e
        if self.dragging:
            self.c["x"] = 160 + (e.pageX - self.start["x"])
            dy = e.pageY - self.start["y"]
            dampen = 1.5 if dy > 0 else 4
            self.c["y"] = int(160 + dy / dampen)

    def stop_drag(self, _):
        if self.dragging:
            self.dragging = False
            dynamics.animate(
                Object.to_js(self.c),
                {"x": 160, "y": 160},
                {"type": dynamics.spring, "duration": 700, "friction": 280},
            )


DraggableHeaderView.register()


class App(VueComponent):
    template = "#main"


App("#app")


================================================
FILE: examples/elastic_header/header-view.html
================================================
<div class="draggable-header-view"
  @mousedown="start_drag" @touchstart="start_drag"
  @mousemove="on_drag" @touchmove="on_drag"
  @mouseup="stop_drag" @touchend="stop_drag" @mouseleave="stop_drag">
  <svg class="bg" width="320" height="560">
    <path :d="header_path" fill="#3F51B5"></path>
  </svg>
  <div class="header" id="header">
    <slot name="header"></slot>
  </div>
  <div class="content" :style="content_position" id="content">
    <slot name="content"></slot>
  </div>
</div>


================================================
FILE: examples/elastic_header/main.html
================================================
<draggable-header-view>
  <template slot="header">
    <h1>Elastic Draggable SVG Header</h1>
    <p>
      with <a href="https://stefanhoelzl.github.io/vue.py/">vue.py</a>
      + <a href="http://dynamicsjs.com">dynamics.js</a>
    </p>
  </template>
  <template slot="content">
    <p>
      Note this is just an effect demo
      - there are of course many additional details
        if you want to use this in production,
        e.g. handling responsive sizes, reload threshold and content scrolling.
        Those are out of scope for this quick little hack.
        However, the idea is that you can hide them as internal details
        of a vue.py component and expose a simple Web-Component-like interface.
    </p>
  </template>
</draggable-header-view>


================================================
FILE: examples/elastic_header/style.css
================================================
h1 {
  font-weight: 300;
  font-size: 1.8em;
  margin-top: 0;
}
a {
  color: #fff;
}
.draggable-header-view {
  background-color: #fff;
  box-shadow: 0 4px 16px rgba(0,0,0,.15);
  width: 320px;
  height: 560px;
  overflow: hidden;
  margin: 30px auto;
  position: relative;
  font-family: 'Roboto', Helvetica, Arial, sans-serif;
  color: #fff;
  font-size: 14px;
  font-weight: 300;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.draggable-header-view .bg {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
}
.draggable-header-view .header, .draggable-header-view .content {
  position: relative;
  z-index: 1;
  padding: 30px;
  box-sizing: border-box;
}
.draggable-header-view .header {
  height: 160px;
}
.draggable-header-view .content {
  color: #333;
  line-height: 1.5em;
}


================================================
FILE: examples/elastic_header/vuepy.yml
================================================
templates:
  header-view: header-view.html
  main: main.html
stylesheets:
  - style.css
scripts:
  - https://unpkg.com/dynamics.js@1.1.5/lib/dynamics.js


================================================
FILE: examples/element_ui/app.py
================================================
from vue import VueComponent
from .components import navigation


navigation.register()


class App(VueComponent):
    template = "#navigation"

    navigation_menu = [
        {
            "id": "one",
            "title": "Navigation One",
            "icon": "el-icon-location",
            "children": [
                {"group": "Group One"},
                {"id": "one.one", "title": "Item One"},
                {"id": "one.two", "title": "Item Two"},
                {"group": "Group Two"},
                {"id": "one.hree", "title": "Item Three"},
                {
                    "id": "one.four",
                    "title": "Item Four",
                    "children": [{"id": "one.four.five", "title": "Item Five"}],
                },
            ],
        },
        {"id": "two", "title": "Navigation Two", "icon": "el-icon-menu"},
        {
            "id": "three",
            "title": "Navigation Three",
            "icon": "el-icon-document",
            "disabled": True,
        },
        {"id": "four", "title": "Navigation Four", "icon": "el-icon-setting"},
    ]

    def clicked(self, item):
        print(item)
        self.notify.info(
            {"title": "Navigation", "message": item.get("title", "NO TITLE")}
        )


App("#app")


================================================
FILE: examples/element_ui/components/navigation.py
================================================
from vue import VueComponent, computed


class NavigationItem(VueComponent):
    item: dict
    template = """
    <el-menu-item-group v-if="is_group_header">
        <span slot="title">{{ item.group }}</span>
    </el-menu-item-group>
    <component v-else
        @click="$emit('click', item)"
        :is="item_tag" 
        :disabled="item.disabled"
        :index="item.id">
        <template v-if="is_submenu">
            <template slot="title">
                <i :class="item.icon"></i>
                <span slot="title">{{ item.title }}</span>
            </template>
            <navigation-item 
                v-for="sub_item in item.children"
                :key="sub_item.id"
                :item="sub_item"
                @click="$emit('click', $event)"
                >
            </navigation-item>
        </template>
        <template v-else>
            <i :class="item.icon"></i>
            {{ item.title }}
        </template>
    </component>
    """

    @computed
    def item_tag(self):
        if self.is_submenu:
            return "el-submenu"
        return "el-menu-item"

    @computed
    def is_menu_item(self):
        return not self.is_group_header and not self.is_submenu

    @computed
    def is_group_header(self):
        return "group" in self.item

    @computed
    def is_submenu(self):
        return "children" in self.item


class NavigationMenu(VueComponent):
    content: list
    template = """
    <div>
        <el-menu
            class="navigation-menu">
            <navigation-item 
                @click="$emit('click', $event)"
                v-for="item in content"
                :key="item.id"
                :item="item"
                >
            </navigation-item>
        </el-menu>
    </div>
    """


def register():
    NavigationItem.register()
    NavigationMenu.register()


================================================
FILE: examples/element_ui/navigation.html
================================================
<div>
  <navigation-menu
    @click="clicked"
    :content="navigation_menu">
  </navigation-menu>
</div>


================================================
FILE: examples/element_ui/style.css
================================================
.navigation-menu:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}


================================================
FILE: examples/element_ui/vuepy.yml
================================================
templates:
  navigation: navigation.html
stylesheets:
  - style.css
  - https://unpkg.com/element-ui/lib/theme-chalk/index.css
scripts:
  - https://unpkg.com/element-ui/lib/index.js


================================================
FILE: examples/github_commits/app.py
================================================
from vue import VueComponent, filters, watch

from browser import window, ajax

url = "https://api.github.com/repos/stefanhoelzl/vue.py/commits?per_page=10&sha={}"

if window.location.hash == "#testing":
    url = "data.json"


class App(VueComponent):
    template = "#commits"

    branches = ["master", "2948e6b"]
    current_branch = "master"
    commits = []

    def created(self):
        self.fetch_data()

    @watch("current_branch")
    def fetch_data_on_current_branch_change(self, new, old):
        self.fetch_data()

    @staticmethod
    @filters
    def truncate(value):
        return value.split("\n", 1)[0]

    @staticmethod
    @filters
    def format_date(value):
        return value.replace("T", " ").replace("Z", "")

    def fetch_data(self):
        self.commits = []

        req = ajax.ajax()
        req.open("GET", url.format(self.current_branch), True)
        req.bind("complete", self.loaded)
        req.send()

    def loaded(self, ev):
        self.commits = window.JSON.parse(ev.text)


App("#app")


================================================
FILE: examples/github_commits/commits.html
================================================
<div>
  <h1>Latest vue.py Commits</h1>
  <template v-for="branch in branches">
    <input type="radio"
      :id="branch"
      :value="branch"
      name="branch"
      v-model="current_branch">
    <label :for="branch">{{ branch }}</label>
  </template>
  <p>stefanhoelzl/vue.py@{{ current_branch }}</p>
  <ul>
    <li v-for="record in commits">
      <a :href="record.html_url" target="_blank" class="commit">{{ record.sha.slice(0, 7) }}</a>
      - <span class="message">{{ record.commit.message | truncate }}</span><br>
      by <span class="author"><a :href="record.author.html_url" target="_blank">{{ record.commit.author.name }}</a></span>
      at <span class="date">{{ record.commit.author.date | format_date }}</span>
    </li>
  </ul>
</div>


================================================
FILE: examples/github_commits/data.json
================================================
[
  {
    "sha": "0a644f825e780555e62d2e4647786d2a3eb06afc",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOjBhNjQ0ZjgyNWU3ODA1NTVlNjJkMmU0NjQ3Nzg2ZDJhM2ViMDZhZmM=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T20:53:14Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-21T10:45:58Z"
      },
      "message": "[testing] refactored VueComponentFactory out and separated decorators",
      "tree": {
        "sha": "e04efd554a872923b487128493f29046de778387",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/e04efd554a872923b487128493f29046de778387"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/0a644f825e780555e62d2e4647786d2a3eb06afc",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/0a644f825e780555e62d2e4647786d2a3eb06afc",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/0a644f825e780555e62d2e4647786d2a3eb06afc",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/0a644f825e780555e62d2e4647786d2a3eb06afc/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "37fb44c39be49ed40d7dce3d4d5978853e4d82f0",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/37fb44c39be49ed40d7dce3d4d5978853e4d82f0",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/37fb44c39be49ed40d7dce3d4d5978853e4d82f0"
      }
    ]
  },
  {
    "sha": "37fb44c39be49ed40d7dce3d4d5978853e4d82f0",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOjM3ZmI0NGMzOWJlNDllZDQwZDdkY2UzZDRkNTk3ODg1M2U0ZDgyZjA=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T20:06:42Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T20:06:42Z"
      },
      "message": "[refactoring] renamed Vue to VueInstance",
      "tree": {
        "sha": "f98aa2b72ca181035a1f6a988a02dbb6d8b3d530",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/f98aa2b72ca181035a1f6a988a02dbb6d8b3d530"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/37fb44c39be49ed40d7dce3d4d5978853e4d82f0",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/37fb44c39be49ed40d7dce3d4d5978853e4d82f0",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/37fb44c39be49ed40d7dce3d4d5978853e4d82f0",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/37fb44c39be49ed40d7dce3d4d5978853e4d82f0/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "5f907e1325d56ba0736f9702d29a523d8a2d00fb",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/5f907e1325d56ba0736f9702d29a523d8a2d00fb",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/5f907e1325d56ba0736f9702d29a523d8a2d00fb"
      }
    ]
  },
  {
    "sha": "5f907e1325d56ba0736f9702d29a523d8a2d00fb",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOjVmOTA3ZTEzMjVkNTZiYTA3MzZmOTcwMmQyOWE1MjNkOGEyZDAwZmI=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T19:54:39Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T19:54:39Z"
      },
      "message": "[docs] directives docs updated",
      "tree": {
        "sha": "7bf96997338f7352247027ee3dad9af699c9f0cb",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/7bf96997338f7352247027ee3dad9af699c9f0cb"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/5f907e1325d56ba0736f9702d29a523d8a2d00fb",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/5f907e1325d56ba0736f9702d29a523d8a2d00fb",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/5f907e1325d56ba0736f9702d29a523d8a2d00fb",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/5f907e1325d56ba0736f9702d29a523d8a2d00fb/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "94a4affca971fc463dd200affd34e2389aef4c0d",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/94a4affca971fc463dd200affd34e2389aef4c0d",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/94a4affca971fc463dd200affd34e2389aef4c0d"
      }
    ]
  },
  {
    "sha": "94a4affca971fc463dd200affd34e2389aef4c0d",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOjk0YTRhZmZjYTk3MWZjNDYzZGQyMDBhZmZkMzRlMjM4OWFlZjRjMGQ=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T19:41:14Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T19:52:42Z"
      },
      "message": "[feature] full local directives supported",
      "tree": {
        "sha": "0c2fa597175f483f83b87ae6be83d4938906467f",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/0c2fa597175f483f83b87ae6be83d4938906467f"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/94a4affca971fc463dd200affd34e2389aef4c0d",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/94a4affca971fc463dd200affd34e2389aef4c0d",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/94a4affca971fc463dd200affd34e2389aef4c0d",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/94a4affca971fc463dd200affd34e2389aef4c0d/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "9ad2d7a56ca8cad51366bd317cab9591c65e72dc",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/9ad2d7a56ca8cad51366bd317cab9591c65e72dc",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/9ad2d7a56ca8cad51366bd317cab9591c65e72dc"
      }
    ]
  },
  {
    "sha": "9ad2d7a56ca8cad51366bd317cab9591c65e72dc",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOjlhZDJkN2E1NmNhOGNhZDUxMzY2YmQzMTdjYWI5NTkxYzY1ZTcyZGM=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T18:40:26Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T18:40:26Z"
      },
      "message": "[docs] updated informations to py/js bridge",
      "tree": {
        "sha": "a33ad0f207db1f77786792d31dc11a8785562210",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/a33ad0f207db1f77786792d31dc11a8785562210"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/9ad2d7a56ca8cad51366bd317cab9591c65e72dc",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/9ad2d7a56ca8cad51366bd317cab9591c65e72dc",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/9ad2d7a56ca8cad51366bd317cab9591c65e72dc",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/9ad2d7a56ca8cad51366bd317cab9591c65e72dc/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "cd655a5bf24284aa0544e84810f3a5eb298336e7",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/cd655a5bf24284aa0544e84810f3a5eb298336e7",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/cd655a5bf24284aa0544e84810f3a5eb298336e7"
      }
    ]
  },
  {
    "sha": "cd655a5bf24284aa0544e84810f3a5eb298336e7",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOmNkNjU1YTViZjI0Mjg0YWEwNTQ0ZTg0ODEwZjNhNWViMjk4MzM2ZTc=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T18:05:02Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T18:05:02Z"
      },
      "message": "[docs] updated limitations",
      "tree": {
        "sha": "40c98949632915ce909caf5abf651f94bebc6002",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/40c98949632915ce909caf5abf651f94bebc6002"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/cd655a5bf24284aa0544e84810f3a5eb298336e7",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/cd655a5bf24284aa0544e84810f3a5eb298336e7",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/cd655a5bf24284aa0544e84810f3a5eb298336e7",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/cd655a5bf24284aa0544e84810f3a5eb298336e7/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "3f4b7ae5db7c445acade2e5421cc58d508eab755",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/3f4b7ae5db7c445acade2e5421cc58d508eab755",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/3f4b7ae5db7c445acade2e5421cc58d508eab755"
      }
    ]
  },
  {
    "sha": "3f4b7ae5db7c445acade2e5421cc58d508eab755",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOjNmNGI3YWU1ZGI3YzQ0NWFjYWRlMmU1NDIxY2M1OGQ1MDhlYWI3NTU=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T17:46:58Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T17:46:58Z"
      },
      "message": "[examples] separate app and components",
      "tree": {
        "sha": "56d5e522c853a6ff1312ea4c59e030e09b9f005e",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/56d5e522c853a6ff1312ea4c59e030e09b9f005e"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/3f4b7ae5db7c445acade2e5421cc58d508eab755",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/3f4b7ae5db7c445acade2e5421cc58d508eab755",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/3f4b7ae5db7c445acade2e5421cc58d508eab755",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/3f4b7ae5db7c445acade2e5421cc58d508eab755/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "cdd9ea9bfbb1993c6d62370456f8cd3f7a182aae",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/cdd9ea9bfbb1993c6d62370456f8cd3f7a182aae",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/cdd9ea9bfbb1993c6d62370456f8cd3f7a182aae"
      }
    ]
  },
  {
    "sha": "cdd9ea9bfbb1993c6d62370456f8cd3f7a182aae",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOmNkZDllYTliZmJiMTk5M2M2ZDYyMzcwNDU2ZjhjZDNmN2ExODJhYWU=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T17:45:44Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T17:45:44Z"
      },
      "message": "[feature] wrap types for directives and filter methods",
      "tree": {
        "sha": "32d8ffca8bbfc96855e1f1388371f48b9cfac544",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/32d8ffca8bbfc96855e1f1388371f48b9cfac544"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/cdd9ea9bfbb1993c6d62370456f8cd3f7a182aae",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/cdd9ea9bfbb1993c6d62370456f8cd3f7a182aae",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/cdd9ea9bfbb1993c6d62370456f8cd3f7a182aae",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/cdd9ea9bfbb1993c6d62370456f8cd3f7a182aae/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "6ced874bbcc8d6db932b6714f30669bf8c5e5e91",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/6ced874bbcc8d6db932b6714f30669bf8c5e5e91",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/6ced874bbcc8d6db932b6714f30669bf8c5e5e91"
      }
    ]
  },
  {
    "sha": "6ced874bbcc8d6db932b6714f30669bf8c5e5e91",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOjZjZWQ4NzRiYmNjOGQ2ZGI5MzJiNjcxNGYzMDY2OWJmOGM1ZTVlOTE=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T17:31:31Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T17:31:31Z"
      },
      "message": "[refactoring] separate wrapping decorator from inject vue instance decorator",
      "tree": {
        "sha": "4f264bde73993a57ed958ffee2de060083f68ea1",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/4f264bde73993a57ed958ffee2de060083f68ea1"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/6ced874bbcc8d6db932b6714f30669bf8c5e5e91",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/6ced874bbcc8d6db932b6714f30669bf8c5e5e91",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/6ced874bbcc8d6db932b6714f30669bf8c5e5e91",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/6ced874bbcc8d6db932b6714f30669bf8c5e5e91/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "c9f4b8309fa288d82e33b10fe7dfecb7e4cb7e55",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/c9f4b8309fa288d82e33b10fe7dfecb7e4cb7e55",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/c9f4b8309fa288d82e33b10fe7dfecb7e4cb7e55"
      }
    ]
  },
  {
    "sha": "c9f4b8309fa288d82e33b10fe7dfecb7e4cb7e55",
    "node_id": "MDY6Q29tbWl0MTM5NDg4NjkwOmM5ZjRiODMwOWZhMjg4ZDgyZTMzYjEwZmU3ZGZlY2I3ZTRjYjdlNTU=",
    "commit": {
      "author": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T17:19:34Z"
      },
      "committer": {
        "name": "Stefan Hoelzl",
        "email": "stefan.hoelzl@posteo.de",
        "date": "2018-07-20T17:19:34Z"
      },
      "message": "[feature] access dict items also as attributes",
      "tree": {
        "sha": "ec4eb5123a1f44f8a8166671de14697ff7c44437",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/trees/ec4eb5123a1f44f8a8166671de14697ff7c44437"
      },
      "url": "https://api.github.com/repos/stefanhoelzl/vue.py/git/commits/c9f4b8309fa288d82e33b10fe7dfecb7e4cb7e55",
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/c9f4b8309fa288d82e33b10fe7dfecb7e4cb7e55",
    "html_url": "https://github.com/stefanhoelzl/vue.py/commit/c9f4b8309fa288d82e33b10fe7dfecb7e4cb7e55",
    "comments_url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/c9f4b8309fa288d82e33b10fe7dfecb7e4cb7e55/comments",
    "author": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "stefanhoelzl",
      "id": 1478183,
      "node_id": "MDQ6VXNlcjE0NzgxODM=",
      "avatar_url": "https://avatars0.githubusercontent.com/u/1478183?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/stefanhoelzl",
      "html_url": "https://github.com/stefanhoelzl",
      "followers_url": "https://api.github.com/users/stefanhoelzl/followers",
      "following_url": "https://api.github.com/users/stefanhoelzl/following{/other_user}",
      "gists_url": "https://api.github.com/users/stefanhoelzl/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/stefanhoelzl/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/stefanhoelzl/subscriptions",
      "organizations_url": "https://api.github.com/users/stefanhoelzl/orgs",
      "repos_url": "https://api.github.com/users/stefanhoelzl/repos",
      "events_url": "https://api.github.com/users/stefanhoelzl/events{/privacy}",
      "received_events_url": "https://api.github.com/users/stefanhoelzl/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "sha": "f9b50084be79b819be262d2e8a37f68d80a182b2",
        "url": "https://api.github.com/repos/stefanhoelzl/vue.py/commits/f9b50084be79b819be262d2e8a37f68d80a182b2",
        "html_url": "https://github.com/stefanhoelzl/vue.py/commit/f9b50084be79b819be262d2e8a37f68d80a182b2"
      }
    ]
  }
]


================================================
FILE: examples/github_commits/style.css
================================================
#demo {
  font-family: 'Helvetica', Arial, sans-serif;
}
a {
  text-decoration: none;
  color: #f66;
}
li {
  line-height: 1.5em;
  margin-bottom: 20px;
}
.author, .date {
  font-weight: bold;
}


================================================
FILE: examples/github_commits/vuepy.yml
================================================
templates:
  commits: commits.html
stylesheets:
  - style.css


================================================
FILE: examples/grid_component/app.py
================================================
from vue import VueComponent, data, computed, filters


class GridComponent(VueComponent):
    template = "#grid"

    content: list
    columns: list
    filter_key: str

    sort_key = ""

    @data
    def sort_orders(self):
        return {key: False for key in self.columns}

    @computed
    def filtered_data(self):
        return list(
            sorted(
                filter(
                    lambda f: self.filter_key.lower() in f["name"].lower(), self.content
                ),
                reverse=self.sort_orders.get(self.sort_key, False),
                key=lambda c: c.get(self.sort_key, self.columns[0]),
            )
        )

    @staticmethod
    @filters
    def capitalize(value):
        return value.capitalize()

    def sort_by(self, key):
        self.sort_key = key
        self.sort_orders[key] = not self.sort_orders[key]


GridComponent.register("demo-grid")


class App(VueComponent):
    template = "#form"

    search_query = ""
    grid_columns = ["name", "power"]
    grid_data = [
        {"name": "Chuck Norris", "power": float("inf")},
        {"name": "Bruce Lee", "power": 9000},
        {"name": "Jackie Chan", "power": 7000},
        {"name": "Jet Li", "power": 8000},
    ]


App("#app")


================================================
FILE: examples/grid_component/form.html
================================================
<div>
  <form id="search">
    Search <input id="query" name="query" v-model="search_query">
  </form>
  <demo-grid
    :content="grid_data"
    :columns="grid_columns"
    :filter_key="search_query">
  </demo-grid>
</div>


================================================
FILE: examples/grid_component/grid.html
================================================
<table>
  <thead>
    <tr>
      <th v-for="key in columns"
        @click="sort_by(key)"
        :id="key"
        :class="{ active: sort_key == key }">
        {{ key | capitalize }}
        <span class="arrow" :class="sort_orders[key] ? 'asc' : 'dsc'">
        </span>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="entry in filtered_data">
      <td v-for="key in columns">
        {{ entry[key] }}
      </td>
    </tr>
  </tbody>
</table>


================================================
FILE: examples/grid_component/style.css
================================================
body {
  font-family: Helvetica Neue, Arial, sans-serif;
  font-size: 14px;
  color: #444;
}

table {
  border: 2px solid #42b983;
  border-radius: 3px;
  background-color: #fff;
}

th {
  background-color: #42b983;
  color: rgba(255,255,255,0.66);
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

td {
  background-color: #f9f9f9;
}

th, td {
  min-width: 120px;
  padding: 10px 20px;
}

th.active {
  color: #fff;
}

th.active .arrow {
  opacity: 1;
}

.arrow {
  display: inline-block;
  vertical-align: middle;
  width: 0;
  height: 0;
  margin-left: 5px;
  opacity: 0.66;
}

.arrow.asc {
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-bottom: 4px solid #fff;
}

.arrow.dsc {
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 4px solid #fff;
}


================================================
FILE: examples/grid_component/vuepy.yml
================================================
templates:
  form: form.html
  grid: grid.html
stylesheets:
  - style.css


================================================
FILE: examples/index.md
================================================
# Example Gallery

## TodoMVC
[Demo](https://stefanhoelzl.github.io/vue.py/examples/todo_mvc) / [Source](https://github.com/stefanhoelzl/vue.py/tree/master/examples/todo_mvc)

![TodoMVC Screenshot](https://raw.githubusercontent.com/stefanhoelzl/vue.py/gh-pages/examples/todo_mvc/screenshot.png)

## SVG Graph
[Demo](https://stefanhoelzl.github.io/vue.py/examples/svg_graph) / [Source](https://github.com/stefanhoelzl/vue.py/tree/master/examples/svg_graph)

![SVG Graph Screenshot](https://raw.githubusercontent.com/stefanhoelzl/vue.py/gh-pages/examples/svg_graph/screenshot.png)


## Markdown Editor
[Demo](https://stefanhoelzl.github.io/vue.py/examples/markdown_editor) / [Source](https://github.com/stefanhoelzl/vue.py/tree/master/examples/markdown_editor)

![Markdown Editor Screenshot](https://raw.githubusercontent.com/stefanhoelzl/vue.py/gh-pages/examples/markdown_editor/screenshot.png)

## GitHub Commits
[Demo](https://stefanhoelzl.github.io/vue.py/examples/github_commits) / [Source](https://github.com/stefanhoelzl/vue.py/tree/master/examples/github_commits)

![GitHub Commits Screenshot](https://raw.githubusercontent.com/stefanhoelzl/vue.py/gh-pages/examples/github_commits/screenshot.png)

## Grid Component
[Demo](https://stefanhoelzl.github.io/vue.py/examples/grid_component) / [Source](https://github.com/stefanhoelzl/vue.py/tree/master/examples/grid_component)

![Grid Component Screenshot](https://raw.githubusercontent.com/stefanhoelzl/vue.py/gh-pages/examples/grid_component/screenshot.png)

## Tree View
[Demo](https://stefanhoelzl.github.io/vue.py/examples/tree_view) / [Source](https://github.com/stefanhoelzl/vue.py/tree/master/examples/tree_view)

![Tree View Screenshot](https://raw.githubusercontent.com/stefanhoelzl/vue.py/gh-pages/examples/tree_view/screenshot.png)

## Modal Component
[Demo](https://stefanhoelzl.github.io/vue.py/examples/modal_component) / [Source](https://github.com/stefanhoelzl/vue.py/tree/master/examples/modal_component)

![Modal Component Screenshot](https://raw.githubusercontent.com/stefanhoelzl/vue.py/gh-pages/examples/modal_component/screenshot.png)

## Elastic Header
[Demo](https://stefanhoelzl.github.io/vue.py/examples/elastic_header) / [Source](https://github.com/stefanhoelzl/vue.py/tree/master/examples/elastic_header)

![Elastic Header Screenshot](https://raw.githubusercontent.com/stefanhoelzl/vue.py/gh-pages/examples/elastic_header/screenshot.png)


## Run Examples Local

```bash
$ git clone https://github.com/stefanhoelzl/vue.py.git
$ cd vue.py
$ make env.up
$ make run APP=examples/<example-name>
```
Goto [http://localhost:5000](http://localhost:5000)



================================================
FILE: examples/markdown_editor/app.py
================================================
from vue import VueComponent, computed
from vue.utils import js_lib

marked = js_lib("marked")


class App(VueComponent):
    template = "#editor-template"

    input = "# Editor"

    @computed
    def compiled_markdown(self):
        return marked(self.input, {"sanitize": True})

    def update(self, event):
        self.input = event.target.value


App("#app")


================================================
FILE: examples/markdown_editor/editor.html
================================================
<div id="editor">
  <textarea :value="input" @input="update" id="markdown"></textarea>
  <div v-html="compiled_markdown"></div>
</div>


================================================
FILE: examples/markdown_editor/style.css
================================================
html, body, #editor {
  margin: 0;
  height: 100%;
  font-family: 'Helvetica Neue', Arial, sans-serif;
  color: #333;
}

textarea, #editor div {
  display: inline-block;
  width: 49%;
  height: 100%;
  vertical-align: top;
  box-sizing: border-box;
  padding: 0 20px;
}

textarea {
  border: none;
  border-right: 1px solid #ccc;
  resize: none;
  outline: none;
  background-color: #f6f6f6;
  font-size: 14px;
  font-family: 'Monaco', courier, monospace;
  padding: 20px;
}

code {
  color: #f66;
}


================================================
FILE: examples/markdown_editor/vuepy.yml
================================================
templates:
  editor-template: editor.html
stylesheets:
  - style.css
scripts:
  - https://unpkg.com/marked@0.3.6


================================================
FILE: examples/modal_component/app.py
================================================
from vue import VueComponent


class Modal(VueComponent):
    template = "#modal-template"


Modal.register()


class App(VueComponent):
    template = "#main"
    show_modal = False


App("#app")


================================================
FILE: examples/modal_component/main.html
================================================
<div>
  <button id="show-modal" @click="show_modal = true">
    Show Modal
  </button>
  <!-- use the modal component, pass in the prop -->
  <modal v-if="show_modal" @close="show_modal = false" id="modal_view">
    <!--
      you can use custom content here to overwrite
      default content
    -->
    <h3 slot="header">custom header</h3>
  </modal>
</div>


================================================
FILE: examples/modal_component/modal-template.html
================================================
    <transition name="modal">
      <div class="modal-mask">
        <div class="modal-wrapper">
          <div class="modal-container">

            <div class="modal-header">
              <slot name="header">
                default header
              </slot>
            </div>

            <div class="modal-body">
              <slot name="body">
                default body
              </slot>
            </div>

            <div class="modal-footer">
              <slot name="footer">
                default footer
                <button class="modal-default-button" @click="$emit('close')">
                  OK
                </button>
              </slot>
            </div>
          </div>
        </div>
      </div>
    </transition>


================================================
FILE: examples/modal_component/style.css
================================================
.modal-mask {
  position: fixed;
  z-index: 9998;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, .5);
  display: table;
  transition: opacity .3s ease;
}

.modal-wrapper {
  display: table-cell;
  vertical-align: middle;
}

.modal-container {
  width: 300px;
  margin: 0px auto;
  padding: 20px 30px;
  background-color: #fff;
  border-radius: 2px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .33);
  transition: all .3s ease;
  font-family: Helvetica, Arial, sans-serif;
}

.modal-header h3 {
  margin-top: 0;
  color: #42b983;
}

.modal-body {
  margin: 20px 0;
}

.modal-default-button {
  float: right;
}

/*
 * The following styles are auto-applied to elements with
 * transition="modal" when their visibility is toggled
 * by Vue.js.
 *
 * You can easily play with the modal transition by editing
 * these styles.
 */

.modal-enter {
  opacity: 0;
}

.modal-leave-active {
  opacity: 0;
}

.modal-enter .modal-container,
.modal-leave-active .modal-container {
  -webkit-transform: scale(1.1);
  transform: scale(1.1);
}


================================================
FILE: examples/modal_component/vuepy.yml
================================================
templates:
  modal-template: modal-template.html
  main: main.html
stylesheets:
  - style.css


================================================
FILE: examples/svg_graph/app-template.html
================================================
<div>
  <!-- Use the component -->
  <svg width="200" height="200">
    <polygraph :stats="stats"></polygraph>
  </svg>
  <!-- controls -->
  <div v-for="stat in stats">
    <label>{{stat.label}}</label>
    <input type="range" v-model="stat.value" min="0" max="100">
    <span>{{stat.value}}</span>
    <button @click="remove(stat)"
            class="remove"
            :disabled="disable_remove">X</button>
  </div>
  <form id="add">
    <input name="new_label" v-model="new_label">
    <button @click="add">Add a Stat</button>
  </form>
  <pre id="raw">{{ stats }}</pre>
  <p style="font-size:12px">* input[type="range"] requires IE10 or above.</p>
</div>


================================================
FILE: examples/svg_graph/app.py
================================================
import math

from vue import VueComponent, computed


stats = [
    {"label": "A", "value": 100},
    {"label": "B", "value": 100},
    {"label": "C", "value": 100},
    {"label": "D", "value": 100},
    {"label": "E", "value": 100},
    {"label": "F", "value": 100},
]


def value_to_point(value, index, total):
    x = 0
    y = -value * 0.8
    angle = math.pi * 2 / total * index
    cos = math.cos(angle)
    sin = math.sin(angle)
    tx = x * cos - y * sin + 100
    ty = x * sin + y * cos + 100
    return tx, ty


class AxisLabel(VueComponent):
    template = '<text :x="point[0]" :y="point[1]">{{stat.label}}</text>'

    stat: dict
    index: int
    total: int

    @computed
    def point(self):
        return value_to_point(+int(self.stat["value"]) + 10, self.index, self.total)


AxisLabel.register()


class Polygraph(VueComponent):
    template = "#polygraph-template"
    stats: list

    @computed
    def points(self):
        return " ".join(
            map(
                lambda e: ",".join(
                    str(p)
                    for p in value_to_point(int(e[1]["value"]), e[0], len(self.stats))
                ),
                enumerate(self.stats),
            )
        )


Polygraph.register()


class App(VueComponent):
    template = "#app-template"
    new_label = ""
    stats = stats

    @computed
    def disable_remove(self):
        return len(self.stats) <= 3

    def add(self, event):
        event.preventDefault()
        if self.new_label:
            self.stats.append({"label": self.new_label, "value": 100})
            self.new_label = ""

    def remove(self, stat):
        del self.stats[self.stats.index(stat)]


App("#app")


================================================
FILE: examples/svg_graph/polygraph-template.html
================================================
<g>
  <polygon :points="points"></polygon>
  <circle cx="100" cy="100" r="80"></circle>
  <axis-label
    v-for="(stat, index) in stats"
    :key="index"
    :stat="stat"
    :index="index"
    :total="stats.length">
  </axis-label>
</g>


================================================
FILE: examples/svg_graph/style.css
================================================
body {
    font-family: Helvetica Neue, Arial, sans-serif;
}

polygon {
    fill: #42b983;
    opacity: .75;
}

circle {
    fill: transparent;
    stroke: #999;
}

text {
    font-family: Helvetica Neue, Arial, sans-serif;
    font-size: 10px;
    fill: #666;
}

label {
    display: inline-block;
    margin-left: 10px;
    width: 20px;
}

#raw {
    position: absolute;
    top: 0;
    left: 300px;
}


================================================
FILE: examples/svg_graph/vuepy.yml
================================================
templates:
  app-template: app-template.html
  polygraph-template: polygraph-template.html
stylesheets:
  - style.css


================================================
FILE: examples/todo_mvc/app-template.html
================================================
<div>
  <section class="todoapp">
    <header class="header">
      <h1>todos</h1>
      <input class="new-todo"
        autofocus autocomplete="off"
        placeholder="What needs to be done?"
        v-model="new_todo"
        id="title-input"
        @keyup.enter="add_todo">
    </header>
    <section class="main" v-show="todos.length" v-cloak>
      <input class="toggle-all" type="checkbox" v-model="all_done">
      <ul class="todo-list">
        <li v-for="todo in filtered_todos"
          class="todo"
          :key="todo.id"
          :class="{ completed: todo.completed, editing: todo == edited_todo }">
          <div class="view">
            <input class="toggle" type="checkbox" v-model="todo.completed">
            <label @dblclick="edit_todo(todo)">{{ todo.title }}</label>
            <button class="destroy" @click="remove_todo(todo)"></button>
          </div>
          <input class="edit" type="text"
            v-model="todo.title"
            v-todo-focus="todo == edited_todo"
            @blur="done_edit(todo)"
            @keyup.enter="done_edit(todo)"
            @keyup.esc="cancel_edit(todo)">
        </li>
      </ul>
    </section>
    <footer class="footer" v-show="todos.length" v-cloak>
      <span class="todo-count">
        <strong>{{ remaining }}</strong> {{ remaining | pluralize }} left
      </span>
      <ul class="filters">
        <li><a href="#all" :class="{ selected: visibility == 'all' }" id="show-all">All</a></li>
        <li><a href="#active" :class="{ selected: visibility == 'active' }" id="show-active">Active</a></li>
        <li><a href="#completed" :class="{ selected: visibility == 'completed' }" id="show-acompleted">Completed</a></li>
      </ul>
      <button class="clear-completed" @click="remove_completed" v-show="todos.length > remaining">
        Clear completed
      </button>
    </footer>
  </section>
  <footer class="info">
    <p>Double-click to edit a todo</p>
    <p>Written by <a href="https://github.com/stefanhoelzl">Stefan Hoelzl</a></p>
    <p>Original JS verison by <a href="http://evanyou.me">Evan You</a></p>
  </footer>
</div>


================================================
FILE: examples/todo_mvc/app.py
================================================
from browser.local_storage import storage
from browser import window
import json
from vue import VueComponent, computed, filters, watch, directive
from vue.bridge import Object

STORAGE_KEY = "todos-vue.py"


class ToDoStorage:
    NEXT_UID = 0

    @classmethod
    def next_uid(cls):
        uid = cls.NEXT_UID
        cls.NEXT_UID += 1
        return uid

    @classmethod
    def fetch(cls):
        cls.NEXT_UID = 0
        todos = json.loads(storage.get(STORAGE_KEY, "[]"))
        return [{"id": cls.next_uid(), **todo} for todo in todos]

    @staticmethod
    def save(todos):
        storage[STORAGE_KEY] = json.dumps(todos)


class VisibilityFilters:
    def __new__(cls, visibility):
        try:
            return getattr(VisibilityFilters, visibility)
        except AttributeError:
            return False

    @staticmethod
    def all(todos):
        return [todo for todo in todos]

    @staticmethod
    def active(todos):
        return [todo for todo in todos if not todo.get("completed", False)]

    @staticmethod
    def completed(todos):
        return [todo for todo in todos if todo.get("completed", False)]


class App(VueComponent):
    template = "#app-template"
    todos = ToDoStorage.fetch()
    new_todo = ""
    edited_todo = None
    edit_cache = ""
    visibility = "all"

    @watch("todos", deep=True)
    def save_todos(self, new, old):
        ToDoStorage.save(Object.to_py(new))

    @computed
    def filtered_todos(self):
        return VisibilityFilters(self.visibility)(self.todos)

    @computed
    def remaining(self):
        return len(VisibilityFilters.active(self.todos))

    @computed
    def all_done(self):
        return self.remaining == 0

    @all_done.setter
    def all_done(self, value):
        for todo in self.todos:
            todo["completed"] = value

    @staticmethod
    @filters
    def pluralize(n):
        return "item" if n == 1 else "items"

    def add_todo(self, ev=None):
        value = self.new_todo.strip()
        if not value:
            return
        self.todos.append(
            {"id": ToDoStorage.next_uid(), "title": value, "completed": False}
        )
        self.new_todo = ""

    def remove_todo(self, todo):
        del self.todos[self.todos.index(todo)]

    def edit_todo(self, todo):
        self.edit_cache = todo["title"]
        self.edited_todo = todo

    def done_edit(self, todo):
        if not self.edited_todo:
            return

        self.edited_todo = None
        todo["title"] = todo["title"].strip()
        if not todo["title"]:
            self.remove_todo(todo)

    def cancel_edit(self, todo):
        self.edited_todo = None
        todo.title = self.edit_cache

    def remove_completed(self, ev=None):
        self.todos = VisibilityFilters.active(self.todos)

    @staticmethod
    @directive
    def todo_focus(el, binding, vnode, old_vnode, *args):
        if binding.value:
            el.focus()


app = App("#app")


def on_hash_change(ev):
    visibility = window.location.hash.replace("#", "").replace("/", "")
    if VisibilityFilters(visibility):
        app.visibility = visibility
    else:
        window.location.hash = ""
        app.visibility = "all"


window.bind("hashchange", on_hash_change)


================================================
FILE: examples/todo_mvc/style.css
================================================
[v-cloak] { display: none; }


================================================
FILE: examples/todo_mvc/vuepy.yml
================================================
templates:
  app-template: app-template.html
stylesheets:
  - style.css
  - https://unpkg.com/todomvc-app-css@2.0.6/index.css


================================================
FILE: examples/tree_view/app-template.html
================================================
<div>
    <p>(You can double click on an item to turn it into a folder.)</p>

    <!-- the demo root element -->
    <ul id="demo">
        <tree
          class="tree"
          :model="tree_data">
        </tree>
    </ul>
</div>


================================================
FILE: examples/tree_view/app.py
================================================
from vue import VueComponent, computed

demo_data = {
    "name": "My Tree",
    "children": [
        {"name": "hello"},
        {"name": "wat"},
        {
            "name": "child folder",
            "children": [
                {
                    "name": "child folder",
                    "children": [{"name": "hello"}, {"name": "wat"}],
                },
                {"name": "hello"},
                {"name": "wat"},
                {
                    "name": "child folder",
                    "children": [{"name": "hello"}, {"name": "wat"}],
                },
            ],
        },
    ],
}


class Tree(VueComponent):
    template = "#tree-template"
    model: dict
    open = False

    @computed
    def is_folder(self):
        return len(self.model.get("children", ())) > 0

    def toggle(self, ev=None):
        if self.is_folder:
            self.open = not self.open

    def change_type(self, ev=None):
        if not self.is_folder:
            self.model["children"] = []
            self.add_child()
            self.open = True

    def add_child(self, ev=None):
        self.model["children"].append({"name": "new stuff"})


Tree.register()


class App(VueComponent):
    template = "#app-template"
    tree_data = demo_data


App("#app")


================================================
FILE: examples/tree_view/style.css
================================================
body {
  font-family: Menlo, Consolas, monospace;
  color: #444;
}
.tree {
  cursor: pointer;
}
.bold {
  font-weight: bold;
}
ul {
  padding-left: 1em;
  line-height: 1.5em;
  list-style-type: dot;
}


================================================
FILE: examples/tree_view/tree-template.html
================================================
<li>
  <div
    :class="{bold: is_folder}"
    @click="toggle"
    @dblclick="change_type">
    {{ model.name }}
    <span v-if="is_folder">[{{ open ? '-' : '+' }}]</span>
  </div>
  <ul v-show="open" v-if="is_folder">
    <tree
      class="tree"
      v-for="(model, index) in model.children"
      :key="index"
      :model="model">
    </tree>
    <li class="add" @click="add_child">+</li>
  </ul>
</li>


================================================
FILE: examples/tree_view/vuepy.yml
================================================
templates:
  tree-template: tree-template.html
  app-template: app-template.html
stylesheets:
  - style.css


================================================
FILE: pyproject.toml
================================================
[build-system]
requires = [
    "setuptools",
    "requests",
    "python-project-tools@git+https://github.com/stefanhoelzl/python-project-tools.git"
]

[tool.python-project-tools]
start-commit = "f4256454256ddfe54a8be6dea493d3fc915ef1a2"


================================================
FILE: requirements.txt
================================================
# provider
Flask==2.2.2

# packaging
wheel==0.38.4

# testing
pytest==7.2.1
selenium==4.8.0
pyderman==3.3.2
requests==2.28.2

# linting
black==23.1.0

# releasing
semver==2.13.0
git+https://github.com/stefanhoelzl/python-project-tools.git


================================================
FILE: setup.py
================================================
from pathlib import Path
from setuptools import setup

import requests
from tools import release


def make_version():
    version = release.version()
    Path("vue/__version__.py").write_text(f'__version__ = "{version}"\n')
    return version


def fetch_vue_cli_js_file(source: str, name: str) -> str:
    js_data_base = Path(__file__).parent / "vuecli" / "js"
    js_data_base.mkdir(exist_ok=True)

    dest_path = js_data_base / name
    resp = requests.get(source)
    assert resp.ok
    dest_path.write_bytes(resp.content)

    return f"js/{name}"


setup(
    name="vuepy",
    version=make_version(),
    description="Pythonic Vue",
    long_description=Path("README.md").read_text(),
    long_description_content_type="text/markdown",
    classifiers=[
        "Development Status :: 4 - Beta",
        "Environment :: Web Environment",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.6",
        "Operating System :: MacOS :: MacOS X",
        "Operating System :: POSIX :: Linux",
        "Operating System :: Microsoft :: Windows",
        "Topic :: Internet :: WWW/HTTP",
        "Topic :: Software Development",
        "Topic :: Software Development :: Libraries :: Application Frameworks",
    ],
    keywords="web reactive gui framework",
    url="https://stefanhoelzl.github.io/vue.py/",
    author="Stefan Hoelzl",
    author_email="stefan.hoelzl@posteo.de",
    license="MIT",
    packages=["vuecli", "vuecli.provider", "vue", "vue.bridge", "vue.decorators"],
    install_requires=["brython==3.8.9", "Jinja2>=2.10", "pyyaml>=5.1"],
    extras_require={"flask": ["Flask>=1.0"]},
    package_data={
        "vuecli": [
            "index.html",
            "loading.gif",
            fetch_vue_cli_js_file("https://unpkg.com/vue@2.6.14/dist/vue.js", "vue.js"),
            fetch_vue_cli_js_file(
                "https://raw.githubusercontent.com/vuejs/vue/dev/LICENSE", "LICENSE_VUE"
            ),
            fetch_vue_cli_js_file(
                "https://unpkg.com/vuex@3.6.2/dist/vuex.js", "vuex.js"
            ),
            fetch_vue_cli_js_file(
                "https://raw.githubusercontent.com/vuejs/vuex/master/LICENSE",
                "LICENSE_VUEX",
            ),
            fetch_vue_cli_js_file(
                "https://unpkg.com/vue-router@3.5.1/dist/vue-router.js", "vue-router.js"
            ),
            fetch_vue_cli_js_file(
                "https://raw.githubusercontent.com/vuejs/vue-router/dev/LICENSE",
                "LICENSE_VUE_ROUTER",
            ),
        ]
    },
    entry_points={
        "console_scripts": ["vue-cli=vuecli.cli:main"],
        "vuecli.provider": [
            "static=vuecli.provider.static:Static",
            "flask=vuecli.provider.flask:Flask",
        ],
    },
    zip_safe=False,
)


================================================
FILE: stubs/browser.py
================================================
"""stub to avoid import errors"""

import local_storage


def load(path):
    ...


def bind(target, ev):
    ...


class window:
    String = str
    Number = int
    Boolean = bool

    class Object:
        def __init__(self, obj):
            ...

        @staticmethod
        def assign(target, *sources):
            ...

        @staticmethod
        def keys(obj):
            ...

    @staticmethod
    def bind(el, ev):
        ...

    class location:
        hash = ""

    class Array:
        def __init__(self, *objs):
            ...

        @classmethod
        def isArray(cls, obj):
            ...

    class Vuex:
        class Store:
            @classmethod
            def new(cls, *args, **kwargs):
                ...

    class VueRouter:
        @classmethod
        def new(cls, *args, **kwargs):
            ...

    class Vue:
        @classmethod
        def new(cls, *args, **kwargs):
            ...

        @classmethod
        def component(cls, name, opts=None):
            ...

        @classmethod
        def set(cls, obj, key, value):
            ...

        @classmethod
        def delete(cls, obj, key):
            ...

        @classmethod
        def use(cls, plugin, *args, **kwargs):
            ...

        @classmethod
        def directive(cls, name, directive=None):
            ...

        @classmethod
        def filter(cls, name, method):
            ...

        @classmethod
        def mixin(cls, mixin):
            ...


class timer:
    @staticmethod
    def set_interval(fn, interval):
        ...


class ajax:
    class ajax:
        def open(self, method, url, asnc):
            ...

        def bind(self, ev, method):
            ...

        def send(self):
            ...


================================================
FILE: stubs/javascript.py
================================================
"""stub to avoid import errors"""


def this():
    return None


================================================
FILE: stubs/local_storage.py
================================================
storage = dict()


================================================
FILE: tests/__init__.py
================================================


================================================
FILE: tests/cli/test_provider.py
================================================
from xml.etree import ElementTree

import yaml
import pytest

from vuecli.provider.provider import Provider


@pytest.fixture
def render_index(tmp_path):
    def render(config=None):
        tmp_path.joinpath("vuepy.yml").write_text(yaml.dump(config or {}))
        provider = Provider(tmp_path)
        return provider.render_index()

    return render


def parse_index(index):
    et = ElementTree.fromstring(index)
    return {
        "stylesheets": [e.attrib["href"] for e in et.findall("head/link")],
        "scripts": [e.attrib["src"] for e in et.findall("head/script")],
        "templates": {
            e.attrib["id"]: e.text.strip()
            for e in et.findall("body/script[@type='x-template']")
        },
        "brython": et.find("body").attrib["onload"],
    }


class TestRenderIndex:
    def test_defaults(self, render_index):
        index = render_index()
        assert parse_index(index) == {
            "stylesheets": [],
            "scripts": ["vuepy.js", "vue.js"],
            "templates": {},
            "brython": "brython();",
        }

    def test_custom_stylesheets(self, render_index):
        index = render_index({"stylesheets": ["first.css", "second.css"]})
        assert parse_index(index)["stylesheets"] == ["first.css", "second.css"]

    @pytest.mark.parametrize(
        "ext, js", [("vuex", "vuex.js"), ("vue-router", "vue-router.js")]
    )
    def test_enable_builtin_script(self, render_index, ext, js):
        index = render_index({"scripts": {ext: True}})
        assert js in parse_index(index)["scripts"]

    @pytest.mark.parametrize("ext", ["vue", "brython", "vuex", "vue-router"])
    def test_customize_builtin_script(self, render_index, ext):
        index = render_index({"scripts": {ext: "custom"}})
        assert "custom" in parse_index(index)["scripts"]

    def test_custom_script(self, render_index):
        index = render_index({"scripts": ["myscript.js"]})
        assert "myscript.js" in parse_index(index)["scripts"]

    def test_custom_template(self, render_index, tmp_path):
        tmp_path.joinpath("my.html").write_text("content")
        index = render_index({"templates": {"my": "my.html"}})
        assert parse_index(index)["templates"] == {"my": "content"}

    def test_custom_brython_args(self, render_index):
        index = render_index({"brython_args": {"debug": 10}})
        assert parse_index(index)["brython"] == "brython({ debug: 10 });"


================================================
FILE: tests/pytest.ini
================================================
[pytest]
addopts =
    --new-first
    --failed-first
    --capture=no
    --tb=short


================================================
FILE: tests/selenium/.gitignore
================================================
_html
chromedriver


================================================
FILE: tests/selenium/chromedriver.py
================================================
import re
from subprocess import run

import pyderman
import requests

LatestReleaseUrl = (
    "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_{version}"
)

chrome_version_output = (
    run(["google-chrome", "--version"], capture_output=True)
    .stdout.decode("utf-8")
    .strip()
)

print(chrome_version_output)
chrome_major_version = re.search("Google Chrome (\d+)", chrome_version_output).group(1)
chromedriver_version = requests.get(
    LatestReleaseUrl.format(version=chrome_major_version)
).text.strip()
print(f"Chromedriver Version {chromedriver_version}")

pyderman.install(
    browser=pyderman.chrome,
    file_directory="tests/selenium",
    filename="chromedriver",
    overwrite=True,
    version=chromedriver_version,
)


================================================
FILE: tests/selenium/conftest.py
================================================
import re
import os
import json
import inspect
from pathlib import Path
from contextlib import contextmanager
from textwrap import dedent
from threading import Thread
from http.server import HTTPServer, SimpleHTTPRequestHandler
from http.client import HTTPConnection

import yaml
import pytest

from vuecli.provider.static import Static

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.common.exceptions import NoSuchElementException

Address = "localhost"
Port = 8001
BaseUrl = f"http://{Address}:{Port}"

TEST_PATH = Path(__file__).parent
CHROME_DRIVER_PATH = TEST_PATH / "chromedriver"
HTML_OUTPUT_PATH = TEST_PATH / "_html"
APP_URL = BaseUrl + "/{}/{}/deploy"
EXAMPLE_URL = BaseUrl + "/examples_static/{}"
EXAMPLE_SCREENSHOT_PATH = "examples_static/{}/screenshot.png"
DEFAULT_TIMEOUT = 5


@pytest.fixture(scope="session")
def http_server():
    timeout = 10

    class RequestHandler(SimpleHTTPRequestHandler):
        protocol_version = "HTTP/1.0"

        def log_message(self, *args):
            pass

    with HTTPServer((Address, Port), RequestHandler) as httpd:
        thread = Thread(target=httpd.serve_forever, daemon=True)
        thread.start()

        c = HTTPConnection(Address, Port, timeout=timeout)
        c.request("GET", "/", "")
        assert c.getresponse().status == 200
        c.close()

        try:
            yield httpd
        finally:
            httpd.shutdown()
            thread.join(timeout=timeout)


@pytest.fixture(scope="session")
def selenium_session(http_server):
    with SeleniumSession() as session:
        yield session


@pytest.fixture()
def selenium(selenium_session, request):
    selenium_session.request = request
    yield selenium_session
    selenium_session.request = None


class ErrorLogException(Exception):
    def __init__(self, errors):
        formatted_errors = []
        for error in errors:
            formatted_error = {**error}
            formatted_error["message"] = formatted_error["message"].split("\\n")
            formatted_errors.append(formatted_error)
        super().__init__(json.dumps(formatted_errors, indent=2))
        self.errors = errors


class SeleniumSession:
    def __init__(self):
        self.driver = None
        self.request = None

        self.allowed_errors = []
        self.logs = []
        self._screenshot_file = None

    def __getattr__(self, item):
        return getattr(self.driver, item)

    def __enter__(self):
        options = webdriver.ChromeOptions()
        options.add_argument("headless")
        options.add_argument("disable-gpu")
        options.add_argument("disable-dev-shm-usage")
        options.add_argument("no-sandbox")
        options.set_capability("goog:loggingPrefs", {"browser": "ALL"})

        self.driver = webdriver.Chrome(
            service=webdriver.chrome.service.Service(
                executable_path=CHROME_DRIVER_PATH,
            ),
            options=options,
        )
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.driver.close()

    def get_logs(self):
        new_logs = self.driver.get_log("browser")
        self.logs.extend(new_logs)
        return new_logs

    def clear_logs(self):
        self.allowed_errors.clear()
        self.get_logs()
        self.logs.clear()

    @contextmanager
    def url(self, url):
        self.clear_logs()
        self.driver.get(url)
        try:
            yield
        finally:
            self.analyze_logs()

    @contextmanager
    def app(self, app, config=None, files=None):
        test_name = self.request.function.__name__
        self._create_app_content(test_name, app, config or {}, files or {})
        url_base = str(self._app_output_path.relative_to(Path(".").absolute()))
        url = APP_URL.format(url_base, test_name)
        with self.url(url):
            yield

    @property
    def _app_output_path(self):
        sub_path = Path(self.request.node.nodeid.split("::", 1)[0])
        try:
            sub_path = sub_path.relative_to("selenium")
        except ValueError:
            pass  # WORKAROUND when running single tests from PyCharm
        output_path = HTML_OUTPUT_PATH / sub_path
        output_path.mkdir(exist_ok=True, parents=True)
        return output_path

    def _create_app_content(self, test_name, app, config, files):
        path = self._app_output_path / test_name
        path.mkdir(exist_ok=True, parents=True)

        code = "from vue import *\n\n\n"
        code += dedent("\n".join(inspect.getsource(app).split("\n")))
        code += """\n\napp = {}("#app")\n""".format(app.__name__)
        (path / "app.py").write_text(code)

        (path / "vuepy.yml").write_text(yaml.dump(config))

        for filename, content in files.items():
            (path / filename).write_text(content)

        provider = Static(path)
        provider.setup()
        provider.deploy(path / "deploy")

    @contextmanager
    def example(self, hash_=None):
        test_name = self.request.function.__name__
        name = test_name[5:]
        self._screenshot_file = Path(EXAMPLE_SCREENSHOT_PATH.format(name))
        url = EXAMPLE_URL.format(name)

        provider = Static("examples/{}".format(name))
        provider.setup()
        provider.deploy("examples_static/{}".format(name), package=True)

        if hash_:
            url = "{}#{}".format(url, hash_)
        with self.url(url):
            try:
                yield
            finally:
                self.screenshot()

    def screenshot(self):
        if self._screenshot_file:
            self.driver.save_screenshot(str(self._screenshot_file))
        self._screenshot_file = None

    def analyze_logs(self):
        errors = []
        exceptions = [
            r"[^ ]+ \d+"
            r" Synchronous XMLHttpRequest on the main thread is deprecated"
            r" because of its detrimental effects to the end user's experience."
            r" For more help, check https://xhr.spec.whatwg.org/.",
            r"[^ ]+ (\d+|-) {}".format(
                re.escape(
                    "Failed to load resource:"
                    " the server responded with a status of 404 (File not found)"
                )
            ),
        ]
        self.get_logs()
        for log in self.logs:
            if log["level"] != "INFO":
                for exception in exceptions + self.allowed_errors:
                    if re.match(exception, log["message"]):
                        break
                else:
                    if log["source"] not in ["deprecation"]:
                        errors.append(log)
        if errors:
            raise ErrorLogException(errors)

    def element_has_text(self, id_, text, timeout=DEFAULT_TIMEOUT):
        return WebDriverWait(self.driver, timeout).until(
            ec.text_to_be_present_in_element((By.ID, id_), text)
        )

    def element_with_tag_name_has_text(self, tag_name, text, timeout=DEFAULT_TIMEOUT):
        return WebDriverWait(self.driver, timeout).until(
            ec.text_to_be_present_in_element((By.TAG_NAME, tag_name), text)
        )

    def element_present(self, id_, timeout=DEFAULT_TIMEOUT):
        return WebDriverWait(self.driver, timeout).until(
            ec.presence_of_element_located((By.ID, id_))
        )

    def element_with_tag_name_present(self, tag, timeout=DEFAULT_TIMEOUT):
        return WebDriverWait(self.driver, timeout).until(
            ec.presence_of_element_located((By.TAG_NAME, tag))
        )

    def element_not_present(self, id_, timeout=DEFAULT_TIMEOUT):
        def check(driver_):
            try:
                driver_.find_element(by=By.ID, value=id_)
            except NoSuchElementException:
                return True
            return False

        return WebDriverWait(self.driver, timeout).until(check)

    def element_attribute_has_value(
        self, id_, attribute, value, timeout=DEFAULT_TIMEOUT
    ):
        def check(driver_):
            element = driver_.find_element(by=By.ID, value=id_)
            if element.get_attribute(attribute) == value:
                return element
            else:
                return False

        return WebDriverWait(self.driver, timeout).until(check)


================================================
FILE: tests/selenium/pytest.ini
================================================
[pytest]
addopts =
    -s
    # allow debug console display values
    --tb=short
    # do not display standard traceback display of python but a more
    # compact one


================================================
FILE: tests/selenium/test_api.py
================================================
from vue import *


def test_app_with_props_and_data(selenium):
    def app_with_props_data(el):
        class App(VueComponent):
            text: str
            template = """
            <div id="el">{{ text }}</div>
            """

        return App(el, props_data={"text": "TEXT"})

    with selenium.app(app_with_props_data):
        assert selenium.element_has_text("el", "TEXT")


def test_emit_method(selenium):
    def call_emit(el):
        class Emitter(VueComponent):
            template = "<p></p>"

            def created(self):
                self.emit("creation", "YES")

        Emitter.register()

        class App(VueComponent):
            text = "NO"
            template = """
            <div>
                <emitter @creation="change"></emitter>
                <div id='el'>{{ text }}</div>
            </div>
            """

            def change(self, ev=None):
                self.text = ev

        return App(el)

    with selenium.app(call_emit):
        assert selenium.element_has_text("el", "YES")


def test_extend(selenium):
    def extended_component(el):
        class Base(VueComponent):
            template = "<div id='comps'>{{ components_string }}</div>"
            comps = []

            def created(self):
                self.comps.append("BASE")

            @computed
            def components_string(self):
                return " ".join(self.comps)

        class Sub(Base):
            extends = True

            def created(self):
                self.comps.append("SUB")

            @computed
            def components_string(self):
                comps = super().components_string()
                return f"SUB({comps})"

        return Sub(el)

    with selenium.app(extended_component):
        assert selenium.element_has_text("comps", "SUB(BASE SUB)")


def test_extend_from_dict(selenium):
    class Component(VueComponent):
        template = "<div id='done'>{{ done }}</div>"
        done = "NO"
        extends = {"created": lambda: print("CREATED BASE")}

        def created(self):
            print("CREATED SUB")
            self.done = "YES"

    with selenium.app(Component):
        assert selenium.element_has_text("done", "YES")
    assert "CREATED BASE" in selenium.logs[-2]["message"]
    assert "CREATED SUB" in selenium.logs[-1]["message"]


================================================
FILE: tests/selenium/test_examples.py
================================================
import time

from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys


def test_markdown_editor(selenium):
    with selenium.example():
        time.sleep(0.5)
        element = selenium.element_present("markdown")
        element.clear()
        element.send_keys("# Title\n\n")
        element.send_keys("* item one\n")
        element.send_keys("* item two\n")
        element.send_keys("\n")
        element.send_keys("_italic_\n")
        element.send_keys("\n")
        element.send_keys("`some code`\n")
        element.send_keys("\n")
        element.send_keys("**bold**\n")
        element.send_keys("\n")
        element.send_keys("## Sub Title\n")
        element.send_keys("\n")
        selenium.element_has_text("sub-title", "Sub Title")


def test_grid_component(selenium):
    with selenium.example():
        time.sleep(0.5)
        query = selenium.element_present("query")
        query.clear()
        query.send_keys("j")
        power = selenium.element_present("power")
        power.click()
        rows = selenium.driver.find_elements(by=By.TAG_NAME, value="td")
        assert "Jet Li" == rows[0].text
        assert "8000" == rows[1].text
        assert "Jackie Chan" == rows[2].text
        assert "7000" == rows[3].text


def test_tree_view(selenium):
    with selenium.example():
        time.sleep(0.5)
        lis = selenium.find_elements(by=By.TAG_NAME, value="li")
        lis[0].click()
        lis[3].click()
        ActionChains(selenium.driver).double_click(lis[8]).perform()
        assert selenium.find_elements(by=By.TAG_NAME, value="li")[9].text == "new stuff"


def test_svg_graph(selenium):
    with selenium.example():
        time.sleep(0.5)
        selenium.find_elements(by=By.TAG_NAME, value="button")[5].click()

        a = selenium.find_elements(by=By.TAG_NAME, value="input")[0]
        d = selenium.find_elements(by=By.TAG_NAME, value="input")[3]
        ActionChains(selenium.driver).click_and_hold(a).move_by_offset(
            20, 0
        ).release().perform()

        ActionChains(selenium.driver).click_and_hold(d).move_by_offset(
            5, 0
        ).release().perform()

        polygon = selenium.find_elements(by=By.TAG_NAME, value="polygon")[0]
        assert 5 == len(polygon.get_attribute("points").split(" "))


def test_modal_component(selenium):
    with selenium.example():
        time.sleep(1)
        show_button = selenium.element_present("show-modal")
        show_button.click()
        time.sleep(1)
        assert selenium.element_present("modal_view", timeout=2)


def test_todo_mvc(selenium):
    with selenium.example():
        time.sleep(0.5)
        title_input = selenium.element_present("title-input")
        title_input.clear()
        title_input.send_keys("new todo")
        title_input.send_keys(Keys.ENTER)
        title_input.send_keys("completed")
        title_input.send_keys(Keys.ENTER)

        toggle_buttons = selenium.driver.find_elements(by=By.CLASS_NAME, value="toggle")
        assert 2 == len(toggle_buttons)

        toggle_buttons[1].click()
        selenium.element_present("show-active").click()
        labels = selenium.driver.find_elements(by=By.TAG_NAME, value="label")
        assert 1 == len(labels)
        assert "new todo" == labels[0].text

        selenium.element_present("show-all").click()
        labels = selenium.driver.find_elements(by=By.TAG_NAME, value="label")
        assert 2 == len(labels)
        assert "new todo" == labels[0].text
        assert "completed" == labels[1].text


def test_github_commits(selenium):
    with selenium.example(hash_="testing"):
        assert selenium.element_with_tag_name_present("ul")
        time.sleep(2)
        assert 10 == len(selenium.driver.find_elements(by=By.TAG_NAME, value="li"))


def test_elastic_header(selenium):
    with selenium.example():
        assert selenium.element_present("header")
        header = selenium.find_element(by=By.ID, value="header")
        content = selenium.find_element(by=By.ID, value="content")

        assert (
            content.get_attribute("style") == "transform:"
            " translate3d(0px, 0px, 0px);"
        )

        ActionChains(selenium.driver).click_and_hold(header).move_by_offset(
            xoffset=0, yoffset=100
        ).perform()
        selenium.screenshot()
        assert (
            content.get_attribute("style") == "transform:"
            " translate3d(0px, 33px, 0px);"
        )

        ActionChains(selenium.driver).release().perform()
        time.sleep(1)
        assert (
            content.get_attribute("style") == "transform:"
            " translate3d(0px, 0px, 0px);"
        )


================================================
FILE: tests/selenium/test_guide/test_components/test_custom_events.py
================================================
from vue import *

from selenium.webdriver.common.by import By


def test_customize_v_model(selenium):
    def app(el):
        class CustomVModel(VueComponent):
            model = Model(prop="checked", event="change")
            checked: bool
            template = """
            <div>
                <p id="component">{{ checked }}</p>
                <input
                    id="c"
                    type="checkbox"
                    :checked="checked"
                    @change="$emit('change', $event.target.checked)"
                >
            </div>
            """

        CustomVModel.register("custom-vmodel")

        class App(VueComponent):
            clicked = False
            template = """
            <div>
                <p id='instance'>{{ clicked }}</p>
                <custom-vmodel v-model="clicked"></custom-vmodel>
            </div>
            """

        return App(el)

    with selenium.app(app):
        assert selenium.element_has_text("instance", "false")
        assert selenium.element_has_text("component", "false")
        selenium.find_element(by=By.ID, value="c").click()
        assert selenium.element_has_text("component", "true")
        assert selenium.element_has_text("instance", "true")


================================================
FILE: tests/selenium/test_guide/test_components/test_props.py
================================================
import pytest

from vue import *


def test_prop_types(selenium):
    def app(el):
        class SubComponent(VueComponent):
            prop: int
            content = ""
            template = "<div>{{ content }}</div>"

            def created(self):
                assert isinstance(self.prop, int)
                self.content = "text"

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component id="component" :prop="100"></sub-component>
            """

        return App(el)

    with selenium.app(app):
        assert selenium.element_has_text("component", "text")


def test_prop_default(selenium):
    def app(el):
        class SubComponent(VueComponent):
            prop: int = 100
            content = ""
            template = "<div>{{ content }}</div>"

            def created(self):
                assert 100 == self.prop
                self.content = "text"

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component id="component"></sub-component>
            """

        return App(el)

    with selenium.app(app):
        assert selenium.element_has_text("component", "text")


def test_prop_required(selenium):
    def app(el):
        class SubComponent(VueComponent):
            prop: int
            content = ""
            template = "<div>{{ content }}</div>"

            def created(self):
                self.content = "text"

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component id="component">SUB</sub-component>
            """

        return App(el)

    with pytest.raises(Exception) as excinfo:
        with selenium.app(app):
            selenium.element_has_text("component", "text")
    assert "[Vue warn]: Missing required prop:" in excinfo.value.errors[0]["message"]


def test_prop_as_initial_value(selenium):
    def app(el):
        class SubComponent(VueComponent):
            prop: str

            @data
            def cnt(self):
                return self.prop

            template = "<div>{{ cnt }}</div>"

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component id="component" prop="text"></sub-component>
            """

        return App(el)

    with selenium.app(app):
        assert selenium.element_has_text("component", "text")


def test_dont_allow_write_prop(selenium):
    def app(el):
        class SubComponent(VueComponent):
            prop: str

            def created(self):
                self.prop = "HALLO"

            template = "<div>{{ prop }}</div>"

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component id="component" prop="text"></sub-component>
            """

        return App(el)

    with pytest.raises(Exception):
        with selenium.app(app):
            with pytest.raises(TimeoutError):
                selenium.element_has_text("component", "HALLO")


def test_prop_validator(selenium):
    def app(el):
        class SubComponent(VueComponent):
            prop: str

            @validator("prop")
            def is_text(self, value):
                return "text" == value

            template = "<div>{{ prop }}</div>"

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component id="component" prop="not text"></sub-component>
            """

        return App(el)

    with pytest.raises(Exception) as excinfo:
        with selenium.app(app):
            assert selenium.element_has_text("component", "not text")
    assert (
        "[Vue warn]: Invalid prop: custom validator check failed for prop"
        in excinfo.value.errors[0]["message"]
    )


================================================
FILE: tests/selenium/test_guide/test_essentials/test_components_basics.py
================================================
from vue import *

from selenium.webdriver.common.by import By


def test_data_must_be_function(selenium):
    def app(el):
        class ClickCounter(VueComponent):
            count = 0
            template = """
            <button v-on:click="count++">{{ count }}</button>
            """

        ClickCounter.register()

        class App(VueComponent):
            template = """
            <div id="components-demo">
              <click-counter id="btn0"></click-counter>
              <click-counter id="btn1"></click-counter>
            </div>
            """

        return App(el)

    with selenium.app(app):
        assert selenium.element_has_text("btn0", "0")
        assert selenium.element_has_text("btn1", "0")
        selenium.find_element(by=By.ID, value="btn1").click()
        assert selenium.element_has_text("btn0", "0")
        assert selenium.element_has_text("btn1", "1")


def test_register_with_name(selenium):
    def app(el):
        class SubComponent(VueComponent):
            template = """
            <div>TEXT</div>
            """

        SubComponent.register("another-name")

        class App(VueComponent):
            template = """
            <another-name id="component"></another-name>
            """

        return App(el)

    with selenium.app(app):
        assert selenium.element_has_text("component", "TEXT")


def test_passing_data_with_props(selenium):
    def app(el):
        class SubComponent(VueComponent):
            prop: str
            template = """
            <div>{{ prop }}</div>
            """

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component id="component" prop="message"></sub-component>
            """

        return App(el)

    with selenium.app(app):
        assert selenium.element_has_text("component", "message")


def test_emit_event(selenium):
    def app(el):
        class SubComponent(VueComponent):
            template = """
            <button @click="$emit('my-event', 'value')"></button>
            """

        SubComponent.register()

        class App(VueComponent):
            text = ""

            def handler(self, value):
                self.text = value

            template = """
            <div>
                <p id="content">{{ text }}</p>
                <sub-component id="component" @my-event='handler'></sub-component>
            </div>
            """

        return App(el)

    with selenium.app(app):
        assert selenium.element_present("component")
        selenium.find_element(by=By.ID, value="component").click()
        assert selenium.element_has_text("content", "value")


================================================
FILE: tests/selenium/test_guide/test_essentials/test_computed_properties.py
================================================
from vue import *


def test_basics(selenium):
    class ComputedPropertiesBasics(VueComponent):
        message = "message"

        @computed
        def reversed_message(self):
            return self.message[::-1]

        template = """
        <div>
            <p id="original">{{ message }}</p>
            <p id="reversed">{{ reversed_message }}</p>
        </div>
        """

    with selenium.app(ComputedPropertiesBasics):
        assert selenium.element_has_text("reversed", "egassem")


def test_watch(selenium):
    class Watch(VueComponent):
        message = "message"
        new_val = ""

        @watch("message")
        def _message(self, new, old):
            self.new_val = new

        def created(self):
            self.message = "changed"

        template = """
        <div>
            <p id="change">{{ new_val }}</p>
        </div>
        """

    with selenium.app(Watch):
        assert selenium.element_has_text("change", "changed")


def test_computed_setter(selenium):
    class ComputedSetter(VueComponent):
        message = ""

        @computed
        def reversed_message(self):
            return self.message[::-1]

        @reversed_message.setter
        def reversed_message(self, reversed_message):
            self.message = reversed_message[::-1]

        def created(self):
            self.reversed_message = "olleh"

        template = """
        <div>
            <p id="msg">{{ message }}</p>
        </div>
        """

    with selenium.app(ComputedSetter):
        assert selenium.element_has_text("msg", "hello")


================================================
FILE: tests/selenium/test_guide/test_essentials/test_event_handler.py
================================================
from vue import *

from selenium.webdriver.common.by import By


def test_inline_handlers(selenium):
    class InlineHandler(VueComponent):
        message = ""

        def change(self, to):
            self.message = to

        template = """
        <button @click="change('changed')" id="btn">{{ message }}</button>
        """

    with selenium.app(InlineHandler):
        assert selenium.element_has_text("btn", "")
        selenium.find_element(by=By.ID, value="btn").click()
        assert selenium.element_has_text("btn", "changed")


================================================
FILE: tests/selenium/test_guide/test_essentials/test_instance.py
================================================
from vue import *


def test_lifecycle_hooks(selenium):
    def lifecycle_hooks(el):
        class ComponentLifecycleHooks(VueComponent):
            text: str
            template = "<div>{{ text }}</div>"

            def before_create(self):
                print("lh: before_created", self)

            def created(self):
                print("lh: created", self)

            def before_mount(self):
                print("lh: before_mount", self)

            def mounted(self):
                print("lh: mounted", self)

            def before_update(self):
                print("lh: before_update", self)

            def updated(self):
                print("lh: updated", self)

            def before_destroy(self):
                print("lh: before_destroy", self)

            def destroyed(self):
                print("lh: destroyed", self)

        ComponentLifecycleHooks.register("clh")

        class App(VueComponent):
            show = True
            text = "created"

            def mounted(self):
                self.text = "mounted"

            def updated(self):
                self.show = False

            template = (
                "<clh v-if='show' :text='text'></clh>"
                "<div v-else id='after' :text='text'></div>"
            )

        return App(el)

    with selenium.app(lifecycle_hooks):
        selenium.element_present("after")
        logs = list(filter(lambda l: "lh: " in l["message"], selenium.get_logs()))
        for idx, log_message in enumerate(
            [
                "lh: before_create",
                "lh: created",
                "lh: before_mount",
                "lh: mounted",
                "lh: before_update",
                "lh: updated",
                "lh: before_destroy",
                "lh: destroyed",
            ]
        ):
            assert log_message in logs[idx]["message"]


================================================
FILE: tests/selenium/test_guide/test_essentials/test_introduction.py
================================================
from vue import *

from selenium.webdriver.common.by import By


def test_declarative_rendering(selenium):
    class DeclarativeRendering(VueComponent):
        message = "MESSAGE CONTENT"
        template = "<div id='content'>{{ message }}</div>"

    with selenium.app(DeclarativeRendering):
        assert selenium.element_has_text("content", "MESSAGE CONTENT")


def test_bind_element_title(selenium):
    class BindElementTitle(VueComponent):
        title = "TITLE"
        template = "<div id='withtitle' v-bind:title='title'></div>"

    with selenium.app(BindElementTitle):
        assert selenium.element_attribute_has_value("withtitle", "title", "TITLE")


def test_if_condition(selenium):
    class IfCondition(VueComponent):
        show = False
        template = (
            "<div>"
            "    <div id='notpresent' v-if='show'>DONT SHOW</div>"
            "    <div id='present' />"
            "</div>"
        )

    with selenium.app(IfCondition):
        assert selenium.element_present("present")
        assert selenium.element_not_present("notpresent")


def test_for_loop(selenium):
    class ForLoop(VueComponent):
        items = ["0", "1", "2"]
        template = (
            "<ol id='list'>"
            "   <li v-for='item in items' :id='item'>{{ item }}</li>"
            "</ol>"
        )

    with selenium.app(ForLoop):
        for idx in range(3):
            assert selenium.element_has_text(str(idx), str(idx))


def test_on_click_method(selenium):
    class OnClickMethod(VueComponent):
        message = "message"
        template = "<button @click='reverse' id='btn'>{{ message }}</button>"

        def reverse(self, event):
            self.message = "".join(reversed(self.message))

    with selenium.app(OnClickMethod):
        assert selenium.element_has_text("btn", "message")
        selenium.find_element(by=By.ID, value="btn").click()
        assert selenium.element_has_text("btn", "egassem")


def test_v_model(selenium):
    class VModel(VueComponent):
        clicked = False
        template = (
            "<div>"
            "    <p id='p'>{{ clicked }}</p>"
            "    <input type='checkbox' id='c' v-model='clicked'>"
            "</div>"
        )

    with selenium.app(VModel):
        assert selenium.element_has_text("p", "false")
        selenium.find_element(by=By.ID, value="c").click()
        assert selenium.element_has_text("p", "true")


def test_component(selenium):
    def components(el):
        class SubComponent(VueComponent):
            template = """
            <h1 id="header">HEADER</h1>
            """

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component></sub-component>
            """

        return App(el)

    with selenium.app(components):
        assert selenium.element_has_text("header", "HEADER")


def test_component_with_props(selenium):
    def components_with_properties(el):
        class SubComponent(VueComponent):
            text: str
            sub = "SUB"
            template = """
            <div>
            <h1 id="header">{{ text }}</h1>
            <h2 id="sub">{{ sub }}</h2>
            </div>
            """

        SubComponent.register()

        class App(VueComponent):
            template = """
            <sub-component text="TEXT"></sub-component>
            """

        return App(el)

    with selenium.app(components_with_properties):
        assert selenium.element_has_text("header", "TEXT")
        assert selenium.element_has_text("sub", "SUB")


================================================
FILE: tests/selenium/test_guide/test_essentials/test_list_rendering.py
================================================
from vue import *


def test_mutation_methods(selenium):
    class MutationMethods(VueComponent):
        array = [1, 2, 3]

        template = "<div id='done' />"

        def created(self):
            print(self.array)  # 1,2,3
            print(self.array.pop())  # 3
            print(self.array)  # 1,2
            self.array.append(4)
            print(self.array)  # 1,2,4
            print(self.array.pop(0))  # 1
            print(self.array)  # 2,4
            self.array[0:0] = [6, 4]
            print(self.array)  # 6,4,2,4
            self.array.insert(2, 8)
            print(self.array)  # 6,4,8,2,4
            del self.array[3]
            print(self.array)  # 6,4,8,4
            self.array.sort(key=lambda a: 0 - a)
            print(self.array)  # 8,6,4,4
            self.array.reverse()
            print(self.array)  # 4,4,6,8

    with selenium.app(MutationMethods):
        selenium.element_present("done")

        logs = [
            l["message"].split(" ", 2)[-1][:-3][1:] for l in selenium.get_logs()[-11:]
        ]
        assert logs == [
            "[1, 2, 3]",
            "3",
            "[1, 2]",
            "[1, 2, 4]",
            "1",
            "[2, 4]",
            "[6, 4, 2, 4]",
            "[6, 4, 8, 2, 4]",
            "[6, 4, 8, 4]",
            "[8, 6, 4, 4]",
            "[4, 4, 6, 8]",
        ]


================================================
FILE: tests/selenium/test_guide/test_reusability_composition/test_filters.py
================================================
from vue import *


def test_local_filter(selenium):
    class ComponentWithFilter(VueComponent):
        message = "Message"

        @staticmethod
        @filters
        def lower_case(value):
            return value.lower()

        template = "<div id='content'>{{ message | lower_case }}</div>"

    with selenium.app(ComponentWithFilter):
        assert selenium.element_has_text("content", "message")


def test_global_filter(selenium):
    def app(el):
        Vue.filter("lower_case", lambda v: v.lower())

        class ComponentUsesGlobalFilter(VueComponent):
            message = "Message"
            template = "<div id='content'>{{ message | lower_case }}</div>"

        return ComponentUsesGlobalFilter(el)

    with selenium.app(app):
        assert selenium.element_has_text("content", "message")


================================================
FILE: tests/selenium/test_guide/test_reusability_composition/test_mixins.py
================================================
from vue import *


def test_local_mixin(selenium):
    def app(el):
        class MyMixin(VueMixin):
            def created(self):
                print("created")

            @staticmethod
            @filters
            def lower_case(value):
                return value.lower()

        class ComponentUsesGlobalFilter(VueComponent):
            message = "Message"
            mixins = [MyMixin]
            template = "<div id='content'>{{ message | lower_case }}</div>"

        return ComponentUsesGlobalFilter(el)

    with selenium.app(app):
        assert selenium.element_has_text("content", "message")
    logs = list(filter(lambda l: "created" in l["message"], selenium.logs))
    assert 1 == len(logs)


================================================
FILE: tests/selenium/test_guide/test_reusability_composition/test_render_function.py
================================================
from vue import *

from selenium.webdriver.common.by import By


def test_basics(selenium):
    def app(el):
        class ComponentWithRenderFunction(VueComponent):
            level = 3

            def render(self, create_element):
                return create_element(f"h{self.level}", "Title")

        return ComponentWithRenderFunction(el)

    with selenium.app(app):
        assert selenium.element_with_tag_name_has_text("h3", "Title")


def test_slots(selenium):
    def app(el):
        class WithSlots(VueComponent):
            def render(self, create_element):
                return create_element(f"p", self.slots.get("default"))

        WithSlots.register()

        class Component(VueComponent):
            template = "<with-slots><p></p><p></p></with-slots>"

        return Component(el)

    with selenium.app(app):
        div = selenium.element_with_tag_name_present("p")
        assert len(div.find_elements(by=By.TAG_NAME, value="p")) == 2


def test_empty_slots(selenium):
    def app(el):
        class WithSlots(VueComponent):
            def render(self, create_element):
                return create_element(f"div", self.slots.get("default"))

        WithSlots.register()

        class Component(VueComponent):
            template = "<with-slots />"

        return Component(el)

    with selenium.app(app):
        pass


def test_props(selenium):
    def app(el):
        class ComponentWithProps(VueComponent):
            prop: str = "p"
            template = "<div :id='prop'></div>"

        ComponentWithProps.register()

        class ComponentRendersWithAttrs(VueComponent):
            def render(self, create_element):
                return create_element("ComponentWithProps", {"props": {"prop": "p"}})

        return ComponentRendersWithAttrs(el)

    with selenium.app(app):
        assert selenium.element_present("p")


================================================
FILE: tests/selenium/test_vuerouter.py
================================================
from selenium.webdriver.common.by import By

VueRouterConfig = {"scripts": {"vue-router": True}}


def test_routes(selenium):
    def app(el):
        from vue import VueComponent, VueRouter, VueRoute

        class Foo(VueComponent):
            template = '<div id="content">foo</div>'

        class Bar(VueComponent):
            text = "bar"
            template = '<div id="content">{{ text }}</div>'

        class Router(VueRouter):
            routes = [VueRoute("/foo", Foo), VueRoute("/bar", Bar)]

        class ComponentUsingRouter(VueComponent):
            template = """
                <div>
                    <p>
                        <router-link to="/foo" id="foo">Go to Foo</router-link>
                        <router-link to="/bar" id="bar">Go to Bar</router-link>
                    </p>
                    <router-view></router-view>
                </div>
            """

        return ComponentUsingRouter(el, router=Router())

    with selenium.app(app, config=VueRouterConfig):
        assert selenium.element_present("foo")
        selenium.find_element(by=By.ID, value="foo").click()
        assert selenium.element_has_text("content", "foo")

        assert selenium.element_present("bar")
        selenium.find_element(by=By.ID, value="bar").click()
        assert selenium.element_has_text("content", "bar")


def test_dynamic_route_matching(selenium):
    def app(el):
        from vue import VueComponent, VueRouter, VueRoute

        class User(VueComponent):
            template = '<div id="user">{{ $route.params.id }}</div>'

        class Router(VueRouter):
            routes = [VueRoute("/user/:id", User)]

        class ComponentUsingRouter(VueComponent):
            template = """
                <div>
                    <p>
                        <router-link to="/user/123" id="link">User</router-link>
                    </p>
                    <router-view></router-view>
                </div>
            """

        return ComponentUsingRouter(el, router=Router())

    with selenium.app(app, config=VueRouterConfig):
        assert selenium.element_present("link")
        selenium.find_element(by=By.ID, value="link").click()
        assert selenium.element_has_text("user", "123")


def test_named_routes(selenium):
    def app(el):
        from vue import VueComponent, VueRouter, VueRoute

        class FooTop(VueComponent):
            template = '<div id="header">foo top</div>'

        class FooBottom(VueComponent):
            template = '<div id="body">foo bottom</div>'

        class BarTop(VueComponent):
            template = '<div id="header">bar top</div>'

        class BarBottom(VueComponent):
            template = '<div id="body">bar bottom</div>'

        class Router(VueRouter):
            routes = [
                VueRoute("/foo", components={"default": FooBottom, "top": FooTop}),
                VueRoute("/bar", components={"default": BarBottom, "top": BarTop}),
            ]

        class ComponentUsingRouter(VueComponent):
            template = """
                <div>
                    <p>
                        <router-link to="/foo" id="foo">Go to Foo</router-link>
                        <router-link to="/bar" id="bar">Go to Bar</router-link>
                    </p>
                    <router-view name="top"></router-view>
                    <hr>
                    <router-view></router-view>
                </div>
            """

        return ComponentUsingRouter(el, router=Router())

    with selenium.app(app, config=VueRouterConfig):
        assert selenium.element_present("foo")
        selenium.find_element(by=By.ID, value="foo").click()
        assert selenium.element_has_text("header", "foo top")
        assert selenium.element_has_text("body", "foo bottom")

        assert selenium.element_present("bar")
        selenium.find_element(by=By.ID, value="bar").click()
        assert selenium.element_has_text("header", "bar top")
        assert selenium.element_has_text("body", "bar bottom")


def test_nested_routes_and_redirect(selenium):
    def app(el):
        from vue import VueComponent, VueRouter, VueRoute

        class UserHome(VueComponent):
            template = '<div id="home">Home</div>'

        class UserProfile(VueComponent):
            template = '<div id="profile">Profile</div>'

        class UserPosts(VueComponent):
            template = '<div id="posts">Posts</div>'

        class ComponentUsingRouter(VueComponent):
            template = """
                <div>
                    <p>
                        <router-link to="/user/foo" id="link-home">/user/foo</router-link>
                        <router-link to="/user/foo/profile" id="link-profile">/user/foo/profile</router-link>
                        <router-link to="/user/foo/posts" id="link-posts">/user/foo/posts</router-link>
                    </p>
                    <h2>User {{ $route.params.id }}</h2>
                    <router-view></router-view>
                </div>
            """

        class Router(VueRouter):
            routes = [
                VueRoute("/", redirect="/user/foo"),
                VueRoute(
                    "/user/:id",
                    ComponentUsingRouter,
                    children=[
                        VueRoute("", UserHome),
                        VueRoute("profile", UserProfile),
                        VueRoute("posts", UserPosts),
                    ],
                ),
            ]

        return ComponentUsingRouter(el, router=Router())

    with selenium.app(app, config=VueRouterConfig):
        assert selenium.element_present("link-home")
        selenium.find_element(by=By.ID, value="link-home").click()
        assert selenium.element_has_text("home", "Home")

        assert selenium.element_present("link-profile")
        selenium.find_element(by=By.ID, value="link-profile").click()
        assert selenium.element_has_text("profile", "Profile")

        assert selenium.element_present("link-posts")
        selenium.find_element(by=By.ID, value="link-posts").click()
        assert selenium.element_has_text("posts", "Posts")


================================================
FILE: tests/selenium/test_vuex.py
================================================
from vue import *

VuexConfig = {"scripts": {"vuex": True}}


def test_state(selenium):
    def app(el):
        class Store(VueStore):
            message = "Message"

        class ComponentUsingStore(VueComponent):
            @computed
            def message(self):
                return self.store.message

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingStore(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message")


def test_mutation_noargs(selenium):
    def app(el):
        class Store(VueStore):
            message = ""

            @mutation
            def mutate_message(self):
                self.message = "Message"

        class ComponentUsingMutation(VueComponent):
            @computed
            def message(self):
                self.store.commit("mutate_message")
                return self.store.message

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingMutation(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message")


def test_mutation(selenium):
    def app(el):
        class Store(VueStore):
            message = ""

            @mutation
            def mutate_message(self, new_message):
                self.message = new_message

        class ComponentUsingMutation(VueComponent):
            @computed
            def message(self):
                self.store.commit("mutate_message", "Message")
                return self.store.message

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingMutation(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message")


def test_mutation_kwargs(selenium):
    def app(el):
        class Store(VueStore):
            message = ""

            @mutation
            def mutate_message(self, new_message, postfix=""):
                self.message = new_message + postfix

        class ComponentUsingMutation(VueComponent):
            @computed
            def message(self):
                self.store.commit("mutate_message", "Message", postfix="!")
                return self.store.message

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingMutation(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message!")


def test_action(selenium):
    def app(el):
        class Store(VueStore):
            message = ""

            @mutation
            def mutate_message(self, new_message):
                self.message = new_message

            @action
            def change_message(self, new_message):
                self.commit("mutate_message", new_message)

        class ComponentUsingAction(VueComponent):
            def created(self):
                self.store.dispatch("change_message", "Message")

            @computed
            def message(self):
                return self.store.message

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingAction(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message")


def test_action_noargs(selenium):
    def app(el):
        class Store(VueStore):
            message = ""

            @mutation
            def mutate_message(self, new_message):
                self.message = new_message

            @action
            def change_message(self):
                self.commit("mutate_message", "Message")

        class ComponentUsingAction(VueComponent):
            def created(self):
                self.store.dispatch("change_message")

            @computed
            def message(self):
                return self.store.message

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingAction(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message")


def test_action_kwargs(selenium):
    def app(el):
        class Store(VueStore):
            message = ""

            @mutation
            def mutate_message(self, new_message):
                self.message = new_message

            @action
            def change_message(self, new_message, postfix=""):
                self.commit("mutate_message", new_message + postfix)

        class ComponentUsingAction(VueComponent):
            def created(self):
                self.store.dispatch("change_message", "Message", postfix="!")

            @computed
            def message(self):
                return self.store.message

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingAction(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message!")


def test_getter_noargs(selenium):
    def app(el):
        class Store(VueStore):
            message = "Message"

            @getter
            def msg(self):
                return self.message

        class ComponentUsingGetter(VueComponent):
            @computed
            def message(self):
                return self.store.msg

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingGetter(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message")


def test_getter_method(selenium):
    def app(el):
        class Store(VueStore):
            message = "Message"

            @getter
            def msg(self, prefix):
                return prefix + self.message

        class ComponentUsingGetter(VueComponent):
            @computed
            def message(self):
                return self.store.msg("pre")

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingGetter(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "preMessage")


def test_getter_kwargs(selenium):
    def app(el):
        class Store(VueStore):
            message = "Message"

            @getter
            def msg(self, prefix, postfix):
                return prefix + self.message + postfix

        class ComponentUsingGetter(VueComponent):
            @computed
            def message(self):
                return self.store.msg("pre", "!")

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingGetter(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "preMessage!")


def test_plugin(selenium):
    def app(el):
        class Plugin(VueStorePlugin):
            def initialize(self, store):
                store.message = "Message"

            def subscribe(self, state, mut, *args, **kwargs):
                print(state.message, mut, args, kwargs)

        class Store(VueStore):
            plugins = [Plugin().install]
            message = ""

            @mutation
            def msg(self, prefix, postfix=""):
                pass

        class ComponentUsingGetter(VueComponent):
            @computed
            def message(self):
                return self.store.message

            def created(self):
                self.store.commit("msg", "Hallo", postfix="!")

            template = "<div id='content'>{{ message }}</div>"

        return ComponentUsingGetter(el, store=Store())

    with selenium.app(app, config=VuexConfig):
        assert selenium.element_has_text("content", "Message")
        last_log_message = selenium.get_logs()[-1]["message"]
        expected_msg = "Message msg ('Hallo',) {'postfix': '!'}"
        assert expected_msg in last_log_message


def test_using_state_within_native_vue_component(selenium):
    def app(el):
        class Store(VueStore):
            message = "Message"

        class ComponentUsingNativeComponent(VueComponent):
            template = "<native />"

        return ComponentUsingNativeComponent(el, store=Store())

    config = {"scripts": {"vuex": True, "my": "my.js"}}
    myjs = """
        Vue.component('native', {
          template: '<div id="content">{{ message }}</div>',
          computed: {
            message () {
              return this.$store.state.message
            }
          }
        });
    """
    with selenium.app(app, config=config, files={"my.js": myjs}):
        assert selenium.element_has_text("content", "Message")


================================================
FILE: tests/test_install.py
================================================
import json
import subprocess
import urllib.request
import time
from contextlib import contextmanager

import pytest

from tools.release import version


def _raise_failed_process(proc, error_msg):
    stdout = proc.stdout if isinstance(proc.stdout, bytes) else proc.stdout.read()
    stderr = proc.stderr if isinstance(proc.stderr, bytes) else proc.stderr.read()
    print(f"return-code: {proc.returncode}")
    print("stdout")
    print(stdout.decode("utf-8"))
    print("stderr")
    print(stderr.decode("utf-8"))
    raise RuntimeError(error_msg)


def shell(*args, env=None, cwd=None):
    proc = subprocess.run(args, env=env, cwd=cwd, capture_output=True)
    if proc.returncode:
        _raise_failed_process(proc, str(args))


@pytest.fixture
def wheel(scope="session"):
    shell("make", "build")
    return f"dist/vuepy-{version()}-py3-none-any.whl"


@pytest.fixture
def venv(tmp_path):
    path = tmp_path / "venv"
    shell("python", "-m", "venv", str(path))
    return path


@pytest.fixture
def install(wheel, venv):
    def _install(extra=None):
        extra = f"[{extra}]" if extra else ""
        shell(
            "pip",
            "install",
            f"{wheel}{extra}",
            env={"PATH": str(venv / "bin"), "PIP_USER": "no"},
        )

    return _install


@contextmanager
def background_task(*args, env=None, cwd=None):
    proc = subprocess.Popen(
        args, env=env, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
    try:
        yield
    finally:
        if proc.poll() is not None:
            _raise_failed_process(proc, "background task finished early")
        proc.kill()
        proc.communicate()


def request(url, retries=0, retry_delay=0):
    for retry in range(1 + retries):
        try:
            with urllib.request.urlopen(url) as response:
                return response
        except urllib.request.URLError:
            if retry >= retries:
                raise
        time.sleep(retry_delay)


@pytest.fixture
def app(tmp_path):
    app_path = tmp_path / "app"
    app_path.mkdir()
    return app_path


@pytest.fixture
def config(app):
    def _config(values):
        app.joinpath("vuepy.yml").write_text(json.dumps(values, indent=2))

    return _config


def test_static(install, venv, tmp_path, app):
    destination = tmp_path / "destination"
    install()
    shell(
        "vue-cli",
        "deploy",
        "static",
        str(destination),
        env={"PATH": f"{venv / 'bin'}"},
        cwd=str(app),
    )
    assert (destination / "index.html").is_file()


def test_flask(install, venv, app):
    install(extra="flask")
    with background_task(
        "vue-cli", "deploy", "flask", env={"PATH": f"{venv / 'bin'}"}, cwd=str(app)
    ):
        assert (
            request("http://localhost:5000", retries=5, retry_delay=0.5).status == 200
        )


def test_flask_settings(install, config, venv, app):
    install(extra="flask")
    config({"provider": {"flask": {"PORT": 5001}}})
    with background_task(
        "vue-cli", "deploy", "flask", env={"PATH": f"{venv / 'bin'}"}, cwd=str(app)
    ):
        assert (
            request("http://localhost:5001", retries=5, retry_delay=0.5).status == 200
        )


================================================
FILE: tests/unit/test_bridge/__init__.py
================================================


================================================
FILE: tests/unit/test_bridge/mocks.py
================================================
class VueMock:
    @staticmethod
    def set(obj, key, value):
        setattr(obj, key, value)

    @staticmethod
    def delete(obj, key):
        delattr(obj, key)


class ObjectMock:
    def __new__(cls, arg):
        return arg

    @staticmethod
    def assign(target, *sources):
        for source in sources:
            target.attributes.update(source)
        return target

    @staticmethod
    def keys(obj):
        return [k for k in obj]


class ArrayMock:
    def __init__(self, *items):
        self._data = list(items)

    def __getitem__(self, item):
        return self._data[item]

    def __setitem__(self, key, value):
        self._data[key] = value

    @property
    def length(self):
        return len(self._data)

    def push(self, *items):
        self._data.extend(items)

    def splice(self, index, delete_count=None, *items):
        delete_count = (
            delete_count if delete_count is not None else len(self._data) - index
        )
        index = index if index >= 0 else len(self._data) + index
        deleted = self._data[index : index + delete_count]
        self._data = (
            self._data[0:index] + list(items) + self._data[index + delete_count :]
        )
        return deleted

    def slice(self, index, stop=None):
        return self._data[index:stop]

    def indexOf(self, obj, start=0):
        try:
            return self._data.index(obj, start)
        except ValueError:
            return -1

    def reverse(self):
        self._data.reverse()
        return self._data


================================================
FILE: tests/unit/test_bridge/test_dict.py
================================================
from unittest import mock

import pytest

from tests.unit.test_bridge.mocks import ObjectMock, VueMock

from vue.bridge.dict import window, Dict


@pytest.fixture(scope="module", autouse=True)
def window_object():
    with mock.patch.object(window, "Object", new=ObjectMock), mock.patch.object(
        window, "Vue", new=VueMock
    ):
        yield


class JsObjectMock:
    def __init__(self, attribtes):
        self.attributes = attribtes

    def __getattr__(self, item):
        return self.attributes[item]

    def __setattr__(self, key, value):
        if key == "attributes":
            super().__setattr__(key, value)
        else:
            self.attributes[key] = value

    def __delattr__(self, item):
        del self.attributes[item]

    def __iter__(self):
        return iter(self.attributes)


def make_dict(dct):
    return Dict(JsObjectMock(dct))


class TestDict:
    def test_getitem(self):
        assert "value" == make_dict({"key": "value"})["key"]

    def test_items(self):
        assert (("a", 1), ("b", 2)) == make_dict({"a": 1, "b": 2}).items()

    def test_eq(self):
        assert {"a": 1} != make_dict({"a": 2})
        assert {"a": 0, "b": 1} == make_dict({"a": 0, "b": 1})

    def test_keys(self):
        assert ("a", "b") == make_dict({"a": 0, "b": 1}).keys()

    def test_iter(self):
        assert ["a", "b"] == list(iter(make_dict({"a": 0, "b": 1})))

    def test_setitem(self):
        d = make_dict({})
        d["a"] = 1
        assert 1 == d["a"]

    def test_contains(self):
        assert "a" in make_dict({"a": 0, "b": 1})

    def test_setdefault(self):
        d = make_dict({})
        assert 1 == d.setdefault("a", 1)
        assert 1 == d.setdefault("a", 2)

    def test_len(self):
        assert 2 == len(make_dict({"a": 0, "b": 1}))

    def test_get(self):
        assert 1 == make_dict({"a": 0, "b": 1}).get("b", "default")
        assert "default" == make_dict({"a": 0, "b": 1}).get("c", "default")

    def test_values(self):
        assert (0, 1) == make_dict({"a": 0, "b": 1}).values()

    def test_repr(self):
        assert str({"a": 0, "b": 1}) == str(make_dict({"a": 0, "b": 1}))

    def test_update(self):
        d = make_dict({"a": 0, "b": 1})
        d.update(a=2)
        assert {"a": 2, "b": 1} == d
        d.update(c=0)
        assert {"a": 2, "b": 1, "c": 0} == d
        d.update({"c": 3, "d": 0})
        assert {"a": 2, "b": 1, "c": 3, "d": 0} == d

    def test_bool(self):
        assert not make_dict({})
        assert make_dict({"a": 0})

    def test_delitem(self):
        d = make_dict({"a": 0, "b": 1})
        del d["a"]
        assert {"b": 1} == d

    def test_pop(self):
        d = make_dict({"a": 0, "b": 1})
        assert 1 == d.pop("b")
        assert {"a": 0} == d

    def test_pop_default(self):
        d = make_dict({"a": 0, "b": 1})
        assert "default" == d.pop("c", "default")
        assert {"a": 0, "b": 1} == d

    def test_pop_key_error(self):
        d = make_dict({"a": 0, "b": 1})
        with pytest.raises(KeyError):
            d.pop("c")

    def test_popitem(self):
        d = make_dict({"a": 2})
        assert ("a", 2) == d.popitem()
        assert {} == d

    def test_clear(self):
        d = make_dict({"a": 0, "b": 1})
        d.clear()
        assert not d

    def test_set(self):
        d = make_dict({"a": 0, "b": 1})
        old_id = id(d)
        d.__set__({"c": 1, "d": 2})
        assert old_id == id(d)
        assert {"c": 1, "d": 2} == d

    def test_getattr(self):
        d = make_dict({"a": 0, "b": 1})
        assert 1 == d.b

    def test_setattr(self):
        d = make_dict({"a": 1})
        d.a = 2
        assert {"a": 2} == d

    def test_str_toString(self):
        d = make_dict({})
        d.toString = lambda: "STRING"
        assert "STRING" == str(d)


================================================
FILE: tests/unit/test_bridge/test_jsobject.py
================================================
from unittest import mock

from browser import window

from vue.bridge import Object
from vue.bridge.vue_instance import VueInstance

from .mocks import ArrayMock


class TestJSObjectWrapper:
    def test_vue(self):
        class This:
            def _isVue(self):
                return True

        assert isinstance(Object.from_js(This()), VueInstance)

    def test_array(self):
        with mock.patch.object(window.Array, "isArray", return_value=True):
            obj = Object.from_js(ArrayMock(1, 2, 3))
        assert [1, 2, 3] == obj

    def test_dict(self):
        assert {"a": 1, "b": 2} == Object.from_js({"a": 1, "b": 2})


================================================
FILE: tests/unit/test_bridge/test_list.py
================================================
import pytest

from vue.bridge.list import List
from .mocks import ArrayMock


class TestList:
    def test_len(self):
        assert 3 == len(List(ArrayMock(1, 2, 3)))

    def test_getitem(self):
        assert 3 == List(ArrayMock(1, 2, 3))[2]
        assert [2, 3] == List(ArrayMock(1, 2, 3, 4))[1:3]
        assert 3 == List(ArrayMock(1, 2, 3))[-1]
        assert [2] == List(ArrayMock(1, 2, 3))[-2:-1]

    def test_delitem(self):
        l = List(ArrayMock(1, 2, 3))
        del l[1]
        assert [1, 3] == l

    def test_delitem_range(self):
        l = List(ArrayMock(1, 2, 3, 4))
        del l[1:3]
        assert [1, 4] == l

    def test_setitem(self):
        l = List(ArrayMock(1, 2, 3))
        l[1] = 5
        assert [1, 5, 3] == l

    def test_setitem_range(self):
        l = List(ArrayMock(1, 2, 3))
        l[:] = [5]
        assert [5] == l

    def test_setitem_negative(self):
        l = List(ArrayMock(1, 2, 3, 4))
        l[-3:-1] = [8, 9]
        assert [1, 8, 9, 4] == l

    def test_iter(self):
        assert [1, 2, 3] == [i for i in List(ArrayMock(1, 2, 3))]

    def test_eq(self):
        assert [1, 2, 3] == List(ArrayMock(1, 2, 3))

    def test_mul(self):
        assert [1, 2, 1, 2, 1, 2] == List(ArrayMock(1, 2)) * 3

    def test_index(self):
        assert 3 == List(ArrayMock(1, 2, 3, 4)).index(4)

    def test_index_start(self):
        assert 4 == List(ArrayMock(4, 1, 2, 3, 4)).index(4, start=1)

    def test_index_not_in_list(self):
        with pytest.raises(ValueError):
            List(ArrayMock(1, 2, 3)).index(4)

    def test_extend(self):
        l = List(ArrayMock(1, 2))
        l.extend([3, 4])
        assert [1, 2, 3, 4] == l

    def test_contains(self):
        assert 3 in List(ArrayMock(1, 2, 3))

    def test_count(self):
        assert 2 == List(ArrayMock(1, 2, 1)).count(1)

    def test_repr(self):
        assert "[1, 2, 3]" == repr(List(ArrayMock(1, 2, 3)))

    def test_str(self):
        assert "[1, 2, 3]" == str(List(ArrayMock(1, 2, 3)))

    def test_append(self):
        l = List(ArrayMock(1, 2))
        l.append(3)
        assert [1, 2, 3] == l

    def test_insert(self):
        l = List(ArrayMock(1, 3))
        l.insert(1, 2)
        assert [1, 2, 3] == l

    def test_remove(self):
        l = List(ArrayMock(1, 2, 1, 3))
        l.remove(1)
        assert [2, 3] == l

    def test_pop(self):
        l = List(ArrayMock(1, 2, 3))
        assert 3 == l.pop()

    def test_sort(self):
        l = List(ArrayMock(4, 3, 6, 1))
        l.sort()
        assert [1, 3, 4, 6] == l

    def test_reverse(self):
        l = List(ArrayMock(4, 3, 6, 1))
        l.reverse()
        assert [1, 6, 3, 4] == l

    def test_set(self):
        l = List(ArrayMock(4, 3, 6, 1))
        l.__set__([1, 2, 3, 4])
        assert [1, 2, 3, 4] == l


================================================
FILE: tests/unit/test_bridge/test_vue.py
================================================
from unittest import mock
import pytest
from .mocks import ArrayMock
from vue.bridge.vue_instance import VueInstance
from browser import window


class TestVue:
    def test_getattr(self):
        class This:
            def __init__(self):
                self.attribute = "value"

        this = This()
        vue = VueInstance(this)
        assert "value" == vue.attribute
        this.attribute = "new_value"
        assert "new_value" == vue.attribute

    def test_get_dollar_attribute(self):
        class This:
            def __getattr__(self, item):
                if item == "$dollar":
                    return "DOLLAR"
                return super().__getattribute__(item)

        vue = VueInstance(This())
        assert "DOLLAR" == vue.dollar
        with pytest.raises(AttributeError):
            assert not vue.no_dollar

    def test_setattr(self):
        class This:
            def __init__(self):
                self.attribute = False

            def __getattr__(self, item):
                if item == "$props":
                    return ()
                return self.__getattribute__(item)

        this = This()
        vue = VueInstance(this)
        vue.attribute = True
        assert vue.attribute
        assert this.attribute

    def test_set_attribute_with_set(self):
        class This:
            def __init__(self):
                self.list = ArrayMock([1, 2, 3, 4])

            def __getattr__(self, item):
                if item == "$props":
                    return ()
                return self.__getattribute__(item)

        this = This()
        vue = VueInstance(this)
        list_id = id(this.list)
        with mock.patch.object(window.Array, "isArray", return_value=True):
            vue.list = [0, 1, 2]
        assert [0, 1, 2] == vue.list._data
        assert list_id
Download .txt
gitextract_pwes50fh/

├── .editorconfig
├── .github/
│   └── workflows/
│       └── push.yaml
├── .gitignore
├── .gitpod.Dockerfile
├── .gitpod.yml
├── CONTRIBUTING.md
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── docs/
│   ├── _config.yml
│   ├── _layouts/
│   │   └── default.html
│   ├── docs/
│   │   ├── index.md
│   │   ├── management/
│   │   │   ├── cli.md
│   │   │   └── configuration.md
│   │   ├── pyjs_bridge.md
│   │   └── vue_concepts/
│   │       ├── computed_properties.md
│   │       ├── custom_directives.md
│   │       ├── custom_vmodel.md
│   │       ├── data_methods.md
│   │       ├── extend.md
│   │       ├── filter.md
│   │       ├── instance_components.md
│   │       ├── lifecycle_hooks.md
│   │       ├── plugins_mixins.md
│   │       ├── props.md
│   │       ├── render_function.md
│   │       ├── vue-router.md
│   │       └── vuex.md
│   └── planning.md
├── examples/
│   ├── elastic_header/
│   │   ├── app.py
│   │   ├── header-view.html
│   │   ├── main.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── element_ui/
│   │   ├── app.py
│   │   ├── components/
│   │   │   └── navigation.py
│   │   ├── navigation.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── github_commits/
│   │   ├── app.py
│   │   ├── commits.html
│   │   ├── data.json
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── grid_component/
│   │   ├── app.py
│   │   ├── form.html
│   │   ├── grid.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── index.md
│   ├── markdown_editor/
│   │   ├── app.py
│   │   ├── editor.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── modal_component/
│   │   ├── app.py
│   │   ├── main.html
│   │   ├── modal-template.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── svg_graph/
│   │   ├── app-template.html
│   │   ├── app.py
│   │   ├── polygraph-template.html
│   │   ├── style.css
│   │   └── vuepy.yml
│   ├── todo_mvc/
│   │   ├── app-template.html
│   │   ├── app.py
│   │   ├── style.css
│   │   └── vuepy.yml
│   └── tree_view/
│       ├── app-template.html
│       ├── app.py
│       ├── style.css
│       ├── tree-template.html
│       └── vuepy.yml
├── pyproject.toml
├── requirements.txt
├── setup.py
├── stubs/
│   ├── browser.py
│   ├── javascript.py
│   └── local_storage.py
├── tests/
│   ├── __init__.py
│   ├── cli/
│   │   └── test_provider.py
│   ├── pytest.ini
│   ├── selenium/
│   │   ├── .gitignore
│   │   ├── chromedriver.py
│   │   ├── conftest.py
│   │   ├── pytest.ini
│   │   ├── test_api.py
│   │   ├── test_examples.py
│   │   ├── test_guide/
│   │   │   ├── test_components/
│   │   │   │   ├── test_custom_events.py
│   │   │   │   └── test_props.py
│   │   │   ├── test_essentials/
│   │   │   │   ├── test_components_basics.py
│   │   │   │   ├── test_computed_properties.py
│   │   │   │   ├── test_event_handler.py
│   │   │   │   ├── test_instance.py
│   │   │   │   ├── test_introduction.py
│   │   │   │   └── test_list_rendering.py
│   │   │   └── test_reusability_composition/
│   │   │       ├── test_filters.py
│   │   │       ├── test_mixins.py
│   │   │       └── test_render_function.py
│   │   ├── test_vuerouter.py
│   │   └── test_vuex.py
│   ├── test_install.py
│   └── unit/
│       ├── test_bridge/
│       │   ├── __init__.py
│       │   ├── mocks.py
│       │   ├── test_dict.py
│       │   ├── test_jsobject.py
│       │   ├── test_list.py
│       │   ├── test_vue.py
│       │   └── test_vuex.py
│       ├── test_transformers/
│       │   ├── conftest.py
│       │   ├── test_component.py
│       │   ├── test_router.py
│       │   └── test_store.py
│       ├── test_utils.py
│       └── test_vue.py
├── vue/
│   ├── __init__.py
│   ├── bridge/
│   │   ├── __init__.py
│   │   ├── dict.py
│   │   ├── list.py
│   │   ├── object.py
│   │   ├── vue_instance.py
│   │   └── vuex_instance.py
│   ├── decorators/
│   │   ├── __init__.py
│   │   ├── action.py
│   │   ├── base.py
│   │   ├── components.py
│   │   ├── computed.py
│   │   ├── custom.py
│   │   ├── data.py
│   │   ├── directive.py
│   │   ├── extends.py
│   │   ├── filters.py
│   │   ├── getter.py
│   │   ├── lifecycle_hook.py
│   │   ├── method.py
│   │   ├── mixins.py
│   │   ├── model.py
│   │   ├── mutation.py
│   │   ├── plugin.py
│   │   ├── prop.py
│   │   ├── render.py
│   │   ├── routes.py
│   │   ├── state.py
│   │   ├── template.py
│   │   └── watcher.py
│   ├── router.py
│   ├── store.py
│   ├── transformers.py
│   ├── utils.py
│   └── vue.py
└── vuecli/
    ├── __init__.py
    ├── cli.py
    ├── index.html
    └── provider/
        ├── __init__.py
        ├── flask.py
        ├── provider.py
        └── static.py
Download .txt
SYMBOL INDEX (623 symbols across 80 files)

FILE: examples/elastic_header/app.py
  class DraggableHeaderView (line 9) | class DraggableHeaderView(VueComponent):
    method c (line 15) | def c(self):
    method start (line 19) | def start(self):
    method header_path (line 23) | def header_path(self):
    method content_position (line 27) | def content_position(self):
    method start_drag (line 32) | def start_drag(self, e):
    method on_drag (line 38) | def on_drag(self, e):
    method stop_drag (line 46) | def stop_drag(self, _):
  class App (line 59) | class App(VueComponent):

FILE: examples/element_ui/app.py
  class App (line 8) | class App(VueComponent):
    method clicked (line 39) | def clicked(self, item):

FILE: examples/element_ui/components/navigation.py
  class NavigationItem (line 4) | class NavigationItem(VueComponent):
    method item_tag (line 36) | def item_tag(self):
    method is_menu_item (line 42) | def is_menu_item(self):
    method is_group_header (line 46) | def is_group_header(self):
    method is_submenu (line 50) | def is_submenu(self):
  class NavigationMenu (line 54) | class NavigationMenu(VueComponent):
  function register (line 72) | def register():

FILE: examples/github_commits/app.py
  class App (line 11) | class App(VueComponent):
    method created (line 18) | def created(self):
    method fetch_data_on_current_branch_change (line 22) | def fetch_data_on_current_branch_change(self, new, old):
    method truncate (line 27) | def truncate(value):
    method format_date (line 32) | def format_date(value):
    method fetch_data (line 35) | def fetch_data(self):
    method loaded (line 43) | def loaded(self, ev):

FILE: examples/grid_component/app.py
  class GridComponent (line 4) | class GridComponent(VueComponent):
    method sort_orders (line 14) | def sort_orders(self):
    method filtered_data (line 18) | def filtered_data(self):
    method capitalize (line 31) | def capitalize(value):
    method sort_by (line 34) | def sort_by(self, key):
  class App (line 42) | class App(VueComponent):

FILE: examples/markdown_editor/app.py
  class App (line 7) | class App(VueComponent):
    method compiled_markdown (line 13) | def compiled_markdown(self):
    method update (line 16) | def update(self, event):

FILE: examples/modal_component/app.py
  class Modal (line 4) | class Modal(VueComponent):
  class App (line 11) | class App(VueComponent):

FILE: examples/svg_graph/app.py
  function value_to_point (line 16) | def value_to_point(value, index, total):
  class AxisLabel (line 27) | class AxisLabel(VueComponent):
    method point (line 35) | def point(self):
  class Polygraph (line 42) | class Polygraph(VueComponent):
    method points (line 47) | def points(self):
  class App (line 62) | class App(VueComponent):
    method disable_remove (line 68) | def disable_remove(self):
    method add (line 71) | def add(self, event):
    method remove (line 77) | def remove(self, stat):

FILE: examples/todo_mvc/app.py
  class ToDoStorage (line 10) | class ToDoStorage:
    method next_uid (line 14) | def next_uid(cls):
    method fetch (line 20) | def fetch(cls):
    method save (line 26) | def save(todos):
  class VisibilityFilters (line 30) | class VisibilityFilters:
    method __new__ (line 31) | def __new__(cls, visibility):
    method all (line 38) | def all(todos):
    method active (line 42) | def active(todos):
    method completed (line 46) | def completed(todos):
  class App (line 50) | class App(VueComponent):
    method save_todos (line 59) | def save_todos(self, new, old):
    method filtered_todos (line 63) | def filtered_todos(self):
    method remaining (line 67) | def remaining(self):
    method all_done (line 71) | def all_done(self):
    method all_done (line 75) | def all_done(self, value):
    method pluralize (line 81) | def pluralize(n):
    method add_todo (line 84) | def add_todo(self, ev=None):
    method remove_todo (line 93) | def remove_todo(self, todo):
    method edit_todo (line 96) | def edit_todo(self, todo):
    method done_edit (line 100) | def done_edit(self, todo):
    method cancel_edit (line 109) | def cancel_edit(self, todo):
    method remove_completed (line 113) | def remove_completed(self, ev=None):
    method todo_focus (line 118) | def todo_focus(el, binding, vnode, old_vnode, *args):
  function on_hash_change (line 126) | def on_hash_change(ev):

FILE: examples/tree_view/app.py
  class Tree (line 27) | class Tree(VueComponent):
    method is_folder (line 33) | def is_folder(self):
    method toggle (line 36) | def toggle(self, ev=None):
    method change_type (line 40) | def change_type(self, ev=None):
    method add_child (line 46) | def add_child(self, ev=None):
  class App (line 53) | class App(VueComponent):

FILE: setup.py
  function make_version (line 8) | def make_version():
  function fetch_vue_cli_js_file (line 14) | def fetch_vue_cli_js_file(source: str, name: str) -> str:

FILE: stubs/browser.py
  function load (line 6) | def load(path):
  function bind (line 10) | def bind(target, ev):
  class window (line 14) | class window:
    class Object (line 19) | class Object:
      method __init__ (line 20) | def __init__(self, obj):
      method assign (line 24) | def assign(target, *sources):
      method keys (line 28) | def keys(obj):
    method bind (line 32) | def bind(el, ev):
    class location (line 35) | class location:
    class Array (line 38) | class Array:
      method __init__ (line 39) | def __init__(self, *objs):
      method isArray (line 43) | def isArray(cls, obj):
    class Vuex (line 46) | class Vuex:
      class Store (line 47) | class Store:
        method new (line 49) | def new(cls, *args, **kwargs):
    class VueRouter (line 52) | class VueRouter:
      method new (line 54) | def new(cls, *args, **kwargs):
    class Vue (line 57) | class Vue:
      method new (line 59) | def new(cls, *args, **kwargs):
      method component (line 63) | def component(cls, name, opts=None):
      method set (line 67) | def set(cls, obj, key, value):
      method delete (line 71) | def delete(cls, obj, key):
      method use (line 75) | def use(cls, plugin, *args, **kwargs):
      method directive (line 79) | def directive(cls, name, directive=None):
      method filter (line 83) | def filter(cls, name, method):
      method mixin (line 87) | def mixin(cls, mixin):
  class timer (line 91) | class timer:
    method set_interval (line 93) | def set_interval(fn, interval):
  class ajax (line 97) | class ajax:
    class ajax (line 98) | class ajax:
      method open (line 99) | def open(self, method, url, asnc):
      method bind (line 102) | def bind(self, ev, method):
      method send (line 105) | def send(self):

FILE: stubs/javascript.py
  function this (line 4) | def this():

FILE: tests/cli/test_provider.py
  function render_index (line 10) | def render_index(tmp_path):
  function parse_index (line 19) | def parse_index(index):
  class TestRenderIndex (line 32) | class TestRenderIndex:
    method test_defaults (line 33) | def test_defaults(self, render_index):
    method test_custom_stylesheets (line 42) | def test_custom_stylesheets(self, render_index):
    method test_enable_builtin_script (line 49) | def test_enable_builtin_script(self, render_index, ext, js):
    method test_customize_builtin_script (line 54) | def test_customize_builtin_script(self, render_index, ext):
    method test_custom_script (line 58) | def test_custom_script(self, render_index):
    method test_custom_template (line 62) | def test_custom_template(self, render_index, tmp_path):
    method test_custom_brython_args (line 67) | def test_custom_brython_args(self, render_index):

FILE: tests/selenium/conftest.py
  function http_server (line 38) | def http_server():
  function selenium_session (line 64) | def selenium_session(http_server):
  function selenium (line 70) | def selenium(selenium_session, request):
  class ErrorLogException (line 76) | class ErrorLogException(Exception):
    method __init__ (line 77) | def __init__(self, errors):
  class SeleniumSession (line 87) | class SeleniumSession:
    method __init__ (line 88) | def __init__(self):
    method __getattr__ (line 96) | def __getattr__(self, item):
    method __enter__ (line 99) | def __enter__(self):
    method __exit__ (line 115) | def __exit__(self, exc_type, exc_val, exc_tb):
    method get_logs (line 118) | def get_logs(self):
    method clear_logs (line 123) | def clear_logs(self):
    method url (line 129) | def url(self, url):
    method app (line 138) | def app(self, app, config=None, files=None):
    method _app_output_path (line 147) | def _app_output_path(self):
    method _create_app_content (line 157) | def _create_app_content(self, test_name, app, config, files):
    method example (line 176) | def example(self, hash_=None):
    method screenshot (line 194) | def screenshot(self):
    method analyze_logs (line 199) | def analyze_logs(self):
    method element_has_text (line 225) | def element_has_text(self, id_, text, timeout=DEFAULT_TIMEOUT):
    method element_with_tag_name_has_text (line 230) | def element_with_tag_name_has_text(self, tag_name, text, timeout=DEFAU...
    method element_present (line 235) | def element_present(self, id_, timeout=DEFAULT_TIMEOUT):
    method element_with_tag_name_present (line 240) | def element_with_tag_name_present(self, tag, timeout=DEFAULT_TIMEOUT):
    method element_not_present (line 245) | def element_not_present(self, id_, timeout=DEFAULT_TIMEOUT):
    method element_attribute_has_value (line 255) | def element_attribute_has_value(

FILE: tests/selenium/test_api.py
  function test_app_with_props_and_data (line 4) | def test_app_with_props_and_data(selenium):
  function test_emit_method (line 18) | def test_emit_method(selenium):
  function test_extend (line 46) | def test_extend(selenium):
  function test_extend_from_dict (line 76) | def test_extend_from_dict(selenium):

FILE: tests/selenium/test_examples.py
  function test_markdown_editor (line 8) | def test_markdown_editor(selenium):
  function test_grid_component (line 28) | def test_grid_component(selenium):
  function test_tree_view (line 43) | def test_tree_view(selenium):
  function test_svg_graph (line 53) | def test_svg_graph(selenium):
  function test_modal_component (line 72) | def test_modal_component(selenium):
  function test_todo_mvc (line 81) | def test_todo_mvc(selenium):
  function test_github_commits (line 107) | def test_github_commits(selenium):
  function test_elastic_header (line 114) | def test_elastic_header(selenium):

FILE: tests/selenium/test_guide/test_components/test_custom_events.py
  function test_customize_v_model (line 6) | def test_customize_v_model(selenium):

FILE: tests/selenium/test_guide/test_components/test_props.py
  function test_prop_types (line 6) | def test_prop_types(selenium):
  function test_prop_default (line 30) | def test_prop_default(selenium):
  function test_prop_required (line 54) | def test_prop_required(selenium):
  function test_prop_as_initial_value (line 79) | def test_prop_as_initial_value(selenium):
  function test_dont_allow_write_prop (line 103) | def test_dont_allow_write_prop(selenium):
  function test_prop_validator (line 128) | def test_prop_validator(selenium):

FILE: tests/selenium/test_guide/test_essentials/test_components_basics.py
  function test_data_must_be_function (line 6) | def test_data_must_be_function(selenium):
  function test_register_with_name (line 34) | def test_register_with_name(selenium):
  function test_passing_data_with_props (line 54) | def test_passing_data_with_props(selenium):
  function test_emit_event (line 75) | def test_emit_event(selenium):

FILE: tests/selenium/test_guide/test_essentials/test_computed_properties.py
  function test_basics (line 4) | def test_basics(selenium):
  function test_watch (line 23) | def test_watch(selenium):
  function test_computed_setter (line 45) | def test_computed_setter(selenium):

FILE: tests/selenium/test_guide/test_essentials/test_event_handler.py
  function test_inline_handlers (line 6) | def test_inline_handlers(selenium):

FILE: tests/selenium/test_guide/test_essentials/test_instance.py
  function test_lifecycle_hooks (line 4) | def test_lifecycle_hooks(selenium):

FILE: tests/selenium/test_guide/test_essentials/test_introduction.py
  function test_declarative_rendering (line 6) | def test_declarative_rendering(selenium):
  function test_bind_element_title (line 15) | def test_bind_element_title(selenium):
  function test_if_condition (line 24) | def test_if_condition(selenium):
  function test_for_loop (line 39) | def test_for_loop(selenium):
  function test_on_click_method (line 53) | def test_on_click_method(selenium):
  function test_v_model (line 67) | def test_v_model(selenium):
  function test_component (line 83) | def test_component(selenium):
  function test_component_with_props (line 103) | def test_component_with_props(selenium):

FILE: tests/selenium/test_guide/test_essentials/test_list_rendering.py
  function test_mutation_methods (line 4) | def test_mutation_methods(selenium):

FILE: tests/selenium/test_guide/test_reusability_composition/test_filters.py
  function test_local_filter (line 4) | def test_local_filter(selenium):
  function test_global_filter (line 19) | def test_global_filter(selenium):

FILE: tests/selenium/test_guide/test_reusability_composition/test_mixins.py
  function test_local_mixin (line 4) | def test_local_mixin(selenium):

FILE: tests/selenium/test_guide/test_reusability_composition/test_render_function.py
  function test_basics (line 6) | def test_basics(selenium):
  function test_slots (line 20) | def test_slots(selenium):
  function test_empty_slots (line 38) | def test_empty_slots(selenium):
  function test_props (line 55) | def test_props(selenium):

FILE: tests/selenium/test_vuerouter.py
  function test_routes (line 6) | def test_routes(selenium):
  function test_dynamic_route_matching (line 43) | def test_dynamic_route_matching(selenium):
  function test_named_routes (line 71) | def test_named_routes(selenium):
  function test_nested_routes_and_redirect (line 120) | def test_nested_routes_and_redirect(selenium):

FILE: tests/selenium/test_vuex.py
  function test_state (line 6) | def test_state(selenium):
  function test_mutation_noargs (line 24) | def test_mutation_noargs(selenium):
  function test_mutation (line 47) | def test_mutation(selenium):
  function test_mutation_kwargs (line 70) | def test_mutation_kwargs(selenium):
  function test_action (line 93) | def test_action(selenium):
  function test_action_noargs (line 122) | def test_action_noargs(selenium):
  function test_action_kwargs (line 151) | def test_action_kwargs(selenium):
  function test_getter_noargs (line 180) | def test_getter_noargs(selenium):
  function test_getter_method (line 202) | def test_getter_method(selenium):
  function test_getter_kwargs (line 224) | def test_getter_kwargs(selenium):
  function test_plugin (line 246) | def test_plugin(selenium):
  function test_using_state_within_native_vue_component (line 282) | def test_using_state_within_native_vue_component(selenium):

FILE: tests/test_install.py
  function _raise_failed_process (line 12) | def _raise_failed_process(proc, error_msg):
  function shell (line 23) | def shell(*args, env=None, cwd=None):
  function wheel (line 30) | def wheel(scope="session"):
  function venv (line 36) | def venv(tmp_path):
  function install (line 43) | def install(wheel, venv):
  function background_task (line 57) | def background_task(*args, env=None, cwd=None):
  function request (line 70) | def request(url, retries=0, retry_delay=0):
  function app (line 82) | def app(tmp_path):
  function config (line 89) | def config(app):
  function test_static (line 96) | def test_static(install, venv, tmp_path, app):
  function test_flask (line 110) | def test_flask(install, venv, app):
  function test_flask_settings (line 120) | def test_flask_settings(install, config, venv, app):

FILE: tests/unit/test_bridge/mocks.py
  class VueMock (line 1) | class VueMock:
    method set (line 3) | def set(obj, key, value):
    method delete (line 7) | def delete(obj, key):
  class ObjectMock (line 11) | class ObjectMock:
    method __new__ (line 12) | def __new__(cls, arg):
    method assign (line 16) | def assign(target, *sources):
    method keys (line 22) | def keys(obj):
  class ArrayMock (line 26) | class ArrayMock:
    method __init__ (line 27) | def __init__(self, *items):
    method __getitem__ (line 30) | def __getitem__(self, item):
    method __setitem__ (line 33) | def __setitem__(self, key, value):
    method length (line 37) | def length(self):
    method push (line 40) | def push(self, *items):
    method splice (line 43) | def splice(self, index, delete_count=None, *items):
    method slice (line 54) | def slice(self, index, stop=None):
    method indexOf (line 57) | def indexOf(self, obj, start=0):
    method reverse (line 63) | def reverse(self):

FILE: tests/unit/test_bridge/test_dict.py
  function window_object (line 11) | def window_object():
  class JsObjectMock (line 18) | class JsObjectMock:
    method __init__ (line 19) | def __init__(self, attribtes):
    method __getattr__ (line 22) | def __getattr__(self, item):
    method __setattr__ (line 25) | def __setattr__(self, key, value):
    method __delattr__ (line 31) | def __delattr__(self, item):
    method __iter__ (line 34) | def __iter__(self):
  function make_dict (line 38) | def make_dict(dct):
  class TestDict (line 42) | class TestDict:
    method test_getitem (line 43) | def test_getitem(self):
    method test_items (line 46) | def test_items(self):
    method test_eq (line 49) | def test_eq(self):
    method test_keys (line 53) | def test_keys(self):
    method test_iter (line 56) | def test_iter(self):
    method test_setitem (line 59) | def test_setitem(self):
    method test_contains (line 64) | def test_contains(self):
    method test_setdefault (line 67) | def test_setdefault(self):
    method test_len (line 72) | def test_len(self):
    method test_get (line 75) | def test_get(self):
    method test_values (line 79) | def test_values(self):
    method test_repr (line 82) | def test_repr(self):
    method test_update (line 85) | def test_update(self):
    method test_bool (line 94) | def test_bool(self):
    method test_delitem (line 98) | def test_delitem(self):
    method test_pop (line 103) | def test_pop(self):
    method test_pop_default (line 108) | def test_pop_default(self):
    method test_pop_key_error (line 113) | def test_pop_key_error(self):
    method test_popitem (line 118) | def test_popitem(self):
    method test_clear (line 123) | def test_clear(self):
    method test_set (line 128) | def test_set(self):
    method test_getattr (line 135) | def test_getattr(self):
    method test_setattr (line 139) | def test_setattr(self):
    method test_str_toString (line 144) | def test_str_toString(self):

FILE: tests/unit/test_bridge/test_jsobject.py
  class TestJSObjectWrapper (line 11) | class TestJSObjectWrapper:
    method test_vue (line 12) | def test_vue(self):
    method test_array (line 19) | def test_array(self):
    method test_dict (line 24) | def test_dict(self):

FILE: tests/unit/test_bridge/test_list.py
  class TestList (line 7) | class TestList:
    method test_len (line 8) | def test_len(self):
    method test_getitem (line 11) | def test_getitem(self):
    method test_delitem (line 17) | def test_delitem(self):
    method test_delitem_range (line 22) | def test_delitem_range(self):
    method test_setitem (line 27) | def test_setitem(self):
    method test_setitem_range (line 32) | def test_setitem_range(self):
    method test_setitem_negative (line 37) | def test_setitem_negative(self):
    method test_iter (line 42) | def test_iter(self):
    method test_eq (line 45) | def test_eq(self):
    method test_mul (line 48) | def test_mul(self):
    method test_index (line 51) | def test_index(self):
    method test_index_start (line 54) | def test_index_start(self):
    method test_index_not_in_list (line 57) | def test_index_not_in_list(self):
    method test_extend (line 61) | def test_extend(self):
    method test_contains (line 66) | def test_contains(self):
    method test_count (line 69) | def test_count(self):
    method test_repr (line 72) | def test_repr(self):
    method test_str (line 75) | def test_str(self):
    method test_append (line 78) | def test_append(self):
    method test_insert (line 83) | def test_insert(self):
    method test_remove (line 88) | def test_remove(self):
    method test_pop (line 93) | def test_pop(self):
    method test_sort (line 97) | def test_sort(self):
    method test_reverse (line 102) | def test_reverse(self):
    method test_set (line 107) | def test_set(self):

FILE: tests/unit/test_bridge/test_vue.py
  class TestVue (line 8) | class TestVue:
    method test_getattr (line 9) | def test_getattr(self):
    method test_get_dollar_attribute (line 20) | def test_get_dollar_attribute(self):
    method test_setattr (line 32) | def test_setattr(self):
    method test_set_attribute_with_set (line 48) | def test_set_attribute_with_set(self):

FILE: tests/unit/test_bridge/test_vuex.py
  class Getter (line 4) | class Getter:
    method __init__ (line 5) | def __init__(self, **kwargs):
    method __getattr__ (line 8) | def __getattr__(self, item):
  class Callable (line 12) | class Callable:
    method __init__ (line 13) | def __init__(self):
    method __call__ (line 16) | def __call__(self, *args):
  function test_get_state (line 20) | def test_get_state():
  function test_get_root_state (line 25) | def test_get_root_state():
  function test_set_state (line 30) | def test_set_state():
  function test_set_root_state (line 37) | def test_set_root_state():
  function test_access_getter (line 44) | def test_access_getter():
  function test_access_root_getter (line 49) | def test_access_root_getter():
  function test_access_comit (line 54) | def test_access_comit():
  function test_access_dispatch (line 62) | def test_access_dispatch():

FILE: tests/unit/test_transformers/conftest.py
  class ArrayMock (line 6) | class ArrayMock(list):
    method __new__ (line 7) | def __new__(cls, *args):
    method isArray (line 11) | def isArray(obj):
  function window_object (line 16) | def window_object():

FILE: tests/unit/test_transformers/test_component.py
  function test_empty (line 6) | def test_empty():
  function test_method (line 13) | def test_method():
  function test_method_as_coroutine (line 25) | def test_method_as_coroutine():
  function test_data (line 33) | def test_data():
  function test_data_as_property (line 40) | def test_data_as_property():
  function test_props (line 49) | def test_props():
  function test_props_with_default (line 57) | def test_props_with_default():
  function test_props_validator (line 66) | def test_props_validator():
  function test_template (line 79) | def test_template():
  function test_lifecycle_hooks (line 87) | def test_lifecycle_hooks():
  function test_customize_model (line 124) | def test_customize_model():
  function test_filter (line 132) | def test_filter():
  function test_watch (line 143) | def test_watch():
  function test_watch_deep (line 154) | def test_watch_deep():
  function test_watch_immediate (line 164) | def test_watch_immediate():
  function test_function_directive (line 174) | def test_function_directive():
  function test_full_directive_different_hooks (line 188) | def test_full_directive_different_hooks():
  function test_full_directive_single_hook (line 221) | def test_full_directive_single_hook():
  function test_directive_replace_dash (line 234) | def test_directive_replace_dash():
  function test_mixins (line 245) | def test_mixins():
  function test_vuepy_mixin (line 252) | def test_vuepy_mixin():
  function test_render_function (line 262) | def test_render_function():
  function test_attributes_from_base_class (line 270) | def test_attributes_from_base_class():
  function test_extends (line 280) | def test_extends():
  function test_template_merging (line 290) | def test_template_merging():
  function test_template_merging_with_slots (line 303) | def test_template_merging_with_slots():
  function test_components (line 318) | def test_components():
  function test_vuepy_components (line 325) | def test_vuepy_components():

FILE: tests/unit/test_transformers/test_router.py
  class TestVueRoute (line 5) | class TestVueRoute:
    method test_path_and_component (line 6) | def test_path_and_component(self):
    method test_path_and_components (line 10) | def test_path_and_components(self):
    method test_path_and_components_and_children (line 22) | def test_path_and_components_and_children(self):
    method test_path_and_redirect (line 40) | def test_path_and_redirect(self):
  class TestVueRouter (line 45) | class TestVueRouter:
    method test_routes (line 46) | def test_routes(self):
    method test_custom_router (line 53) | def test_custom_router(self):

FILE: tests/unit/test_transformers/test_store.py
  function test_state (line 5) | def test_state():
  function test_mutation (line 12) | def test_mutation():
  function test_action (line 23) | def test_action():
  function test_getter (line 43) | def test_getter():
  function test_getter_method (line 54) | def test_getter_method():
  function test_plugin_registration (line 66) | def test_plugin_registration():

FILE: tests/unit/test_utils.py
  function fix_load_and_window (line 8) | def fix_load_and_window():
  class TestJsLoad (line 34) | class TestJsLoad:
    method test_single (line 35) | def test_single(self):
    method test_multiple (line 38) | def test_multiple(self):
    method test_different (line 41) | def test_different(self):
    method test_using_cache (line 46) | def test_using_cache(self):
    method test_none (line 51) | def test_none(self):
    method test_ignore_dollar (line 54) | def test_ignore_dollar(self):
  class TestJsLib (line 58) | class TestJsLib:
    method test_getattr_of_window (line 59) | def test_getattr_of_window(self):
    method test_get_default (line 66) | def test_get_default(self):

FILE: tests/unit/test_vue.py
  function window_object (line 11) | def window_object():
  class VueMock (line 16) | class VueMock(mock.MagicMock):
    method __init__ (line 17) | def __init__(self, *args, **kwargs):
    method new (line 25) | def new(self):
    method component (line 31) | def component(self):
    method directive (line 41) | def directive(self):
    method filter (line 49) | def filter(self):
    method mixin (line 56) | def mixin(self):
    method use (line 62) | def use(self):
  class TestVueComponent (line 68) | class TestVueComponent:
    method test_el (line 69) | def test_el(self):
    method test_props_data (line 77) | def test_props_data(self):
    method test_register (line 85) | def test_register(self):
  class TestVue (line 98) | class TestVue:
    method test_directive (line 99) | def test_directive(self):
    method test_directive_with_implicit_name (line 109) | def test_directive_with_implicit_name(self):
    method test_function_directive (line 119) | def test_function_directive(self):
    method test_function_directive_with_implicit_name (line 128) | def test_function_directive_with_implicit_name(self):
    method test_directive_getter (line 137) | def test_directive_getter(self):
    method test_filter (line 143) | def test_filter(self):
    method test_filter_use_function_name (line 149) | def test_filter_use_function_name(self):
    method test_mixin (line 158) | def test_mixin(self):
    method test_use (line 167) | def test_use(self):
    method test_component (line 172) | def test_component(self):
    method test_vuepy_component (line 178) | def test_vuepy_component(self):
    method test_vuepy_component_implicit_naming (line 187) | def test_vuepy_component_implicit_naming(self):
    method test_component_getter (line 196) | def test_component_getter(self):

FILE: vue/bridge/dict.py
  class Dict (line 5) | class Dict(Object):
    method __unwraps__ (line 7) | def __unwraps__():
    method __can_wrap__ (line 11) | def __can_wrap__(obj):
    method __eq__ (line 18) | def __eq__(self, other):
    method __getitem__ (line 21) | def __getitem__(self, item):
    method __iter__ (line 24) | def __iter__(self):
    method pop (line 27) | def pop(self, k, default=...):
    method popitem (line 34) | def popitem(self):
    method setdefault (line 38) | def setdefault(self, k, default=None):
    method __len__ (line 43) | def __len__(self):
    method __contains__ (line 46) | def __contains__(self, item):
    method __delitem__ (line 49) | def __delitem__(self, key):
    method __setitem__ (line 52) | def __setitem__(self, key, value):
    method get (line 58) | def get(self, k, default=None):
    method values (line 63) | def values(self):
    method update (line 66) | def update(self, _m=None, **kwargs):
    method clear (line 72) | def clear(self):
    method fromkeys (line 77) | def fromkeys(cls, seq):
    method copy (line 80) | def copy(self):
    method items (line 83) | def items(self):
    method keys (line 86) | def keys(self):
    method __str__ (line 89) | def __str__(self):
    method __repr__ (line 94) | def __repr__(self):
    method __set__ (line 99) | def __set__(self, new):
    method __bool__ (line 103) | def __bool__(self):
    method __getattr__ (line 106) | def __getattr__(self, item):
    method __setattr__ (line 112) | def __setattr__(self, key, value):
    method __py__ (line 117) | def __py__(self):
    method __js__ (line 120) | def __js__(self):

FILE: vue/bridge/list.py
  class List (line 5) | class List(Object):
    method __unwraps__ (line 7) | def __unwraps__():
    method __can_wrap__ (line 11) | def __can_wrap__(obj):
    method _slice (line 14) | def _slice(self, slc):
    method __eq__ (line 23) | def __eq__(self, other):
    method __mul__ (line 26) | def __mul__(self, other):
    method index (line 29) | def index(self, obj, start=0, _stop=-1):
    method extend (line 35) | def extend(self, iterable):
    method __len__ (line 38) | def __len__(self):
    method __contains__ (line 41) | def __contains__(self, item):
    method __imul__ (line 48) | def __imul__(self, other):
    method count (line 51) | def count(self, obj):
    method reverse (line 54) | def reverse(self):
    method __delitem__ (line 57) | def __delitem__(self, key):
    method __setitem__ (line 61) | def __setitem__(self, key, value):
    method __getitem__ (line 66) | def __getitem__(self, item):
    method __reversed__ (line 73) | def __reversed__(self):
    method __rmul__ (line 76) | def __rmul__(self, other):
    method append (line 79) | def append(self, obj):
    method insert (line 82) | def insert(self, index, obj):
    method remove (line 85) | def remove(self, obj):
    method __iadd__ (line 91) | def __iadd__(self, other):
    method __iter__ (line 94) | def __iter__(self):
    method pop (line 101) | def pop(self, index=-1):
    method sort (line 104) | def sort(self, key=None, reverse=False):
    method __add__ (line 107) | def __add__(self, other):
    method clear (line 110) | def clear(self):
    method copy (line 113) | def copy(self):
    method __set__ (line 116) | def __set__(self, new):
    method __repr__ (line 119) | def __repr__(self):
    method __py__ (line 122) | def __py__(self):
    method __js__ (line 125) | def __js__(self):

FILE: vue/bridge/object.py
  class Object (line 1) | class Object:
    method sub_classes (line 6) | def sub_classes(cls):
    method from_js (line 10) | def from_js(cls, jsobj):
    method to_js (line 17) | def to_js(cls, obj):
    method to_py (line 26) | def to_py(cls, obj):
    method __can_wrap__ (line 36) | def __can_wrap__(obj):
    method __unwraps__ (line 40) | def __unwraps__():
    method __init__ (line 43) | def __init__(self, js):
    method __js__ (line 46) | def __js__(self):
    method __py__ (line 49) | def __py__(self):

FILE: vue/bridge/vue_instance.py
  class VueInstance (line 5) | class VueInstance(Object):
    method __can_wrap__ (line 7) | def __can_wrap__(obj):
    method store (line 11) | def store(self):
    method __getattr__ (line 20) | def __getattr__(self, item):
    method __setattr__ (line 28) | def __setattr__(self, key, value):

FILE: vue/bridge/vuex_instance.py
  class VuexInstance (line 1) | class VuexInstance:
    method __init__ (line 2) | def __init__(
    method __getattr__ (line 18) | def __getattr__(self, item):
    method __setattr__ (line 32) | def __setattr__(self, key, value):
    method commit (line 41) | def commit(self, mutation_name, *args, **kwargs):
    method dispatch (line 44) | def dispatch(self, mutation_name, *args, **kwargs):

FILE: vue/decorators/action.py
  class Action (line 5) | class Action(VueDecorator):
    method __init__ (line 6) | def __init__(self, name, value):
  function action (line 11) | def action(fn):

FILE: vue/decorators/base.py
  class VueDecorator (line 5) | class VueDecorator:
  function pyjs_bridge (line 10) | def pyjs_bridge(fn, inject_vue_instance=False):

FILE: vue/decorators/components.py
  class Components (line 4) | class Components(VueDecorator):
    method __init__ (line 7) | def __init__(self, *mixins):

FILE: vue/decorators/computed.py
  class Computed (line 4) | class Computed(VueDecorator):
    method __init__ (line 5) | def __init__(self, fn):
    method setter (line 11) | def setter(self, fn):
    method __value__ (line 16) | def __value__(self):
  function computed (line 23) | def computed(fn):

FILE: vue/decorators/custom.py
  class Custom (line 4) | class Custom(VueDecorator):
    method __init__ (line 5) | def __init__(self, fn, key, name=None, static=False):
  function custom (line 10) | def custom(key, name=None, static=False):

FILE: vue/decorators/data.py
  class Data (line 4) | class Data(VueDecorator):
    method __init__ (line 5) | def __init__(self, name, value):
  function data (line 10) | def data(fn):

FILE: vue/decorators/directive.py
  function map_hook (line 4) | def map_hook(hook_name):
  class DirectiveHook (line 10) | class DirectiveHook(VueDecorator):
    method __init__ (line 11) | def __init__(self, fn, hooks=(), name=None):
  function _directive_hook (line 20) | def _directive_hook(name, hooks):
  function directive (line 28) | def directive(fn, *hooks):

FILE: vue/decorators/extends.py
  class Extends (line 4) | class Extends(VueDecorator):
    method __init__ (line 7) | def __init__(self, init_dict):

FILE: vue/decorators/filters.py
  class Filter (line 4) | class Filter(VueDecorator):
    method __init__ (line 5) | def __init__(self, fn, name):
  function filters (line 11) | def filters(fn):

FILE: vue/decorators/getter.py
  class Getter (line 5) | class Getter(VueDecorator):
    method __init__ (line 6) | def __init__(self, name, value):
  function getter (line 11) | def getter(fn):

FILE: vue/decorators/lifecycle_hook.py
  class LifecycleHook (line 4) | class LifecycleHook(VueDecorator):
    method __init__ (line 16) | def __init__(self, name, fn):
  function lifecycle_hook (line 21) | def lifecycle_hook(name):

FILE: vue/decorators/method.py
  class Method (line 4) | class Method(VueDecorator):
    method __init__ (line 5) | def __init__(self, fn):
  function coroutine (line 12) | def coroutine(_coroutine):
  function method (line 22) | def method(_method):

FILE: vue/decorators/mixins.py
  class Mixins (line 4) | class Mixins(VueDecorator):
    method __init__ (line 7) | def __init__(self, *mixins):

FILE: vue/decorators/model.py
  class Model (line 4) | class Model(VueDecorator):
    method __init__ (line 7) | def __init__(self, prop="value", event="input"):
    method __value__ (line 12) | def __value__(self):

FILE: vue/decorators/mutation.py
  class Mutation (line 5) | class Mutation(VueDecorator):
    method __init__ (line 6) | def __init__(self, name, value):
  function mutation (line 11) | def mutation(fn):

FILE: vue/decorators/plugin.py
  class Plugin (line 4) | class Plugin(VueDecorator):
    method __init__ (line 7) | def __init__(self, plugins):

FILE: vue/decorators/prop.py
  class Prop (line 5) | class Prop(VueDecorator):
    method __init__ (line 17) | def __init__(self, name, typ, mixin=None):
  class Validator (line 23) | class Validator(VueDecorator):
    method __init__ (line 24) | def __init__(self, prop, fn):
  function validator (line 29) | def validator(prop):

FILE: vue/decorators/render.py
  class Render (line 4) | class Render(VueDecorator):
    method __init__ (line 7) | def __init__(self, fn):

FILE: vue/decorators/routes.py
  class Routes (line 4) | class Routes(VueDecorator):
    method __init__ (line 7) | def __init__(self, routes):

FILE: vue/decorators/state.py
  class State (line 4) | class State(VueDecorator):
    method __init__ (line 5) | def __init__(self, name, value):

FILE: vue/decorators/template.py
  class Template (line 4) | class Template(VueDecorator):
    method __init__ (line 7) | def __init__(self, template):

FILE: vue/decorators/watcher.py
  class Watcher (line 4) | class Watcher(VueDecorator):
    method __init__ (line 5) | def __init__(self, name, fn, deep=False, immediate=False):
    method __value__ (line 12) | def __value__(self):
  function watch (line 16) | def watch(name, deep=False, immediate=False):

FILE: vue/router.py
  class VueRouter (line 5) | class VueRouter(Transformable):
    method init_dict (line 9) | def init_dict(cls):
    method __new__ (line 12) | def __new__(cls):
  class VueRoute (line 17) | class VueRoute:
    method __new__ (line 18) | def __new__(cls, path, component=None, components=None, **kwargs):

FILE: vue/store.py
  class VueStore (line 7) | class VueStore(Transformable):
    method init_dict (line 9) | def init_dict(cls):
    method __new__ (line 12) | def __new__(cls):
  class VueStorePlugin (line 16) | class VueStorePlugin:
    method initialize (line 17) | def initialize(self, store):
    method subscribe (line 20) | def subscribe(self, state, mutation, *args, **kwargs):
    method __subscribe__ (line 23) | def __subscribe__(self, muation_info, state):
    method install (line 31) | def install(self, store):

FILE: vue/transformers.py
  function _merge_templates (line 53) | def _merge_templates(sub):
  class _TransformableType (line 74) | class _TransformableType(type):
  class Transformable (line 78) | class Transformable(metaclass=_TransformableType):
  class ClassAttributeDictTransformer (line 82) | class ClassAttributeDictTransformer:
    method transform (line 95) | def transform(cls, transformable):
    method decorate (line 107) | def decorate(cls, transformable, attribute_name, attribute_value):
    method _iter_attributes (line 113) | def _iter_attributes(cls, transformable):
    method _get_base (line 123) | def _get_base(cls, transformable):
    method _inject_into (line 130) | def _inject_into(cls, destination, key, value):
  class VueComponentTransformer (line 143) | class VueComponentTransformer(ClassAttributeDictTransformer):
    method transform (line 150) | def transform(cls, transformable):
    method decorate (line 167) | def decorate(cls, transformable, attribute_name, attribute_value):
  class VueDirectiveTransformer (line 210) | class VueDirectiveTransformer(ClassAttributeDictTransformer):
    method transform (line 217) | def transform(cls, transformable):
    method decorate (line 223) | def decorate(cls, transformable, attribute_name, attribute_value):
  class VueStoreTransformer (line 231) | class VueStoreTransformer(ClassAttributeDictTransformer):
    method decorate (line 238) | def decorate(cls, transformable, attribute_name, attribute_value):
  class VueRouterTransformer (line 247) | class VueRouterTransformer(ClassAttributeDictTransformer):
    method decorate (line 254) | def decorate(cls, transformable, attribute_name, attribute_value):

FILE: vue/utils.py
  function js_load (line 6) | def js_load(path):
  function js_lib (line 22) | def js_lib(name):

FILE: vue/vue.py
  class Vue (line 12) | class Vue:
    method directive (line 14) | def directive(name, directive=None):
    method filter (line 32) | def filter(method_or_name, method=None):
    method mixin (line 43) | def mixin(mixin):
    method use (line 47) | def use(plugin, *args, **kwargs):
    method component (line 51) | def component(component_or_name, component=None):
  class VueComponent (line 62) | class VueComponent(Transformable):
    method init_dict (line 64) | def init_dict(cls):
    method __new__ (line 67) | def __new__(cls, el, **kwargs):
    method register (line 77) | def register(cls, name=None):
  class VueMixin (line 84) | class VueMixin(Transformable):
  class VueDirective (line 88) | class VueDirective(Transformable):
  class VuePlugin (line 92) | class VuePlugin:
    method install (line 94) | def install(*args, **kwargs):

FILE: vuecli/cli.py
  function deploy (line 11) | def deploy(provider_class, arguments):
  function package (line 29) | def package(destination, app):
  function main (line 43) | def main():

FILE: vuecli/provider/__init__.py
  function _load (line 4) | def _load(ep):

FILE: vuecli/provider/flask.py
  class Flask (line 9) | class Flask(Provider):
    method __init__ (line 10) | def __init__(self, *args, **kwargs):
    method content (line 14) | def content(self, endpoint, route, content):
    method directory (line 17) | def directory(self, endpoint, route, path, deep=False):
    method deploy (line 29) | def deploy(self):

FILE: vuecli/provider/provider.py
  class Provider (line 25) | class Provider:
    method __init__ (line 28) | def __init__(self, path=None):
    method _normalize_config (line 33) | def _normalize_config(config):
    method load_config (line 49) | def load_config(self):
    method render_index (line 58) | def render_index(self):
    method setup (line 76) | def setup(self):
    method content (line 88) | def content(self, endpoint, route, content):
    method directory (line 91) | def directory(self, endpoint, route, path, deep=False):
    method deploy (line 94) | def deploy(self, **kwargs):

FILE: vuecli/provider/static.py
  function copytree (line 11) | def copytree(src, dst, deep=True):
  class Static (line 25) | class Static(Provider):
    method __init__ (line 31) | def __init__(self, *args, **kwargs):
    method temppath (line 36) | def temppath(self):
    method content (line 39) | def content(self, endpoint, route, content):
    method directory (line 49) | def directory(self, endpoint, route, path, deep=False):
    method deploy (line 53) | def deploy(self, destination, package=False):
    method _create_package (line 70) | def _create_package(self):
    method _brython (line 78) | def _brython(self, *args):
Condensed preview — 158 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (276K chars).
[
  {
    "path": ".editorconfig",
    "chars": 351,
    "preview": "root = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ninsert_final_newline = true\ntri"
  },
  {
    "path": ".github/workflows/push.yaml",
    "chars": 3028,
    "preview": "name: CI\n\non:\n  pull_request:\n    branches:\n    - master\n  push:\n    branches:\n      - '**'\n    tags:\n      - 'release-c"
  },
  {
    "path": ".gitignore",
    "chars": 178,
    "preview": "venv\n.idea\n.vscode\n.pytest_cache\n__pycache__\ngh-pages-build\n\ndebug\nexamples/*/screenshot.png\nvuecli/js\nexamples_static\n\n"
  },
  {
    "path": ".gitpod.Dockerfile",
    "chars": 432,
    "preview": "FROM gitpod/workspace-full\n\nUSER gitpod\n\n# Install Google key\nRUN wget -q -O - https://dl-ssl.google.com/linux/linux_sig"
  },
  {
    "path": ".gitpod.yml",
    "chars": 198,
    "preview": "image:\n  file: .gitpod.Dockerfile\ntasks:\n- init: make env.up\n  command: make serve\nports:\n  - port: 8000\n    onOpen: ope"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 926,
    "preview": "# Contributing a PR\nFirst of: Thanks for contributing a PR to this project!!\n\nThere a four main guidelines I try to foll"
  },
  {
    "path": "LICENSE",
    "chars": 1070,
    "preview": "MIT License\n\nCopyright (c) 2018 Stefan Hoelzl\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
  },
  {
    "path": "MANIFEST.in",
    "chars": 16,
    "preview": "include LICENSE\n"
  },
  {
    "path": "Makefile",
    "chars": 1369,
    "preview": "PYTHONPATH=.:stubs\n\n.PHONY: env.pip\nenv.pip:\n\tpip install -r requirements.txt\n\tpip install -e .\n\n.PHONY: env.chrome\nenv."
  },
  {
    "path": "README.md",
    "chars": 3336,
    "preview": "# vue.py\n[![Build Status](https://github.com/stefanhoelzl/vue.py/workflows/CI/badge.svg)](https://github.com/stefanhoelz"
  },
  {
    "path": "docs/_config.yml",
    "chars": 310,
    "preview": "theme: jekyll-theme-cayman\ntitle: vue.py\ndescription: Pythonic Vue.js\ninclude:\n  - __init__.py\n  - __entry_point__.py\nna"
  },
  {
    "path": "docs/_layouts/default.html",
    "chars": 1531,
    "preview": "<!DOCTYPE html>\n<html lang=\"{{ site.lang | default: \"en-US\" }}\">\n  <head>\n    <meta charset=\"UTF-8\">\n\n{% seo %}\n    <met"
  },
  {
    "path": "docs/docs/index.md",
    "chars": 2104,
    "preview": "# Documentation\n`vue.py` provides bindings for [Vue.js](https://vuejs.org/).\nIf you are not familiar with [Vue.js](https"
  },
  {
    "path": "docs/docs/management/cli.md",
    "chars": 1209,
    "preview": "# Command Line Interface\n\n`vue.py` provides a command line tool `vue-cli` to deploy your application.\n\n## Deployment\nA `"
  },
  {
    "path": "docs/docs/management/configuration.md",
    "chars": 1941,
    "preview": "# Configuration\n\nYour `vue.py` application can be customized via a `vuepy.yml` file\nlocated in your application folder.\n"
  },
  {
    "path": "docs/docs/pyjs_bridge.md",
    "chars": 4787,
    "preview": "# Python/Javascript Bridge\n## Call Javascript Functions\n`vue.py` provides some utilities to access Javascript libraris.\n"
  },
  {
    "path": "docs/docs/vue_concepts/computed_properties.md",
    "chars": 1269,
    "preview": "# Computed Properties\nComputed properties can be defined with the `@computed` decorator\n```python\nfrom vue import VueCom"
  },
  {
    "path": "docs/docs/vue_concepts/custom_directives.md",
    "chars": 2596,
    "preview": "# Custom Directives\n\n## Local Registration\nFor [function directives](https://vuejs.org/v2/guide/custom-directive.html#Fu"
  },
  {
    "path": "docs/docs/vue_concepts/custom_vmodel.md",
    "chars": 534,
    "preview": "# Customize V-Model\nTo customize the event and prop used by `v-model` a class variable of the type `Model()` can be defi"
  },
  {
    "path": "docs/docs/vue_concepts/data_methods.md",
    "chars": 1020,
    "preview": "# Data\nAll class variables of a `vue.py` component are available as data fields in the Vue instance.\n```python\nclass Com"
  },
  {
    "path": "docs/docs/vue_concepts/extend.md",
    "chars": 2904,
    "preview": "# Extend\nVue.js uses `Vue.extend` or the `extends` Component attribute to extend\ncomponents. Per default `vue.py` sticks"
  },
  {
    "path": "docs/docs/vue_concepts/filter.md",
    "chars": 1070,
    "preview": "# Filter\n\n## Local Registration\nLocal registration of filters is done with the `@filters` decorator.\n```python\nfrom vue "
  },
  {
    "path": "docs/docs/vue_concepts/instance_components.md",
    "chars": 2312,
    "preview": "# Components\n## Define\nA Vue component can be defined by writing a sub-class of `VueComponent`\n```python\nfrom vue import"
  },
  {
    "path": "docs/docs/vue_concepts/lifecycle_hooks.md",
    "chars": 648,
    "preview": "# Lifecycle Hooks\nCertain names for component methods are reserved, to specify lifecycle hooks.\n```python\nfrom vue impor"
  },
  {
    "path": "docs/docs/vue_concepts/plugins_mixins.md",
    "chars": 1695,
    "preview": "# Mixins\n\n## Write Mixins\nMixins are created by sub-classing from `VueMixin` and from there it is\nsimilar to write a `Vu"
  },
  {
    "path": "docs/docs/vue_concepts/props.md",
    "chars": 920,
    "preview": "# Props\nA prop is defined by adding a type hint to a class variable.\n```python\nfrom vue import VueComponent\n\nclass Compo"
  },
  {
    "path": "docs/docs/vue_concepts/render_function.md",
    "chars": 989,
    "preview": "# Render Function\nA render function can be defined by overwriting the `render`  method.\n```python\nfrom vue import VueCom"
  },
  {
    "path": "docs/docs/vue_concepts/vue-router.md",
    "chars": 909,
    "preview": "# Vue Router\n## Define\nA vue router can be used by writing a sub-class of `VueRouter`, \nsetting some `VueRoute`s \nand se"
  },
  {
    "path": "docs/docs/vue_concepts/vuex.md",
    "chars": 2822,
    "preview": "# Vuex\n## Define\nA Vuex store can be defined by writing a sub-class of `VueStore`\nand used by setting the `store` parame"
  },
  {
    "path": "docs/planning.md",
    "chars": 471,
    "preview": "# Future Plans\n## Performance\n* How to improve loading times?\n* create benchmarks\n\n## Tools\n* Docker deployment\n\n## Vue."
  },
  {
    "path": "examples/elastic_header/app.py",
    "chars": 1544,
    "preview": "from vue import VueComponent, data, computed\nfrom vue.bridge import Object\nfrom vue.utils import js_lib\n\n\ndynamics = js_"
  },
  {
    "path": "examples/elastic_header/header-view.html",
    "chars": 491,
    "preview": "<div class=\"draggable-header-view\"\n  @mousedown=\"start_drag\" @touchstart=\"start_drag\"\n  @mousemove=\"on_drag\" @touchmove="
  },
  {
    "path": "examples/elastic_header/main.html",
    "chars": 764,
    "preview": "<draggable-header-view>\n  <template slot=\"header\">\n    <h1>Elastic Draggable SVG Header</h1>\n    <p>\n      with <a href="
  },
  {
    "path": "examples/elastic_header/style.css",
    "chars": 846,
    "preview": "h1 {\n  font-weight: 300;\n  font-size: 1.8em;\n  margin-top: 0;\n}\na {\n  color: #fff;\n}\n.draggable-header-view {\n  backgrou"
  },
  {
    "path": "examples/elastic_header/vuepy.yml",
    "chars": 153,
    "preview": "templates:\n  header-view: header-view.html\n  main: main.html\nstylesheets:\n  - style.css\nscripts:\n  - https://unpkg.com/d"
  },
  {
    "path": "examples/element_ui/app.py",
    "chars": 1280,
    "preview": "from vue import VueComponent\nfrom .components import navigation\n\n\nnavigation.register()\n\n\nclass App(VueComponent):\n    t"
  },
  {
    "path": "examples/element_ui/components/navigation.py",
    "chars": 1863,
    "preview": "from vue import VueComponent, computed\n\n\nclass NavigationItem(VueComponent):\n    item: dict\n    template = \"\"\"\n    <el-m"
  },
  {
    "path": "examples/element_ui/navigation.html",
    "chars": 106,
    "preview": "<div>\n  <navigation-menu\n    @click=\"clicked\"\n    :content=\"navigation_menu\">\n  </navigation-menu>\n</div>\n"
  },
  {
    "path": "examples/element_ui/style.css",
    "chars": 82,
    "preview": ".navigation-menu:not(.el-menu--collapse) {\n  width: 200px;\n  min-height: 400px;\n}\n"
  },
  {
    "path": "examples/element_ui/vuepy.yml",
    "chars": 182,
    "preview": "templates:\n  navigation: navigation.html\nstylesheets:\n  - style.css\n  - https://unpkg.com/element-ui/lib/theme-chalk/ind"
  },
  {
    "path": "examples/github_commits/app.py",
    "chars": 1038,
    "preview": "from vue import VueComponent, filters, watch\n\nfrom browser import window, ajax\n\nurl = \"https://api.github.com/repos/stef"
  },
  {
    "path": "examples/github_commits/commits.html",
    "chars": 754,
    "preview": "<div>\n  <h1>Latest vue.py Commits</h1>\n  <template v-for=\"branch in branches\">\n    <input type=\"radio\"\n      :id=\"branch"
  },
  {
    "path": "examples/github_commits/data.json",
    "chars": 39396,
    "preview": "[\n  {\n    \"sha\": \"0a644f825e780555e62d2e4647786d2a3eb06afc\",\n    \"node_id\": \"MDY6Q29tbWl0MTM5NDg4NjkwOjBhNjQ0ZjgyNWU3ODA"
  },
  {
    "path": "examples/github_commits/style.css",
    "chars": 195,
    "preview": "#demo {\n  font-family: 'Helvetica', Arial, sans-serif;\n}\na {\n  text-decoration: none;\n  color: #f66;\n}\nli {\n  line-heigh"
  },
  {
    "path": "examples/github_commits/vuepy.yml",
    "chars": 62,
    "preview": "templates:\n  commits: commits.html\nstylesheets:\n  - style.css\n"
  },
  {
    "path": "examples/grid_component/app.py",
    "chars": 1246,
    "preview": "from vue import VueComponent, data, computed, filters\n\n\nclass GridComponent(VueComponent):\n    template = \"#grid\"\n\n    c"
  },
  {
    "path": "examples/grid_component/form.html",
    "chars": 223,
    "preview": "<div>\n  <form id=\"search\">\n    Search <input id=\"query\" name=\"query\" v-model=\"search_query\">\n  </form>\n  <demo-grid\n    "
  },
  {
    "path": "examples/grid_component/grid.html",
    "chars": 456,
    "preview": "<table>\n  <thead>\n    <tr>\n      <th v-for=\"key in columns\"\n        @click=\"sort_by(key)\"\n        :id=\"key\"\n        :cla"
  },
  {
    "path": "examples/grid_component/style.css",
    "chars": 902,
    "preview": "body {\n  font-family: Helvetica Neue, Arial, sans-serif;\n  font-size: 14px;\n  color: #444;\n}\n\ntable {\n  border: 2px soli"
  },
  {
    "path": "examples/grid_component/vuepy.yml",
    "chars": 74,
    "preview": "templates:\n  form: form.html\n  grid: grid.html\nstylesheets:\n  - style.css\n"
  },
  {
    "path": "examples/index.md",
    "chars": 2629,
    "preview": "# Example Gallery\n\n## TodoMVC\n[Demo](https://stefanhoelzl.github.io/vue.py/examples/todo_mvc) / [Source](https://github."
  },
  {
    "path": "examples/markdown_editor/app.py",
    "chars": 366,
    "preview": "from vue import VueComponent, computed\nfrom vue.utils import js_lib\n\nmarked = js_lib(\"marked\")\n\n\nclass App(VueComponent)"
  },
  {
    "path": "examples/markdown_editor/editor.html",
    "chars": 135,
    "preview": "<div id=\"editor\">\n  <textarea :value=\"input\" @input=\"update\" id=\"markdown\"></textarea>\n  <div v-html=\"compiled_markdown\""
  },
  {
    "path": "examples/markdown_editor/style.css",
    "chars": 500,
    "preview": "html, body, #editor {\n  margin: 0;\n  height: 100%;\n  font-family: 'Helvetica Neue', Arial, sans-serif;\n  color: #333;\n}\n"
  },
  {
    "path": "examples/markdown_editor/vuepy.yml",
    "chars": 113,
    "preview": "templates:\n  editor-template: editor.html\nstylesheets:\n  - style.css\nscripts:\n  - https://unpkg.com/marked@0.3.6\n"
  },
  {
    "path": "examples/modal_component/app.py",
    "chars": 197,
    "preview": "from vue import VueComponent\n\n\nclass Modal(VueComponent):\n    template = \"#modal-template\"\n\n\nModal.register()\n\n\nclass Ap"
  },
  {
    "path": "examples/modal_component/main.html",
    "chars": 361,
    "preview": "<div>\n  <button id=\"show-modal\" @click=\"show_modal = true\">\n    Show Modal\n  </button>\n  <!-- use the modal component, p"
  },
  {
    "path": "examples/modal_component/modal-template.html",
    "chars": 760,
    "preview": "    <transition name=\"modal\">\n      <div class=\"modal-mask\">\n        <div class=\"modal-wrapper\">\n          <div class=\"m"
  },
  {
    "path": "examples/modal_component/style.css",
    "chars": 1059,
    "preview": ".modal-mask {\n  position: fixed;\n  z-index: 9998;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  background-color"
  },
  {
    "path": "examples/modal_component/vuepy.yml",
    "chars": 94,
    "preview": "templates:\n  modal-template: modal-template.html\n  main: main.html\nstylesheets:\n  - style.css\n"
  },
  {
    "path": "examples/svg_graph/app-template.html",
    "chars": 661,
    "preview": "<div>\n  <!-- Use the component -->\n  <svg width=\"200\" height=\"200\">\n    <polygraph :stats=\"stats\"></polygraph>\n  </svg>\n"
  },
  {
    "path": "examples/svg_graph/app.py",
    "chars": 1690,
    "preview": "import math\n\nfrom vue import VueComponent, computed\n\n\nstats = [\n    {\"label\": \"A\", \"value\": 100},\n    {\"label\": \"B\", \"va"
  },
  {
    "path": "examples/svg_graph/polygraph-template.html",
    "chars": 238,
    "preview": "<g>\n  <polygon :points=\"points\"></polygon>\n  <circle cx=\"100\" cy=\"100\" r=\"80\"></circle>\n  <axis-label\n    v-for=\"(stat, "
  },
  {
    "path": "examples/svg_graph/style.css",
    "chars": 404,
    "preview": "body {\n    font-family: Helvetica Neue, Arial, sans-serif;\n}\n\npolygon {\n    fill: #42b983;\n    opacity: .75;\n}\n\ncircle {"
  },
  {
    "path": "examples/svg_graph/vuepy.yml",
    "chars": 118,
    "preview": "templates:\n  app-template: app-template.html\n  polygraph-template: polygraph-template.html\nstylesheets:\n  - style.css\n"
  },
  {
    "path": "examples/todo_mvc/app-template.html",
    "chars": 2122,
    "preview": "<div>\n  <section class=\"todoapp\">\n    <header class=\"header\">\n      <h1>todos</h1>\n      <input class=\"new-todo\"\n       "
  },
  {
    "path": "examples/todo_mvc/app.py",
    "chars": 3247,
    "preview": "from browser.local_storage import storage\nfrom browser import window\nimport json\nfrom vue import VueComponent, computed,"
  },
  {
    "path": "examples/todo_mvc/style.css",
    "chars": 29,
    "preview": "[v-cloak] { display: none; }\n"
  },
  {
    "path": "examples/todo_mvc/vuepy.yml",
    "chars": 126,
    "preview": "templates:\n  app-template: app-template.html\nstylesheets:\n  - style.css\n  - https://unpkg.com/todomvc-app-css@2.0.6/inde"
  },
  {
    "path": "examples/tree_view/app-template.html",
    "chars": 232,
    "preview": "<div>\n    <p>(You can double click on an item to turn it into a folder.)</p>\n\n    <!-- the demo root element -->\n    <ul"
  },
  {
    "path": "examples/tree_view/app.py",
    "chars": 1287,
    "preview": "from vue import VueComponent, computed\n\ndemo_data = {\n    \"name\": \"My Tree\",\n    \"children\": [\n        {\"name\": \"hello\"}"
  },
  {
    "path": "examples/tree_view/style.css",
    "chars": 201,
    "preview": "body {\n  font-family: Menlo, Consolas, monospace;\n  color: #444;\n}\n.tree {\n  cursor: pointer;\n}\n.bold {\n  font-weight: b"
  },
  {
    "path": "examples/tree_view/tree-template.html",
    "chars": 408,
    "preview": "<li>\n  <div\n    :class=\"{bold: is_folder}\"\n    @click=\"toggle\"\n    @dblclick=\"change_type\">\n    {{ model.name }}\n    <sp"
  },
  {
    "path": "examples/tree_view/vuepy.yml",
    "chars": 108,
    "preview": "templates:\n  tree-template: tree-template.html\n  app-template: app-template.html\nstylesheets:\n  - style.css\n"
  },
  {
    "path": "pyproject.toml",
    "chars": 239,
    "preview": "[build-system]\nrequires = [\n    \"setuptools\",\n    \"requests\",\n    \"python-project-tools@git+https://github.com/stefanhoe"
  },
  {
    "path": "requirements.txt",
    "chars": 239,
    "preview": "# provider\nFlask==2.2.2\n\n# packaging\nwheel==0.38.4\n\n# testing\npytest==7.2.1\nselenium==4.8.0\npyderman==3.3.2\nrequests==2."
  },
  {
    "path": "setup.py",
    "chars": 2908,
    "preview": "from pathlib import Path\nfrom setuptools import setup\n\nimport requests\nfrom tools import release\n\n\ndef make_version():\n "
  },
  {
    "path": "stubs/browser.py",
    "chars": 1752,
    "preview": "\"\"\"stub to avoid import errors\"\"\"\n\nimport local_storage\n\n\ndef load(path):\n    ...\n\n\ndef bind(target, ev):\n    ...\n\n\nclas"
  },
  {
    "path": "stubs/javascript.py",
    "chars": 64,
    "preview": "\"\"\"stub to avoid import errors\"\"\"\n\n\ndef this():\n    return None\n"
  },
  {
    "path": "stubs/local_storage.py",
    "chars": 17,
    "preview": "storage = dict()\n"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/cli/test_provider.py",
    "chars": 2438,
    "preview": "from xml.etree import ElementTree\n\nimport yaml\nimport pytest\n\nfrom vuecli.provider.provider import Provider\n\n\n@pytest.fi"
  },
  {
    "path": "tests/pytest.ini",
    "chars": 86,
    "preview": "[pytest]\naddopts =\n    --new-first\n    --failed-first\n    --capture=no\n    --tb=short\n"
  },
  {
    "path": "tests/selenium/.gitignore",
    "chars": 19,
    "preview": "_html\nchromedriver\n"
  },
  {
    "path": "tests/selenium/chromedriver.py",
    "chars": 752,
    "preview": "import re\nfrom subprocess import run\n\nimport pyderman\nimport requests\n\nLatestReleaseUrl = (\n    \"https://chromedriver.st"
  },
  {
    "path": "tests/selenium/conftest.py",
    "chars": 8443,
    "preview": "import re\nimport os\nimport json\nimport inspect\nfrom pathlib import Path\nfrom contextlib import contextmanager\nfrom textw"
  },
  {
    "path": "tests/selenium/pytest.ini",
    "chars": 169,
    "preview": "[pytest]\naddopts =\n    -s\n    # allow debug console display values\n    --tb=short\n    # do not display standard tracebac"
  },
  {
    "path": "tests/selenium/test_api.py",
    "chars": 2337,
    "preview": "from vue import *\n\n\ndef test_app_with_props_and_data(selenium):\n    def app_with_props_data(el):\n        class App(VueCo"
  },
  {
    "path": "tests/selenium/test_examples.py",
    "chars": 4751,
    "preview": "import time\n\nfrom selenium.webdriver.common.action_chains import ActionChains\nfrom selenium.webdriver.common.by import B"
  },
  {
    "path": "tests/selenium/test_guide/test_components/test_custom_events.py",
    "chars": 1257,
    "preview": "from vue import *\n\nfrom selenium.webdriver.common.by import By\n\n\ndef test_customize_v_model(selenium):\n    def app(el):\n"
  },
  {
    "path": "tests/selenium/test_guide/test_components/test_props.py",
    "chars": 3855,
    "preview": "import pytest\n\nfrom vue import *\n\n\ndef test_prop_types(selenium):\n    def app(el):\n        class SubComponent(VueCompone"
  },
  {
    "path": "tests/selenium/test_guide/test_essentials/test_components_basics.py",
    "chars": 2685,
    "preview": "from vue import *\n\nfrom selenium.webdriver.common.by import By\n\n\ndef test_data_must_be_function(selenium):\n    def app(e"
  },
  {
    "path": "tests/selenium/test_guide/test_essentials/test_computed_properties.py",
    "chars": 1578,
    "preview": "from vue import *\n\n\ndef test_basics(selenium):\n    class ComputedPropertiesBasics(VueComponent):\n        message = \"mess"
  },
  {
    "path": "tests/selenium/test_guide/test_essentials/test_event_handler.py",
    "chars": 544,
    "preview": "from vue import *\n\nfrom selenium.webdriver.common.by import By\n\n\ndef test_inline_handlers(selenium):\n    class InlineHan"
  },
  {
    "path": "tests/selenium/test_guide/test_essentials/test_instance.py",
    "chars": 1889,
    "preview": "from vue import *\n\n\ndef test_lifecycle_hooks(selenium):\n    def lifecycle_hooks(el):\n        class ComponentLifecycleHoo"
  },
  {
    "path": "tests/selenium/test_guide/test_essentials/test_introduction.py",
    "chars": 3569,
    "preview": "from vue import *\n\nfrom selenium.webdriver.common.by import By\n\n\ndef test_declarative_rendering(selenium):\n    class Dec"
  },
  {
    "path": "tests/selenium/test_guide/test_essentials/test_list_rendering.py",
    "chars": 1355,
    "preview": "from vue import *\n\n\ndef test_mutation_methods(selenium):\n    class MutationMethods(VueComponent):\n        array = [1, 2,"
  },
  {
    "path": "tests/selenium/test_guide/test_reusability_composition/test_filters.py",
    "chars": 820,
    "preview": "from vue import *\n\n\ndef test_local_filter(selenium):\n    class ComponentWithFilter(VueComponent):\n        message = \"Mes"
  },
  {
    "path": "tests/selenium/test_guide/test_reusability_composition/test_mixins.py",
    "chars": 721,
    "preview": "from vue import *\n\n\ndef test_local_mixin(selenium):\n    def app(el):\n        class MyMixin(VueMixin):\n            def cr"
  },
  {
    "path": "tests/selenium/test_guide/test_reusability_composition/test_render_function.py",
    "chars": 1877,
    "preview": "from vue import *\n\nfrom selenium.webdriver.common.by import By\n\n\ndef test_basics(selenium):\n    def app(el):\n        cla"
  },
  {
    "path": "tests/selenium/test_vuerouter.py",
    "chars": 6163,
    "preview": "from selenium.webdriver.common.by import By\n\nVueRouterConfig = {\"scripts\": {\"vue-router\": True}}\n\n\ndef test_routes(selen"
  },
  {
    "path": "tests/selenium/test_vuex.py",
    "chars": 8717,
    "preview": "from vue import *\n\nVuexConfig = {\"scripts\": {\"vuex\": True}}\n\n\ndef test_state(selenium):\n    def app(el):\n        class S"
  },
  {
    "path": "tests/test_install.py",
    "chars": 3219,
    "preview": "import json\nimport subprocess\nimport urllib.request\nimport time\nfrom contextlib import contextmanager\n\nimport pytest\n\nfr"
  },
  {
    "path": "tests/unit/test_bridge/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/unit/test_bridge/mocks.py",
    "chars": 1548,
    "preview": "class VueMock:\n    @staticmethod\n    def set(obj, key, value):\n        setattr(obj, key, value)\n\n    @staticmethod\n    d"
  },
  {
    "path": "tests/unit/test_bridge/test_dict.py",
    "chars": 3824,
    "preview": "from unittest import mock\n\nimport pytest\n\nfrom tests.unit.test_bridge.mocks import ObjectMock, VueMock\n\nfrom vue.bridge."
  },
  {
    "path": "tests/unit/test_bridge/test_jsobject.py",
    "chars": 640,
    "preview": "from unittest import mock\n\nfrom browser import window\n\nfrom vue.bridge import Object\nfrom vue.bridge.vue_instance import"
  },
  {
    "path": "tests/unit/test_bridge/test_list.py",
    "chars": 2819,
    "preview": "import pytest\n\nfrom vue.bridge.list import List\nfrom .mocks import ArrayMock\n\n\nclass TestList:\n    def test_len(self):\n "
  },
  {
    "path": "tests/unit/test_bridge/test_vue.py",
    "chars": 1853,
    "preview": "from unittest import mock\nimport pytest\nfrom .mocks import ArrayMock\nfrom vue.bridge.vue_instance import VueInstance\nfro"
  },
  {
    "path": "tests/unit/test_bridge/test_vuex.py",
    "chars": 1387,
    "preview": "from vue.bridge.vuex_instance import VuexInstance\n\n\nclass Getter:\n    def __init__(self, **kwargs):\n        self.vars = "
  },
  {
    "path": "tests/unit/test_transformers/conftest.py",
    "chars": 396,
    "preview": "from unittest import mock\nfrom vue.bridge.list import window as list_window\nimport pytest\n\n\nclass ArrayMock(list):\n    d"
  },
  {
    "path": "tests/unit/test_transformers/test_component.py",
    "chars": 8158,
    "preview": "from unittest import mock\n\nfrom vue import *\n\n\ndef test_empty():\n    class Empty(VueComponent):\n        pass\n\n    assert"
  },
  {
    "path": "tests/unit/test_transformers/test_router.py",
    "chars": 1958,
    "preview": "from unittest.mock import Mock\nfrom vue import *\n\n\nclass TestVueRoute:\n    def test_path_and_component(self):\n        ro"
  },
  {
    "path": "tests/unit/test_transformers/test_store.py",
    "chars": 1637,
    "preview": "from vue import *\nfrom vue.bridge import VuexInstance\n\n\ndef test_state():\n    class Store(VueStore):\n        attribute ="
  },
  {
    "path": "tests/unit/test_utils.py",
    "chars": 1987,
    "preview": "import pytest\nfrom unittest import mock\nfrom vue import utils\nfrom vue.utils import *\n\n\n@pytest.fixture\ndef fix_load_and"
  },
  {
    "path": "tests/unit/test_vue.py",
    "chars": 6387,
    "preview": "from contextlib import contextmanager\nfrom unittest import mock\n\nimport pytest\n\nfrom vue import Vue, VueComponent, VueDi"
  },
  {
    "path": "vue/__init__.py",
    "chars": 465,
    "preview": "from .__version__ import __version__\n\ntry:\n    from .vue import VueComponent, VueMixin, Vue, VueDirective, VuePlugin\n   "
  },
  {
    "path": "vue/bridge/__init__.py",
    "chars": 151,
    "preview": "from .object import Object\nfrom .list import List\nfrom .dict import Dict\nfrom .vue_instance import VueInstance\nfrom .vue"
  },
  {
    "path": "vue/bridge/dict.py",
    "chars": 3263,
    "preview": "from browser import window\nfrom .object import Object\n\n\nclass Dict(Object):\n    @staticmethod\n    def __unwraps__():\n   "
  },
  {
    "path": "vue/bridge/list.py",
    "chars": 3440,
    "preview": "from browser import window\nfrom .object import Object\n\n\nclass List(Object):\n    @staticmethod\n    def __unwraps__():\n   "
  },
  {
    "path": "vue/bridge/object.py",
    "chars": 1220,
    "preview": "class Object:\n    SubClasses = []\n    Default = None\n\n    @classmethod\n    def sub_classes(cls):\n        return cls.SubC"
  },
  {
    "path": "vue/bridge/vue_instance.py",
    "chars": 1082,
    "preview": "from .object import Object\nfrom .vuex_instance import VuexInstance\n\n\nclass VueInstance(Object):\n    @staticmethod\n    de"
  },
  {
    "path": "vue/bridge/vuex_instance.py",
    "chars": 1556,
    "preview": "class VuexInstance:\n    def __init__(\n        self,\n        state=None,\n        getters=None,\n        root_state=None,\n "
  },
  {
    "path": "vue/decorators/__init__.py",
    "chars": 323,
    "preview": "from .computed import computed\nfrom .prop import validator\nfrom .directive import directive, DirectiveHook\nfrom .filters"
  },
  {
    "path": "vue/decorators/action.py",
    "chars": 804,
    "preview": "from .base import pyjs_bridge, VueDecorator\nfrom vue.bridge import VuexInstance\n\n\nclass Action(VueDecorator):\n    def __"
  },
  {
    "path": "vue/decorators/base.py",
    "chars": 498,
    "preview": "from vue.bridge import Object\nimport javascript\n\n\nclass VueDecorator:\n    __key__ = None\n    __value__ = None\n\n\ndef pyjs"
  },
  {
    "path": "vue/decorators/components.py",
    "chars": 164,
    "preview": "from .base import VueDecorator\n\n\nclass Components(VueDecorator):\n    __key__ = \"components\"\n\n    def __init__(self, *mix"
  },
  {
    "path": "vue/decorators/computed.py",
    "chars": 593,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\nclass Computed(VueDecorator):\n    def __init__(self, fn):\n        self.__k"
  },
  {
    "path": "vue/decorators/custom.py",
    "chars": 411,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\nclass Custom(VueDecorator):\n    def __init__(self, fn, key, name=None, sta"
  },
  {
    "path": "vue/decorators/data.py",
    "chars": 240,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\nclass Data(VueDecorator):\n    def __init__(self, name, value):\n        sel"
  },
  {
    "path": "vue/decorators/directive.py",
    "chars": 812,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\ndef map_hook(hook_name):\n    if hook_name == \"component_updated\":\n        "
  },
  {
    "path": "vue/decorators/extends.py",
    "chars": 157,
    "preview": "from .base import VueDecorator\n\n\nclass Extends(VueDecorator):\n    __key__ = \"extends\"\n\n    def __init__(self, init_dict)"
  },
  {
    "path": "vue/decorators/filters.py",
    "chars": 269,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\nclass Filter(VueDecorator):\n    def __init__(self, fn, name):\n        self"
  },
  {
    "path": "vue/decorators/getter.py",
    "chars": 624,
    "preview": "from .base import pyjs_bridge, VueDecorator\nfrom vue.bridge import VuexInstance\n\n\nclass Getter(VueDecorator):\n    def __"
  },
  {
    "path": "vue/decorators/lifecycle_hook.py",
    "chars": 643,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\nclass LifecycleHook(VueDecorator):\n    mapping = {\n        \"before_create\""
  },
  {
    "path": "vue/decorators/method.py",
    "chars": 650,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\nclass Method(VueDecorator):\n    def __init__(self, fn):\n        if hasattr"
  },
  {
    "path": "vue/decorators/mixins.py",
    "chars": 156,
    "preview": "from .base import VueDecorator\n\n\nclass Mixins(VueDecorator):\n    __key__ = \"mixins\"\n\n    def __init__(self, *mixins):\n  "
  },
  {
    "path": "vue/decorators/model.py",
    "chars": 284,
    "preview": "from .base import VueDecorator\n\n\nclass Model(VueDecorator):\n    __key__ = \"model\"\n\n    def __init__(self, prop=\"value\", "
  },
  {
    "path": "vue/decorators/mutation.py",
    "chars": 479,
    "preview": "from vue.bridge import VuexInstance\nfrom .base import pyjs_bridge, VueDecorator\n\n\nclass Mutation(VueDecorator):\n    def "
  },
  {
    "path": "vue/decorators/plugin.py",
    "chars": 158,
    "preview": "from .base import VueDecorator\n\n\nclass Plugin(VueDecorator):\n    __key__ = \"plugins\"\n\n    def __init__(self, plugins):\n "
  },
  {
    "path": "vue/decorators/prop.py",
    "chars": 821,
    "preview": "from browser import window\nfrom .base import pyjs_bridge, VueDecorator\n\n\nclass Prop(VueDecorator):\n    type_map = {\n    "
  },
  {
    "path": "vue/decorators/render.py",
    "chars": 193,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\nclass Render(VueDecorator):\n    __key__ = \"render\"\n\n    def __init__(self,"
  },
  {
    "path": "vue/decorators/routes.py",
    "chars": 155,
    "preview": "from .base import VueDecorator\n\n\nclass Routes(VueDecorator):\n    __key__ = \"routes\"\n\n    def __init__(self, routes):\n   "
  },
  {
    "path": "vue/decorators/state.py",
    "chars": 167,
    "preview": "from .base import VueDecorator\n\n\nclass State(VueDecorator):\n    def __init__(self, name, value):\n        self.__key__ = "
  },
  {
    "path": "vue/decorators/template.py",
    "chars": 157,
    "preview": "from .base import VueDecorator\n\n\nclass Template(VueDecorator):\n    __key__ = \"template\"\n\n    def __init__(self, template"
  },
  {
    "path": "vue/decorators/watcher.py",
    "chars": 570,
    "preview": "from .base import pyjs_bridge, VueDecorator\n\n\nclass Watcher(VueDecorator):\n    def __init__(self, name, fn, deep=False, "
  },
  {
    "path": "vue/router.py",
    "chars": 785,
    "preview": "from browser import window\nfrom .transformers import Transformable, VueRouterTransformer\n\n\nclass VueRouter(Transformable"
  },
  {
    "path": "vue/store.py",
    "chars": 1123,
    "preview": "from browser import window\nfrom .transformers import Transformable, VueStoreTransformer\nfrom .bridge import Object\nfrom "
  },
  {
    "path": "vue/transformers.py",
    "chars": 8481,
    "preview": "\"\"\"\nTransformers are used to create dictionaries to initialize Vue-Objects from Python classes.\n\ne.g.\n```python\nclass Co"
  },
  {
    "path": "vue/utils.py",
    "chars": 570,
    "preview": "from browser import window, load\n\nCACHE = {}\n\n\ndef js_load(path):\n    if path in CACHE:\n        return CACHE[path]\n    b"
  },
  {
    "path": "vue/vue.py",
    "chars": 2597,
    "preview": "from browser import window\nfrom .transformers import (\n    VueComponentTransformer,\n    Transformable,\n    VueDirectiveT"
  },
  {
    "path": "vuecli/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "vuecli/cli.py",
    "chars": 2639,
    "preview": "import sys\nimport argparse\nfrom tempfile import TemporaryDirectory as TempDir\nfrom pathlib import Path\n\nfrom vuecli.prov"
  },
  {
    "path": "vuecli/index.html",
    "chars": 612,
    "preview": "<html>\n<head>\n  {% for script in scripts.values() %}\n    <script src=\"{{ script }}\"></script>\n  {% endfor %}\n\n  {% for s"
  },
  {
    "path": "vuecli/provider/__init__.py",
    "chars": 271,
    "preview": "import pkg_resources as _pkgres\n\n\ndef _load(ep):\n    try:\n        return ep.load()\n    except ModuleNotFoundError:\n     "
  },
  {
    "path": "vuecli/provider/flask.py",
    "chars": 1131,
    "preview": "import os\nfrom pathlib import Path\n\nfrom flask import Flask as FlaskApp, send_file, abort\n\nfrom .provider import Provide"
  },
  {
    "path": "vuecli/provider/provider.py",
    "chars": 3204,
    "preview": "from pkg_resources import resource_filename, resource_string\nfrom functools import partial\nfrom pathlib import Path\n\nimp"
  },
  {
    "path": "vuecli/provider/static.py",
    "chars": 2564,
    "preview": "import os\nimport sys\nimport shutil\nfrom tempfile import TemporaryDirectory as TempDir\nimport subprocess\nfrom pathlib imp"
  }
]

About this extraction

This page contains the full source code of the stefanhoelzl/vue.py GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 158 files (245.3 KB), approximately 67.0k tokens, and a symbol index with 623 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!