Full Code of google/uniflow-polymer for AI

master dce13b9d10ff cached
33 files
171.1 KB
40.3k tokens
1 requests
Download .txt
Repository: google/uniflow-polymer
Branch: master
Commit: dce13b9d10ff
Files: 33
Total size: 171.1 KB

Directory structure:
gitextract_6nfjv9bm/

├── .bower.json
├── .gitignore
├── CONTRIBUTING
├── LICENSE
├── README.md
├── action-dispatcher.html
├── action-emitter.html
├── all-imports.html
├── analysis.json
├── application-state.html
├── bower.json
├── index.html
├── list-view.html
├── model-view.html
├── state-aware.html
├── state-mutator.html
├── test/
│   ├── action-dispatcher_test.html
│   ├── action-emitter_test.html
│   ├── application-state_test.html
│   ├── list-view_test.html
│   ├── model-view_test.html
│   └── state-aware_test.html
└── todomvc/
    ├── README.md
    ├── elements/
    │   ├── todo-action-dispatcher.html
    │   ├── todo-app.html
    │   ├── todo-footer.html
    │   ├── todo-header.html
    │   ├── todo-item.html
    │   ├── todo-items.html
    │   └── todo-styles.html
    ├── index.html
    ├── manifest.json
    └── scripts/
        └── actions.js

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

================================================
FILE: .bower.json
================================================
{
  "name": "uniflow-polymer",
  "version": "0.6.0",
  "authors": [
    "Andrei Militeev <militeev@gmail.com>"
  ],
  "description": "A straightforward way to architect Polymer application using unidirectional data flow.",
  "keywords": [
    "web-component",
    "polymer",
    "uniflow",
    "unidirectional",
    "flow"
  ],
  "license": "https://raw.githubusercontent.com/google/uniflow-polymer/master/LICENSE",
  "homepage": "https://github.com/google/uniflow-polymer/",
  "ignore": [
    "/.*",
    "/test/"
  ],
  "dependencies": {
    "polymer": "Polymer/polymer#^2.0.0"
  },
  "devDependencies": {
    "app-route": "PolymerElements/app-route#^2.0.2",
    "iron-component-page": "PolymerElements/iron-component-page#^3.0.0",
    "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^2.0.0",
    "iron-icon": "PolymerElements/iron-icon#^2.0.0",
    "iron-icons": "PolymerElements/iron-icons#^2.0.0",
    "iron-test-helpers": "PolymerElements/iron-test-helpers#^2.0.0",
    "paper-styles": "PolymerElements/paper-styles#^2.0.0",
    "test-fixture": "PolymerElements/test-fixture#3.0.0-rc.1",
    "web-component-tester": "^v6.0.0",
    "todomvc-app-css": "^2.1.0",
    "iron-localstorage": "PolymerElements/iron-localstorage#^2.0.0"
  }
}


================================================
FILE: .gitignore
================================================
bower_components/
.idea/

================================================
FILE: CONTRIBUTING
================================================
Want to contribute? Great! First, read this page (including the small print at the end).

### Before you contribute
Before we can use your code, you must sign the
[Google Individual Contributor License Agreement]
(https://cla.developers.google.com/about/google-individual)
(CLA), which you can do online. The CLA is necessary mainly because you own the
copyright to your changes, even after your contribution becomes part of our
codebase, so we need your permission to use and distribute your code. We also
need to be sure of various other things—for instance that you'll tell us if you
know that your code infringes on other people's patents. You don't have to sign
the CLA until after you've submitted your code for review and a member has
approved it, but you must do it before we can put your code into our codebase.
Before you start working on a larger contribution, you should get in touch with
us first through the issue tracker with your idea so that we can help out and
possibly guide you. Coordinating up front makes it much easier to avoid
frustration later on.

### Code reviews
All submissions, including submissions by project members, require review. We
use Github pull requests for this purpose.

### The small print
Contributions made by corporations are covered by a different agreement than
the one above, the
[Software Grant and Corporate Contributor License Agreement]
(https://cla.developers.google.com/about/google-corporate).


================================================
FILE: LICENSE
================================================


                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2016 Google Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.



================================================
FILE: README.md
================================================
# UniFlow for Polymer 2.x

Set of mixins to enable uni-directional data flow in Polymer application.

**Important!**

*This library was developed as part of internal project at Google and isn't directly affiliated with the Polymer project (although Polymer team has provided some good feedback on UniFlow implementation).*

## History & Overview

When you start working on a new Polymer application, it's easy to start and build the first few elements, and make them communicate via events and data binding, so everything looks nice and rosy. However, as the number and complexity of elements grows, it becomes increasingly difficult to manage relationships between them, trace where/when the data changes happened, and debug the problems. So this project started as an attempt by our team at Google to find a good way to architect large Polymer application. 

Inspired by React's community Flux (and, later, Redux) architecture, we implemented a unidirectional data flow pattern (data down, events up) for Polymer. We found that when using UniFlow application code becomes more streamlined (e.g. it is clear what the responsibilities of each element are) and much easier to manage; the code has fewer bugs, and debugging is a lot more efficient. Adding new functionality no longer exponentially increases complexity.

This project was also inspired by Backbone Marionette. Backbone.js back in the days of its glory was a great library that provided a nice set of building blocks for building JavaScript applications. However, it left much of the application design, architecture and scalability to the developer, including memory management, view management, and more. Marionette brought an application architecture to Backbone, along with built in view management and memory management. It was designed to be a lightweight and flexible library of tools that sits on top of Backbone, providing the framework for building a scalable application. Uniflow strives to achieve similar goal for Polymer. 

We feel that Polymer, and web components in general, is a great concept that takes interoperability and encapsulation in Web development to the next level. But it lacked the patterns for building large and complex applications, and this is the void we expect UniFlow to fill. It is still in beta, so breaking changes may be happening before the first release. However, we believe that abstractions implemented in the library can be useful for Polymer community, so we encourage people to try, fork, ask questions, send  comments, and submit pull requests.

## Applicability

This library implements the architectural pattern called 'unidirectional data flow'. It works best if application logic involves complicated data management, when multiple elements need to have access to or modify the same data. Even though the pattern can be implemented just using built-in Polymer concepts, such as custom events and data binding, the UniFlow library provides a useful set of tools and abstractions, and helps to structure application code.

## Implementation

UniFlow is implemented as a set of mixins that developers apply to their elements. It is assumed that each application has a singleton application element that maintains state of entire application. Each element that needs access to the data is bound, directly or indirectly, to sub-tree of application state tree. Two way data binding is never used to send data up, from child to parent, so only parent elements send data to children using one way data binding. Child elements, in turn, send the events (emit actions) responding to user actions, indicating that the data may need to be modified. Special non-visual elements called action dispatchers mutate the data, then all elements listening to the data changes render new data. 

# API Documentation

## Action Dispatcher

Use UniFlow.ActionDispatcher for non-visual elements that process actions emitted by visual
elements. Action dispatchers usually placed at the application level. Each action dispatcher
element gets a chance to process the action in the order the elements are present in the
DOM tree. It is important that action dispatcher elements get two-way data binding to
application state as follows:

   <action-dispatcher state="{{state}}"></action-dispatcher>

Action dispatcher elements can include nested action dispatchers, so you can have a
hierarchical organization of action dispatchers.

### Example:

#### HTML:
```html
<dom-module id="parent-dispatcher">
<template>
  <child-dispatcher-a state="{{state}}"></child-dispatcher-a>
  <child-dispatcher-b state="{{state}}"></child-dispatcher-b>
</template>
</dom-module>
```

#### JavaScript:

```javascript
class ParentDispatcher extends UniFlow.ActionDispatcher(Polymer.Element) {

  static get is() { return 'parent-dispatcher'; }
  
  MY_ACTION(detail) {
   // do MY_ACTION processing here
   // return false if you want to prevent other action dispatchers from
   // further processing of this action
  };
}

customElements.define(ParentDispatcher.is, ParentDispatcher);
```

## Action Emitter

Whenever element needs to emit an action, this mixin should be used. Action object must always include type property.

## Application State

Assign this mixin to your main application element. It provides global
state and functionality to maintain individual elements states. This mixin
is responsible for notifying all state-aware elements about their state
changes (provided those elements have `statePath` property defined).
Only one element in the application is supposed to have this mixin.

### Example:

#### HTML:
```html
<template>
  <!-- action dispatchers in the order of action processing -->
  <action-dispatcher-a state="{{state}}"></action-dispatcher-a>
  <action-dispatcher-b state="{{state}}"></action-dispatcher-b>
  
  <!-- state-aware elements -->
  <some-element state-path="state.someElement"></some-element>
</template>
```
#### JavaScript:

```javascript
class MyApp extends UniFlow.ApplicationState(Polymer.Element) {

  static get is() { return 'my-app'; }

  connectedCallback() {
    super.connectedCallback();
    this.state = {
      someElement: {}
    }
  }
}

customElements.define(MayApp.is, MyApp);
```

In the example above, `<some-element>` will receive notification of any changes to the state,
as if it was declared as follows:

```html
<some-element state="[[state]]"></some-element>
```

Also, if `<some-element>` has `propertyA`, on element attach this property will be assigned
the value of `state.someElement.propertyA`, and receive all notification of the property change
whenever the corresponding data in state tree changes. This essentially translates to following
declaration:

```html
<some-element state="[[state]]"
              propertyA="[[state.someElement.propertyA]]">
</some-element>
```

Note that data binding is one-way in both cases. Although state-aware elements can modify their
own state, it is considered their private state and no other elements will be notified of those
changes.

## List View

This mixin used by elements that need to render multiple models backed
by 'list' array. You may want to use ModelView to render individual
models in the list. The mixin supports element selection by setting predefined
$selected property on list elements.

### Example:

#### HTML:

```html
<ul>
  <template id="list-template" is="dom-repeat" items="[[list]]">
    <li id="[[item.id]]">
      <paper-checkbox checked="{{item.$selected}}">
      <model-view state-path="[[statePath]].list.#[[index]]"></model-view>
    </li>
  </template>
</ul>
Selected: [[selectedCount]] items
<paper-button on-tap="onDeleteTap">Delete</paper-button>
```

#### JavaScript:

```javascript
class ListElement extends Polymer.GestureEventListeners(UniFlow.ListView(UniFlow.StateAware(Polymer.Element))) {

  static get is() { return "list-element"; }

  onDeleteTap() {
    this.deleteSelected();
  }

}

customElements.define(ListElement.is, ListElement);
```

In the example above list view element is also state-aware, meaning it has its own place
in the application state tree. Assuming it has been declared as follows:

```html
<list-element state-path="state.listElement"></list-element>
```

it will be rendering `state.listElement.list` and observing changes to it. Each `model-view`
within dom-repeat template will have `state-path` property  set to
`state.listElement.list.#<index>`  where `index` is the element's index in the array.

## Model View

Element rendering data represented by a single object (model) in the
application state should use ModelView mixin. Model View is a powerful
concept that encapsulates model data (likely the data received from the
server and to be persisted to the server if modified as a result of user
actions), status (validity of the data, flag that data was modified,
notifications for the user, etc.). Auxiliary data supplied by action
dispatchers and needed for display purposes or element's logic
should be defined as element’s properties. Same applies to data
created/modified by the element but not intended to be persisted.
If `StateAware` mixin is used along with `ModelView`, you can take advantage
of statePath property that indicates path to the element's state in the
application state tree. Whenever any data is mutated by action dispatchers
at statePath or below, the element will receive notification of its
properties' change (even if there is no explicit binding for those
properties). See `UniFlow.StateAware` for more details and example.
ModelView mixin defines some properties that are intended to be overridden
in the elements:

+ `validation` property allows to specify validation rules
that will be applied when validateModel() method is called. As a result of
this method validation status will be updated to indicate result for each
model field that has validation rule associated with it.
+ `saveAction` property indicates which action should be emitted when
saveModel method is called to perform save of the model.
+ `getMessage` should be overridden with the function returning message
string for given error code (to translate validation error code to message)


### Example:

#### HTML:

```html
<template>
 Model: [[model.id]]
 <paper-input value="{{model.name}}"
              label="Name"
              invalid="[[status.validation.name.invalid]]"
              error-message="[[status.validation.name.errorMessage]]">
 </paper-input>
 <paper-button on-tap="onSaveTap">Save</paper-button>
</template>
```

#### JavaScript:

```javascript
class MyModel extends Polymer.GestureEventListeners(UniFlow.ModelView(Polymer.Element)) {

  static get is() { return "my-model"; }
  
  get saveAction() { return 'MY_SAVE'; }
  
  get validation() { 
    return {
      name: (value) => {
        if (!value || !value.trim()) {
          return 'Name is not specified';
        }
      }
    }
  }
  
  connectedCallback() {
   super.connectedCallback();
   this.fetchData();
  },
  
  fetchData() {
   this.emitAction({
     type: 'MY_FETCH',
     path: 'model'
   });
  },
  
  onSaveTap() {
   this.validateAndSave();
  }
}

customElements.define(MyModel.is, MyModel);
```

In the example above model view has input field for `name` property and Save button. On
element attach the action is emitted to fetch the model's data. Note that in `emitAction()` method
the path is specified as `'model'`. ActionEmitter mixin is responsible of expanding the path
with element's state path, ensuring that when action dispatcher gets to process the action, the
path contains full path in the state tree. So assuming that `my-model` is declared as follows:

```html
<my-model state-path="state.myModel"></my-model>
```

the path in `MY_FETCH` action gets expanded to `state.myModel.model`.

`validation` property is an object that contains methods for fields validation. The keys in
this object should match model field names, the values are validation methods. Method receives
current value of the field and should return non-falsy value (string or error code) if the value
of the field didn't pass validation. `status.validation` object will be populated with the results
of validation with the keys matching field names and values being objects containing two fields:
- `invalid`: true when the value is not valid
- `errorMessage`: the message to show to user


So in the example above if user clicks on Save button with name not entered, they will get
'Name is not specified' error message on the input element. When the name is non-empty, validation
will pass and `MY_SAVE` action will be emitted with model passed as a parameter and `'model'` as
path.

## State Aware

 Key mixin that must be assigned to all elements that need to access
 application state and/or have access to the application element. The element is
 notified of any changes to application's state, as well as all its properties
 when they're modified by state mutator elements. `state-path` property must
 be used to identify path to element's state in application state tree. 


### Example:

#### HTML:

```html
<template>
 <div>Value A: [[state.valueA]]</div>
 <div>Value B: [[valueB]]</div>
</template>
```

#### JavaScript:

```javascript
class MyElement extends UniFlow.StateAware(Polymer.Element) {

  static get is() { return 'my-element'; }
  
  properties: {
    valueB: String
  }
}

customElements.define(MyElement.is, MyElement);
```

When above element is declared as follows:

```html
<my-element state-path="state.myElement"></my-element>
```

it will be notified about changes (and render those) to `state.valueA` or
`state.myElement.valueB` in action dispatchers or other state mutating
elements.

## State Mutator

Some non-visual elements, like action dispatchers, need to modify application
state, in which case they should have this mixin assigned. Implements state-
aware and re-declares state property with notify attribute. State mutator elements
are only supposed to exist at the application level.



================================================
FILE: action-dispatcher.html
================================================
<!--
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="state-aware.html">
<link rel="import" href="state-mutator.html">


<script type="text/javascript">
  var UniFlow = window.UniFlow || {};

  /**
   Use UniFlow.ActionDispatcher for non-visual elements that process actions emitted by visual
   elements. Action dispatchers usually placed at the application level. Each action dispatcher
   element gets a chance to process the action in the order the elements are present in the
   DOM tree. It is important that action dispatcher elements get two-way data binding to
   application state as follows:

   <action-dispatcher state="{{state}}"></action-dispatcher>

   Action dispatcher elements can include nested action dispatchers, so you can have a
   hierarchical organization of action dispatchers.

   ### Example:

   #### HTML:

   <dom-module id="parent-dispatcher">
   <template>
   <child-dispatcher-a state="{{state}}"></child-dispatcher-a>
   <child-dispatcher-b state="{{state}}"></child-dispatcher-b>
   </template>
   </dom-module>

   #### JavaScript:
   class ParentDispatcher extends UniFlow.ActionDispatcher(Polymer.Element) {
         static get is() { return 'parent-dispatcher'; }

         MY_ACTION(detail) {
           // do MY_ACTION processing here
           // return false if you want to prevent other action dispatchers from
           // further processing of this action
         };
       }

   customElements.define(ParentDispatcher.is, ParentDispatcher);

   @polymer
   @mixinFunction
   @appliesMixin UniFlow.StateMutator
   */
  UniFlow.ActionDispatcher = Polymer.dedupingMixin((base) =>
      class extends UniFlow.StateMutator(base) {
        static get properties() {
          return {
            actionDispatcher: {
              type: Boolean,
              value: true,
              reflectToAttribute: true
            }
          }
        }

        /**
         * Dispatches action by invoking the method with the name that matches
         * action type (`detail.type`) passing detail object as a parameter;
         * also selects all children action dispatchers in the element's DOM tree and
         * invokes dispatchAction method on them. False returned by an action dispatcher method
         * results in dispatchAction method returning false (which in turn stops further processing
         * of the action by other action dispatchers).
         *
         * @param {{type: string}} detail
         * @return {boolean}
         */
        dispatchAction(detail) {
          if (this[detail.type] && typeof this[detail.type] === 'function') {
            if (this[detail.type](detail) === false) {
              return false;
            }
          }
          //dispatch action on nested dispatchers
          var nodes = this.root ? this.root.querySelectorAll('[action-dispatcher]') : [];
          return Array.prototype.every.call(nodes,
              (element) => element.dispatchAction(detail) !== false);
        }

      });
</script>

================================================
FILE: action-emitter.html
================================================
<!--
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<link rel="import" href="../polymer/polymer-element.html">


<script>
  var UniFlow = window.UniFlow || {};

  /**
   Whenever element needs to emit an action, this mixin should be applied.
   Action object must always include type property.

   @polymer
   @mixinFunction
  */
  UniFlow.ActionEmitter = Polymer.dedupingMixin((base) =>
    class extends base {

      /**
       * Fired when action is emitted to be processed by action dispatcher.
       *
       * @event dispatch-action
       */

      /**
       * Emits the action described by detail parameter. Detail object must always
       * include type property. To emit the action we use custom event
       * mechanism. Application element listens to the `dispatch-action` event and
       * invokes `dispatchAction` methods for all action dispatchers associated with
       * the application. Make sure your element is attached to DOM tree, otherwise
       * event will never reach your application element. It is a good practice to
       * ensure that `state` property is initialized (for state-aware elements) before
       * emitting any actions (as `state` property is initialized on attach).
       *
       * @param {*} detail
       */
      emitAction(detail) {
        if (detail.path && this.statePath) {
          if (!detail.path.startsWith('state.')) {
            detail.path = this.statePath + '.' + detail.path;
          }
        }
        
        this.dispatchEvent(new CustomEvent('dispatch-action', {
          detail,
          bubbles: true,
          composed: true
        }));
      }

    });
</script>

================================================
FILE: all-imports.html
================================================
<link rel="import" href="action-dispatcher.html">
<link rel="import" href="action-emitter.html">
<link rel="import" href="application-state.html">
<link rel="import" href="list-view.html">
<link rel="import" href="model-view.html">
<link rel="import" href="state-aware.html">
<link rel="import" href="state-mutator.html">


================================================
FILE: analysis.json
================================================
{
  "schema_version": "1.0.0",
  "mixins": [
    {
      "description": "   Key mixin that must be assigned to all elements that need to access\n   application state and/or have access to the application element. The element is\n   notified of any changes to application's state, as well as all its properties\n   when they're modified by state mutator elements. `state-path` property must\n   be used to identify path to element's state in application state tree. \n\n   ### Example:\n\n   #### HTML:\n\n      <template>\n\n        <div>Value A: [[state.valueA]]</div>\n        <div>Value B: [[valueB]]</div>\n\n      </template>\n\n   #### JavaScript:\n\n      class MyElement extends UniFlow.StateAware(Polymer.Element) {\n        static get is() { return 'my-element'; }\n\n        static get properties() {\n          return {\n            valueB: String\n          }\n        }\n      }\n\n      customElement.define(MyElement.is, MyElement);\n\n   When above element is declared as follows:\n\n      <my-element state-path=\"state.myElement\"></my-element>\n\n   it will be notified about changes (and render those) to `state.valueA` or\n   `state.myElement.valueB` in action dispatchers or other state mutating\n   elements.\n\n   ",
      "summary": "",
      "path": "state-aware.html",
      "properties": [
        {
          "name": "state",
          "type": "Object",
          "description": "Application state, shared among application and all state-aware elements.\nThis property is initialized when element is attached.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 74,
              "column": 10
            },
            "end": {
              "line": 76,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          }
        },
        {
          "name": "statePath",
          "type": "string",
          "description": "Path to element state in the application state tree.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 81,
              "column": 10
            },
            "end": {
              "line": 83,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          }
        },
        {
          "name": "stateAware",
          "type": "boolean",
          "description": "",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 85,
              "column": 10
            },
            "end": {
              "line": 89,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "defaultValue": "true"
        },
        {
          "name": "application",
          "type": "Object",
          "description": "Application-level object that allows all state-aware objects access\nto application element. This property is shared among all state-aware\nelements and has element field initialized with reference to application\nelement. Any state-aware element can access application element using\n`getApplication()` method. Also, all state-aware elements\nadd themselves to `application.stateAwareElements` array on attach (and\nremove on detach); this list is used to send notification about\nelement state changes.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 101,
              "column": 10
            },
            "end": {
              "line": 107,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          }
        }
      ],
      "methods": [
        {
          "name": "connectedCallback",
          "description": "Adds this element to the list of state-aware elements in the application.\nSets the value of state property to the state of the application element.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 116,
              "column": 6
            },
            "end": {
              "line": 132,
              "column": 7
            }
          },
          "metadata": {},
          "params": []
        },
        {
          "name": "disconnectedCallback",
          "description": "Removes this element from the list of state-aware elements in the\napplication.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 138,
              "column": 6
            },
            "end": {
              "line": 145,
              "column": 7
            }
          },
          "metadata": {},
          "params": []
        },
        {
          "name": "getApplication",
          "description": "Returns application element.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 151,
              "column": 6
            },
            "end": {
              "line": 153,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "return": {
            "type": "Element"
          }
        }
      ],
      "staticMethods": [],
      "demos": [],
      "metadata": {},
      "sourceRange": {
        "start": {
          "line": 65,
          "column": 2
        },
        "end": {
          "line": 155,
          "column": 6
        }
      },
      "privacy": "public",
      "name": "UniFlow.StateAware",
      "attributes": [
        {
          "name": "state",
          "description": "Application state, shared among application and all state-aware elements.\nThis property is initialized when element is attached.",
          "sourceRange": {
            "start": {
              "line": 74,
              "column": 10
            },
            "end": {
              "line": 76,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object"
        },
        {
          "name": "state-path",
          "description": "Path to element state in the application state tree.",
          "sourceRange": {
            "start": {
              "line": 81,
              "column": 10
            },
            "end": {
              "line": 83,
              "column": 11
            }
          },
          "metadata": {},
          "type": "string"
        },
        {
          "name": "state-aware",
          "description": "",
          "sourceRange": {
            "start": {
              "line": 85,
              "column": 10
            },
            "end": {
              "line": 89,
              "column": 11
            }
          },
          "metadata": {},
          "type": "boolean"
        },
        {
          "name": "application",
          "description": "Application-level object that allows all state-aware objects access\nto application element. This property is shared among all state-aware\nelements and has element field initialized with reference to application\nelement. Any state-aware element can access application element using\n`getApplication()` method. Also, all state-aware elements\nadd themselves to `application.stateAwareElements` array on attach (and\nremove on detach); this list is used to send notification about\nelement state changes.",
          "sourceRange": {
            "start": {
              "line": 101,
              "column": 10
            },
            "end": {
              "line": 107,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object"
        }
      ],
      "events": [],
      "styling": {
        "cssVariables": [],
        "selectors": []
      },
      "slots": []
    },
    {
      "description": "   Some non-visual elements, like action dispatchers, need to modify application\n   state, in which case they should have this mixin applied. Implements state-\n   aware and re-declares state property with notify attribute. State mutator elements\n   are only supposed to exist at the application level.\n\n   ",
      "summary": "",
      "path": "state-mutator.html",
      "properties": [
        {
          "name": "state",
          "type": "Object",
          "description": "Application state.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 41,
              "column": 10
            },
            "end": {
              "line": 44,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {
              "notify": true
            }
          }
        },
        {
          "name": "statePath",
          "type": "string",
          "description": "Path to element state in the application state tree.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 81,
              "column": 10
            },
            "end": {
              "line": 83,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "stateAware",
          "type": "boolean",
          "description": "",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 85,
              "column": 10
            },
            "end": {
              "line": 89,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "defaultValue": "true",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "application",
          "type": "Object",
          "description": "Application-level object that allows all state-aware objects access\nto application element. This property is shared among all state-aware\nelements and has element field initialized with reference to application\nelement. Any state-aware element can access application element using\n`getApplication()` method. Also, all state-aware elements\nadd themselves to `application.stateAwareElements` array on attach (and\nremove on detach); this list is used to send notification about\nelement state changes.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 101,
              "column": 10
            },
            "end": {
              "line": 107,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "inheritedFrom": "UniFlow.StateAware"
        }
      ],
      "methods": [
        {
          "name": "connectedCallback",
          "description": "Adds this element to the list of state-aware elements in the application.\nSets the value of state property to the state of the application element.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 116,
              "column": 6
            },
            "end": {
              "line": 132,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "disconnectedCallback",
          "description": "Removes this element from the list of state-aware elements in the\napplication.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 138,
              "column": 6
            },
            "end": {
              "line": 145,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "getApplication",
          "description": "Returns application element.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 151,
              "column": 6
            },
            "end": {
              "line": 153,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "return": {
            "type": "Element"
          },
          "inheritedFrom": "UniFlow.StateAware"
        }
      ],
      "staticMethods": [],
      "demos": [],
      "metadata": {},
      "sourceRange": {
        "start": {
          "line": 33,
          "column": 2
        },
        "end": {
          "line": 48,
          "column": 6
        }
      },
      "privacy": "public",
      "name": "UniFlow.StateMutator",
      "attributes": [
        {
          "name": "state",
          "description": "Application state.",
          "sourceRange": {
            "start": {
              "line": 41,
              "column": 10
            },
            "end": {
              "line": 44,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object"
        },
        {
          "name": "state-path",
          "description": "Path to element state in the application state tree.",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 81,
              "column": 10
            },
            "end": {
              "line": 83,
              "column": 11
            }
          },
          "metadata": {},
          "type": "string",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "state-aware",
          "description": "",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 85,
              "column": 10
            },
            "end": {
              "line": 89,
              "column": 11
            }
          },
          "metadata": {},
          "type": "boolean",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "application",
          "description": "Application-level object that allows all state-aware objects access\nto application element. This property is shared among all state-aware\nelements and has element field initialized with reference to application\nelement. Any state-aware element can access application element using\n`getApplication()` method. Also, all state-aware elements\nadd themselves to `application.stateAwareElements` array on attach (and\nremove on detach); this list is used to send notification about\nelement state changes.",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 101,
              "column": 10
            },
            "end": {
              "line": 107,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object",
          "inheritedFrom": "UniFlow.StateAware"
        }
      ],
      "events": [],
      "styling": {
        "cssVariables": [],
        "selectors": []
      },
      "slots": [],
      "mixins": [
        "UniFlow.StateAware"
      ]
    },
    {
      "description": "   Use UniFlow.ActionDispatcher for non-visual elements that process actions emitted by visual\n   elements. Action dispatchers usually placed at the application level. Each action dispatcher\n   element gets a chance to process the action in the order the elements are present in the\n   DOM tree. It is important that action dispatcher elements get two-way data binding to\n   application state as follows:\n\n   <action-dispatcher state=\"{{state}}\"></action-dispatcher>\n\n   Action dispatcher elements can include nested action dispatchers, so you can have a\n   hierarchical organization of action dispatchers.\n\n   ### Example:\n\n   #### HTML:\n\n   <dom-module id=\"parent-dispatcher\">\n   <template>\n   <child-dispatcher-a state=\"{{state}}\"></child-dispatcher-a>\n   <child-dispatcher-b state=\"{{state}}\"></child-dispatcher-b>\n   </template>\n   </dom-module>\n\n   #### JavaScript:\n   class ParentDispatcher extends UniFlow.ActionDispatcher(Polymer.Element) {\n         static get is() { return 'parent-dispatcher'; }\n\n         MY_ACTION(detail) {\n           // do MY_ACTION processing here\n           // return false if you want to prevent other action dispatchers from\n           // further processing of this action\n         };\n       }\n\n   customElements.define(ParentDispatcher.is, ParentDispatcher);\n\n   ",
      "summary": "",
      "path": "action-dispatcher.html",
      "properties": [
        {
          "name": "state",
          "type": "Object",
          "description": "Application state.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-mutator.html",
            "start": {
              "line": 41,
              "column": 10
            },
            "end": {
              "line": 44,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {
              "notify": true
            }
          },
          "inheritedFrom": "UniFlow.StateMutator"
        },
        {
          "name": "statePath",
          "type": "string",
          "description": "Path to element state in the application state tree.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 81,
              "column": 10
            },
            "end": {
              "line": 83,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "stateAware",
          "type": "boolean",
          "description": "",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 85,
              "column": 10
            },
            "end": {
              "line": 89,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "defaultValue": "true",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "application",
          "type": "Object",
          "description": "Application-level object that allows all state-aware objects access\nto application element. This property is shared among all state-aware\nelements and has element field initialized with reference to application\nelement. Any state-aware element can access application element using\n`getApplication()` method. Also, all state-aware elements\nadd themselves to `application.stateAwareElements` array on attach (and\nremove on detach); this list is used to send notification about\nelement state changes.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 101,
              "column": 10
            },
            "end": {
              "line": 107,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "actionDispatcher",
          "type": "boolean",
          "description": "",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 68,
              "column": 12
            },
            "end": {
              "line": 72,
              "column": 13
            }
          },
          "metadata": {
            "polymer": {}
          },
          "defaultValue": "true"
        }
      ],
      "methods": [
        {
          "name": "connectedCallback",
          "description": "Adds this element to the list of state-aware elements in the application.\nSets the value of state property to the state of the application element.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 116,
              "column": 6
            },
            "end": {
              "line": 132,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "disconnectedCallback",
          "description": "Removes this element from the list of state-aware elements in the\napplication.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 138,
              "column": 6
            },
            "end": {
              "line": 145,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "getApplication",
          "description": "Returns application element.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 151,
              "column": 6
            },
            "end": {
              "line": 153,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "return": {
            "type": "Element"
          },
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "dispatchAction",
          "description": "Dispatches action by invoking the method with the name that matches\naction type (`detail.type`) passing detail object as a parameter;\nalso selects all children action dispatchers in the element's DOM tree and\ninvokes dispatchAction method on them. False returned by an action dispatcher method\nresults in dispatchAction method returning false (which in turn stops further processing\nof the action by other action dispatchers).",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 87,
              "column": 8
            },
            "end": {
              "line": 97,
              "column": 9
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "detail",
              "type": "{type: string}"
            }
          ],
          "return": {
            "type": "boolean"
          }
        }
      ],
      "staticMethods": [],
      "demos": [],
      "metadata": {},
      "sourceRange": {
        "start": {
          "line": 64,
          "column": 2
        },
        "end": {
          "line": 99,
          "column": 8
        }
      },
      "privacy": "public",
      "name": "UniFlow.ActionDispatcher",
      "attributes": [
        {
          "name": "state",
          "description": "Application state.",
          "sourceRange": {
            "file": "state-mutator.html",
            "start": {
              "line": 41,
              "column": 10
            },
            "end": {
              "line": 44,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object",
          "inheritedFrom": "UniFlow.StateMutator"
        },
        {
          "name": "state-path",
          "description": "Path to element state in the application state tree.",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 81,
              "column": 10
            },
            "end": {
              "line": 83,
              "column": 11
            }
          },
          "metadata": {},
          "type": "string",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "state-aware",
          "description": "",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 85,
              "column": 10
            },
            "end": {
              "line": 89,
              "column": 11
            }
          },
          "metadata": {},
          "type": "boolean",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "application",
          "description": "Application-level object that allows all state-aware objects access\nto application element. This property is shared among all state-aware\nelements and has element field initialized with reference to application\nelement. Any state-aware element can access application element using\n`getApplication()` method. Also, all state-aware elements\nadd themselves to `application.stateAwareElements` array on attach (and\nremove on detach); this list is used to send notification about\nelement state changes.",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 101,
              "column": 10
            },
            "end": {
              "line": 107,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "action-dispatcher",
          "description": "",
          "sourceRange": {
            "start": {
              "line": 68,
              "column": 12
            },
            "end": {
              "line": 72,
              "column": 13
            }
          },
          "metadata": {},
          "type": "boolean"
        }
      ],
      "events": [],
      "styling": {
        "cssVariables": [],
        "selectors": []
      },
      "slots": [],
      "mixins": [
        "UniFlow.StateMutator"
      ]
    },
    {
      "description": "   Whenever element needs to emit an action, this mixin should be applied.\n   Action object must always include type property.\n\n   ",
      "summary": "",
      "path": "action-emitter.html",
      "properties": [],
      "methods": [
        {
          "name": "emitAction",
          "description": "Emits the action described by detail parameter. Detail object must always\ninclude type property. To emit the action we use custom event\nmechanism. Application element listens to the `dispatch-action` event and\ninvokes `dispatchAction` methods for all action dispatchers associated with\nthe application. Make sure your element is attached to DOM tree, otherwise\nevent will never reach your application element. It is a good practice to\nensure that `state` property is initialized (for state-aware elements) before\nemitting any actions (as `state` property is initialized on attach).",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 50,
              "column": 6
            },
            "end": {
              "line": 62,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "detail",
              "type": "*"
            }
          ]
        }
      ],
      "staticMethods": [],
      "demos": [],
      "metadata": {},
      "sourceRange": {
        "start": {
          "line": 29,
          "column": 2
        },
        "end": {
          "line": 64,
          "column": 6
        }
      },
      "privacy": "public",
      "name": "UniFlow.ActionEmitter",
      "attributes": [],
      "events": [
        {
          "type": "CustomEvent",
          "name": "dispatch-action",
          "description": "dispatch-action",
          "metadata": {}
        }
      ],
      "styling": {
        "cssVariables": [],
        "selectors": []
      },
      "slots": []
    },
    {
      "description": "   Apply this mixin to your main application element. It provides global\n   state and functionality to maintain individual elements states. This mixin\n   is responsible for notifying all state-aware elements about their state\n   changes (provided those elements have `statePath` property defined).\n   Only one element in the application is supposed to have this mixin.\n\n   ### Example:\n\n   #### HTML:\n\n       <template>\n\n         <!-- action dispatchers in the order of action processing -->\n         <action-dispatcher-a state=\"{{state}}\"></action-dispatcher-a>\n         <action-dispatcher-b state=\"{{state}}\"></action-dispatcher-b>\n\n         <!-- state-aware elements -->\n         <some-element state-path=\"state.someElement\"></some-element>\n\n       </template>\n\n   #### JavaScript:\n       class MyApp extends UniFlow.ApplicationState(Polymer.Element) {\n         static get is() { return 'my-app'; }\n\n         connectedCallback() {\n           super.ConnectedCallback();\n\n           this.state = {\n             someElement: {}\n           }\n         }\n       }\n\n       customElements.define(MyApp.is,MyApp);\n\n   In the example above, `<some-element>` will receive notification of any changes to the state,\n   as if it was declared as follows:\n\n       <some-element state=\"[[state]]\"></some-element>\n\n   Also, if `<some-element>` has `propertyA`, on element attach this property will be assigned\n   the value of `state.someElement.propertyA`, and receive all notification of the property change\n   whenever the corresponding data in state tree changes. This essentially translates to following\n   declaration:\n\n       <some-element state=\"[[state]]\"\n                     propertyA=\"[[state.someElement.propertyA]]\">\n       </some-element>\n\n   Note that data binding is one-way in both cases. Although state-aware elements can modify their\n   own state, it is considered their private state and no other elements will be notified of those\n   changes.\n\n   ",
      "summary": "",
      "path": "application-state.html",
      "properties": [
        {
          "name": "state",
          "type": "Object",
          "description": "Application state.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-mutator.html",
            "start": {
              "line": 41,
              "column": 10
            },
            "end": {
              "line": 44,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {
              "notify": true
            }
          },
          "inheritedFrom": "UniFlow.StateMutator"
        },
        {
          "name": "statePath",
          "type": "string",
          "description": "Path to element state in the application state tree.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 81,
              "column": 10
            },
            "end": {
              "line": 83,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "stateAware",
          "type": "boolean",
          "description": "",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 85,
              "column": 10
            },
            "end": {
              "line": 89,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "defaultValue": "true",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "application",
          "type": "Object",
          "description": "Application-level object that allows all state-aware objects access\nto application element. This property is shared among all state-aware\nelements and has element field initialized with reference to application\nelement. Any state-aware element can access application element using\n`getApplication()` method. Also, all state-aware elements\nadd themselves to `application.stateAwareElements` array on attach (and\nremove on detach); this list is used to send notification about\nelement state changes.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 101,
              "column": 10
            },
            "end": {
              "line": 107,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "actionDispatcher",
          "type": "boolean",
          "description": "",
          "privacy": "public",
          "sourceRange": {
            "file": "action-dispatcher.html",
            "start": {
              "line": 68,
              "column": 12
            },
            "end": {
              "line": 72,
              "column": 13
            }
          },
          "metadata": {
            "polymer": {}
          },
          "defaultValue": "true",
          "inheritedFrom": "UniFlow.ActionDispatcher"
        }
      ],
      "methods": [
        {
          "name": "connectedCallback",
          "description": "Adds this element to the list of state-aware elements in the application.\nSets the value of state property to the state of the application element.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 116,
              "column": 6
            },
            "end": {
              "line": 132,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "disconnectedCallback",
          "description": "Removes this element from the list of state-aware elements in the\napplication.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 138,
              "column": 6
            },
            "end": {
              "line": 145,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "getApplication",
          "description": "Returns application element.",
          "privacy": "public",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 151,
              "column": 6
            },
            "end": {
              "line": 153,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "return": {
            "type": "Element"
          },
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "dispatchAction",
          "description": "Dispatches action by invoking the method with the name that matches\naction type (`detail.type`) passing detail object as a parameter;\nalso selects all children action dispatchers in the element's DOM tree and\ninvokes dispatchAction method on them. False returned by an action dispatcher method\nresults in dispatchAction method returning false (which in turn stops further processing\nof the action by other action dispatchers).",
          "privacy": "public",
          "sourceRange": {
            "file": "action-dispatcher.html",
            "start": {
              "line": 87,
              "column": 8
            },
            "end": {
              "line": 97,
              "column": 9
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "detail",
              "type": "{type: string}"
            }
          ],
          "return": {
            "type": "boolean"
          },
          "inheritedFrom": "UniFlow.ActionDispatcher"
        },
        {
          "name": "onDispatchAction_",
          "description": "Application state listens to dispatch-action method and invokes\ndispatchAction method on itself (which in turn invokes dispatchAction\non all action dispatchers declared within application element).",
          "privacy": "private",
          "sourceRange": {
            "start": {
              "line": 102,
              "column": 6
            },
            "end": {
              "line": 104,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "event",
              "type": "!Event"
            }
          ]
        },
        {
          "name": "stateChanged_",
          "description": "Called when state.* changes. Notifies state-aware elements of their\nstate changes, if applicable.",
          "privacy": "private",
          "sourceRange": {
            "start": {
              "line": 113,
              "column": 6
            },
            "end": {
              "line": 115,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "change",
              "type": "!Object",
              "description": "the Polymer change event for the path"
            }
          ]
        },
        {
          "name": "notifyStateAwareElements_",
          "description": "Iterates through the array of state-aware elements in the application\nand notifies them about their state change, if applicable. Note that\nstate-aware elements must be attached to DOM tree in order to receive\nnotifications.",
          "privacy": "private",
          "sourceRange": {
            "start": {
              "line": 126,
              "column": 6
            },
            "end": {
              "line": 149,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "change"
            }
          ]
        },
        {
          "name": "ready",
          "description": "Sets application.element value to this element (so all state-aware elements\nhave access to application element). Registers event listener to dispatch-action event.",
          "privacy": "protected",
          "sourceRange": {
            "start": {
              "line": 155,
              "column": 6
            },
            "end": {
              "line": 159,
              "column": 7
            }
          },
          "metadata": {},
          "params": []
        }
      ],
      "staticMethods": [],
      "demos": [],
      "metadata": {},
      "sourceRange": {
        "start": {
          "line": 85,
          "column": 2
        },
        "end": {
          "line": 161,
          "column": 6
        }
      },
      "privacy": "public",
      "name": "UniFlow.ApplicationState",
      "attributes": [
        {
          "name": "state",
          "description": "Application state.",
          "sourceRange": {
            "file": "state-mutator.html",
            "start": {
              "line": 41,
              "column": 10
            },
            "end": {
              "line": 44,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object",
          "inheritedFrom": "UniFlow.StateMutator"
        },
        {
          "name": "state-path",
          "description": "Path to element state in the application state tree.",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 81,
              "column": 10
            },
            "end": {
              "line": 83,
              "column": 11
            }
          },
          "metadata": {},
          "type": "string",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "state-aware",
          "description": "",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 85,
              "column": 10
            },
            "end": {
              "line": 89,
              "column": 11
            }
          },
          "metadata": {},
          "type": "boolean",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "application",
          "description": "Application-level object that allows all state-aware objects access\nto application element. This property is shared among all state-aware\nelements and has element field initialized with reference to application\nelement. Any state-aware element can access application element using\n`getApplication()` method. Also, all state-aware elements\nadd themselves to `application.stateAwareElements` array on attach (and\nremove on detach); this list is used to send notification about\nelement state changes.",
          "sourceRange": {
            "file": "state-aware.html",
            "start": {
              "line": 101,
              "column": 10
            },
            "end": {
              "line": 107,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object",
          "inheritedFrom": "UniFlow.StateAware"
        },
        {
          "name": "action-dispatcher",
          "description": "",
          "sourceRange": {
            "file": "action-dispatcher.html",
            "start": {
              "line": 68,
              "column": 12
            },
            "end": {
              "line": 72,
              "column": 13
            }
          },
          "metadata": {},
          "type": "boolean",
          "inheritedFrom": "UniFlow.ActionDispatcher"
        }
      ],
      "events": [],
      "styling": {
        "cssVariables": [],
        "selectors": []
      },
      "slots": [],
      "mixins": [
        "UniFlow.ActionDispatcher"
      ]
    },
    {
      "description": "   This mixin used by elements that need to render multiple models backed\n   by 'list' array. You may want to use ModelView to render individual\n   models in the list. The mixin supports element selection by setting predefined\n   $selected property on list elements.\n\n   ### Example:\n\n   #### HTML:\n\n       <ul>\n         <template id=\"list-template\" is=\"dom-repeat\" items=\"[[list]]\">\n           <li id=\"[[item.id]]\">\n             <paper-checkbox checked=\"{{item.$selected}}\">\n             <model-view state-path=\"[[statePath]].list.#[[index]]\"></model-view>\n           </li>\n         </template>\n       </ul>\n       Selected: [[selectedCount]] items\n       <paper-button on-tap=\"onDeleteTap\">Delete</paper-button>\n\n   #### JavaScript:\n       class ListElement extends UniFlow.StateAware(UniFlow.ListView(Polymer.Element)) {\n         static get is() { return \"list-element\"; }\n\n         onDeleteTap() {\n           this.deleteSelected();\n         }\n       }\n\n       customElements.define(ListElement.is, ListElement);\n\n   In the example above list view element is also state-aware, meaning it has its own place\n   in the application state tree. Assuming it has been declared as follows:\n\n       <list-element state-path=\"state.listElement\"></list-element>\n\n   it will be rendering `state.listElement.list` and observing changes to it. Each `model-view`\n   within dom-repeat template will have `state-path` property  set to\n   `state.listElement.list.#<index>`  where `index` is the element's index in the array.\n\n\n   ",
      "summary": "",
      "path": "list-view.html",
      "properties": [
        {
          "name": "list",
          "type": "Array",
          "description": "Array which data is to be rendered by the element.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 78,
              "column": 10
            },
            "end": {
              "line": 81,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          },
          "defaultValue": "[]"
        },
        {
          "name": "selectedCount",
          "type": "number",
          "description": "If element supports item selection (using meta-property $selected) then\nselectedCount property will keep track of number of selected items.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 87,
              "column": 10
            },
            "end": {
              "line": 91,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {
              "notify": true
            }
          },
          "defaultValue": "0"
        },
        {
          "name": "deleteAction",
          "type": "string",
          "description": "Action name that will be emitted when deleteSelected method is called\nwithout parameter.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 97,
              "column": 10
            },
            "end": {
              "line": 97,
              "column": 30
            }
          },
          "metadata": {
            "polymer": {}
          }
        }
      ],
      "methods": [
        {
          "name": "emitAction",
          "description": "Emits the action described by detail parameter. Detail object must always\ninclude type property. To emit the action we use custom event\nmechanism. Application element listens to the `dispatch-action` event and\ninvokes `dispatchAction` methods for all action dispatchers associated with\nthe application. Make sure your element is attached to DOM tree, otherwise\nevent will never reach your application element. It is a good practice to\nensure that `state` property is initialized (for state-aware elements) before\nemitting any actions (as `state` property is initialized on attach).",
          "privacy": "public",
          "sourceRange": {
            "file": "action-emitter.html",
            "start": {
              "line": 50,
              "column": 6
            },
            "end": {
              "line": 62,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "detail",
              "type": "*"
            }
          ],
          "inheritedFrom": "UniFlow.ActionEmitter"
        },
        {
          "name": "itemChanged_",
          "description": "Whenever list is set or mutated (elements added/removed), as well as\nmeta-property $selected is modified, updates selectedCount.",
          "privacy": "private",
          "sourceRange": {
            "start": {
              "line": 114,
              "column": 6
            },
            "end": {
              "line": 121,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "change",
              "type": "!Object"
            }
          ]
        },
        {
          "name": "updateSelectedCount_",
          "description": "Updates selectedCount property of the element by iterating the list and\ncounting each item that has meta-property $selected set.",
          "privacy": "private",
          "sourceRange": {
            "start": {
              "line": 128,
              "column": 6
            },
            "end": {
              "line": 134,
              "column": 7
            }
          },
          "metadata": {},
          "params": []
        },
        {
          "name": "deleteSelected",
          "description": "Emits deleteAction for each selected element in the list (for which\nmeta-property $selected is set).",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 143,
              "column": 6
            },
            "end": {
              "line": 157,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "deleteAction",
              "type": "string=",
              "description": "Action type for the action that  will be emitted\n    for each selected element. If not specified, `deleteAction` property of the element\n    will be used."
            }
          ]
        }
      ],
      "staticMethods": [],
      "demos": [],
      "metadata": {},
      "sourceRange": {
        "start": {
          "line": 70,
          "column": 2
        },
        "end": {
          "line": 159,
          "column": 6
        }
      },
      "privacy": "public",
      "name": "UniFlow.ListView",
      "attributes": [
        {
          "name": "list",
          "description": "Array which data is to be rendered by the element.",
          "sourceRange": {
            "start": {
              "line": 78,
              "column": 10
            },
            "end": {
              "line": 81,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Array"
        },
        {
          "name": "selected-count",
          "description": "If element supports item selection (using meta-property $selected) then\nselectedCount property will keep track of number of selected items.",
          "sourceRange": {
            "start": {
              "line": 87,
              "column": 10
            },
            "end": {
              "line": 91,
              "column": 11
            }
          },
          "metadata": {},
          "type": "number"
        },
        {
          "name": "delete-action",
          "description": "Action name that will be emitted when deleteSelected method is called\nwithout parameter.",
          "sourceRange": {
            "start": {
              "line": 97,
              "column": 10
            },
            "end": {
              "line": 97,
              "column": 30
            }
          },
          "metadata": {},
          "type": "string"
        }
      ],
      "events": [
        {
          "type": "CustomEvent",
          "name": "dispatch-action",
          "description": "dispatch-action",
          "metadata": {},
          "inheritedFrom": "UniFlow.ActionEmitter"
        }
      ],
      "styling": {
        "cssVariables": [],
        "selectors": []
      },
      "slots": [],
      "mixins": [
        "UniFlow.ActionEmitter"
      ]
    },
    {
      "description": "   Element rendering data represented by a single object (model) in the\n   application state should use ModelView mixin. Model View is a powerful\n   concept that encapsulates model data (likely the data received from the\n   server and to be persisted to the server if modified as a result of user\n   actions), status (validity of the data, flag that data was modified,\n   notifications for the user, etc.). Auxiliary data supplied by action\n   dispatchers and needed for display purposes or element's logic\n   should be defined as element’s properties. Same applies to data\n   created/modified by the element but not intended to be persisted.\n   If `StateAware` mixin is used along with `ModelView`, you can take advantage\n   of statePath property that indicates path to the element's state in the\n   application state tree. Whenever any data is mutated by action dispatchers\n   at statePath or below, the element will receive notification of its\n   properties' change (even if there is no explicit binding for those\n   properties). See `UniFlow.StateAware` for more details and example.\n   ModelView mixin defines some properties that are intended to be overridden\n   in the elements:\n\n   + `validation` property allows to specify validation rules\n   that will be applied when validateModel() method is called. As a result of\n   this method validation status will be updated to indicate result for each\n   model field that has validation rule associated with it.\n   + `saveAction` property indicates which action should be emitted when\n   saveModel method is called to perform save of the model.\n   + `getMessage` should be overridden with the function returning message\n   string for given error code (to translate validation error code to message)\n\n\n   ### Example:\n\n   #### HTML:\n\n      <template>\n        Model: [[model.id]]\n        <paper-input value=\"{{model.name}}\"\n                     label=\"Name\"\n                     invalid=\"[[status.validation.name.invalid]]\"\n                     error-message=\"[[status.validation.name.errorMessage]]\">\n        </paper-input>\n        <paper-button on-tap=\"onSaveTap\">Save</paper-button>\n      </template>\n\n   #### JavaScript:\n      class MyModel extends UniFlow.ModelView(Polymer.Element) {\n        static get is() { return \"my-model\"; }\n        \n        get saveAction() { return 'MY_SAVE' }\n\n        get validation() { return {\n          name: (value) => {\n            if (!value || !value.trim()) {\n              return 'Name is not specified';\n            }\n          }\n        }}\n\n        connectedCallback() {\n          this.super();\n          this.fetchData();\n        },\n\n        fetchData() {\n          this.emitAction({\n            type: 'MY_FETCH',\n            path: 'model'\n          });\n        },\n\n        onSaveTap() {\n          this.validateAndSave();\n        }\n      }\n\n      customElements.define(MyModel.is, MyModel);\n\n   In the example above model view has input field for `name` property and Save button. On\n   element attach the action is emitted to fetch the model's data. Note that in `emitAction()` method\n   the path is specified as `'model'`. ActionEmitter mixin is responsible of expanding the path\n   with element's state path, ensuring that when action dispatcher gets to process the action, the\n   path contains full path in the state tree. So assuming that `my-model` is declared as follows:\n\n      <my-model state-path=\"state.myModel\"></my-model>\n\n   the path in `MY_FETCH` action gets expanded to `state.myModel.model`.\n\n   `validation` property is an object that contains methods for fields validation. The keys in\n   this object should match model field names, the values are validation methods. Method receives\n   current value of the field and should return non-falsy value (string or error code) if the value\n   of the field didn't pass validation. `status.validation` object will be populated with the results\n   of validation with the keys matching field names and values being objects containing two fields:\n   - `invalid`: true when the value is not valid\n   - `errorMessage`: the message to show to user\n\n\n   So in the example above if user clicks on Save button with name not entered, they will get\n   'Name is not specified' error message on the input element. When the name is non-empty, validation\n   will pass and `MY_SAVE` action will be emitted with model passed as a parameter and `'model'` as\n   path.\n\n\n   ",
      "summary": "",
      "path": "model-view.html",
      "properties": [
        {
          "name": "model",
          "type": "Object",
          "description": "Object containing model data, usually mirroring server-side object.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 138,
              "column": 10
            },
            "end": {
              "line": 140,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          }
        },
        {
          "name": "status",
          "type": "Object",
          "description": "Object to contain model status, including validity of the data,\nflag that data was modified, notifications for the user, etc.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 146,
              "column": 10
            },
            "end": {
              "line": 148,
              "column": 11
            }
          },
          "metadata": {
            "polymer": {}
          }
        }
      ],
      "methods": [
        {
          "name": "emitAction",
          "description": "Emits the action described by detail parameter. Detail object must always\ninclude type property. To emit the action we use custom event\nmechanism. Application element listens to the `dispatch-action` event and\ninvokes `dispatchAction` methods for all action dispatchers associated with\nthe application. Make sure your element is attached to DOM tree, otherwise\nevent will never reach your application element. It is a good practice to\nensure that `state` property is initialized (for state-aware elements) before\nemitting any actions (as `state` property is initialized on attach).",
          "privacy": "public",
          "sourceRange": {
            "file": "action-emitter.html",
            "start": {
              "line": 50,
              "column": 6
            },
            "end": {
              "line": 62,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "detail",
              "type": "*"
            }
          ],
          "inheritedFrom": "UniFlow.ActionEmitter"
        },
        {
          "name": "getMessage",
          "description": "Function that translates error code (numeric or text) into human readable\nerror message (used to translate validation error code into error text).",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 176,
              "column": 6
            },
            "end": {
              "line": 178,
              "column": 7
            }
          },
          "metadata": {},
          "params": []
        },
        {
          "name": "saveModel",
          "description": "Method emitting passed action or this.saveAction, sending model with\nthe action options.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 191,
              "column": 6
            },
            "end": {
              "line": 207,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "action",
              "type": "(Object|string)="
            }
          ]
        },
        {
          "name": "initValidationStatus_",
          "description": "Method initializes status.validation object with invalid = false for all\nkeys defined in this.validation object. This is needed for proper UI\nbinding (if the value of invalid attribute is undefined, paper-input is\nmisbehaving).",
          "privacy": "private",
          "sourceRange": {
            "start": {
              "line": 216,
              "column": 6
            },
            "end": {
              "line": 226,
              "column": 7
            }
          },
          "metadata": {},
          "params": []
        },
        {
          "name": "validateModel",
          "description": "Performs validation of model object according to rules defined in\nthis.validation object. Sets status.validation.<property-name> fields with\ntwo properties: invalid and errorMessage.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 236,
              "column": 6
            },
            "end": {
              "line": 256,
              "column": 7
            }
          },
          "metadata": {},
          "params": [],
          "return": {
            "type": "boolean",
            "desc": "True if all fields validated successfully (or\n    this.validation is not defined in the element)."
          }
        },
        {
          "name": "validateAndSave",
          "description": "Validates and saves model if there were no validation errors.",
          "privacy": "public",
          "sourceRange": {
            "start": {
              "line": 262,
              "column": 6
            },
            "end": {
              "line": 266,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "action",
              "type": "string=",
              "description": "Optional action type to emit for save action."
            }
          ]
        },
        {
          "name": "modelViewModelChanged_",
          "description": "Observer of any changes to the model. Resets status object and initializes\nvalidation status.",
          "privacy": "private",
          "sourceRange": {
            "start": {
              "line": 274,
              "column": 6
            },
            "end": {
              "line": 288,
              "column": 7
            }
          },
          "metadata": {},
          "params": [
            {
              "name": "change",
              "type": "!Object"
            }
          ]
        }
      ],
      "staticMethods": [],
      "demos": [],
      "metadata": {},
      "sourceRange": {
        "start": {
          "line": 129,
          "column": 2
        },
        "end": {
          "line": 290,
          "column": 6
        }
      },
      "privacy": "public",
      "name": "UniFlow.ModelView",
      "attributes": [
        {
          "name": "model",
          "description": "Object containing model data, usually mirroring server-side object.",
          "sourceRange": {
            "start": {
              "line": 138,
              "column": 10
            },
            "end": {
              "line": 140,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object"
        },
        {
          "name": "status",
          "description": "Object to contain model status, including validity of the data,\nflag that data was modified, notifications for the user, etc.",
          "sourceRange": {
            "start": {
              "line": 146,
              "column": 10
            },
            "end": {
              "line": 148,
              "column": 11
            }
          },
          "metadata": {},
          "type": "Object"
        }
      ],
      "events": [
        {
          "type": "CustomEvent",
          "name": "dispatch-action",
          "description": "dispatch-action",
          "metadata": {},
          "inheritedFrom": "UniFlow.ActionEmitter"
        }
      ],
      "styling": {
        "cssVariables": [],
        "selectors": []
      },
      "slots": [],
      "mixins": [
        "UniFlow.ActionEmitter"
      ]
    }
  ]
}


================================================
FILE: application-state.html
================================================
<!--
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="action-dispatcher.html">
<link rel="import" href="state-aware.html">
<link rel="import" href="state-mutator.html">


<script type="text/javascript">
  var UniFlow = window.UniFlow || {};

  /**

   Apply this mixin to your main application element. It provides global
   state and functionality to maintain individual elements states. This mixin
   is responsible for notifying all state-aware elements about their state
   changes (provided those elements have `statePath` property defined).
   Only one element in the application is supposed to have this mixin.

   ### Example:

   #### HTML:

       <template>

         <!-- action dispatchers in the order of action processing -->
         <action-dispatcher-a state="{{state}}"></action-dispatcher-a>
         <action-dispatcher-b state="{{state}}"></action-dispatcher-b>

         <!-- state-aware elements -->
         <some-element state-path="state.someElement"></some-element>

       </template>

   #### JavaScript:
       class MyApp extends UniFlow.ApplicationState(Polymer.Element) {
         static get is() { return 'my-app'; }

         connectedCallback() {
           super.ConnectedCallback();

           this.state = {
             someElement: {}
           }
         }
       }

       customElements.define(MyApp.is,MyApp);

   In the example above, `<some-element>` will receive notification of any changes to the state,
   as if it was declared as follows:

       <some-element state="[[state]]"></some-element>

   Also, if `<some-element>` has `propertyA`, on element attach this property will be assigned
   the value of `state.someElement.propertyA`, and receive all notification of the property change
   whenever the corresponding data in state tree changes. This essentially translates to following
   declaration:

       <some-element state="[[state]]"
                     propertyA="[[state.someElement.propertyA]]">
       </some-element>

   Note that data binding is one-way in both cases. Although state-aware elements can modify their
   own state, it is considered their private state and no other elements will be notified of those
   changes.

   @polymer
   @mixinFunction
   @appliesMixin UniFlow.ActionDispatcher
  */
  UniFlow.ApplicationState = Polymer.dedupingMixin((base) =>
    class extends UniFlow.ActionDispatcher(base) {

      static get observers() {
        return [
          'stateChanged_(state.*)'
        ]
      }

      /**
       * Application state listens to dispatch-action method and invokes
       * dispatchAction method on itself (which in turn invokes dispatchAction
       * on all action dispatchers declared within application element).
       *
       * @param {!Event} event
       * @private
       */
      onDispatchAction_(event) {
        this.dispatchAction(event.detail);
      }

      /**
       * Called when state.* changes. Notifies state-aware elements of their
       * state changes, if applicable.
       *
       * @param {!Object} change the Polymer change event for the path
       * @private
       */
      stateChanged_(change) {
        this.notifyStateAwareElements_(change);
      }

      /**
       * Iterates through the array of state-aware elements in the application
       * and notifies them about their state change, if applicable. Note that
       * state-aware elements must be attached to DOM tree in order to receive
       * notifications.
       *
       * @param change
       * @private
       */
      notifyStateAwareElements_(change) {
        this.get('application.stateAwareElements').forEach(element => {
          element.notifyPath(change.path, change.value, true);
          if (element.statePath && change.path.startsWith(element.statePath)) {
            let pathToNotify = change.path.slice(element.statePath.length + 1);
            if (pathToNotify) {
              if (element.get(pathToNotify) !== change.value &&
                !change.path.endsWith('.splices')) {
                element.set(pathToNotify, change.value);
              } else {
                element.notifyPath(pathToNotify, change.value, true);
              }
            } else {
              // If pathToNotify empty, that means the whole element state need
              // to be replaced. Iterating through the keys of new value (which
              // has to be an object) and setting element properties with the same
              // name.
              Object.keys(change.value).forEach((key) => {
                element.set(key, change.value[key]);
              });
            }
          }
        });
      }

      /**
       * Sets application.element value to this element (so all state-aware elements
       * have access to application element). Registers event listener to dispatch-action event.
       */
      ready() {
        super.ready();
        this.set('application.element', this);
        this.addEventListener('dispatch-action', (event) => this.onDispatchAction_(event));
      }

    });
</script>

================================================
FILE: bower.json
================================================
{
  "name": "uniflow-polymer",
  "version": "0.6.0",
  "authors": [
    "Andrei Militeev <militeev@gmail.com>"
  ],
  "description": "A straightforward way to architect Polymer application using unidirectional data flow.",
  "keywords": [
    "web-component",
    "polymer",
    "uniflow",
    "unidirectional",
    "flow"
  ],
  "license": "https://raw.githubusercontent.com/google/uniflow-polymer/master/LICENSE",
  "homepage": "https://github.com/google/uniflow-polymer/",
  "ignore": [
    "/.*",
    "/test/"
  ],
  "dependencies": {
    "polymer": "Polymer/polymer#^2.0.0"
  },
  "devDependencies": {
    "app-route": "PolymerElements/app-route#^2.0.2",
    "iron-component-page": "PolymerElements/iron-component-page#^3.0.0",
    "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^2.0.0",
    "iron-icon": "PolymerElements/iron-icon#^2.0.0",
    "iron-icons": "PolymerElements/iron-icons#^2.0.0",
    "iron-test-helpers": "PolymerElements/iron-test-helpers#^2.0.0",
    "paper-styles": "PolymerElements/paper-styles#^2.0.0",
    "test-fixture": "PolymerElements/test-fixture#3.0.0-rc.1",
    "web-component-tester": "^v6.0.0",
    "todomvc-app-css": "^2.1.0",
    "iron-localstorage": "PolymerElements/iron-localstorage#^2.0.0"
  }
}


================================================
FILE: index.html
================================================
<!doctype html>
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <script src="../webcomponentsjs/webcomponents-loader.js"></script>
  <link rel="import" href="../iron-component-page/iron-component-page.html">

</head>
<body unresolved>
  <!-- Note: if the main element for this repository doesn't
       match the folder name, add a src="&lt;main-component&gt;.html" attribute,
       where &lt;main-component&gt;.html" is a file that imports all of the
       components you want documented. -->
  <iron-component-page></iron-component-page>

</body>
</html>


================================================
FILE: list-view.html
================================================
<!--
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="action-emitter.html">


<script type="text/javascript">
  var UniFlow = window.UniFlow || {};

  /**

   This mixin used by elements that need to render multiple models backed
   by 'list' array. You may want to use ModelView to render individual
   models in the list. The mixin supports element selection by setting predefined
   $selected property on list elements.

   ### Example:

   #### HTML:

       <ul>
         <template id="list-template" is="dom-repeat" items="[[list]]">
           <li id="[[item.id]]">
             <paper-checkbox checked="{{item.$selected}}">
             <model-view state-path="[[statePath]].list.#[[index]]"></model-view>
           </li>
         </template>
       </ul>
       Selected: [[selectedCount]] items
       <paper-button on-tap="onDeleteTap">Delete</paper-button>

   #### JavaScript:
       class ListElement extends UniFlow.StateAware(UniFlow.ListView(Polymer.Element)) {
         static get is() { return "list-element"; }

         onDeleteTap() {
           this.deleteSelected();
         }
       }

       customElements.define(ListElement.is, ListElement);

   In the example above list view element is also state-aware, meaning it has its own place
   in the application state tree. Assuming it has been declared as follows:

       <list-element state-path="state.listElement"></list-element>

   it will be rendering `state.listElement.list` and observing changes to it. Each `model-view`
   within dom-repeat template will have `state-path` property  set to
   `state.listElement.list.#<index>`  where `index` is the element's index in the array.


   @polymer
   @mixinFunction
   @appliesMixin UniFlow.ActionEmitter
  */
  UniFlow.ListView = Polymer.dedupingMixin((base) =>
    class extends UniFlow.ActionEmitter(base) {
      static get properties() {
        return {

          /**
           * Array which data is to be rendered by the element.
           */
          list: {
            type: Array,
            value: () => []
          },

          /**
           * If element supports item selection (using meta-property $selected) then
           * selectedCount property will keep track of number of selected items.
           */
          selectedCount: {
            type: Number,
            notify: true,
            value: 0,
          },

          /**
           * Action name that will be emitted when deleteSelected method is called
           * without parameter.
           */
          deleteAction: String

        }
      }

      static get observers() {
        return [
          'itemChanged_(list.*)',
        ]
      }

      /**
       * Whenever list is set or mutated (elements added/removed), as well as
       * meta-property $selected is modified, updates selectedCount.
       * @param {!Object} change
       * @private
       */
      itemChanged_(change) {
        if (this.get('list') &&
          (change.path == 'list' ||
            change.path == 'list.splices' ||
            change.path.endsWith('$selected'))) {
          this.updateSelectedCount_();
        }
      }

      /**
       * Updates selectedCount property of the element by iterating the list and
       * counting each item that has meta-property $selected set.
       * @private
       */
      updateSelectedCount_() {
        let selectedCount = 0;
        this.get('list').forEach((elem) => {
          selectedCount += elem.$selected ? 1 : 0;
        });
        this.selectedCount = selectedCount;
      }

      /**
       * Emits deleteAction for each selected element in the list (for which
       * meta-property $selected is set).
       * @param {string=} deleteAction Action type for the action that  will be emitted
       *     for each selected element. If not specified, `deleteAction` property of the element
       *     will be used.
       */
      deleteSelected(deleteAction) {
        deleteAction = deleteAction || this.deleteAction;
        if (deleteAction) {
          this.list.forEach(item => {
            if (item.$selected) {
              this.emitAction({
                type: deleteAction,
                item: item
              });
            }
          });
        } else {
          console.warn('delete action is not defined');
        }
      }

    });
</script>

================================================
FILE: model-view.html
================================================
<!--
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="action-emitter.html">


<script>
  var UniFlow = window.UniFlow || {};

  /**

   Element rendering data represented by a single object (model) in the
   application state should use ModelView mixin. Model View is a powerful
   concept that encapsulates model data (likely the data received from the
   server and to be persisted to the server if modified as a result of user
   actions), status (validity of the data, flag that data was modified,
   notifications for the user, etc.). Auxiliary data supplied by action
   dispatchers and needed for display purposes or element's logic
   should be defined as element’s properties. Same applies to data
   created/modified by the element but not intended to be persisted.
   If `StateAware` mixin is used along with `ModelView`, you can take advantage
   of statePath property that indicates path to the element's state in the
   application state tree. Whenever any data is mutated by action dispatchers
   at statePath or below, the element will receive notification of its
   properties' change (even if there is no explicit binding for those
   properties). See `UniFlow.StateAware` for more details and example.
   ModelView mixin defines some properties that are intended to be overridden
   in the elements:

   + `validation` property allows to specify validation rules
   that will be applied when validateModel() method is called. As a result of
   this method validation status will be updated to indicate result for each
   model field that has validation rule associated with it.
   + `saveAction` property indicates which action should be emitted when
   saveModel method is called to perform save of the model.
   + `getMessage` should be overridden with the function returning message
   string for given error code (to translate validation error code to message)


   ### Example:

   #### HTML:

      <template>
        Model: [[model.id]]
        <paper-input value="{{model.name}}"
                     label="Name"
                     invalid="[[status.validation.name.invalid]]"
                     error-message="[[status.validation.name.errorMessage]]">
        </paper-input>
        <paper-button on-tap="onSaveTap">Save</paper-button>
      </template>

   #### JavaScript:
      class MyModel extends UniFlow.ModelView(Polymer.Element) {
        static get is() { return "my-model"; }
        
        get saveAction() { return 'MY_SAVE' }

        get validation() { return {
          name: (value) => {
            if (!value || !value.trim()) {
              return 'Name is not specified';
            }
          }
        }}

        connectedCallback() {
          this.super();
          this.fetchData();
        },

        fetchData() {
          this.emitAction({
            type: 'MY_FETCH',
            path: 'model'
          });
        },

        onSaveTap() {
          this.validateAndSave();
        }
      }

      customElements.define(MyModel.is, MyModel);

   In the example above model view has input field for `name` property and Save button. On
   element attach the action is emitted to fetch the model's data. Note that in `emitAction()` method
   the path is specified as `'model'`. ActionEmitter mixin is responsible of expanding the path
   with element's state path, ensuring that when action dispatcher gets to process the action, the
   path contains full path in the state tree. So assuming that `my-model` is declared as follows:

      <my-model state-path="state.myModel"></my-model>

   the path in `MY_FETCH` action gets expanded to `state.myModel.model`.

   `validation` property is an object that contains methods for fields validation. The keys in
   this object should match model field names, the values are validation methods. Method receives
   current value of the field and should return non-falsy value (string or error code) if the value
   of the field didn't pass validation. `status.validation` object will be populated with the results
   of validation with the keys matching field names and values being objects containing two fields:
   - `invalid`: true when the value is not valid
   - `errorMessage`: the message to show to user


   So in the example above if user clicks on Save button with name not entered, they will get
   'Name is not specified' error message on the input element. When the name is non-empty, validation
   will pass and `MY_SAVE` action will be emitted with model passed as a parameter and `'model'` as
   path.


   @polymer
   @mixinFunction
   @appliesMixin UniFlow.ActionEmitter
  */
  UniFlow.ModelView = Polymer.dedupingMixin((base) =>
    class extends UniFlow.ActionEmitter(base) {

      static get properties() {
        return {

          /**
           * Object containing model data, usually mirroring server-side object.
           */
          model: {
            type: Object
          },

          /**
           * Object to contain model status, including validity of the data,
           * flag that data was modified, notifications for the user, etc.
           */
          status: {
            type: Object
          }

        }
      }

      /**
       * Validation rules for model properties (optional), should be defined in the
       * element.
       * @type {Object|undefined}
       */
      get validation() {
        return undefined;
      }

      /**
       * Save action that will be emitted when saveModel() method is called without
       * parameters.
       * @type {string|undefined}
       */
      get saveAction() {
        return undefined;
      }

      /**
       * Function that translates error code (numeric or text) into human readable
       * error message (used to translate validation error code into error text).
       * @type {Function|undefined}
       */
      getMessage() {
        return undefined;
      }

      static get observers() {
        return [
          'modelViewModelChanged_(model.*)'
        ]
      }

      /**
       * Method emitting passed action or this.saveAction, sending model with
       * the action options.
       * @param {Object|string=} action
       */
      saveModel(action) {
        let actionToEmit = {
          model: this.model
        };
        if (typeof action === 'object') {
          Object.assign(actionToEmit, action);
        } else {
          actionToEmit.type = action;
        }
        if (!actionToEmit.type) {
          actionToEmit.type = this.saveAction;
        }
        if (!actionToEmit.path) {
          actionToEmit.path = 'model';
        }
        this.emitAction(actionToEmit);
      }

      /**
       * Method initializes status.validation object with invalid = false for all
       * keys defined in this.validation object. This is needed for proper UI
       * binding (if the value of invalid attribute is undefined, paper-input is
       * misbehaving).
       * @private
       */
      initValidationStatus_() {
        let validationStatus = {};
        if (this.validation) {
          for (let key of Object.keys(this.validation)) {
            validationStatus[key] = {
              invalid: false
            };
          }
        }
        this.set('status.validation', validationStatus);
      }

      /**
       * Performs validation of model object according to rules defined in
       * this.validation object. Sets status.validation.<property-name> fields with
       * two properties: invalid and errorMessage.
       *
       * @return {boolean} True if all fields validated successfully (or
       *     this.validation is not defined in the element).
       */
      validateModel() {
        if (!this.validation) {
          return true;
        }
        let isValid = true;
        for (let key of Object.keys(this.validation)) {
          let result = this.validation[key].call(this,
            this.get('model.' + key));
          let errorMessage = !result || typeof result === 'string' ?
            result : (this.getMessage ? this.getMessage(result) :
              'Message Code ' + result);
          this.set('status.validation.' + key, {
            invalid: !!result,
            errorMessage
          });
          if (result) {
            isValid = false;
          }
        }
        return isValid;
      }

      /**
       * Validates and saves model if there were no validation errors.
       * @param {string=} action Optional action type to emit for save action.
       */
      validateAndSave(action) {
        if (this.validateModel()) {
          this.saveModel(action);
        }
      }

      /**
       * Observer of any changes to the model. Resets status object and initializes
       * validation status.
       * @param {!Object} change
       * @private
       */
      modelViewModelChanged_(change) {
        // Resetting status when model changed
        if (change.path === 'model' && this.get('model')) {
          this.set('status', {
            isModified: false
          });
          this.initValidationStatus_();
        } else {
          if (this.get('model')) {
            this.set('status.isModified', true);
          } else {
            this.set('status', {});
          }
        }
      }

    });
</script>

================================================
FILE: state-aware.html
================================================
<!--
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<link rel="import" href="../polymer/polymer-element.html">


<script type="text/javascript">
  var UniFlow = window.UniFlow || {};

  /**
   Key mixin that must be assigned to all elements that need to access
   application state and/or have access to the application element. The element is
   notified of any changes to application's state, as well as all its properties
   when they're modified by state mutator elements. `state-path` property must
   be used to identify path to element's state in application state tree. 

   ### Example:

   #### HTML:

      <template>

        <div>Value A: [[state.valueA]]</div>
        <div>Value B: [[valueB]]</div>

      </template>

   #### JavaScript:

      class MyElement extends UniFlow.StateAware(Polymer.Element) {
        static get is() { return 'my-element'; }

        static get properties() {
          return {
            valueB: String
          }
        }
      }

      customElement.define(MyElement.is, MyElement);

   When above element is declared as follows:

      <my-element state-path="state.myElement"></my-element>

   it will be notified about changes (and render those) to `state.valueA` or
   `state.myElement.valueB` in action dispatchers or other state mutating
   elements.

   @polymer
   @mixinFunction
  */
  UniFlow.StateAware = Polymer.dedupingMixin((base) =>
    class extends base {
      static get properties() {
        return {

          /**
           * Application state, shared among application and all state-aware elements.
           * This property is initialized when element is attached.
           */
          state: {
            type: Object
          },

          /**
           * Path to element state in the application state tree.
           */
          statePath: {
            type: String
          },

          stateAware: {
            type: Boolean,
            value: true,
            reflectToAttribute: true
          },

          /**
           * Application-level object that allows all state-aware objects access
           * to application element. This property is shared among all state-aware
           * elements and has element field initialized with reference to application
           * element. Any state-aware element can access application element using
           * `getApplication()` method. Also, all state-aware elements
           * add themselves to `application.stateAwareElements` array on attach (and
           * remove on detach); this list is used to send notification about
           * element state changes.
           */
          application: {
            type: Object,
            value: {
              element: null,
              stateAwareElements: []
            }
          }

        }
      }

      /**
       * Adds this element to the list of state-aware elements in the application.
       * Sets the value of state property to the state of the application element.
       */
      connectedCallback() {
        super.connectedCallback();

        this.push('application.stateAwareElements', this);
        // Copying application's element state to this element. ApplicationState behavior is
        // responsible of notifying state aware elements about application's state changes.
        if (!this.get('state') && this.get('application.element.state')) {
          this.set('state', this.get('application.element.state'));
        }
        // If this element's state exists, the properties ot the element are
        // initialized from state.
        if (this.statePath && this.get(this.statePath)) {
          Object.keys(this.get(this.statePath)).forEach((key) => {
            this.set(key, this.get([this.statePath, key]));
          });
        }
      }

      /**
       * Removes this element from the list of state-aware elements in the
       * application.
       */
      disconnectedCallback() {
        const index = this.application.stateAwareElements.indexOf(this);
        if (index !== -1) {
          this.application.stateAwareElements.splice(index, 1);
        }

        super.disconnectedCallback();
      }

      /**
       * Returns application element.
       * @return {Element}
       */
      getApplication() {
        return this.get('application.element');
      }

    });
</script>

================================================
FILE: state-mutator.html
================================================
<!--
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="state-aware.html">

<script>
  var UniFlow = window.UniFlow || {};

  /**

   Some non-visual elements, like action dispatchers, need to modify application
   state, in which case they should have this mixin applied. Implements state-
   aware and re-declares state property with notify attribute. State mutator elements
   are only supposed to exist at the application level.

   @polymer
   @mixinFunction
   @appliesMixin UniFlow.StateAware
  */
  UniFlow.StateMutator = Polymer.dedupingMixin((base) =>
    class extends UniFlow.StateAware(base) {

      static get properties() {
        return {
          /**
           * Application state.
           */
          state: {
            type: Object,
            notify: true
          }
        }

      };
    });
</script>

================================================
FILE: test/action-dispatcher_test.html
================================================
<!doctype html>
<!--
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>

<head>
  <meta charset="UTF-8">
  <title>UniFlow.ActionDispatcher tests</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../web-component-tester/browser.js"></script>

  <link rel="import" href="../action-dispatcher.html">

</head>

<body>

  <dom-module id="action-dispatcher-test">
    <script>
      class ActionDispatcherTest extends UniFlow.ActionDispatcher(Polymer.Element) {
        static get is() {
          return 'action-dispatcher-test';
        }
      }

      customElements.define(ActionDispatcherTest.is, ActionDispatcherTest);
    </script>
  </dom-module>

  <dom-module id="nested-action-dispatcher-test">
    <template>
      <action-dispatcher-test id="inner"></action-dispatcher-test>
    </template>
    <script>
      class NestedActionDispatcherTest extends UniFlow.ActionDispatcher(Polymer.Element) {
        static get is() {
          return 'nested-action-dispatcher-test';
        }
      }

      customElements.define(NestedActionDispatcherTest.is, NestedActionDispatcherTest);
    </script>
  </dom-module>

  <test-fixture id="basic">
    <template>
      <action-dispatcher-test></action-dispatcher-test>
    </template>
  </test-fixture>

  <test-fixture id="nested">
    <template>
      <nested-action-dispatcher-test></nested-action-dispatcher-test>
    </template>
  </test-fixture>

  <script>
    'use strict';

    suite('UniFlow.ActionDispatcher', () => {

      let actionDispatcher;

      setup(() => {
        actionDispatcher = fixture('basic');
      });

      test('is ok', () => {
        expect(actionDispatcher).to.be.ok;
      });

      test('dispatchAction calls method with name that matches action ' +
        'type', () => {
          actionDispatcher.MY_ACTION = sinon.spy();
          const detail = {
            type: 'MY_ACTION',
            foo: 'bar'
          };
          actionDispatcher.dispatchAction(detail);
          expect(actionDispatcher.MY_ACTION).to.be.calledWith(detail);
        });

      test('dispatchAction returns false when action processor method' +
        ' returns false', () => {
          actionDispatcher.MY_ACTION = () => false;
          const detail = {
            type: 'MY_ACTION'
          };
          expect(actionDispatcher.dispatchAction(detail)).to.be.false;
        });

      test('nested action dispatcher\'s method is invoked', () => {
        actionDispatcher = fixture('nested');
        expect(actionDispatcher).to.be.ok;
        expect(actionDispatcher.$.inner).to.be.ok;
        actionDispatcher.$.inner.MY_ACTION = sinon.spy();
        const detail = {
          type: 'MY_ACTION',
          foo: 'bar'
        };
        actionDispatcher.dispatchAction(detail);
        expect(actionDispatcher.$.inner.MY_ACTION).to.be.calledWith(detail);
      });

    });
  </script>

</body>

</html>

================================================
FILE: test/action-emitter_test.html
================================================
<!doctype html>
<!--
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>

<head>
  <meta charset="UTF-8">
  <title>UniFlow.ActionEmitter tests</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../web-component-tester/browser.js"></script>

  <link rel="import" href="../action-emitter.html">

</head>

<body>

  <dom-module id="action-emitter-test">
    <script>
      class ActionEmitterTest extends UniFlow.ActionEmitter(Polymer.Element) {
        static get is() {
          return 'action-emitter-test';
        }
      }

      customElements.define(ActionEmitterTest.is, ActionEmitterTest);
    </script>
  </dom-module>

  <test-fixture id="basic">
    <template>
      <action-emitter-test></action-emitter-test>
    </template>
  </test-fixture>

  <script>
    'use strict';

    suite('UniFlow.ActionEmitter', () => {

      let actionEmitter;

      setup(() => {
        actionEmitter = fixture('basic');
      });

      test('is ok', () => {
        expect(actionEmitter).to.be.ok;
      });

      test('fires a dispatch-action event on action emit', () => {
        sinon.spy(actionEmitter, 'dispatchEvent');
        let action = 'detail';
        actionEmitter.emitAction(action);
        expect(actionEmitter.dispatchEvent).to.be.calledWithMatch({
          type: 'dispatch-action',
          detail: action
        });
      });

      test('prepends statePath to detail.path', () => {
        sinon.spy(actionEmitter, 'dispatchEvent');
        let action = {
          path: 'my.path'
        };
        actionEmitter.statePath = 'state';
        actionEmitter.emitAction(action);
        expect(actionEmitter.dispatchEvent).to.be.calledWithMatch({
          type: 'dispatch-action',
          detail: {
            path: 'state.my.path'
          }
        });
      });

      test('does not change detail.path if it starts with "state."', () => {
        sinon.spy(actionEmitter, 'dispatchEvent');
        let action = {
          path: 'state.my.path'
        };
        actionEmitter.statePath = 'some.state.path';
        actionEmitter.emitAction(action);
        expect(actionEmitter.dispatchEvent).to.be.calledWithMatch({
          type: 'dispatch-action',
          detail: {
            path: 'state.my.path'
          }
        });
      });

    });
  </script>

</body>

</html>

================================================
FILE: test/application-state_test.html
================================================
<!doctype html>
<!--
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>

<head>
  <meta charset="UTF-8">
  <title>UniFlow.ApplicationState tests</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../web-component-tester/browser.js"></script>

  <link rel="import" href="../application-state.html">
  <link rel="import" href="../state-aware.html">

</head>

<body>

  <dom-module id="application-state-test">
    <script>
      class ApplicationStateTest extends UniFlow.ApplicationState(Polymer.Element) {
        static get is() {
          return 'application-state-test';
        }

        connectedCallback() {
          super.connectedCallback();

          this.state = {
            a: {
              innerValue: 'aaa'
            },
            b: {}
          };
        }
      }

      customElements.define(ApplicationStateTest.is, ApplicationStateTest);
    </script>
  </dom-module>

  <dom-module id="state-aware-a">
    <template>
      <div id="inner">[[innerValue]]</div>
    </template>
    <script>
      class StateAwareA extends UniFlow.StateAware(Polymer.Element) {
        static get is() {
          return 'state-aware-a';
        }
      }

      customElements.define(StateAwareA.is, StateAwareA);
    </script>
  </dom-module>

  <dom-module id="state-aware-b">
    <template>
      <div id="inner-state">[[state.value]]</div>
      <div id="inner">[[innerValue]]</div>
      <state-aware-a id="a" state-path="state.a"></state-aware-a>
    </template>
    <script>
      class StateAwareB extends UniFlow.StateAware(Polymer.Element) {
        static get is() {
          return 'state-aware-b';
        }
      }

      customElements.define(StateAwareB.is, StateAwareB);
    </script>
  </dom-module>

  <test-fixture id="basic">
    <template>
      <application-state-test>
        <state-aware-b id="b" state-path="state.b">
        </state-aware-b>
      </application-state-test>
    </template>
  </test-fixture>

  <script>
    'use strict';

    suite('UniFlow.ApplicationState', () => {

      let applicationState;

      setup(() => {
        applicationState = fixture('basic');
      });

      test('is ok', () => {
        expect(applicationState).to.be.ok;
        expect(applicationState.querySelector('#b')).to.be.ok;
        expect(applicationState.querySelector('#b').root.querySelector('#a')).to.be.ok;
      });

      test('dispatchAction is called on dispatch-action event', () => {
        sinon.spy(applicationState, 'dispatchAction');
        applicationState.dispatchEvent(new CustomEvent('dispatch-action', {
          detail: 'detail',
          bubbles: true,
          composed: true
        }));
        expect(applicationState.dispatchAction)
          .to.have.been.calledWith('detail');
      });

      test('application.element is set to element itself', () => {
        expect(applicationState.get('application.element'))
          .to.equal(applicationState);
      });

      test('state aware elements receive initial element state', () => {
        expect(applicationState.querySelector('#b').root.querySelector('#a').root.querySelector('#inner').innerText)
          .to.equal('aaa');
      });

      test('state aware elements listen to application state', () => {
        applicationState.set('state.value', 'something');
        expect(applicationState.querySelector('#b').root.querySelector('#inner-state').innerText)
          .to.equal('something');
      });

      test('state aware elements listen to element state changes', () => {
        applicationState.set('state.a.innerValue', 'aaa.modified');
        expect(applicationState.querySelector('#b').root.querySelector('#a').root.querySelector('#inner').innerText)
          .to.equal('aaa.modified');
        applicationState.set('state.b.innerValue', 'bbb');
        expect(applicationState.querySelector('#b').root.querySelector('#inner').innerText)
          .to.equal('bbb');
      });

      test('state aware elements handle entire element state replacement', () => {
        applicationState.set('state.a', {
          innerValue: 'aaa.new'
        });
        expect(applicationState.querySelector('#b').root.querySelector('#a').root.querySelector('#inner').innerText)
          .to.equal('aaa.new');
      });

      //      test('fires a dispatch-action event on action emit', () => {
      //        sinon.spy(applicationState, 'fire');
      //        let action = 'detail';
      //        applicationState.emitAction(action);
      //        expect(applicationState.fire).to.be.calledWith('dispatch-action', action);
      //      });

    });
  </script>

</body>

</html>

================================================
FILE: test/list-view_test.html
================================================
<!doctype html>
<!--
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>

<head>
  <meta charset="UTF-8">
  <title>UniFlow.ListView tests</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../web-component-tester/browser.js"></script>

  <link rel="import" href="../list-view.html">

</head>

<body>

  <dom-module id="list-view-test">
    <script>
      class ListViewTest extends UniFlow.ListView(Polymer.Element) {
        static get is() {
          return 'list-view-test';
        }
      }

      customElements.define(ListViewTest.is, ListViewTest);
    </script>
  </dom-module>

  <test-fixture id="basic">
    <template>
      <list-view-test list="[[list]]">
      </list-view-test>
    </template>
  </test-fixture>

  <script>
    'use strict';

    suite('UniFlow.ListView', () => {

      let listView;

      setup(() => {
        listView = fixture('basic');
        listView.set('list', []);
      });

      test('is ok', () => {
        expect(listView).to.be.ok;
      });

      test('updates selected count on new list', () => {
        sinon.spy(listView, 'updateSelectedCount_');
        listView.set('list', [{
          foo: 'bar'
        }]);
        expect(listView.updateSelectedCount_).to.be.called;
      });

      test('updates selected count on element added/removed', () => {
        sinon.spy(listView, 'updateSelectedCount_');
        listView.push('list', {
          foo: 'bar'
        });
        listView.splice('list', 0, 1);
        expect(listView.updateSelectedCount_).to.be.calledTwice;
      });

      test('updates selected count on $selected changed', () => {
        listView.set('list', [{
          foo: 'bar'
        }]);
        sinon.spy(listView, 'updateSelectedCount_');
        listView.set(['list', 0, '$selected'], true);
        listView.set(['list', 0, '$selected'], false);
        expect(listView.updateSelectedCount_).to.be.calledTwice;
      });

      test('calculates selectedCount', () => {
        listView.set('list', [{
            foo: 'bar',
            $selected: true
          },
          {
            foo: 'baz',
            $selected: false
          },
          {},
        ]);
        expect(listView.selectedCount).to.equal(1);
      });

      test('emits delete action for selected', () => {
        sinon.spy(listView, 'emitAction');
        listView.set('list', [{
            foo: 'bar',
            $selected: true
          },
          {
            foo: 'baz',
            $selected: false
          },
          {},
        ]);
        listView.deleteSelected('DELETE');
        expect(listView.emitAction).to.be.calledOnce;
        expect(listView.emitAction).to.be.calledWith({
          type: 'DELETE',
          item: {
            foo: 'bar',
            $selected: true
          }
        });
      });

    });
  </script>

</body>

</html>

================================================
FILE: test/model-view_test.html
================================================
<!doctype html>
<!--
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>

<head>
  <meta charset="UTF-8">
  <title>UniFlow.ModelView tests</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../web-component-tester/browser.js"></script>

  <link rel="import" href="../model-view.html">

</head>

<body>

  <dom-module id="model-view-test">
    <script>
      class ModelViewTest extends UniFlow.ModelView(Polymer.Element) {
        static get is() {
          return 'model-view-test';
        }

        constructor() {
          super();
          this._validation = {
            'foo': (value) => !value ? 'bad foo' : '',
            'xxx': (value) => value == 'yyy' ? 123 : ''
          }
        }

        get validation() {
          return this._validation;
        }
        
        get saveAction() {
          return 'SAVE';
        }

        getMessage(code) {
          return code === 123 ? 'error 123' : '';
        }
      }

      customElements.define(ModelViewTest.is, ModelViewTest);
    </script>
  </dom-module>

  <test-fixture id="basic">
    <template is="dom-template">
      <model-view-test>
      </model-view-test>
    </template>
  </test-fixture>

  <script>
    'use strict';

    suite('UniFlow.ModelView', () => {

      let modelView, model, status;

      setup(() => {
        model = {foo: 'bar'};
        status = {};
        modelView = fixture('basic');
        modelView.set('model', model);
      });

      test('is ok', () => {
        expect(modelView).to.be.ok;
      });

      test('saves model', () => {
        sinon.spy(modelView, 'emitAction');
        modelView.saveModel();
        expect(modelView.emitAction).to.be.calledWith({
          model,
          path: 'model',
          type: 'SAVE'
        });
      });

      test('saves model with custom action type', () => {
        sinon.spy(modelView, 'emitAction');
        modelView.saveModel('CUSTOM-SAVE');
        expect(modelView.emitAction).to.be.calledWith({
          model,
          path: 'model',
          type: 'CUSTOM-SAVE'
        });
      });

      test('saves model with custom action', () => {
        sinon.spy(modelView, 'emitAction');
        modelView.saveModel({
          type: 'XXX',
          path: 'my.model'
        });
        expect(modelView.emitAction).to.be.calledWith({
          model,
          path: 'my.model',
          type: 'XXX'
        });
      });

      test('initializes validation status', () => {
        modelView.initValidationStatus_();
        expect(modelView.get('status.validation')).to.deep.equal({
          foo: {
            invalid: false
          },
          xxx: {
            invalid: false
          }
        });
      });

      test('validates model when validation not specified', () => {
        modelView._validation = undefined;
        expect(modelView.validateModel()).to.be.true;
      });

      test('validates model to true', () => {
        expect(modelView.validateModel()).to.be.true;
        expect(modelView.get('status.validation')).to.deep.equal({
          foo: {
            invalid: false,
            errorMessage: ''
          },
          xxx: {
            invalid: false,
            errorMessage: ''
          }
        });
      });

      test('validates model to false with error text', () => {
        modelView.set('model.foo', '');
        expect(modelView.validateModel()).to.be.false;
        expect(modelView.get('status.validation.foo')).to.deep.equal({
          invalid: true,
          errorMessage: 'bad foo'
        });
      });

      test('validates model to false with error code', () => {
        modelView.set('model.xxx', 'yyy');
        expect(modelView.validateModel()).to.be.false;
        expect(modelView.get('status.validation.xxx')).to.deep.equal({
          invalid: true,
          errorMessage: 'error 123'
        });
      });

      test('validates to true and saves', () => {
        sinon.spy(modelView, 'saveModel');
        modelView.validateAndSave('action');
        expect(modelView.saveModel).to.be.calledWith('action');
      });

      test('validates to false and does not save', () => {
        sinon.spy(modelView, 'saveModel');
        modelView.set('model.foo', '');
        modelView.validateAndSave('action');
        expect(modelView.saveModel).to.have.not.been.called;
      });

      test('handles model change', () => {
        sinon.spy(modelView, 'initValidationStatus_');
        modelView.set('model', {
          foo: 'bar'
        });
        expect(modelView.initValidationStatus_).to.have.been.called;
        expect(modelView.get('status')).to.deep.equal({
          isModified: false,
          validation: {
            foo: {
              invalid: false
            },
            xxx: {
              invalid: false
            }
          }
        });
      });

      test('handles model change to null', () => {
        sinon.spy(modelView, 'initValidationStatus_');
        modelView.set('model', null);
        expect(modelView.initValidationStatus_).to.have.not.been.called;
        expect(modelView.get('status')).to.deep.equal({});
      });

      test('handles model field change', () => {
        sinon.spy(modelView, 'initValidationStatus_');
        modelView.set('model.foo', 'baz');
        expect(modelView.initValidationStatus_).to.have.not.been.called;
        expect(modelView.get('status.isModified')).to.equal(true);
      });

    });
  </script>

</body>

</html>

================================================
FILE: test/state-aware_test.html
================================================
<!doctype html>
<!--
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>

<head>
  <meta charset="UTF-8">
  <title>UniFlow.StateAware tests</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../web-component-tester/browser.js"></script>

  <link rel="import" href="../state-aware.html">

</head>

<body>

<dom-module id="state-aware-test">
  <script>
    class StateAwareTest extends UniFlow.StateAware(Polymer.Element) {
      static get is() {
        return 'state-aware-test';
      }
    }

    customElements.define(StateAwareTest.is, StateAwareTest);
  </script>
</dom-module>

<test-fixture id="basic">
  <template is="dom-template">
    <state-aware-test>
    </state-aware-test>
  </template>
</test-fixture>

<script>
  'use strict';

  suite('UniFlow.StateAware', () => {

    let stateAware;

    setup(() => {
      stateAware = fixture('basic');
    });

    test('is ok', () => {
      expect(stateAware).to.be.ok;
    });

    test('added to application.stateAwareElements', () => {
      expect(stateAware.get('application.stateAwareElements')).to.deep.equal(
          [stateAware]);
    });

    test('removed from application.stateAwareElements', () => {
      stateAware.parentNode.removeChild(stateAware);
      expect(stateAware.get('application.stateAwareElements'))
          .to.deep.equal([]);
    });

    test('added to application.stateAwareElements', () => {
      sinon.stub(stateAware, 'get').withArgs('application.element')
          .returns('my-app');
      expect(stateAware.getApplication()).to.equal('my-app');
    });

  });
</script>

</body>

</html>

================================================
FILE: todomvc/README.md
================================================
# \<todo\>

ToDo MVC application implemented with UniFlow for Polymer 2.x

## Install Bower Dependencies

Run `bower install`

## Install the Polymer-CLI

Make sure you have the [Polymer CLI](https://www.npmjs.com/package/polymer-cli) installed.

## Run the Application

Run `polymer serve` to serve your application locally.

Open the by adding todomvc to the url, for example:

http://127.0.0.1:8081/components/uniflow-polymer/todomvc

================================================
FILE: todomvc/elements/todo-action-dispatcher.html
================================================
<link rel="import" href="../../../polymer/polymer-element.html">
<link rel="import" href="../../action-dispatcher.html">
<link rel="import" href="../../action-emitter.html">

<dom-module id="todo-action-dispatcher">
  <script>
    class TodoActionDispatcher extends UniFlow.ActionEmitter(UniFlow.ActionDispatcher(Polymer.Element)) {
      static get is() {
        return 'todo-action-dispatcher';
      }

      _updateAllCompleted() {
        if (this.get('state.todoList') && this.get('state.todoList').length) {
          this.set('state.allCompleted',
              this.get('state.todoList').every((item) => item.completed));
        } else {
          this.set('state.allCompleted', false);
        }
      }

      _updateActiveCount() {
        this.set('state.activeCount', this.get('state.todoList').reduce((count, todoItem) => count + (
            todoItem.completed ? 0 : 1), 0));
      }

      [todo.actions.INIT_APPLICATION](details) {
        this._updateAllCompleted();
        this._updateActiveCount();
      }

      [todo.actions.ADD_TODO](details) {
        this.push('state.todoList', {
          text: details.text,
          completed: false
        });
        this._updateAllCompleted();
        this._updateActiveCount();
      }

      [todo.actions.UPDATE_TODO](details) {
        let index = this.get('state.todoList').indexOf(details.model);
        this.set(['state.todoList', index, 'text'], details.text);
      }

      [todo.actions.REMOVE_TODO](details) {
        let index = this.state.todoList.indexOf(details.model);
        this.splice('state.todoList', index, 1);
        this._updateAllCompleted();
        this._updateActiveCount();
      }

      [todo.actions.SELECTION_CHANGED](details) {
        if (details.applyToAll) {
          this.set('state.allCompleted', details.value);
          this.get('state.todoList').forEach((item, index) => {
            this.set(['state.todoList', index, 'completed'],
                details.value);
          });
        } else {
          if (details.model) {
            let index = this.get('state.todoList').indexOf(details.model);
            this.set(['state.todoList', index, 'completed'], details.completed);
          }
          this._updateAllCompleted();
        }
        this._updateActiveCount();
      }

      [todo.actions.CLEAR_COMPLETED](details) {
        let completed = this.get('state.todoList').filter(
            elem => elem.completed);
        completed.forEach(elem => {
          let index = this.state.todoList.indexOf(elem);
          this.splice('state.todoList', index, 1);
        });
        this._updateAllCompleted();
      }
    }

    customElements.define(TodoActionDispatcher.is, TodoActionDispatcher);
  </script>
</dom-module>

================================================
FILE: todomvc/elements/todo-app.html
================================================
<link rel="import" href="../../../polymer/polymer-element.html">
<link rel="import" href="../../action-emitter.html">
<link rel="import" href="../../application-state.html">
<link rel="import" href="../../../app-route/app-route.html">
<link rel="import" href="../../../app-route/app-location.html">
<link rel="import" href="../../../iron-localstorage/iron-localstorage.html">

<link rel="import" href="todo-header.html">
<link rel="import" href="todo-items.html">
<link rel="import" href="todo-footer.html">
<link rel="import" href="todo-action-dispatcher.html">

<link rel="import" href="todo-styles.html">

<dom-module id="todo-app">
  <template>
    <style include="todo-styles">

    </style>

    <todo-action-dispatcher state="{{state}}"></todo-action-dispatcher>
    <section class="todoapp">
      <app-location route="{{route}}" use-hash-as-path></app-location>
      <app-route route="{{route}}" pattern="/:filterBy" data="{{routeData}}"></app-route>
      <iron-localstorage name="todo-list" value="{{state.todoList}}" on-iron-localstorage-load-empty="onStorageLoadEmpty" on-iron-localstorage-load="onStorageLoad">
      </iron-localstorage>
      <todo-header></todo-header>
      <template is="dom-if" if="[[state.todoList.length]]">
        <!-- This section should be hidden by default and shown when there are todos -->
        <section class="main">
          <input class="toggle-all" type="checkbox" checked="[[state.allCompleted]]" on-change="onAllCompletedChange">
          <label for="toggle-all">Mark all as complete</label>
          <todo-items id="list" list="[[state.todoList]]" filter-by="[[routeData.filterBy]]">
          </todo-items>
        </section>
        <!-- This footer should hidden by default and shown when there are todos -->
        <todo-footer filter-by="[[routeData.filterBy]]">
        </todo-footer>
      </template>
    </section>

  </template>
  <script>
    class TodoApp extends UniFlow.ActionEmitter(UniFlow.ApplicationState(Polymer.Element)) {
      static get is() {
        return 'todo-app';
      }

      static get properties() {
        return {
          route: Object,
          routeData: Object
        }
      }

      ready() {
        super.ready();
        this.set('state', {
          allCompleted: false,
          activeCount: 0
        });
      }

      onStorageLoadEmpty() {
        this.set('state.todoList', []);
      }

      onStorageLoad() {
        this.emitAction({
          type: todo.actions.INIT_APPLICATION
        });
      }
    }

    customElements.define(TodoApp.is, TodoApp);
  </script>
</dom-module>

================================================
FILE: todomvc/elements/todo-footer.html
================================================
<link rel="import" href="../../../polymer/polymer-element.html">
<link rel="import" href="../../../polymer/lib/mixins/gesture-event-listeners.html">
<link rel="import" href="../../action-emitter.html">
<link rel="import" href="../../state-aware.html">

<link rel="import" href="todo-styles.html">

<dom-module id="todo-footer">
  <template>
    <style include="todo-styles">
      .filters li a[selected] {
        border-color: rgba(175, 47, 47, 0.2);
      }
    </style>

    <footer class="footer">
      <span class="todo-count">
        <strong>[[state.activeCount]]</strong>
        item<span hidden$="[[isEqual(state.activeCount, 1)]]">s</span> left
      </span>
      <ul class="filters">
        <li>
          <a selected$="[[!filterBy]]" href="#/">All</a>
        </li>
        <li>
          <a selected$="[[isEqual(filterBy, 'active')]]" href="#/active">Active</a>
        </li>
        <li>
          <a selected$="[[isEqual(filterBy, 'completed')]]" href="#/completed">Completed</a>
        </li>
      </ul>
      <!-- Hidden if no completed items are left -->
      <template is="dom-if" if="[[completedCount]]">
        <button class="clear-completed" on-tap="onClearCompletedButtonTap">Clear completed</button>
      </template>
    </footer>

  </template>
  <script>
    class TodoFooter extends Polymer.GestureEventListeners(UniFlow.ActionEmitter(UniFlow.StateAware(Polymer.Element))) {

      static get is() {
        return 'todo-footer';
      }

      static get properties() {
        return {
          filterBy: String,
          completedCount: {
            type: Number,
            computed: 'getCompletedCount(state.todoList.length, state.activeCount)'
          }
        }
      }

      isEqual(val1, val2) {
        return val1 == val2;
      }

      getCompletedCount(totalCount, activeCount) {
        return totalCount - activeCount;
      }

      onClearCompletedButtonTap() {
        this.emitAction({
          type: todo.actions.CLEAR_COMPLETED
        })
      }
    }

    customElements.define(TodoFooter.is, TodoFooter);
  </script>
</dom-module>

================================================
FILE: todomvc/elements/todo-header.html
================================================
<link rel="import" href="../../../polymer/polymer-element.html">
<link rel="import" href="../../action-emitter.html">
<link rel="import" href="../../state-aware.html">

<link rel="import" href="todo-styles.html">

<dom-module id="todo-header">
  <template>
    <style include="todo-styles">

    </style>

    <header class="header">
      <h1>todos</h1>
      <input class="new-todo" placeholder="What needs to be done?" value="{{todoText::input}}" on-keydown="onInputKeyDown" autofocus>
    </header>

  </template>
  <script>
    class TodoHeader extends UniFlow.ActionEmitter(Polymer.Element) {
      static get is() {
        return 'todo-header';
      }

      static properties() {
        return {
          todoText: String
        }
      }

      onInputKeyDown(e) {
        if (e.keyCode == 13 && this.todoText.trim().length) {
          this.emitAction({
            type: todo.actions.ADD_TODO,
            text: this.todoText
          });
          this.todoText = '';
        }
      }
    }

    customElements.define(TodoHeader.is, TodoHeader);
  </script>
</dom-module>

================================================
FILE: todomvc/elements/todo-item.html
================================================
<link rel="import" href="../../../polymer/polymer-element.html">
<link rel="import" href="../../../polymer/lib/mixins/gesture-event-listeners.html">
<link rel="import" href="../../model-view.html">

<link rel="import" href="todo-styles.html">

<dom-module id="todo-item">
  <template>
    <style include="todo-styles">
       :host {
        display: block;
      }

      li {
        position: relative;
        font-size: 24px;
      }

      li[editing] {
        border-bottom: none;
        padding: 0;
      }

      li[editing] .edit {
        display: block;
        width: 506px;
        padding: 12px 16px;
        margin: 0 0 0 43px;
      }

      li[editing] .view {
        display: none;
      }

      li .toggle {
        text-align: center;
        width: 40px;
        /* auto, since non-WebKit browsers doesn't support input styling */
        height: auto;
        position: absolute;
        top: 9px;
        margin: auto 0;
        border: none;
        /* Mobile Safari */
        -webkit-appearance: none;
        appearance: none;
      }

      li .toggle:after {
        content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
      }

      li .toggle:checked:after {
        content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
      }

      li label {
        word-break: break-all;
        padding: 15px 60px 15px 15px;
        margin-left: 45px;
        display: block;
        line-height: 1.2;
        transition: color 0.4s;
      }

      li[completed] label {
        color: #d9d9d9;
        text-decoration: line-through;
      }

      li .destroy {
        display: none;
        position: absolute;
        top: 0;
        right: 10px;
        bottom: 0;
        width: 40px;
        height: 40px;
        margin: auto 0;
        font-size: 30px;
        color: #cc9a9a;
        margin-bottom: 11px;
        transition: color 0.2s ease-out;
      }

      li .destroy:hover {
        color: #af5b5e;
      }

      li .destroy:after {
        content: '×';
      }

      li:hover .destroy {
        display: block;
      }

      li[editing]:last-child {
        margin-bottom: -1px;
      }

      li .toggle:after {
        content: url('../resources/unchecked.svg');
      }

      li .toggle:checked:after {
        content: url('../resources/checked.svg');
      }
    </style>

    <li completed$="[[model.completed]]" editing$="[[isEditing]]">
      <div class="view">
        <input class="toggle" type="checkbox" checked="[[model.completed]]" on-change="onCompletedChange">
        <label on-dblclick="onViewDblClick">[[model.text]]</label>
        <button class="destroy" on-tap="onDestroyTap"></button>
      </div>
      <input id="text-input" class="edit" hidden="[[!isEditing]]" on-blur="onInputBlur" on-keydown="onInputKeyDown">
    </li>
  </template>
  <script>
    class TodoItem extends UniFlow.ModelView(Polymer.GestureEventListeners(Polymer.Element)) {

      static get is() {
        return 'todo-item';
      }


      static get properties() {
        return {
          filterBy: String,
          isEditing: {
            type: Boolean,
            value: false
          }
        }
      }

      onCompletedChange(e) {
        this.emitAction({
          type: todo.actions.SELECTION_CHANGED,
          model: this.model,
          completed: e.target.checked
        });
      }

      _removeTodo() {
        this.emitAction({
          type: todo.actions.REMOVE_TODO,
          model: this.model
        });
      }

      onDestroyTap(e) {
        this._removeTodo();
      }

      onViewDblClick(e) {
        this.$['text-input'].value = this.model.text;
        this.isEditing = true;
        this.$['text-input'].focus();
      }

      onInputBlur(e) {
        if (this.isEditing) {
          this._confirmEdit();
        }
      }

      _confirmEdit() {
        this.emitAction({
          type: todo.actions.UPDATE_TODO,
          model: this.model,
          text: this.$['text-input'].value
        });
        this.isEditing = false;
        if (!this.model.text.trim()) {
          this._removeTodo();
        }
      }

      onInputKeyDown(e) {
        if (e.keyCode == 13) {
          this.$['text-input'].blur();
        } else if (e.keyCode == 27) {
          this.isEditing = false;
        }
      }
    }

    customElements.define(TodoItem.is, TodoItem);
  </script>
</dom-module>

================================================
FILE: todomvc/elements/todo-items.html
================================================
<link rel="import" href="../../../polymer/polymer-element.html">
<link rel="import" href="../../list-view.html">

<link rel="import" href="todo-item.html">

<link rel="import" href="todo-styles.html">

<dom-module id="todo-items">
  <template>
    <style include="todo-styles">
      :host {
        display: block;
      }

      todo-item {
        border-top: 1px solid #ededed;
      }

      todo-item:first-child {
        border-top: none;
      }
    </style>

    <section class="main">
      <input id="ta" class="toggle-all" type="checkbox" checked="[[state.allCompleted]]" on-change="onAllCompletedChange">
      <label for="ta">Mark all as complete</label>
      <ul class="todo-list">
        <template is="dom-repeat" id="list-template" items="[[list]]" as="todoItem" filter="[[filterList(filterBy)]]" observe="completed">
          <todo-item model="[[todoItem]]"></todo-item>
        </template>
      </ul>
    </section>
  </template>
  <script>
    class TodoItems extends UniFlow.ListView(UniFlow.StateAware(Polymer.Element)) {
      static get is() {
        return 'todo-items';
      }

      filterList(filterBy) {
        return function (item) {
          switch (filterBy) {
            case 'active':
              return !item.completed;
            case 'completed':
              return item.completed;
            default:
              return true;
          }
        };
      }

      onAllCompletedChange(event) {
        this.emitAction({
          type: todo.actions.SELECTION_CHANGED,
          applyToAll: true,
          value: event.target.checked
        })
      }
    }

    customElements.define(TodoItems.is, TodoItems);
  </script>
</dom-module>

================================================
FILE: todomvc/elements/todo-styles.html
================================================
<dom-module id="todo-styles">
  <template>
    <style>
      button {
        margin: 0;
        padding: 0;
        border: 0;
        background: none;
        font-size: 100%;
        vertical-align: baseline;
        font-family: inherit;
        font-weight: inherit;
        color: inherit;
        -webkit-appearance: none;
        appearance: none;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
      }

      :focus {
        outline: 0;
      }

      .hidden {
        display: none;
      }

      .todoapp {
        background: #fff;
        margin: 130px 0 40px 0;
        position: relative;
        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
        0 25px 50px 0 rgba(0, 0, 0, 0.1);
      }

      input::-webkit-input-placeholder {
        font-style: italic;
        font-weight: 300;
        color: #e6e6e6;
      }

      input::-moz-placeholder {
        font-style: italic;
        font-weight: 300;
        color: #e6e6e6;
      }

      input::input-placeholder {
        font-style: italic;
        font-weight: 300;
        color: #e6e6e6;
      }

      h1 {
        position: absolute;
        top: -155px;
        width: 100%;
        font-size: 100px;
        font-weight: 100;
        text-align: center;
        color: rgba(175, 47, 47, 0.15);
        -webkit-text-rendering: optimizeLegibility;
        -moz-text-rendering: optimizeLegibility;
        text-rendering: optimizeLegibility;
      }

      .new-todo,
      .edit {
        position: relative;
        margin: 0;
        width: 100%;
        font-size: 24px;
        font-family: inherit;
        font-weight: inherit;
        line-height: 1.4em;
        border: 0;
        color: inherit;
        padding: 6px;
        border: 1px solid #999;
        box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
        box-sizing: border-box;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
      }

      .new-todo {
        padding: 16px 16px 16px 60px;
        border: none;
        background: rgba(0, 0, 0, 0.003);
        box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
      }

      .main {
        position: relative;
        z-index: 2;
        border-top: 1px solid #e6e6e6;
      }

      .toggle-all {
        text-align: center;
        border: none;
        /* Mobile Safari */
        opacity: 0;
        position: absolute;
      }

      .toggle-all+label {
        width: 60px;
        height: 34px;
        font-size: 0;
        position: absolute;
        top: -52px;
        left: -13px;
        -webkit-transform: rotate(90deg);
        transform: rotate(90deg);
      }

      .toggle-all+label:before {
        content: '❯';
        font-size: 22px;
        color: #e6e6e6;
        padding: 10px 27px 10px 27px;
      }

      .toggle-all:checked+label:before {
        color: #737373;
      }

      .todo-list {
        margin: 0;
        padding: 0;
        list-style: none;
      }

      .todo-list li {
        position: relative;
        font-size: 24px;
        border-bottom: 1px solid #ededed;
      }

      .todo-list li:last-child {
        border-bottom: none;
      }

      .todo-list li.editing {
        border-bottom: none;
        padding: 0;
      }

      .todo-list li.editing .edit {
        display: block;
        width: 506px;
        padding: 12px 16px;
        margin: 0 0 0 43px;
      }

      .todo-list li.editing .view {
        display: none;
      }

      .todo-list li .toggle {
        text-align: center;
        width: 40px;
        /* auto, since non-WebKit browsers doesn't support input styling */
        height: auto;
        position: absolute;
        top: 0;
        bottom: 0;
        margin: auto 0;
        border: none;
        /* Mobile Safari */
        -webkit-appearance: none;
        appearance: none;
      }

      .todo-list li .toggle {
        opacity: 0;
      }

      .todo-list li .toggle+label {
        /*
		Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
		IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
	*/
        background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
        background-repeat: no-repeat;
        background-position: center left;
      }

      .todo-list li .toggle:checked+label {
        background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
      }

      .todo-list li label {
        word-break: break-all;
        padding: 15px 15px 15px 60px;
        display: block;
        line-height: 1.2;
        transition: color 0.4s;
      }

      .todo-list li.completed label {
        color: #d9d9d9;
        text-decoration: line-through;
      }

      .todo-list li .destroy {
        display: none;
        position: absolute;
        top: 0;
        right: 10px;
        bottom: 0;
        width: 40px;
        height: 40px;
        margin: auto 0;
        font-size: 30px;
        color: #cc9a9a;
        margin-bottom: 11px;
        transition: color 0.2s ease-out;
      }

      .todo-list li .destroy:hover {
        color: #af5b5e;
      }

      .todo-list li .destroy:after {
        content: '×';
      }

      .todo-list li:hover .destroy {
        display: block;
      }

      .todo-list li .edit {
        display: none;
      }

      .todo-list li.editing:last-child {
        margin-bottom: -1px;
      }

      .footer {
        color: #777;
        padding: 10px 15px;
        height: 20px;
        text-align: center;
        border-top: 1px solid #e6e6e6;
      }

      .footer:before {
        content: '';
        position: absolute;
        right: 0;
        bottom: 0;
        left: 0;
        height: 50px;
        overflow: hidden;
        box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
        0 8px 0 -3px #f6f6f6,
        0 9px 1px -3px rgba(0, 0, 0, 0.2),
        0 16px 0 -6px #f6f6f6,
        0 17px 2px -6px rgba(0, 0, 0, 0.2);
      }

      .todo-count {
        float: left;
        text-align: left;
      }

      .todo-count strong {
        font-weight: 300;
      }

      .filters {
        margin: 0;
        padding: 0;
        list-style: none;
        position: absolute;
        right: 0;
        left: 0;
      }

      .filters li {
        display: inline;
      }

      .filters li a {
        color: inherit;
        margin: 3px;
        padding: 3px 7px;
        text-decoration: none;
        border: 1px solid transparent;
        border-radius: 3px;
      }

      .filters li a:hover {
        border-color: rgba(175, 47, 47, 0.1);
      }

      .filters li a.selected {
        border-color: rgba(175, 47, 47, 0.2);
      }

      .clear-completed,
      html .clear-completed:active {
        float: right;
        position: relative;
        line-height: 20px;
        text-decoration: none;
        cursor: pointer;
      }

      .clear-completed:hover {
        text-decoration: underline;
      }

      .info {
        margin: 65px auto 0;
        color: #bfbfbf;
        font-size: 10px;
        text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
        text-align: center;
      }

      .info p {
        line-height: 1;
      }

      .info a {
        color: inherit;
        text-decoration: none;
        font-weight: 400;
      }

      .info a:hover {
        text-decoration: underline;
      }
      /*
	Hack to remove background from Mobile Safari.
	Can't use it globally since it destroys checkboxes in Firefox
*/

      @media screen and (-webkit-min-device-pixel-ratio:0) {
        .toggle-all,
        .todo-list li .toggle {
          background: none;
        }

        .todo-list li .toggle {
          height: 40px;
        }
      }

      @media (max-width: 430px) {
        .footer {
          height: 50px;
        }

        .filters {
          bottom: 10px;
        }
      }
    </style>
  </template>
</dom-module>

================================================
FILE: todomvc/index.html
================================================
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>TodoMVC with UniFlow</title>
    <link rel="stylesheet" href="../../todomvc-app-css/index.css">

    <!-- See https://goo.gl/OOhYW5 -->
    <link rel="manifest" href="/manifest.json">

    <script src="scripts/actions.js"></script>

    <script src="../../webcomponentsjs/webcomponents-loader.js"></script>
    <link rel="import" href="elements/todo-app.html">

  </head>
  <body>
    <todo-app></todo-app>

    <footer class="info">
      <p>Double-click to edit a todo</p>
      <!-- Remove the below line -->
      <p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
      <!-- Change this out with your name and url -->
      <p>Created by <a href="mailto:militeev@google.com">Andrei Militeev</a></p>
      <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
    </footer>
  </body>
</html>


================================================
FILE: todomvc/manifest.json
================================================
{
  "name": "todo",
  "short_name": "todo",
  "description": "ToDo MVC application implemented with UniFlow for Polymer",
  "start_url": "/",
  "display": "standalone"
}


================================================
FILE: todomvc/scripts/actions.js
================================================
window.todo = window.todo || {};

todo.actions = {

  INIT_APPLICATION: "INIT_APPLICATION",
  ADD_TODO: "ADD_TODO",
  REMOVE_TODO: "REMOVE_TODO",
  UPDATE_TODO: "UPDATE_TODO",
  SELECTION_CHANGED: "SELECTION_CHANGED",
  CLEAR_COMPLETED: "CLEAR_COMPLETED"

}
Download .txt
gitextract_6nfjv9bm/

├── .bower.json
├── .gitignore
├── CONTRIBUTING
├── LICENSE
├── README.md
├── action-dispatcher.html
├── action-emitter.html
├── all-imports.html
├── analysis.json
├── application-state.html
├── bower.json
├── index.html
├── list-view.html
├── model-view.html
├── state-aware.html
├── state-mutator.html
├── test/
│   ├── action-dispatcher_test.html
│   ├── action-emitter_test.html
│   ├── application-state_test.html
│   ├── list-view_test.html
│   ├── model-view_test.html
│   └── state-aware_test.html
└── todomvc/
    ├── README.md
    ├── elements/
    │   ├── todo-action-dispatcher.html
    │   ├── todo-app.html
    │   ├── todo-footer.html
    │   ├── todo-header.html
    │   ├── todo-item.html
    │   ├── todo-items.html
    │   └── todo-styles.html
    ├── index.html
    ├── manifest.json
    └── scripts/
        └── actions.js
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (187K chars).
[
  {
    "path": ".bower.json",
    "chars": 1247,
    "preview": "{\n  \"name\": \"uniflow-polymer\",\n  \"version\": \"0.6.0\",\n  \"authors\": [\n    \"Andrei Militeev <militeev@gmail.com>\"\n  ],\n  \"d"
  },
  {
    "path": ".gitignore",
    "chars": 24,
    "preview": "bower_components/\n.idea/"
  },
  {
    "path": "CONTRIBUTING",
    "chars": 1450,
    "preview": "Want to contribute? Great! First, read this page (including the small print at the end).\n\n### Before you contribute\nBefo"
  },
  {
    "path": "LICENSE",
    "chars": 11344,
    "preview": "\n\n                                 Apache License\n                           Version 2.0, January 2004\n                 "
  },
  {
    "path": "README.md",
    "chars": 13983,
    "preview": "# UniFlow for Polymer 2.x\n\nSet of mixins to enable uni-directional data flow in Polymer application.\n\n**Important!**\n\n*T"
  },
  {
    "path": "action-dispatcher.html",
    "chars": 3593,
    "preview": "<!--\nCopyright 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
  },
  {
    "path": "action-emitter.html",
    "chars": 2155,
    "preview": "<!--\nCopyright 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
  },
  {
    "path": "all-imports.html",
    "chars": 322,
    "preview": "<link rel=\"import\" href=\"action-dispatcher.html\">\n<link rel=\"import\" href=\"action-emitter.html\">\n<link rel=\"import\" href"
  },
  {
    "path": "analysis.json",
    "chars": 62911,
    "preview": "{\n  \"schema_version\": \"1.0.0\",\n  \"mixins\": [\n    {\n      \"description\": \"   Key mixin that must be assigned to all eleme"
  },
  {
    "path": "application-state.html",
    "chars": 5640,
    "preview": "<!--\nCopyright 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
  },
  {
    "path": "bower.json",
    "chars": 1247,
    "preview": "{\n  \"name\": \"uniflow-polymer\",\n  \"version\": \"0.6.0\",\n  \"authors\": [\n    \"Andrei Militeev <militeev@gmail.com>\"\n  ],\n  \"d"
  },
  {
    "path": "index.html",
    "chars": 1140,
    "preview": "<!doctype html>\n<!--\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be"
  },
  {
    "path": "list-view.html",
    "chars": 4934,
    "preview": "<!--\nCopyright 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
  },
  {
    "path": "model-view.html",
    "chars": 9848,
    "preview": "<!--\nCopyright 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
  },
  {
    "path": "state-aware.html",
    "chars": 4853,
    "preview": "<!--\nCopyright 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
  },
  {
    "path": "state-mutator.html",
    "chars": 1422,
    "preview": "<!--\nCopyright 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
  },
  {
    "path": "test/action-dispatcher_test.html",
    "chars": 3529,
    "preview": "<!doctype html>\n<!--\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may"
  },
  {
    "path": "test/action-emitter_test.html",
    "chars": 2958,
    "preview": "<!doctype html>\n<!--\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may"
  },
  {
    "path": "test/application-state_test.html",
    "chars": 5273,
    "preview": "<!doctype html>\n<!--\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may"
  },
  {
    "path": "test/list-view_test.html",
    "chars": 3491,
    "preview": "<!doctype html>\n<!--\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may"
  },
  {
    "path": "test/model-view_test.html",
    "chars": 6113,
    "preview": "<!doctype html>\n<!--\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may"
  },
  {
    "path": "test/state-aware_test.html",
    "chars": 2238,
    "preview": "<!doctype html>\n<!--\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may"
  },
  {
    "path": "todomvc/README.md",
    "chars": 436,
    "preview": "# \\<todo\\>\n\nToDo MVC application implemented with UniFlow for Polymer 2.x\n\n## Install Bower Dependencies\n\nRun `bower ins"
  },
  {
    "path": "todomvc/elements/todo-action-dispatcher.html",
    "chars": 2759,
    "preview": "<link rel=\"import\" href=\"../../../polymer/polymer-element.html\">\n<link rel=\"import\" href=\"../../action-dispatcher.html\">"
  },
  {
    "path": "todomvc/elements/todo-app.html",
    "chars": 2602,
    "preview": "<link rel=\"import\" href=\"../../../polymer/polymer-element.html\">\n<link rel=\"import\" href=\"../../action-emitter.html\">\n<l"
  },
  {
    "path": "todomvc/elements/todo-footer.html",
    "chars": 2100,
    "preview": "<link rel=\"import\" href=\"../../../polymer/polymer-element.html\">\n<link rel=\"import\" href=\"../../../polymer/lib/mixins/ge"
  },
  {
    "path": "todomvc/elements/todo-header.html",
    "chars": 1090,
    "preview": "<link rel=\"import\" href=\"../../../polymer/polymer-element.html\">\n<link rel=\"import\" href=\"../../action-emitter.html\">\n<l"
  },
  {
    "path": "todomvc/elements/todo-item.html",
    "chars": 4765,
    "preview": "<link rel=\"import\" href=\"../../../polymer/polymer-element.html\">\n<link rel=\"import\" href=\"../../../polymer/lib/mixins/ge"
  },
  {
    "path": "todomvc/elements/todo-items.html",
    "chars": 1694,
    "preview": "<link rel=\"import\" href=\"../../../polymer/polymer-element.html\">\n<link rel=\"import\" href=\"../../list-view.html\">\n\n<link "
  },
  {
    "path": "todomvc/elements/todo-styles.html",
    "chars": 8622,
    "preview": "<dom-module id=\"todo-styles\">\n  <template>\n    <style>\n      button {\n        margin: 0;\n        padding: 0;\n        bor"
  },
  {
    "path": "todomvc/index.html",
    "chars": 971,
    "preview": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-wid"
  },
  {
    "path": "todomvc/manifest.json",
    "chars": 170,
    "preview": "{\n  \"name\": \"todo\",\n  \"short_name\": \"todo\",\n  \"description\": \"ToDo MVC application implemented with UniFlow for Polymer\""
  },
  {
    "path": "todomvc/scripts/actions.js",
    "chars": 258,
    "preview": "window.todo = window.todo || {};\n\ntodo.actions = {\n\n  INIT_APPLICATION: \"INIT_APPLICATION\",\n  ADD_TODO: \"ADD_TODO\",\n  RE"
  }
]

About this extraction

This page contains the full source code of the google/uniflow-polymer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (171.1 KB), approximately 40.3k tokens. 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!