├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── dist ├── react-helix.js └── react-helix.min.js ├── karma.conf.js ├── package.json ├── src ├── AgentComponent.js ├── AgentMixin.js ├── SendActionEvent.js ├── StageComponent.js ├── StageMixin.js ├── agent-impl.js ├── index.js └── stage-impl.js └── test ├── .eslintrc ├── agent.js └── stage.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "plugins": [ 7 | "react" 8 | ], 9 | "globals": { 10 | "process": true 11 | }, 12 | "rules": { 13 | "no-var": 2, 14 | "react/display-name": 2, 15 | "react/jsx-quotes": 2, 16 | "react/jsx-no-undef": 2, 17 | "react/jsx-uses-react": 2, 18 | "react/jsx-uses-vars": 2, 19 | "react/no-did-mount-set-state": 2, 20 | "react/no-did-update-set-state": 2, 21 | "react/no-multi-comp": 2, 22 | "react/prop-types": 2, 23 | "react/react-in-jsx-scope": 2, 24 | "react/self-closing-comp": 2, 25 | "react/wrap-multilines": 2 26 | }, 27 | "ecmaFeatures": { 28 | "arrowFunctions": true, 29 | "binaryLiterals": true, 30 | "blockBindings": true, 31 | "classes": true, 32 | "defaultParams": true, 33 | "destructuring": true, 34 | "forOf": true, 35 | "generators": true, 36 | "modules": true, 37 | "objectLiteralComputedProperties": true, 38 | "objectLiteralDuplicateProperties": true, 39 | "objectLiteralShorthandMethods": true, 40 | "objectLiteralShorthandProperties": true, 41 | "octalLiterals": true, 42 | "regexUFlag": true, 43 | "regexYFlag": true, 44 | "superInFunctions": true, 45 | "templateStrings": true, 46 | "unicodeCodePointEscapes": true, 47 | "globalReturn": true, 48 | "jsx": true 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /lib 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Toru Nagashima 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-helix 2 | 3 | [![npm](https://img.shields.io/npm/v/react-helix.svg)](https://www.npmjs.com/package/react-helix) 4 | 5 | A minimal library for [Flux](http://facebook.github.io/flux)-like architecture. 6 | 7 | - [Motivation](#motivation) 8 | - [Overview Helix](#overview-helix) 9 | - [Overview react-helix](#overview-react-helix) 10 | - [Installation](#installation) 11 | - [Usage](#usage) 12 | - [StageComponent / StageMixin](#stagecomponent--stagemixin) 13 | - [AgentComponent / AgentMixin](#agentcomponent--agentmixin) 14 | - [Helix in depth](#helix-in-depth) 15 | - [More Actions (together Promise / Generator)](#more-actions-together-promise--generator) 16 | - [Nested StageComponent](#nested-stagecomponent) 17 | - [Server-side Rendering](#server-side-rendering) 18 | - [Examples](https://github.com/mysticatea/react-helix-examples) 19 | 20 | 21 | ## Motivation 22 | 23 | - Makes *Model* to be immutable. 24 | - Makes the border of *Model* and *View* to be clearly. 25 | - No boilerplate. Minimal requisite. 26 | - Doesn't block the server-side rendering. 27 | 28 | 29 | ## Overview Helix 30 | 31 | ``` 32 | This has the Application Model 33 | +------------------------------- 34 | / 35 | +---------------------+ 36 | | The Root Component | 37 | +---| (Application class) |<<-+ 38 | | +---------------------+ | 39 | | | 40 | Distribute changes | | Send actions 41 | via Virtual DOMs | | via Event Bubblings 42 | | | 43 | | +---------------------+ | 44 | | | Detail Components | | 45 | +->>| |---+ 46 | +---------------------+ 47 | / 48 | Those accept user's interactions / 49 | ---------------------------------+ 50 | ``` 51 | 52 | First, I named this Flux-like design. It's Helix. 53 | This section explains about Helix. 54 | 55 | Helix inherits the most basic concept of Flux, Unidirectional Data Flow, and provides better way that works immutable data. 56 | 57 | ### Three elements 58 | 59 | Helix has three elements: 60 | 61 | * *Model* is immutable data structures. 62 | * *Action* is domain logics, they are just functions that transforms from the previous *Model* to the next *Model*. 63 | * *View* is appearance, they show the *Model* and send *Action* by user operations. 64 | 65 | It's important, *Action* is just a function. 66 | 67 | ### Two dataflows 68 | 69 | Since *Model* is immutable, the root component has ownership of the application model uniquely. But the model will be updated by detail components (they would not know the root component). 70 | Helix has two symmetric flows as solution of this gap. 71 | 72 | - *Downward Flow* is to distribute changes via Virtual DOMs (diff & patch). 73 | - *Upward Flow* is to send actions via Event Bubbling. 74 | 75 | Let's see the flow of the whole. 76 | 77 | When a necessity to update *Model* was occurred by user operations, then a detail component fires a send action event with an *Action* and parameters. The event carries the action and parameters to the root component via event bubbling. (*Upward Flow*). 78 | 79 | The root component has the application model. When it received a send action event, it takes *Action* and parameters from the event, then it applies the *Action* with the parameters to its model. And the root component distributes changes via Virtual DOM (Diff & Patch). (Downward Flow). 80 | 81 | Thus, the root component keeps ownership of the application model, and detail components can determine details of updating completely. And we don't need boilerplates (e.g. declares constants, registers handlers to Dispatcher/Store, ...), and and it's scalable to increase *Action*s. 82 | 83 | 84 | ## Overview react-helix 85 | 86 | `react-helix` is a library for Helix, provides two classes (and mixins). 87 | 88 | - `StageComponent` (or `StageMixin`) has an ability to catch *Action*s that sent 89 | from its descendant, and apply the *Action* to its state automatically. 90 | This is an implement for the root component. 91 | - `AgentComponent` (or `AgentMixin`) has an ability to dispatch bubbling events 92 | to send *Action*s. 93 | This is an implement for detail components. 94 | 95 | For example: 96 | 97 | * [Action Definition](https://github.com/mysticatea/react-helix-examples/blob/master/src/todos/client/action/TodoApp.js#L41-50): 98 | 99 | ```js 100 | export function removeTodoItem(model, id) { 101 | return model.withItems(items => 102 | items.filter(item => item.id !== id) 103 | ); 104 | } 105 | ``` 106 | 107 | * [Send Action](https://github.com/mysticatea/react-helix-examples/blob/master/src/todos/client/view/TodoItem.js#L89-92): 108 | 109 | ```js 110 | onRemoveButtonClick(/*event*/) { 111 | const id = this.props.value.id; 112 | this.request(removeTodoItem, id); 113 | } 114 | ``` 115 | 116 | That's almost all. 117 | 118 | See Also [Examples](https://github.com/mysticatea/react-helix-examples) 119 | 120 | 121 | ## Installation 122 | 123 | ``` 124 | npm install react react-helix 125 | ``` 126 | 127 | ## Usage 128 | 129 | ### StageComponent / StageMixin 130 | 131 | ```ts 132 | declare class StageComponent extends React.Component { 133 | constructor(props: any, stageValuePath: string = ""); 134 | 135 | stageValue: any; 136 | setStageValue(value: any, callback?: () => void): void; 137 | 138 | filterAction(event: SendActionEvent): boolean; 139 | 140 | // When you would override those methods, must call `super`. 141 | componentDidMount(): void; 142 | componentWillUnmount(): void; 143 | componentWillUpdate(): void; 144 | componentDidUpdate(): void; 145 | } 146 | 147 | const StageMixin = { 148 | stageValue: any; 149 | setStageValue(value: any, callback?: () => void): void; 150 | 151 | // stageValuePath: string = ""; 152 | // You can define `stageValuePath` property. 153 | // The property is used from `componentWillMount` in order to define 154 | // `stageValue` and `setStageValue`. 155 | 156 | // filterAction(event: SendActionEvent): boolean 157 | // You can define `filterAction` method. 158 | }; 159 | ``` 160 | 161 | #### stageValuePath 162 | 163 | `StageComponent` has one parameter `stageValuePath`. 164 | This string is a path to stage value in its state. 165 | 166 | For example, when you set `model` to `stageValuePath`, then the component saves 167 | its stage value into `this.state.model`. 168 | Other example, when `stageValuePath` is `myapp.model`, then the component saves 169 | its stage value into `this.state.myapp.model`. 170 | 171 | #### stageValue 172 | 173 | `stageValue` is a getter property to get the value of `stageValuePath`. 174 | 175 | #### setStageValue 176 | 177 | `setStageValue` is a method to set the value of `stageValuePath`. 178 | 179 | #### filterAction 180 | 181 | `filterAction` is a method to determine whether it should handle the action. 182 | 183 | * `event.action` is the action. 184 | * `event.arguments` is an array of arguments for the action. 185 | * If returned `false`, this component ignores the action. 186 | 187 | By default, always returns `true`. 188 | 189 | 190 | ### AgentComponent / AgentMixin 191 | 192 | ```ts 193 | declare class AgentComponent extends React.Component { 194 | constructor(props: any); 195 | request(action: (stageValue: any, ...) => any, ...args: any[]): void; 196 | } 197 | 198 | const AgentMixin = { 199 | request(action: (stageValue: any, ...) => any, ...args: any[]): void; 200 | }; 201 | ``` 202 | 203 | #### request 204 | 205 | `request` is a method to send an action. 206 | `action` is a function. 207 | `args` is an array of arguments for `action`. 208 | 209 | You can replace this method to a spy for unit tests. 210 | User interactions will trigger this method in the end. 211 | 212 | -------------------------------------------------------------------------------- 213 | 214 | ## Helix in depth 215 | 216 | ### More Actions (together Promise / Generator) 217 | 218 | react-helix allows actions return a promise or a generator. 219 | In this case, `StageComponent` treats the return value specially. 220 | 221 | > Implementation of react-helix does NOT depend on Promise/Generator. 222 | > Thus react-helix works even if those are not implemented. 223 | > However, if application want to use Promise/Generator, will require Polyfills. 224 | 225 | #### Promise 226 | 227 | If the return value is a promise, `StageComponent` waits for the promise 228 | fulfilled, then sets the result to its stage value. 229 | If the result of the promise is a function, `StageComponent` calls the function 230 | with its stage value immediately, and sets the result of the function to its 231 | stage value. 232 | 233 | ```js 234 | function promiseAction(model) { 235 | // ↑ This model is a instance at the time of this action was called. 236 | return hogeAsync() 237 | .then(function() { 238 | // ↓ This model2 is a instance at the time of this promise became 239 | // fulfilled. 240 | return function(model2) { ... }; 241 | }); 242 | } 243 | ``` 244 | 245 | #### Generator 246 | 247 | If the return value is a generator, `StageComponent` advances it until done. 248 | While advancing, `StageComponent` treats yielded values. 249 | 250 | * If undefined was yielded, just ignores it. 251 | * If a function was yielded, `StageComponent` calls the function with its stage 252 | value immediately, and sets the result to its stage value. 253 | * If a promise was yielded, `StageComponent` backs the result of the promise 254 | (it is similar to [co](https://github.com/tj/co)). 255 | * If a generator was yielded, it is processed recurcively. 256 | * Otherwise, `StageComponent` sets the value to its stage value. 257 | * `yield` (excludes case of give a promise) backs the current stage value. 258 | 259 | ```js 260 | function* generatorAction(model) { 261 | // ↑ This model is a instance at the time of this action was called. 262 | 263 | // Lonly `yield` just returns the current model. 264 | const model2 = yield; 265 | 266 | // Give an object to `yield`, it updates model, and returns new model. 267 | const model3 = yield model.withStatus(1); 268 | 269 | // Give a promise to `yield`, it returns the result. 270 | const threeSevens = yield Promise.resolve(777); 271 | 272 | // Give a promise (will be rejected) to `yield`, it throws. 273 | try { 274 | yield Promise.reject(new Error()); 275 | } 276 | catch (err) { 277 | //... 278 | } 279 | } 280 | ``` 281 | 282 | The Action with a generator is useful to implement complex business logics. 283 | 284 | 285 | ### Nested StageComponent 286 | 287 | Actions is carried by event bubbling, it will handled at the first 288 | `StageComponent`. At this time, the `StageComponent` confirms whether it should 289 | handle the action to `this.filterAction()`. 290 | 291 | ``` 292 | +-------+ 293 | | Stage |<---------+ 294 | +-------+ | 295 | / \ | 296 | +--+ +--+ | 297 | | | | | | 298 | +--+ +--+ | 299 | / \ \ | 300 | +--+ +--+ +-------+ | 301 | | | | | | Stage |<-+ If filterAction() determines ignore, more bubbles. 302 | +--+ +--+ +-------+ | 303 | / / / \ | 304 | +--+ +--+ +--+ +--+ | 305 | | | | | | | |//|--+ 306 | +--+ +--+ +--+ +--+ 307 | ``` 308 | 309 | This will allow us to use nested `StageComponent`. 310 | 311 | 312 | ### Server-side Rendering 313 | 314 | Event bubbling to carry actions does not work in server side. 315 | But, react-helix does not prevent rendering (maybe. NEEDS TEST). 316 | 317 | 318 | ### Browser Compatibility 319 | 320 | Implementation of react-helix is using: 321 | 322 | * `Object.defineProperties` 323 | * `Function.prototype.bind` 324 | * `EventTarget.prototype.addEventListener` 325 | * `EventTarget.prototype.removeEventListener` 326 | 327 | Thus IE8 and older are not supported. 328 | -------------------------------------------------------------------------------- /dist/react-helix.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.reactHelix = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 1) { 56 | for (var i = 1; i < lastIndex; ++i) { 57 | args.push(arguments[i]); 58 | } 59 | if (typeof arguments[lastIndex] !== "function") { 60 | args.push(arguments[lastIndex]); 61 | } else { 62 | callback = arguments[lastIndex]; 63 | } 64 | } 65 | return _agentImpl2["default"].sendAction(this, action, args, callback); 66 | } 67 | }]); 68 | 69 | return AgentComponent; 70 | })(_react2["default"].Component); 71 | 72 | exports["default"] = AgentComponent; 73 | module.exports = exports["default"]; 74 | },{"./agent-impl":6,"react":"react"}],2:[function(require,module,exports){ 75 | "use strict"; 76 | 77 | Object.defineProperty(exports, "__esModule", { 78 | value: true 79 | }); 80 | 81 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 82 | 83 | var _agentImpl = require("./agent-impl"); 84 | 85 | var _agentImpl2 = _interopRequireDefault(_agentImpl); 86 | 87 | exports["default"] = { 88 | componentDidMount: function componentDidMount() { 89 | if ("development" !== "production") { 90 | _agentImpl2["default"].validate(this); 91 | } 92 | }, 93 | 94 | componentDidUpdate: function componentDidUpdate() { 95 | if ("development" !== "production") { 96 | _agentImpl2["default"].validate(this); 97 | } 98 | }, 99 | 100 | request: function request(action /* [, ...args] [, callback] */) { 101 | var args = []; 102 | var lastIndex = arguments.length - 1; 103 | var callback = undefined; 104 | if (lastIndex >= 1) { 105 | for (var i = 1; i < lastIndex; ++i) { 106 | args.push(arguments[i]); 107 | } 108 | if (typeof arguments[lastIndex] !== "function") { 109 | args.push(arguments[lastIndex]); 110 | } else { 111 | callback = arguments[lastIndex]; 112 | } 113 | } 114 | return _agentImpl2["default"].sendAction(this, action, args, callback); 115 | } 116 | }; 117 | module.exports = exports["default"]; 118 | },{"./agent-impl":6}],3:[function(require,module,exports){ 119 | // There are several cross-callings in this file. 120 | /*eslint no-use-before-define:[2,"nofunc"]*/ 121 | 122 | "use strict"; 123 | 124 | Object.defineProperty(exports, "__esModule", { 125 | value: true 126 | }); 127 | 128 | /** 129 | * @param action {function} - A function to transform the state. 130 | * @param args {any[]} - Information for action. This value is given to the 131 | * second argument of action. 132 | * @return {SendActionEvent} - The created event object. 133 | */ 134 | exports.createSendActionEvent = createSendActionEvent; 135 | 136 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } 137 | 138 | function invariant(condition, message) { 139 | if (!condition) { 140 | throw new Error(message); 141 | } 142 | } 143 | 144 | function isFunction(x) { 145 | return typeof x === "function"; 146 | } 147 | 148 | function isThenable(x) { 149 | return typeof x === "object" && typeof x.then === "function"; 150 | } 151 | 152 | function isGenerator(x) { 153 | return typeof x === "object" && typeof x.next === "function" && typeof x["throw"] === "function"; 154 | } 155 | 156 | function printError(error) { 157 | // This function is used in (process.env.NODE_ENV !== "production"). 158 | if (error != null) { 159 | console.error(error); //eslint-disable-line no-console 160 | } 161 | } 162 | 163 | // A handler that is used if value is not all of undefined, a function, a 164 | // promise, and a generator. 165 | // Just it sets the value to the component's stage value. 166 | function set(component, isInGenerator, value, resolve, reject) { 167 | try { 168 | component.setStageValue(value, resolve); 169 | } catch (err) { 170 | reject(err); 171 | } 172 | } 173 | 174 | // A handler that is used if value is a function. 175 | // It calls the function together with the component's stage value. 176 | // Then set the result to the component's stage value. 177 | function callAndSet(component, isInGenerator, func, resolve, reject) { 178 | var result = undefined; 179 | try { 180 | result = func(component.stageValue); 181 | } catch (error) { 182 | reject(error); 183 | return; 184 | } 185 | setUnified(component, isInGenerator, result, resolve, reject); 186 | } 187 | 188 | // A handler that is used if value is a promise. 189 | // It waits for the promise become fulfilled. 190 | // Then set the result to the component's stage value. 191 | // But if is while advancing a generator, it doesn't set to the stage value, 192 | // just returns the result. 193 | function waitAndSet(component, isInGenerator, promise, resolve, reject) { 194 | var promise2 = promise.then(function (result) { 195 | if (isInGenerator) { 196 | resolve(result); 197 | } else { 198 | setUnified(component, isInGenerator, result, resolve, reject); 199 | } 200 | }, reject); 201 | 202 | if ("development" !== "production") { 203 | promise2["catch"](printError); 204 | } 205 | } 206 | 207 | // A handler that is used if value is a generator. 208 | // Process a generator. ping-pong the component's stage value. 209 | function advanceToEnd(component, isInGenerator, generator, resolve, reject) { 210 | onFulfilled(undefined); 211 | 212 | function onFulfilled(stageValue) { 213 | var ret = undefined; 214 | try { 215 | ret = generator.next(stageValue); 216 | } catch (err) { 217 | reject(err); 218 | return; 219 | } 220 | 221 | next(ret); 222 | } 223 | 224 | function onRejected(err) { 225 | var ret = undefined; 226 | try { 227 | ret = generator["throw"](err); 228 | } catch (err2) { 229 | reject(err2); 230 | return; 231 | } 232 | 233 | next(ret); 234 | } 235 | 236 | function next(ret) { 237 | if (ret.done) { 238 | setUnified(component, true, ret.value, resolve, reject); 239 | } else { 240 | setUnified(component, true, ret.value, onFulfilled, onRejected); 241 | } 242 | } 243 | } 244 | 245 | // Check type of the value, and handle the value. 246 | function setUnified(component, isInGenerator, value, resolve, reject) { 247 | if (value === undefined) { 248 | resolve(component.stageValue); 249 | return; 250 | } 251 | 252 | var handle = isFunction(value) ? callAndSet : isThenable(value) ? waitAndSet : isGenerator(value) ? advanceToEnd : 253 | /* otherwise */set; 254 | 255 | handle(component, isInGenerator, value, resolve, reject); 256 | } 257 | 258 | /** 259 | * The event name for `SendActionEvent`. 260 | * @type {string} 261 | */ 262 | var EVENT_NAME = "helix-sent-action";exports.EVENT_NAME = EVENT_NAME; 263 | 264 | function createSendActionEvent(action, args, callback) { 265 | if ("development" !== "production") { 266 | invariant(typeof action === "function", "action should be a function."); 267 | invariant(Array.isArray(args), "args should be an array."); 268 | invariant(callback == null || typeof callback === "function", "callback should be a function or nothing."); 269 | 270 | if (callback == null) { 271 | callback = printError; 272 | } 273 | } 274 | 275 | var event = document.createEvent("CustomEvent"); 276 | var handled = false; 277 | 278 | event.initCustomEvent(EVENT_NAME, true, true, null); 279 | Object.defineProperties(event, { 280 | action: { 281 | value: action, 282 | configurable: true, 283 | enumerable: true, 284 | writable: true 285 | }, 286 | 287 | arguments: { 288 | value: args, 289 | configurable: true, 290 | enumerable: true, 291 | writable: true 292 | }, 293 | 294 | // This is internal method, called from StageMixin. 295 | applyTo: { value: function applyTo(component) { 296 | if ("development" !== "production") { 297 | var get = Object.getOwnPropertyDescriptor(component, "stageValue"); 298 | var _set = Object.getOwnPropertyDescriptor(component, "setStageValue"); 299 | invariant(isFunction(get.get), "component.stageValue should be a getter property."); 300 | invariant(isFunction(_set.value), "component.setStageValue should be a function."); 301 | invariant(handled === false, "this " + EVENT_NAME + " event had been applied already."); 302 | invariant(isFunction(this.action), "this.action should be a function."); 303 | invariant(Array.isArray(this.arguments), "this.arguments should be an array."); 304 | } 305 | handled = true; 306 | 307 | var value = undefined; 308 | try { 309 | value = this.action.apply(this, [component.stageValue].concat(_toConsumableArray(this.arguments))); 310 | } catch (error) { 311 | if (callback != null) { 312 | callback(error); 313 | } 314 | return; 315 | } 316 | 317 | setUnified(component, false, value, function (result) { 318 | return callback && callback(null, result); 319 | }, callback); 320 | } }, 321 | 322 | // This is internal method, called from AgentMixin. 323 | rejectIfNotHandled: { value: function rejectIfNotHandled() { 324 | if (handled === false) { 325 | handled = true; 326 | if (callback != null) { 327 | callback(new Error("not handled")); 328 | } 329 | } 330 | } } 331 | }); 332 | 333 | return event; 334 | } 335 | },{}],4:[function(require,module,exports){ 336 | "use strict"; 337 | 338 | Object.defineProperty(exports, "__esModule", { 339 | value: true 340 | }); 341 | 342 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 343 | 344 | var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; desc = parent = getter = undefined; _again = false; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 345 | 346 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 347 | 348 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 349 | 350 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } 351 | 352 | var _react = require("react"); 353 | 354 | var _react2 = _interopRequireDefault(_react); 355 | 356 | var _stageImpl = require("./stage-impl"); 357 | 358 | var _stageImpl2 = _interopRequireDefault(_stageImpl); 359 | 360 | var StageComponent = (function (_React$Component) { 361 | function StageComponent(props) { 362 | var stageValuePath = arguments[1] === undefined ? "" : arguments[1]; 363 | 364 | _classCallCheck(this, StageComponent); 365 | 366 | _get(Object.getPrototypeOf(StageComponent.prototype), "constructor", this).call(this, props); 367 | _stageImpl2["default"].initialize(this, stageValuePath); 368 | } 369 | 370 | _inherits(StageComponent, _React$Component); 371 | 372 | _createClass(StageComponent, [{ 373 | key: "componentDidMount", 374 | value: function componentDidMount() { 375 | _stageImpl2["default"].setupHandler(this); 376 | } 377 | }, { 378 | key: "componentDidUpdate", 379 | value: function componentDidUpdate() { 380 | _stageImpl2["default"].setupHandler(this); 381 | } 382 | }, { 383 | key: "componentWillUnmount", 384 | value: function componentWillUnmount() { 385 | _stageImpl2["default"].teardownHandler(this); 386 | } 387 | }, { 388 | key: "componentWillUpdate", 389 | value: function componentWillUpdate() { 390 | _stageImpl2["default"].teardownHandler(this); 391 | } 392 | }, { 393 | key: "filterAction", 394 | value: function filterAction() { 395 | return true; 396 | } 397 | }]); 398 | 399 | return StageComponent; 400 | })(_react2["default"].Component); 401 | 402 | exports["default"] = StageComponent; 403 | module.exports = exports["default"]; 404 | /* event */ 405 | },{"./stage-impl":7,"react":"react"}],5:[function(require,module,exports){ 406 | "use strict"; 407 | 408 | Object.defineProperty(exports, "__esModule", { 409 | value: true 410 | }); 411 | 412 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 413 | 414 | var _stageImpl = require("./stage-impl"); 415 | 416 | var _stageImpl2 = _interopRequireDefault(_stageImpl); 417 | 418 | exports["default"] = { 419 | componentWillMount: function componentWillMount() { 420 | _stageImpl2["default"].initialize(this, this.stageValuePath || ""); 421 | }, 422 | 423 | componentDidMount: function componentDidMount() { 424 | _stageImpl2["default"].setupHandler(this); 425 | }, 426 | 427 | componentDidUpdate: function componentDidUpdate() { 428 | _stageImpl2["default"].setupHandler(this); 429 | }, 430 | 431 | componentWillUpdate: function componentWillUpdate() { 432 | _stageImpl2["default"].teardownHandler(this); 433 | }, 434 | 435 | componentWillUnmount: function componentWillUnmount() { 436 | _stageImpl2["default"].teardownHandler(this); 437 | } 438 | }; 439 | module.exports = exports["default"]; 440 | },{"./stage-impl":7}],6:[function(require,module,exports){ 441 | "use strict"; 442 | 443 | Object.defineProperty(exports, "__esModule", { 444 | value: true 445 | }); 446 | 447 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 448 | 449 | var _react = require("react"); 450 | 451 | var _react2 = _interopRequireDefault(_react); 452 | 453 | var _SendActionEvent = require("./SendActionEvent"); 454 | 455 | function invariant(condition, message) { 456 | if (!condition) { 457 | throw new Error(message); 458 | } 459 | } 460 | 461 | exports["default"] = { 462 | validate: function validate(component) { 463 | if ("development" !== "production") { 464 | var node = _react2["default"].findDOMNode(component); 465 | invariant(node != null, "AgentMixin: requires to be rendered."); 466 | } 467 | }, 468 | 469 | sendAction: function sendAction(component, action, args, callback) { 470 | var node = _react2["default"].findDOMNode(component); 471 | var event = (0, _SendActionEvent.createSendActionEvent)(action, args, callback); 472 | 473 | if ("development" !== "production") { 474 | invariant(node != null, "AgentMixin: requires to be rendered."); 475 | } 476 | node.dispatchEvent(event); 477 | event.rejectIfNotHandled(); 478 | } 479 | }; 480 | module.exports = exports["default"]; 481 | },{"./SendActionEvent":3,"react":"react"}],7:[function(require,module,exports){ 482 | "use strict"; 483 | 484 | Object.defineProperty(exports, "__esModule", { 485 | value: true 486 | }); 487 | 488 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 489 | 490 | var _react = require("react"); 491 | 492 | var _react2 = _interopRequireDefault(_react); 493 | 494 | var _SendActionEvent = require("./SendActionEvent"); 495 | 496 | function invariant(condition, message) { 497 | if (!condition) { 498 | throw new Error(message); 499 | } 500 | } 501 | 502 | //------------------------------------------------------------------------------ 503 | function parseStageValuePath(value) { 504 | return typeof value === "string" ? value.split(".").filter(Boolean) : []; 505 | } 506 | 507 | //------------------------------------------------------------------------------ 508 | function defineGetStageValue(parts) { 509 | var body = undefined; 510 | switch (parts.length) { 511 | case 0: 512 | body = "return this.state;"; 513 | break; 514 | 515 | default: 516 | var lastIndex = parts.length - 1; 517 | body = "var tmp0 = this.state;"; 518 | for (var i = 0; i < lastIndex; ++i) { 519 | body += "\nif (tmp" + i + " == null) {\n return undefined;\n}\nvar tmp" + (i + 1) + " = tmp" + i + "." + parts[i] + ";"; 520 | } 521 | body += "\nreturn tmp" + lastIndex + " && tmp" + lastIndex + "." + parts[lastIndex] + ";"; 522 | break; 523 | } 524 | 525 | return Function(body); 526 | } 527 | 528 | //------------------------------------------------------------------------------ 529 | function defineSetStageValue(parts) { 530 | var body = "var cb2 = cb && function() { cb(this.stageValue); }.bind(this);"; 531 | 532 | switch (parts.length) { 533 | case 0: 534 | body += "\nthis.setState(value, cb2);"; 535 | break; 536 | 537 | case 1: 538 | body += "\nthis.setState({" + parts[0] + ": value}, cb2);"; 539 | break; 540 | 541 | default: 542 | var lastIndex = parts.length - 1; 543 | body += "\nvar tmp0 = this.state || {};"; 544 | for (var i = 0; i < lastIndex; ++i) { 545 | body += "\nvar tmp" + (i + 1) + " = tmp" + i + "." + parts[i] + ";\nif (tmp" + (i + 1) + " == null) {\n tmp" + (i + 1) + " = tmp" + i + "." + parts[i] + " = {};\n}"; 546 | } 547 | body += "\ntmp" + lastIndex + "." + parts[lastIndex] + " = value;\nthis.setState(tmp0, cb2);"; 548 | break; 549 | } 550 | 551 | return Function("value", "cb", body); 552 | } 553 | 554 | //------------------------------------------------------------------------------ 555 | function handleSendAction(event) { 556 | if (event.defaultPrevented) { 557 | return; 558 | } 559 | if (typeof this.filterAction === "function" && !this.filterAction(event)) { 560 | return; 561 | } 562 | event.stopPropagation(); 563 | event.applyTo(this); 564 | } 565 | 566 | //------------------------------------------------------------------------------ 567 | exports["default"] = { 568 | initialize: function initialize(component, stageValuePath) { 569 | if (component.stageMixinInitialized) { 570 | return; 571 | } 572 | 573 | var parts = parseStageValuePath(stageValuePath); 574 | var getStageValue = defineGetStageValue(parts); 575 | 576 | if ("development" !== "production") { 577 | invariant(stageValuePath == null || typeof stageValuePath === "string", "StageMixin: stageValuePath should be a string."); 578 | 579 | try { 580 | getStageValue.call(component); 581 | } catch (cause) { 582 | var err = new Error("StageMixin: stageValuePath is invalid (" + stageValuePath + ")."); 583 | err.cause = cause; 584 | throw err; 585 | } 586 | } 587 | 588 | Object.defineProperties(component, { 589 | stageMixinInitialized: { 590 | value: true, 591 | configurable: true 592 | }, 593 | 594 | stageMixinHandleSendAction: { 595 | value: handleSendAction.bind(component), 596 | configurable: true 597 | }, 598 | 599 | stageValue: { 600 | get: getStageValue, 601 | configurable: true, 602 | enumerable: true 603 | }, 604 | 605 | setStageValue: { 606 | value: defineSetStageValue(parts).bind(component), 607 | configurable: true 608 | } 609 | }); 610 | }, 611 | 612 | setupHandler: function setupHandler(component) { 613 | var node = _react2["default"].findDOMNode(component); 614 | if ("development" !== "production") { 615 | invariant(node != null, "StageMixin: requires to be rendered."); 616 | } 617 | node.addEventListener(_SendActionEvent.EVENT_NAME, component.stageMixinHandleSendAction); 618 | }, 619 | 620 | teardownHandler: function teardownHandler(component) { 621 | var node = _react2["default"].findDOMNode(component); 622 | if (node != null) { 623 | node.removeEventListener(_SendActionEvent.EVENT_NAME, component.stageMixinHandleSendAction); 624 | } 625 | } 626 | }; 627 | module.exports = exports["default"]; 628 | },{"./SendActionEvent":3,"react":"react"}],8:[function(require,module,exports){ 629 | "use strict"; 630 | 631 | Object.defineProperty(exports, "__esModule", { 632 | value: true 633 | }); 634 | 635 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 636 | 637 | var _AgentComponent = require("./AgentComponent"); 638 | 639 | var _AgentComponent2 = _interopRequireDefault(_AgentComponent); 640 | 641 | var _AgentMixin = require("./AgentMixin"); 642 | 643 | var _AgentMixin2 = _interopRequireDefault(_AgentMixin); 644 | 645 | var _StageComponent = require("./StageComponent"); 646 | 647 | var _StageComponent2 = _interopRequireDefault(_StageComponent); 648 | 649 | var _StageMixin = require("./StageMixin"); 650 | 651 | var _StageMixin2 = _interopRequireDefault(_StageMixin); 652 | 653 | exports["default"] = { 654 | AgentComponent: _AgentComponent2["default"], 655 | AgentMixin: _AgentMixin2["default"], 656 | StageComponent: _StageComponent2["default"], 657 | StageMixin: _StageMixin2["default"] 658 | }; 659 | exports.AgentComponent = _AgentComponent2["default"]; 660 | exports.AgentMixin = _AgentMixin2["default"]; 661 | exports.StageComponent = _StageComponent2["default"]; 662 | exports.StageMixin = _StageMixin2["default"]; 663 | },{"./AgentComponent":1,"./AgentMixin":2,"./StageComponent":4,"./StageMixin":5}]},{},[8])(8) 664 | }); 665 | //# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJsaWIvQWdlbnRDb21wb25lbnQuanMiLCJsaWIvQWdlbnRNaXhpbi5qcyIsImxpYi9TZW5kQWN0aW9uRXZlbnQuanMiLCJsaWIvU3RhZ2VDb21wb25lbnQuanMiLCJsaWIvU3RhZ2VNaXhpbi5qcyIsImxpYi9hZ2VudC1pbXBsLmpzIiwibGliL3N0YWdlLWltcGwuanMiLCJsaWIvaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdk5BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJcInVzZSBzdHJpY3RcIjtcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxudmFyIF9jcmVhdGVDbGFzcyA9IChmdW5jdGlvbiAoKSB7IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoXCJ2YWx1ZVwiIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlOyBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBkZXNjcmlwdG9yLmtleSwgZGVzY3JpcHRvcik7IH0gfSByZXR1cm4gZnVuY3Rpb24gKENvbnN0cnVjdG9yLCBwcm90b1Byb3BzLCBzdGF0aWNQcm9wcykgeyBpZiAocHJvdG9Qcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IsIHN0YXRpY1Byb3BzKTsgcmV0dXJuIENvbnN0cnVjdG9yOyB9OyB9KSgpO1xuXG5mdW5jdGlvbiBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KG9iaikgeyByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyBcImRlZmF1bHRcIjogb2JqIH07IH1cblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IGNhbGwgYSBjbGFzcyBhcyBhIGZ1bmN0aW9uXCIpOyB9IH1cblxuZnVuY3Rpb24gX2luaGVyaXRzKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IGlmICh0eXBlb2Ygc3VwZXJDbGFzcyAhPT0gXCJmdW5jdGlvblwiICYmIHN1cGVyQ2xhc3MgIT09IG51bGwpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN1cGVyIGV4cHJlc3Npb24gbXVzdCBlaXRoZXIgYmUgbnVsbCBvciBhIGZ1bmN0aW9uLCBub3QgXCIgKyB0eXBlb2Ygc3VwZXJDbGFzcyk7IH0gc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzICYmIHN1cGVyQ2xhc3MucHJvdG90eXBlLCB7IGNvbnN0cnVjdG9yOiB7IHZhbHVlOiBzdWJDbGFzcywgZW51bWVyYWJsZTogZmFsc2UsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSB9KTsgaWYgKHN1cGVyQ2xhc3MpIHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxudmFyIF9yZWFjdCA9IHJlcXVpcmUoXCJyZWFjdFwiKTtcblxudmFyIF9yZWFjdDIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF9yZWFjdCk7XG5cbnZhciBfYWdlbnRJbXBsID0gcmVxdWlyZShcIi4vYWdlbnQtaW1wbFwiKTtcblxudmFyIF9hZ2VudEltcGwyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfYWdlbnRJbXBsKTtcblxudmFyIEFnZW50Q29tcG9uZW50ID0gKGZ1bmN0aW9uIChfUmVhY3QkQ29tcG9uZW50KSB7XG4gIGZ1bmN0aW9uIEFnZW50Q29tcG9uZW50KCkge1xuICAgIF9jbGFzc0NhbGxDaGVjayh0aGlzLCBBZ2VudENvbXBvbmVudCk7XG5cbiAgICBpZiAoX1JlYWN0JENvbXBvbmVudCAhPSBudWxsKSB7XG4gICAgICBfUmVhY3QkQ29tcG9uZW50LmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuICB9XG5cbiAgX2luaGVyaXRzKEFnZW50Q29tcG9uZW50LCBfUmVhY3QkQ29tcG9uZW50KTtcblxuICBfY3JlYXRlQ2xhc3MoQWdlbnRDb21wb25lbnQsIFt7XG4gICAga2V5OiBcImNvbXBvbmVudERpZE1vdW50XCIsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGNvbXBvbmVudERpZE1vdW50KCkge1xuICAgICAgaWYgKFwiZGV2ZWxvcG1lbnRcIiAhPT0gXCJwcm9kdWN0aW9uXCIpIHtcbiAgICAgICAgX2FnZW50SW1wbDJbXCJkZWZhdWx0XCJdLnZhbGlkYXRlKHRoaXMpO1xuICAgICAgfVxuICAgIH1cbiAgfSwge1xuICAgIGtleTogXCJjb21wb25lbnREaWRVcGRhdGVcIixcbiAgICB2YWx1ZTogZnVuY3Rpb24gY29tcG9uZW50RGlkVXBkYXRlKCkge1xuICAgICAgaWYgKFwiZGV2ZWxvcG1lbnRcIiAhPT0gXCJwcm9kdWN0aW9uXCIpIHtcbiAgICAgICAgX2FnZW50SW1wbDJbXCJkZWZhdWx0XCJdLnZhbGlkYXRlKHRoaXMpO1xuICAgICAgfVxuICAgIH1cbiAgfSwge1xuICAgIGtleTogXCJyZXF1ZXN0XCIsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIHJlcXVlc3QoYWN0aW9uIC8qIFssIC4uLmFyZ3NdIFssIGNhbGxiYWNrXSAqLykge1xuICAgICAgdmFyIGFyZ3MgPSBbXTtcbiAgICAgIHZhciBsYXN0SW5kZXggPSBhcmd1bWVudHMubGVuZ3RoIC0gMTtcbiAgICAgIHZhciBjYWxsYmFjayA9IHVuZGVmaW5lZDtcbiAgICAgIGlmIChsYXN0SW5kZXggPj0gMSkge1xuICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8IGxhc3RJbmRleDsgKytpKSB7XG4gICAgICAgICAgYXJncy5wdXNoKGFyZ3VtZW50c1tpXSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHR5cGVvZiBhcmd1bWVudHNbbGFzdEluZGV4XSAhPT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgYXJncy5wdXNoKGFyZ3VtZW50c1tsYXN0SW5kZXhdKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjYWxsYmFjayA9IGFyZ3VtZW50c1tsYXN0SW5kZXhdO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gX2FnZW50SW1wbDJbXCJkZWZhdWx0XCJdLnNlbmRBY3Rpb24odGhpcywgYWN0aW9uLCBhcmdzLCBjYWxsYmFjayk7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIEFnZW50Q29tcG9uZW50O1xufSkoX3JlYWN0MltcImRlZmF1bHRcIl0uQ29tcG9uZW50KTtcblxuZXhwb3J0c1tcImRlZmF1bHRcIl0gPSBBZ2VudENvbXBvbmVudDtcbm1vZHVsZS5leHBvcnRzID0gZXhwb3J0c1tcImRlZmF1bHRcIl07IiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICB2YWx1ZTogdHJ1ZVxufSk7XG5cbmZ1bmN0aW9uIF9pbnRlcm9wUmVxdWlyZURlZmF1bHQob2JqKSB7IHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7IFwiZGVmYXVsdFwiOiBvYmogfTsgfVxuXG52YXIgX2FnZW50SW1wbCA9IHJlcXVpcmUoXCIuL2FnZW50LWltcGxcIik7XG5cbnZhciBfYWdlbnRJbXBsMiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX2FnZW50SW1wbCk7XG5cbmV4cG9ydHNbXCJkZWZhdWx0XCJdID0ge1xuICBjb21wb25lbnREaWRNb3VudDogZnVuY3Rpb24gY29tcG9uZW50RGlkTW91bnQoKSB7XG4gICAgaWYgKFwiZGV2ZWxvcG1lbnRcIiAhPT0gXCJwcm9kdWN0aW9uXCIpIHtcbiAgICAgIF9hZ2VudEltcGwyW1wiZGVmYXVsdFwiXS52YWxpZGF0ZSh0aGlzKTtcbiAgICB9XG4gIH0sXG5cbiAgY29tcG9uZW50RGlkVXBkYXRlOiBmdW5jdGlvbiBjb21wb25lbnREaWRVcGRhdGUoKSB7XG4gICAgaWYgKFwiZGV2ZWxvcG1lbnRcIiAhPT0gXCJwcm9kdWN0aW9uXCIpIHtcbiAgICAgIF9hZ2VudEltcGwyW1wiZGVmYXVsdFwiXS52YWxpZGF0ZSh0aGlzKTtcbiAgICB9XG4gIH0sXG5cbiAgcmVxdWVzdDogZnVuY3Rpb24gcmVxdWVzdChhY3Rpb24gLyogWywgLi4uYXJnc10gWywgY2FsbGJhY2tdICovKSB7XG4gICAgdmFyIGFyZ3MgPSBbXTtcbiAgICB2YXIgbGFzdEluZGV4ID0gYXJndW1lbnRzLmxlbmd0aCAtIDE7XG4gICAgdmFyIGNhbGxiYWNrID0gdW5kZWZpbmVkO1xuICAgIGlmIChsYXN0SW5kZXggPj0gMSkge1xuICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPCBsYXN0SW5kZXg7ICsraSkge1xuICAgICAgICBhcmdzLnB1c2goYXJndW1lbnRzW2ldKTtcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgYXJndW1lbnRzW2xhc3RJbmRleF0gIT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBhcmdzLnB1c2goYXJndW1lbnRzW2xhc3RJbmRleF0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY2FsbGJhY2sgPSBhcmd1bWVudHNbbGFzdEluZGV4XTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIF9hZ2VudEltcGwyW1wiZGVmYXVsdFwiXS5zZW5kQWN0aW9uKHRoaXMsIGFjdGlvbiwgYXJncywgY2FsbGJhY2spO1xuICB9XG59O1xubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzW1wiZGVmYXVsdFwiXTsiLCIvLyBUaGVyZSBhcmUgc2V2ZXJhbCBjcm9zcy1jYWxsaW5ncyBpbiB0aGlzIGZpbGUuXG4vKmVzbGludCBuby11c2UtYmVmb3JlLWRlZmluZTpbMixcIm5vZnVuY1wiXSovXG5cblwidXNlIHN0cmljdFwiO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xuXG4vKipcbiAqIEBwYXJhbSBhY3Rpb24ge2Z1bmN0aW9ufSAtIEEgZnVuY3Rpb24gdG8gdHJhbnNmb3JtIHRoZSBzdGF0ZS5cbiAqIEBwYXJhbSBhcmdzIHthbnlbXX0gLSBJbmZvcm1hdGlvbiBmb3IgYWN0aW9uLiAgVGhpcyB2YWx1ZSBpcyBnaXZlbiB0byB0aGVcbiAqICAgc2Vjb25kIGFyZ3VtZW50IG9mIGFjdGlvbi5cbiAqIEByZXR1cm4ge1NlbmRBY3Rpb25FdmVudH0gLSBUaGUgY3JlYXRlZCBldmVudCBvYmplY3QuXG4gKi9cbmV4cG9ydHMuY3JlYXRlU2VuZEFjdGlvbkV2ZW50ID0gY3JlYXRlU2VuZEFjdGlvbkV2ZW50O1xuXG5mdW5jdGlvbiBfdG9Db25zdW1hYmxlQXJyYXkoYXJyKSB7IGlmIChBcnJheS5pc0FycmF5KGFycikpIHsgZm9yICh2YXIgaSA9IDAsIGFycjIgPSBBcnJheShhcnIubGVuZ3RoKTsgaSA8IGFyci5sZW5ndGg7IGkrKykgYXJyMltpXSA9IGFycltpXTsgcmV0dXJuIGFycjI7IH0gZWxzZSB7IHJldHVybiBBcnJheS5mcm9tKGFycik7IH0gfVxuXG5mdW5jdGlvbiBpbnZhcmlhbnQoY29uZGl0aW9uLCBtZXNzYWdlKSB7XG4gIGlmICghY29uZGl0aW9uKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKG1lc3NhZ2UpO1xuICB9XG59XG5cbmZ1bmN0aW9uIGlzRnVuY3Rpb24oeCkge1xuICByZXR1cm4gdHlwZW9mIHggPT09IFwiZnVuY3Rpb25cIjtcbn1cblxuZnVuY3Rpb24gaXNUaGVuYWJsZSh4KSB7XG4gIHJldHVybiB0eXBlb2YgeCA9PT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgeC50aGVuID09PSBcImZ1bmN0aW9uXCI7XG59XG5cbmZ1bmN0aW9uIGlzR2VuZXJhdG9yKHgpIHtcbiAgcmV0dXJuIHR5cGVvZiB4ID09PSBcIm9iamVjdFwiICYmIHR5cGVvZiB4Lm5leHQgPT09IFwiZnVuY3Rpb25cIiAmJiB0eXBlb2YgeFtcInRocm93XCJdID09PSBcImZ1bmN0aW9uXCI7XG59XG5cbmZ1bmN0aW9uIHByaW50RXJyb3IoZXJyb3IpIHtcbiAgLy8gVGhpcyBmdW5jdGlvbiBpcyB1c2VkIGluIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gXCJwcm9kdWN0aW9uXCIpLlxuICBpZiAoZXJyb3IgIT0gbnVsbCkge1xuICAgIGNvbnNvbGUuZXJyb3IoZXJyb3IpOyAvL2VzbGludC1kaXNhYmxlLWxpbmUgbm8tY29uc29sZVxuICB9XG59XG5cbi8vIEEgaGFuZGxlciB0aGF0IGlzIHVzZWQgaWYgdmFsdWUgaXMgbm90IGFsbCBvZiB1bmRlZmluZWQsIGEgZnVuY3Rpb24sIGFcbi8vIHByb21pc2UsIGFuZCBhIGdlbmVyYXRvci5cbi8vIEp1c3QgaXQgc2V0cyB0aGUgdmFsdWUgdG8gdGhlIGNvbXBvbmVudCdzIHN0YWdlIHZhbHVlLlxuZnVuY3Rpb24gc2V0KGNvbXBvbmVudCwgaXNJbkdlbmVyYXRvciwgdmFsdWUsIHJlc29sdmUsIHJlamVjdCkge1xuICB0cnkge1xuICAgIGNvbXBvbmVudC5zZXRTdGFnZVZhbHVlKHZhbHVlLCByZXNvbHZlKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgcmVqZWN0KGVycik7XG4gIH1cbn1cblxuLy8gQSBoYW5kbGVyIHRoYXQgaXMgdXNlZCBpZiB2YWx1ZSBpcyBhIGZ1bmN0aW9uLlxuLy8gSXQgY2FsbHMgdGhlIGZ1bmN0aW9uIHRvZ2V0aGVyIHdpdGggdGhlIGNvbXBvbmVudCdzIHN0YWdlIHZhbHVlLlxuLy8gVGhlbiBzZXQgdGhlIHJlc3VsdCB0byB0aGUgY29tcG9uZW50J3Mgc3RhZ2UgdmFsdWUuXG5mdW5jdGlvbiBjYWxsQW5kU2V0KGNvbXBvbmVudCwgaXNJbkdlbmVyYXRvciwgZnVuYywgcmVzb2x2ZSwgcmVqZWN0KSB7XG4gIHZhciByZXN1bHQgPSB1bmRlZmluZWQ7XG4gIHRyeSB7XG4gICAgcmVzdWx0ID0gZnVuYyhjb21wb25lbnQuc3RhZ2VWYWx1ZSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgcmVqZWN0KGVycm9yKTtcbiAgICByZXR1cm47XG4gIH1cbiAgc2V0VW5pZmllZChjb21wb25lbnQsIGlzSW5HZW5lcmF0b3IsIHJlc3VsdCwgcmVzb2x2ZSwgcmVqZWN0KTtcbn1cblxuLy8gQSBoYW5kbGVyIHRoYXQgaXMgdXNlZCBpZiB2YWx1ZSBpcyBhIHByb21pc2UuXG4vLyBJdCB3YWl0cyBmb3IgdGhlIHByb21pc2UgYmVjb21lIGZ1bGZpbGxlZC5cbi8vIFRoZW4gc2V0IHRoZSByZXN1bHQgdG8gdGhlIGNvbXBvbmVudCdzIHN0YWdlIHZhbHVlLlxuLy8gQnV0IGlmIGlzIHdoaWxlIGFkdmFuY2luZyBhIGdlbmVyYXRvciwgaXQgZG9lc24ndCBzZXQgdG8gdGhlIHN0YWdlIHZhbHVlLFxuLy8ganVzdCByZXR1cm5zIHRoZSByZXN1bHQuXG5mdW5jdGlvbiB3YWl0QW5kU2V0KGNvbXBvbmVudCwgaXNJbkdlbmVyYXRvciwgcHJvbWlzZSwgcmVzb2x2ZSwgcmVqZWN0KSB7XG4gIHZhciBwcm9taXNlMiA9IHByb21pc2UudGhlbihmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgaWYgKGlzSW5HZW5lcmF0b3IpIHtcbiAgICAgIHJlc29sdmUocmVzdWx0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgc2V0VW5pZmllZChjb21wb25lbnQsIGlzSW5HZW5lcmF0b3IsIHJlc3VsdCwgcmVzb2x2ZSwgcmVqZWN0KTtcbiAgICB9XG4gIH0sIHJlamVjdCk7XG5cbiAgaWYgKFwiZGV2ZWxvcG1lbnRcIiAhPT0gXCJwcm9kdWN0aW9uXCIpIHtcbiAgICBwcm9taXNlMltcImNhdGNoXCJdKHByaW50RXJyb3IpO1xuICB9XG59XG5cbi8vIEEgaGFuZGxlciB0aGF0IGlzIHVzZWQgaWYgdmFsdWUgaXMgYSBnZW5lcmF0b3IuXG4vLyBQcm9jZXNzIGEgZ2VuZXJhdG9yLiBwaW5nLXBvbmcgdGhlIGNvbXBvbmVudCdzIHN0YWdlIHZhbHVlLlxuZnVuY3Rpb24gYWR2YW5jZVRvRW5kKGNvbXBvbmVudCwgaXNJbkdlbmVyYXRvciwgZ2VuZXJhdG9yLCByZXNvbHZlLCByZWplY3QpIHtcbiAgb25GdWxmaWxsZWQodW5kZWZpbmVkKTtcblxuICBmdW5jdGlvbiBvbkZ1bGZpbGxlZChzdGFnZVZhbHVlKSB7XG4gICAgdmFyIHJldCA9IHVuZGVmaW5lZDtcbiAgICB0cnkge1xuICAgICAgcmV0ID0gZ2VuZXJhdG9yLm5leHQoc3RhZ2VWYWx1ZSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICByZWplY3QoZXJyKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBuZXh0KHJldCk7XG4gIH1cblxuICBmdW5jdGlvbiBvblJlamVjdGVkKGVycikge1xuICAgIHZhciByZXQgPSB1bmRlZmluZWQ7XG4gICAgdHJ5IHtcbiAgICAgIHJldCA9IGdlbmVyYXRvcltcInRocm93XCJdKGVycik7XG4gICAgfSBjYXRjaCAoZXJyMikge1xuICAgICAgcmVqZWN0KGVycjIpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIG5leHQocmV0KTtcbiAgfVxuXG4gIGZ1bmN0aW9uIG5leHQocmV0KSB7XG4gICAgaWYgKHJldC5kb25lKSB7XG4gICAgICBzZXRVbmlmaWVkKGNvbXBvbmVudCwgdHJ1ZSwgcmV0LnZhbHVlLCByZXNvbHZlLCByZWplY3QpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzZXRVbmlmaWVkKGNvbXBvbmVudCwgdHJ1ZSwgcmV0LnZhbHVlLCBvbkZ1bGZpbGxlZCwgb25SZWplY3RlZCk7XG4gICAgfVxuICB9XG59XG5cbi8vIENoZWNrIHR5cGUgb2YgdGhlIHZhbHVlLCBhbmQgaGFuZGxlIHRoZSB2YWx1ZS5cbmZ1bmN0aW9uIHNldFVuaWZpZWQoY29tcG9uZW50LCBpc0luR2VuZXJhdG9yLCB2YWx1ZSwgcmVzb2x2ZSwgcmVqZWN0KSB7XG4gIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmVzb2x2ZShjb21wb25lbnQuc3RhZ2VWYWx1ZSk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdmFyIGhhbmRsZSA9IGlzRnVuY3Rpb24odmFsdWUpID8gY2FsbEFuZFNldCA6IGlzVGhlbmFibGUodmFsdWUpID8gd2FpdEFuZFNldCA6IGlzR2VuZXJhdG9yKHZhbHVlKSA/IGFkdmFuY2VUb0VuZCA6XG4gIC8qIG90aGVyd2lzZSAqL3NldDtcblxuICBoYW5kbGUoY29tcG9uZW50LCBpc0luR2VuZXJhdG9yLCB2YWx1ZSwgcmVzb2x2ZSwgcmVqZWN0KTtcbn1cblxuLyoqXG4gKiBUaGUgZXZlbnQgbmFtZSBmb3IgYFNlbmRBY3Rpb25FdmVudGAuXG4gKiBAdHlwZSB7c3RyaW5nfVxuICovXG52YXIgRVZFTlRfTkFNRSA9IFwiaGVsaXgtc2VudC1hY3Rpb25cIjtleHBvcnRzLkVWRU5UX05BTUUgPSBFVkVOVF9OQU1FO1xuXG5mdW5jdGlvbiBjcmVhdGVTZW5kQWN0aW9uRXZlbnQoYWN0aW9uLCBhcmdzLCBjYWxsYmFjaykge1xuICBpZiAoXCJkZXZlbG9wbWVudFwiICE9PSBcInByb2R1Y3Rpb25cIikge1xuICAgIGludmFyaWFudCh0eXBlb2YgYWN0aW9uID09PSBcImZ1bmN0aW9uXCIsIFwiYWN0aW9uIHNob3VsZCBiZSBhIGZ1bmN0aW9uLlwiKTtcbiAgICBpbnZhcmlhbnQoQXJyYXkuaXNBcnJheShhcmdzKSwgXCJhcmdzIHNob3VsZCBiZSBhbiBhcnJheS5cIik7XG4gICAgaW52YXJpYW50KGNhbGxiYWNrID09IG51bGwgfHwgdHlwZW9mIGNhbGxiYWNrID09PSBcImZ1bmN0aW9uXCIsIFwiY2FsbGJhY2sgc2hvdWxkIGJlIGEgZnVuY3Rpb24gb3Igbm90aGluZy5cIik7XG5cbiAgICBpZiAoY2FsbGJhY2sgPT0gbnVsbCkge1xuICAgICAgY2FsbGJhY2sgPSBwcmludEVycm9yO1xuICAgIH1cbiAgfVxuXG4gIHZhciBldmVudCA9IGRvY3VtZW50LmNyZWF0ZUV2ZW50KFwiQ3VzdG9tRXZlbnRcIik7XG4gIHZhciBoYW5kbGVkID0gZmFsc2U7XG5cbiAgZXZlbnQuaW5pdEN1c3RvbUV2ZW50KEVWRU5UX05BTUUsIHRydWUsIHRydWUsIG51bGwpO1xuICBPYmplY3QuZGVmaW5lUHJvcGVydGllcyhldmVudCwge1xuICAgIGFjdGlvbjoge1xuICAgICAgdmFsdWU6IGFjdGlvbixcbiAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgICB3cml0YWJsZTogdHJ1ZVxuICAgIH0sXG5cbiAgICBhcmd1bWVudHM6IHtcbiAgICAgIHZhbHVlOiBhcmdzLFxuICAgICAgY29uZmlndXJhYmxlOiB0cnVlLFxuICAgICAgZW51bWVyYWJsZTogdHJ1ZSxcbiAgICAgIHdyaXRhYmxlOiB0cnVlXG4gICAgfSxcblxuICAgIC8vIFRoaXMgaXMgaW50ZXJuYWwgbWV0aG9kLCBjYWxsZWQgZnJvbSBTdGFnZU1peGluLlxuICAgIGFwcGx5VG86IHsgdmFsdWU6IGZ1bmN0aW9uIGFwcGx5VG8oY29tcG9uZW50KSB7XG4gICAgICAgIGlmIChcImRldmVsb3BtZW50XCIgIT09IFwicHJvZHVjdGlvblwiKSB7XG4gICAgICAgICAgdmFyIGdldCA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoY29tcG9uZW50LCBcInN0YWdlVmFsdWVcIik7XG4gICAgICAgICAgdmFyIF9zZXQgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGNvbXBvbmVudCwgXCJzZXRTdGFnZVZhbHVlXCIpO1xuICAgICAgICAgIGludmFyaWFudChpc0Z1bmN0aW9uKGdldC5nZXQpLCBcImNvbXBvbmVudC5zdGFnZVZhbHVlIHNob3VsZCBiZSBhIGdldHRlciBwcm9wZXJ0eS5cIik7XG4gICAgICAgICAgaW52YXJpYW50KGlzRnVuY3Rpb24oX3NldC52YWx1ZSksIFwiY29tcG9uZW50LnNldFN0YWdlVmFsdWUgc2hvdWxkIGJlIGEgZnVuY3Rpb24uXCIpO1xuICAgICAgICAgIGludmFyaWFudChoYW5kbGVkID09PSBmYWxzZSwgXCJ0aGlzIFwiICsgRVZFTlRfTkFNRSArIFwiIGV2ZW50IGhhZCBiZWVuIGFwcGxpZWQgYWxyZWFkeS5cIik7XG4gICAgICAgICAgaW52YXJpYW50KGlzRnVuY3Rpb24odGhpcy5hY3Rpb24pLCBcInRoaXMuYWN0aW9uIHNob3VsZCBiZSBhIGZ1bmN0aW9uLlwiKTtcbiAgICAgICAgICBpbnZhcmlhbnQoQXJyYXkuaXNBcnJheSh0aGlzLmFyZ3VtZW50cyksIFwidGhpcy5hcmd1bWVudHMgc2hvdWxkIGJlIGFuIGFycmF5LlwiKTtcbiAgICAgICAgfVxuICAgICAgICBoYW5kbGVkID0gdHJ1ZTtcblxuICAgICAgICB2YXIgdmFsdWUgPSB1bmRlZmluZWQ7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgdmFsdWUgPSB0aGlzLmFjdGlvbi5hcHBseSh0aGlzLCBbY29tcG9uZW50LnN0YWdlVmFsdWVdLmNvbmNhdChfdG9Db25zdW1hYmxlQXJyYXkodGhpcy5hcmd1bWVudHMpKSk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgaWYgKGNhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgICAgIGNhbGxiYWNrKGVycm9yKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgc2V0VW5pZmllZChjb21wb25lbnQsIGZhbHNlLCB2YWx1ZSwgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgIHJldHVybiBjYWxsYmFjayAmJiBjYWxsYmFjayhudWxsLCByZXN1bHQpO1xuICAgICAgICB9LCBjYWxsYmFjayk7XG4gICAgICB9IH0sXG5cbiAgICAvLyBUaGlzIGlzIGludGVybmFsIG1ldGhvZCwgY2FsbGVkIGZyb20gQWdlbnRNaXhpbi5cbiAgICByZWplY3RJZk5vdEhhbmRsZWQ6IHsgdmFsdWU6IGZ1bmN0aW9uIHJlamVjdElmTm90SGFuZGxlZCgpIHtcbiAgICAgICAgaWYgKGhhbmRsZWQgPT09IGZhbHNlKSB7XG4gICAgICAgICAgaGFuZGxlZCA9IHRydWU7XG4gICAgICAgICAgaWYgKGNhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgICAgIGNhbGxiYWNrKG5ldyBFcnJvcihcIm5vdCBoYW5kbGVkXCIpKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gfVxuICB9KTtcblxuICByZXR1cm4gZXZlbnQ7XG59IiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICB2YWx1ZTogdHJ1ZVxufSk7XG5cbnZhciBfY3JlYXRlQ2xhc3MgPSAoZnVuY3Rpb24gKCkgeyBmdW5jdGlvbiBkZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH0gcmV0dXJuIGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfTsgfSkoKTtcblxudmFyIF9nZXQgPSBmdW5jdGlvbiBnZXQoX3gyLCBfeDMsIF94NCkgeyB2YXIgX2FnYWluID0gdHJ1ZTsgX2Z1bmN0aW9uOiB3aGlsZSAoX2FnYWluKSB7IHZhciBvYmplY3QgPSBfeDIsIHByb3BlcnR5ID0gX3gzLCByZWNlaXZlciA9IF94NDsgZGVzYyA9IHBhcmVudCA9IGdldHRlciA9IHVuZGVmaW5lZDsgX2FnYWluID0gZmFsc2U7IHZhciBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihvYmplY3QsIHByb3BlcnR5KTsgaWYgKGRlc2MgPT09IHVuZGVmaW5lZCkgeyB2YXIgcGFyZW50ID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKG9iamVjdCk7IGlmIChwYXJlbnQgPT09IG51bGwpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSBlbHNlIHsgX3gyID0gcGFyZW50OyBfeDMgPSBwcm9wZXJ0eTsgX3g0ID0gcmVjZWl2ZXI7IF9hZ2FpbiA9IHRydWU7IGNvbnRpbnVlIF9mdW5jdGlvbjsgfSB9IGVsc2UgaWYgKFwidmFsdWVcIiBpbiBkZXNjKSB7IHJldHVybiBkZXNjLnZhbHVlOyB9IGVsc2UgeyB2YXIgZ2V0dGVyID0gZGVzYy5nZXQ7IGlmIChnZXR0ZXIgPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9IHJldHVybiBnZXR0ZXIuY2FsbChyZWNlaXZlcik7IH0gfSB9O1xuXG5mdW5jdGlvbiBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KG9iaikgeyByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyBcImRlZmF1bHRcIjogb2JqIH07IH1cblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IGNhbGwgYSBjbGFzcyBhcyBhIGZ1bmN0aW9uXCIpOyB9IH1cblxuZnVuY3Rpb24gX2luaGVyaXRzKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IGlmICh0eXBlb2Ygc3VwZXJDbGFzcyAhPT0gXCJmdW5jdGlvblwiICYmIHN1cGVyQ2xhc3MgIT09IG51bGwpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN1cGVyIGV4cHJlc3Npb24gbXVzdCBlaXRoZXIgYmUgbnVsbCBvciBhIGZ1bmN0aW9uLCBub3QgXCIgKyB0eXBlb2Ygc3VwZXJDbGFzcyk7IH0gc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzICYmIHN1cGVyQ2xhc3MucHJvdG90eXBlLCB7IGNvbnN0cnVjdG9yOiB7IHZhbHVlOiBzdWJDbGFzcywgZW51bWVyYWJsZTogZmFsc2UsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSB9KTsgaWYgKHN1cGVyQ2xhc3MpIHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxudmFyIF9yZWFjdCA9IHJlcXVpcmUoXCJyZWFjdFwiKTtcblxudmFyIF9yZWFjdDIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF9yZWFjdCk7XG5cbnZhciBfc3RhZ2VJbXBsID0gcmVxdWlyZShcIi4vc3RhZ2UtaW1wbFwiKTtcblxudmFyIF9zdGFnZUltcGwyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfc3RhZ2VJbXBsKTtcblxudmFyIFN0YWdlQ29tcG9uZW50ID0gKGZ1bmN0aW9uIChfUmVhY3QkQ29tcG9uZW50KSB7XG4gIGZ1bmN0aW9uIFN0YWdlQ29tcG9uZW50KHByb3BzKSB7XG4gICAgdmFyIHN0YWdlVmFsdWVQYXRoID0gYXJndW1lbnRzWzFdID09PSB1bmRlZmluZWQgPyBcIlwiIDogYXJndW1lbnRzWzFdO1xuXG4gICAgX2NsYXNzQ2FsbENoZWNrKHRoaXMsIFN0YWdlQ29tcG9uZW50KTtcblxuICAgIF9nZXQoT2JqZWN0LmdldFByb3RvdHlwZU9mKFN0YWdlQ29tcG9uZW50LnByb3RvdHlwZSksIFwiY29uc3RydWN0b3JcIiwgdGhpcykuY2FsbCh0aGlzLCBwcm9wcyk7XG4gICAgX3N0YWdlSW1wbDJbXCJkZWZhdWx0XCJdLmluaXRpYWxpemUodGhpcywgc3RhZ2VWYWx1ZVBhdGgpO1xuICB9XG5cbiAgX2luaGVyaXRzKFN0YWdlQ29tcG9uZW50LCBfUmVhY3QkQ29tcG9uZW50KTtcblxuICBfY3JlYXRlQ2xhc3MoU3RhZ2VDb21wb25lbnQsIFt7XG4gICAga2V5OiBcImNvbXBvbmVudERpZE1vdW50XCIsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGNvbXBvbmVudERpZE1vdW50KCkge1xuICAgICAgX3N0YWdlSW1wbDJbXCJkZWZhdWx0XCJdLnNldHVwSGFuZGxlcih0aGlzKTtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwiY29tcG9uZW50RGlkVXBkYXRlXCIsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGNvbXBvbmVudERpZFVwZGF0ZSgpIHtcbiAgICAgIF9zdGFnZUltcGwyW1wiZGVmYXVsdFwiXS5zZXR1cEhhbmRsZXIodGhpcyk7XG4gICAgfVxuICB9LCB7XG4gICAga2V5OiBcImNvbXBvbmVudFdpbGxVbm1vdW50XCIsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGNvbXBvbmVudFdpbGxVbm1vdW50KCkge1xuICAgICAgX3N0YWdlSW1wbDJbXCJkZWZhdWx0XCJdLnRlYXJkb3duSGFuZGxlcih0aGlzKTtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwiY29tcG9uZW50V2lsbFVwZGF0ZVwiLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBjb21wb25lbnRXaWxsVXBkYXRlKCkge1xuICAgICAgX3N0YWdlSW1wbDJbXCJkZWZhdWx0XCJdLnRlYXJkb3duSGFuZGxlcih0aGlzKTtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwiZmlsdGVyQWN0aW9uXCIsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGZpbHRlckFjdGlvbigpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfV0pO1xuXG4gIHJldHVybiBTdGFnZUNvbXBvbmVudDtcbn0pKF9yZWFjdDJbXCJkZWZhdWx0XCJdLkNvbXBvbmVudCk7XG5cbmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gU3RhZ2VDb21wb25lbnQ7XG5tb2R1bGUuZXhwb3J0cyA9IGV4cG9ydHNbXCJkZWZhdWx0XCJdO1xuLyogZXZlbnQgKi8iLCJcInVzZSBzdHJpY3RcIjtcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxuZnVuY3Rpb24gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChvYmopIHsgcmV0dXJuIG9iaiAmJiBvYmouX19lc01vZHVsZSA/IG9iaiA6IHsgXCJkZWZhdWx0XCI6IG9iaiB9OyB9XG5cbnZhciBfc3RhZ2VJbXBsID0gcmVxdWlyZShcIi4vc3RhZ2UtaW1wbFwiKTtcblxudmFyIF9zdGFnZUltcGwyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfc3RhZ2VJbXBsKTtcblxuZXhwb3J0c1tcImRlZmF1bHRcIl0gPSB7XG4gIGNvbXBvbmVudFdpbGxNb3VudDogZnVuY3Rpb24gY29tcG9uZW50V2lsbE1vdW50KCkge1xuICAgIF9zdGFnZUltcGwyW1wiZGVmYXVsdFwiXS5pbml0aWFsaXplKHRoaXMsIHRoaXMuc3RhZ2VWYWx1ZVBhdGggfHwgXCJcIik7XG4gIH0sXG5cbiAgY29tcG9uZW50RGlkTW91bnQ6IGZ1bmN0aW9uIGNvbXBvbmVudERpZE1vdW50KCkge1xuICAgIF9zdGFnZUltcGwyW1wiZGVmYXVsdFwiXS5zZXR1cEhhbmRsZXIodGhpcyk7XG4gIH0sXG5cbiAgY29tcG9uZW50RGlkVXBkYXRlOiBmdW5jdGlvbiBjb21wb25lbnREaWRVcGRhdGUoKSB7XG4gICAgX3N0YWdlSW1wbDJbXCJkZWZhdWx0XCJdLnNldHVwSGFuZGxlcih0aGlzKTtcbiAgfSxcblxuICBjb21wb25lbnRXaWxsVXBkYXRlOiBmdW5jdGlvbiBjb21wb25lbnRXaWxsVXBkYXRlKCkge1xuICAgIF9zdGFnZUltcGwyW1wiZGVmYXVsdFwiXS50ZWFyZG93bkhhbmRsZXIodGhpcyk7XG4gIH0sXG5cbiAgY29tcG9uZW50V2lsbFVubW91bnQ6IGZ1bmN0aW9uIGNvbXBvbmVudFdpbGxVbm1vdW50KCkge1xuICAgIF9zdGFnZUltcGwyW1wiZGVmYXVsdFwiXS50ZWFyZG93bkhhbmRsZXIodGhpcyk7XG4gIH1cbn07XG5tb2R1bGUuZXhwb3J0cyA9IGV4cG9ydHNbXCJkZWZhdWx0XCJdOyIsIlwidXNlIHN0cmljdFwiO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xuXG5mdW5jdGlvbiBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KG9iaikgeyByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyBcImRlZmF1bHRcIjogb2JqIH07IH1cblxudmFyIF9yZWFjdCA9IHJlcXVpcmUoXCJyZWFjdFwiKTtcblxudmFyIF9yZWFjdDIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF9yZWFjdCk7XG5cbnZhciBfU2VuZEFjdGlvbkV2ZW50ID0gcmVxdWlyZShcIi4vU2VuZEFjdGlvbkV2ZW50XCIpO1xuXG5mdW5jdGlvbiBpbnZhcmlhbnQoY29uZGl0aW9uLCBtZXNzYWdlKSB7XG4gIGlmICghY29uZGl0aW9uKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKG1lc3NhZ2UpO1xuICB9XG59XG5cbmV4cG9ydHNbXCJkZWZhdWx0XCJdID0ge1xuICB2YWxpZGF0ZTogZnVuY3Rpb24gdmFsaWRhdGUoY29tcG9uZW50KSB7XG4gICAgaWYgKFwiZGV2ZWxvcG1lbnRcIiAhPT0gXCJwcm9kdWN0aW9uXCIpIHtcbiAgICAgIHZhciBub2RlID0gX3JlYWN0MltcImRlZmF1bHRcIl0uZmluZERPTU5vZGUoY29tcG9uZW50KTtcbiAgICAgIGludmFyaWFudChub2RlICE9IG51bGwsIFwiQWdlbnRNaXhpbjogcmVxdWlyZXMgdG8gYmUgcmVuZGVyZWQuXCIpO1xuICAgIH1cbiAgfSxcblxuICBzZW5kQWN0aW9uOiBmdW5jdGlvbiBzZW5kQWN0aW9uKGNvbXBvbmVudCwgYWN0aW9uLCBhcmdzLCBjYWxsYmFjaykge1xuICAgIHZhciBub2RlID0gX3JlYWN0MltcImRlZmF1bHRcIl0uZmluZERPTU5vZGUoY29tcG9uZW50KTtcbiAgICB2YXIgZXZlbnQgPSAoMCwgX1NlbmRBY3Rpb25FdmVudC5jcmVhdGVTZW5kQWN0aW9uRXZlbnQpKGFjdGlvbiwgYXJncywgY2FsbGJhY2spO1xuXG4gICAgaWYgKFwiZGV2ZWxvcG1lbnRcIiAhPT0gXCJwcm9kdWN0aW9uXCIpIHtcbiAgICAgIGludmFyaWFudChub2RlICE9IG51bGwsIFwiQWdlbnRNaXhpbjogcmVxdWlyZXMgdG8gYmUgcmVuZGVyZWQuXCIpO1xuICAgIH1cbiAgICBub2RlLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgIGV2ZW50LnJlamVjdElmTm90SGFuZGxlZCgpO1xuICB9XG59O1xubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzW1wiZGVmYXVsdFwiXTsiLCJcInVzZSBzdHJpY3RcIjtcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxuZnVuY3Rpb24gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChvYmopIHsgcmV0dXJuIG9iaiAmJiBvYmouX19lc01vZHVsZSA/IG9iaiA6IHsgXCJkZWZhdWx0XCI6IG9iaiB9OyB9XG5cbnZhciBfcmVhY3QgPSByZXF1aXJlKFwicmVhY3RcIik7XG5cbnZhciBfcmVhY3QyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfcmVhY3QpO1xuXG52YXIgX1NlbmRBY3Rpb25FdmVudCA9IHJlcXVpcmUoXCIuL1NlbmRBY3Rpb25FdmVudFwiKTtcblxuZnVuY3Rpb24gaW52YXJpYW50KGNvbmRpdGlvbiwgbWVzc2FnZSkge1xuICBpZiAoIWNvbmRpdGlvbikge1xuICAgIHRocm93IG5ldyBFcnJvcihtZXNzYWdlKTtcbiAgfVxufVxuXG4vLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZnVuY3Rpb24gcGFyc2VTdGFnZVZhbHVlUGF0aCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiID8gdmFsdWUuc3BsaXQoXCIuXCIpLmZpbHRlcihCb29sZWFuKSA6IFtdO1xufVxuXG4vLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZnVuY3Rpb24gZGVmaW5lR2V0U3RhZ2VWYWx1ZShwYXJ0cykge1xuICB2YXIgYm9keSA9IHVuZGVmaW5lZDtcbiAgc3dpdGNoIChwYXJ0cy5sZW5ndGgpIHtcbiAgICBjYXNlIDA6XG4gICAgICBib2R5ID0gXCJyZXR1cm4gdGhpcy5zdGF0ZTtcIjtcbiAgICAgIGJyZWFrO1xuXG4gICAgZGVmYXVsdDpcbiAgICAgIHZhciBsYXN0SW5kZXggPSBwYXJ0cy5sZW5ndGggLSAxO1xuICAgICAgYm9keSA9IFwidmFyIHRtcDAgPSB0aGlzLnN0YXRlO1wiO1xuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsYXN0SW5kZXg7ICsraSkge1xuICAgICAgICBib2R5ICs9IFwiXFxuaWYgKHRtcFwiICsgaSArIFwiID09IG51bGwpIHtcXG4gIHJldHVybiB1bmRlZmluZWQ7XFxufVxcbnZhciB0bXBcIiArIChpICsgMSkgKyBcIiA9IHRtcFwiICsgaSArIFwiLlwiICsgcGFydHNbaV0gKyBcIjtcIjtcbiAgICAgIH1cbiAgICAgIGJvZHkgKz0gXCJcXG5yZXR1cm4gdG1wXCIgKyBsYXN0SW5kZXggKyBcIiAmJiB0bXBcIiArIGxhc3RJbmRleCArIFwiLlwiICsgcGFydHNbbGFzdEluZGV4XSArIFwiO1wiO1xuICAgICAgYnJlYWs7XG4gIH1cblxuICByZXR1cm4gRnVuY3Rpb24oYm9keSk7XG59XG5cbi8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5mdW5jdGlvbiBkZWZpbmVTZXRTdGFnZVZhbHVlKHBhcnRzKSB7XG4gIHZhciBib2R5ID0gXCJ2YXIgY2IyID0gY2IgJiYgZnVuY3Rpb24oKSB7IGNiKHRoaXMuc3RhZ2VWYWx1ZSk7IH0uYmluZCh0aGlzKTtcIjtcblxuICBzd2l0Y2ggKHBhcnRzLmxlbmd0aCkge1xuICAgIGNhc2UgMDpcbiAgICAgIGJvZHkgKz0gXCJcXG50aGlzLnNldFN0YXRlKHZhbHVlLCBjYjIpO1wiO1xuICAgICAgYnJlYWs7XG5cbiAgICBjYXNlIDE6XG4gICAgICBib2R5ICs9IFwiXFxudGhpcy5zZXRTdGF0ZSh7XCIgKyBwYXJ0c1swXSArIFwiOiB2YWx1ZX0sIGNiMik7XCI7XG4gICAgICBicmVhaztcblxuICAgIGRlZmF1bHQ6XG4gICAgICB2YXIgbGFzdEluZGV4ID0gcGFydHMubGVuZ3RoIC0gMTtcbiAgICAgIGJvZHkgKz0gXCJcXG52YXIgdG1wMCA9IHRoaXMuc3RhdGUgfHwge307XCI7XG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxhc3RJbmRleDsgKytpKSB7XG4gICAgICAgIGJvZHkgKz0gXCJcXG52YXIgdG1wXCIgKyAoaSArIDEpICsgXCIgPSB0bXBcIiArIGkgKyBcIi5cIiArIHBhcnRzW2ldICsgXCI7XFxuaWYgKHRtcFwiICsgKGkgKyAxKSArIFwiID09IG51bGwpIHtcXG4gIHRtcFwiICsgKGkgKyAxKSArIFwiID0gdG1wXCIgKyBpICsgXCIuXCIgKyBwYXJ0c1tpXSArIFwiID0ge307XFxufVwiO1xuICAgICAgfVxuICAgICAgYm9keSArPSBcIlxcbnRtcFwiICsgbGFzdEluZGV4ICsgXCIuXCIgKyBwYXJ0c1tsYXN0SW5kZXhdICsgXCIgPSB2YWx1ZTtcXG50aGlzLnNldFN0YXRlKHRtcDAsIGNiMik7XCI7XG4gICAgICBicmVhaztcbiAgfVxuXG4gIHJldHVybiBGdW5jdGlvbihcInZhbHVlXCIsIFwiY2JcIiwgYm9keSk7XG59XG5cbi8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5mdW5jdGlvbiBoYW5kbGVTZW5kQWN0aW9uKGV2ZW50KSB7XG4gIGlmIChldmVudC5kZWZhdWx0UHJldmVudGVkKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGlmICh0eXBlb2YgdGhpcy5maWx0ZXJBY3Rpb24gPT09IFwiZnVuY3Rpb25cIiAmJiAhdGhpcy5maWx0ZXJBY3Rpb24oZXZlbnQpKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICBldmVudC5hcHBseVRvKHRoaXMpO1xufVxuXG4vLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZXhwb3J0c1tcImRlZmF1bHRcIl0gPSB7XG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uIGluaXRpYWxpemUoY29tcG9uZW50LCBzdGFnZVZhbHVlUGF0aCkge1xuICAgIGlmIChjb21wb25lbnQuc3RhZ2VNaXhpbkluaXRpYWxpemVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIHBhcnRzID0gcGFyc2VTdGFnZVZhbHVlUGF0aChzdGFnZVZhbHVlUGF0aCk7XG4gICAgdmFyIGdldFN0YWdlVmFsdWUgPSBkZWZpbmVHZXRTdGFnZVZhbHVlKHBhcnRzKTtcblxuICAgIGlmIChcImRldmVsb3BtZW50XCIgIT09IFwicHJvZHVjdGlvblwiKSB7XG4gICAgICBpbnZhcmlhbnQoc3RhZ2VWYWx1ZVBhdGggPT0gbnVsbCB8fCB0eXBlb2Ygc3RhZ2VWYWx1ZVBhdGggPT09IFwic3RyaW5nXCIsIFwiU3RhZ2VNaXhpbjogc3RhZ2VWYWx1ZVBhdGggc2hvdWxkIGJlIGEgc3RyaW5nLlwiKTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgZ2V0U3RhZ2VWYWx1ZS5jYWxsKGNvbXBvbmVudCk7XG4gICAgICB9IGNhdGNoIChjYXVzZSkge1xuICAgICAgICB2YXIgZXJyID0gbmV3IEVycm9yKFwiU3RhZ2VNaXhpbjogc3RhZ2VWYWx1ZVBhdGggaXMgaW52YWxpZCAoXCIgKyBzdGFnZVZhbHVlUGF0aCArIFwiKS5cIik7XG4gICAgICAgIGVyci5jYXVzZSA9IGNhdXNlO1xuICAgICAgICB0aHJvdyBlcnI7XG4gICAgICB9XG4gICAgfVxuXG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoY29tcG9uZW50LCB7XG4gICAgICBzdGFnZU1peGluSW5pdGlhbGl6ZWQ6IHtcbiAgICAgICAgdmFsdWU6IHRydWUsXG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICAgICAgfSxcblxuICAgICAgc3RhZ2VNaXhpbkhhbmRsZVNlbmRBY3Rpb246IHtcbiAgICAgICAgdmFsdWU6IGhhbmRsZVNlbmRBY3Rpb24uYmluZChjb21wb25lbnQpLFxuICAgICAgICBjb25maWd1cmFibGU6IHRydWVcbiAgICAgIH0sXG5cbiAgICAgIHN0YWdlVmFsdWU6IHtcbiAgICAgICAgZ2V0OiBnZXRTdGFnZVZhbHVlLFxuICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICAgIGVudW1lcmFibGU6IHRydWVcbiAgICAgIH0sXG5cbiAgICAgIHNldFN0YWdlVmFsdWU6IHtcbiAgICAgICAgdmFsdWU6IGRlZmluZVNldFN0YWdlVmFsdWUocGFydHMpLmJpbmQoY29tcG9uZW50KSxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgICB9XG4gICAgfSk7XG4gIH0sXG5cbiAgc2V0dXBIYW5kbGVyOiBmdW5jdGlvbiBzZXR1cEhhbmRsZXIoY29tcG9uZW50KSB7XG4gICAgdmFyIG5vZGUgPSBfcmVhY3QyW1wiZGVmYXVsdFwiXS5maW5kRE9NTm9kZShjb21wb25lbnQpO1xuICAgIGlmIChcImRldmVsb3BtZW50XCIgIT09IFwicHJvZHVjdGlvblwiKSB7XG4gICAgICBpbnZhcmlhbnQobm9kZSAhPSBudWxsLCBcIlN0YWdlTWl4aW46IHJlcXVpcmVzIHRvIGJlIHJlbmRlcmVkLlwiKTtcbiAgICB9XG4gICAgbm9kZS5hZGRFdmVudExpc3RlbmVyKF9TZW5kQWN0aW9uRXZlbnQuRVZFTlRfTkFNRSwgY29tcG9uZW50LnN0YWdlTWl4aW5IYW5kbGVTZW5kQWN0aW9uKTtcbiAgfSxcblxuICB0ZWFyZG93bkhhbmRsZXI6IGZ1bmN0aW9uIHRlYXJkb3duSGFuZGxlcihjb21wb25lbnQpIHtcbiAgICB2YXIgbm9kZSA9IF9yZWFjdDJbXCJkZWZhdWx0XCJdLmZpbmRET01Ob2RlKGNvbXBvbmVudCk7XG4gICAgaWYgKG5vZGUgIT0gbnVsbCkge1xuICAgICAgbm9kZS5yZW1vdmVFdmVudExpc3RlbmVyKF9TZW5kQWN0aW9uRXZlbnQuRVZFTlRfTkFNRSwgY29tcG9uZW50LnN0YWdlTWl4aW5IYW5kbGVTZW5kQWN0aW9uKTtcbiAgICB9XG4gIH1cbn07XG5tb2R1bGUuZXhwb3J0cyA9IGV4cG9ydHNbXCJkZWZhdWx0XCJdOyIsIlwidXNlIHN0cmljdFwiO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xuXG5mdW5jdGlvbiBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KG9iaikgeyByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyBcImRlZmF1bHRcIjogb2JqIH07IH1cblxudmFyIF9BZ2VudENvbXBvbmVudCA9IHJlcXVpcmUoXCIuL0FnZW50Q29tcG9uZW50XCIpO1xuXG52YXIgX0FnZW50Q29tcG9uZW50MiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX0FnZW50Q29tcG9uZW50KTtcblxudmFyIF9BZ2VudE1peGluID0gcmVxdWlyZShcIi4vQWdlbnRNaXhpblwiKTtcblxudmFyIF9BZ2VudE1peGluMiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX0FnZW50TWl4aW4pO1xuXG52YXIgX1N0YWdlQ29tcG9uZW50ID0gcmVxdWlyZShcIi4vU3RhZ2VDb21wb25lbnRcIik7XG5cbnZhciBfU3RhZ2VDb21wb25lbnQyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfU3RhZ2VDb21wb25lbnQpO1xuXG52YXIgX1N0YWdlTWl4aW4gPSByZXF1aXJlKFwiLi9TdGFnZU1peGluXCIpO1xuXG52YXIgX1N0YWdlTWl4aW4yID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfU3RhZ2VNaXhpbik7XG5cbmV4cG9ydHNbXCJkZWZhdWx0XCJdID0ge1xuICBBZ2VudENvbXBvbmVudDogX0FnZW50Q29tcG9uZW50MltcImRlZmF1bHRcIl0sXG4gIEFnZW50TWl4aW46IF9BZ2VudE1peGluMltcImRlZmF1bHRcIl0sXG4gIFN0YWdlQ29tcG9uZW50OiBfU3RhZ2VDb21wb25lbnQyW1wiZGVmYXVsdFwiXSxcbiAgU3RhZ2VNaXhpbjogX1N0YWdlTWl4aW4yW1wiZGVmYXVsdFwiXVxufTtcbmV4cG9ydHMuQWdlbnRDb21wb25lbnQgPSBfQWdlbnRDb21wb25lbnQyW1wiZGVmYXVsdFwiXTtcbmV4cG9ydHMuQWdlbnRNaXhpbiA9IF9BZ2VudE1peGluMltcImRlZmF1bHRcIl07XG5leHBvcnRzLlN0YWdlQ29tcG9uZW50ID0gX1N0YWdlQ29tcG9uZW50MltcImRlZmF1bHRcIl07XG5leHBvcnRzLlN0YWdlTWl4aW4gPSBfU3RhZ2VNaXhpbjJbXCJkZWZhdWx0XCJdOyJdfQ== 666 | -------------------------------------------------------------------------------- /dist/react-helix.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.reactHelix=e()}}(function(){return function e(t,n,o){function r(i,a){if(!n[i]){if(!t[i]){var l="function"==typeof require&&require;if(!a&&l)return l(i,!0);if(u)return u(i,!0);var f=new Error("Cannot find module '"+i+"'");throw f.code="MODULE_NOT_FOUND",f}var c=n[i]={exports:{}};t[i][0].call(c.exports,function(e){var n=t[i][1][e];return r(n?n:e)},c,c.exports,e,t,n,o)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i=1){for(var r=1;n>r;++r)t.push(arguments[r]);"function"!=typeof arguments[n]?t.push(arguments[n]):o=arguments[n]}return c["default"].sendAction(this,e,t,o)}}]),t}(l["default"].Component);n["default"]=d,t.exports=n["default"]},{"./agent-impl":6,react:"react"}],2:[function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(n,"__esModule",{value:!0});var r=e("./agent-impl"),u=o(r);n["default"]={componentDidMount:function(){},componentDidUpdate:function(){},request:function(e){var t=[],n=arguments.length-1,o=void 0;if(n>=1){for(var r=1;n>r;++r)t.push(arguments[r]);"function"!=typeof arguments[n]?t.push(arguments[n]):o=arguments[n]}return u["default"].sendAction(this,e,t,o)}},t.exports=n["default"]},{"./agent-impl":6}],3:[function(e,t,n){"use strict";function o(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);to;++o)t+="\nif (tmp"+o+" == null) {\n return undefined;\n}\nvar tmp"+(o+1)+" = tmp"+o+"."+e[o]+";";t+="\nreturn tmp"+n+" && tmp"+n+"."+e[n]+";"}return Function(t)}function i(e){var t="var cb2 = cb && function() { cb(this.stageValue); }.bind(this);";switch(e.length){case 0:t+="\nthis.setState(value, cb2);";break;case 1:t+="\nthis.setState({"+e[0]+": value}, cb2);";break;default:var n=e.length-1;t+="\nvar tmp0 = this.state || {};";for(var o=0;n>o;++o)t+="\nvar tmp"+(o+1)+" = tmp"+o+"."+e[o]+";\nif (tmp"+(o+1)+" == null) {\n tmp"+(o+1)+" = tmp"+o+"."+e[o]+" = {};\n}";t+="\ntmp"+n+"."+e[n]+" = value;\nthis.setState(tmp0, cb2);"}return Function("value","cb",t)}function a(e){e.defaultPrevented||("function"!=typeof this.filterAction||this.filterAction(e))&&(e.stopPropagation(),e.applyTo(this))}Object.defineProperty(n,"__esModule",{value:!0});var l=e("react"),f=o(l),c=e("./SendActionEvent");n["default"]={initialize:function(e,t){if(!e.stageMixinInitialized){var n=r(t),o=u(n);Object.defineProperties(e,{stageMixinInitialized:{value:!0,configurable:!0},stageMixinHandleSendAction:{value:a.bind(e),configurable:!0},stageValue:{get:o,configurable:!0,enumerable:!0},setStageValue:{value:i(n).bind(e),configurable:!0}})}},setupHandler:function(e){var t=f["default"].findDOMNode(e);t.addEventListener(c.EVENT_NAME,e.stageMixinHandleSendAction)},teardownHandler:function(e){var t=f["default"].findDOMNode(e);null!=t&&t.removeEventListener(c.EVENT_NAME,e.stageMixinHandleSendAction)}},t.exports=n["default"]},{"./SendActionEvent":3,react:"react"}],8:[function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(n,"__esModule",{value:!0});var r=e("./AgentComponent"),u=o(r),i=e("./AgentMixin"),a=o(i),l=e("./StageComponent"),f=o(l),c=e("./StageMixin"),d=o(c);n["default"]={AgentComponent:u["default"],AgentMixin:a["default"],StageComponent:f["default"],StageMixin:d["default"]},n.AgentComponent=u["default"],n.AgentMixin=a["default"],n.StageComponent=f["default"],n.StageMixin=d["default"]},{"./AgentComponent":1,"./AgentMixin":2,"./StageComponent":4,"./StageMixin":5}]},{},[8])(8)}); 2 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: "", 4 | frameworks: ["browserify", "mocha"], 5 | files: [ 6 | "node_modules/babel/node_modules/babel-core/browser-polyfill.js", 7 | "test/*.js" 8 | ], 9 | browsers: ["Chrome", "Firefox", "IE"], 10 | 11 | preprocessors: { 12 | "test/*.js": ["browserify"] 13 | }, 14 | browserify: { 15 | debug: true, 16 | transform: [ 17 | "babelify", 18 | ["envify", {"NODE_ENV": "development"}], 19 | "espowerify" 20 | ] 21 | } 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-helix", 3 | "version": "1.0.2", 4 | "description": "A minimal library for Flux-like architecture.", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "dist", 8 | "lib" 9 | ], 10 | "scripts": { 11 | "clean": "rimraf lib dist", 12 | "lint": "eslint src", 13 | "build": "npm-run-all clean lint build:*", 14 | "build:lib": "babel src --out-dir lib --source-maps-inline", 15 | "build:dist": "mkdirp dist && browserify lib/index.js --debug --transform [envify --NODE_ENV=development] --external react --standalone react-helix > dist/react-helix.js", 16 | "build:dist-min": "mkdirp dist && browserify lib/index.js --transform [envify --NODE_ENV=production] --external react --standalone react-helix | uglifyjs - --compress --mangle > dist/react-helix.min.js", 17 | "test": "npm-run-all lint test:karma", 18 | "test:karma": "karma start karma.conf.js --single-run", 19 | "testing": "karma start karma.conf.js --auto-watch --reporters growl,progress" 20 | }, 21 | "peerDependencies": { 22 | "react": "^0.13.3" 23 | }, 24 | "devDependencies": { 25 | "babel": "^5.4.3", 26 | "babelify": "^6.1.1", 27 | "browserify": "^10.2.0", 28 | "envify": "^3.4.0", 29 | "eslint": "0.21.0", 30 | "eslint-plugin-react": "^2.3.0", 31 | "espowerify": "^0.10.0", 32 | "karma": "^0.12.31", 33 | "karma-browserify": "^4.0.0", 34 | "karma-chrome-launcher": "^0.1.12", 35 | "karma-firefox-launcher": "^0.1.6", 36 | "karma-growl-reporter": "^0.1.1", 37 | "karma-ie-launcher": "^0.1.5", 38 | "karma-mocha": "^0.1.10", 39 | "mkdirp": "^0.5.1", 40 | "mocha": "^2.2.5", 41 | "npm-run-all": "^1.2.5", 42 | "power-assert": "^0.11.0", 43 | "rimraf": "^2.3.3", 44 | "uglify-js": "^2.4.21" 45 | }, 46 | "repository": { 47 | "type": "git", 48 | "url": "https://github.com/mysticatea/react-helix.git" 49 | }, 50 | "keywords": [ 51 | "react", 52 | "flux" 53 | ], 54 | "author": "Toru Nagashima", 55 | "license": "MIT", 56 | "bugs": { 57 | "url": "https://github.com/mysticatea/react-helix/issues" 58 | }, 59 | "homepage": "https://github.com/mysticatea/react-helix" 60 | } 61 | -------------------------------------------------------------------------------- /src/AgentComponent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Impl from "./agent-impl"; 3 | 4 | export default class AgentComponent extends React.Component { 5 | componentDidMount() { 6 | if (process.env.NODE_ENV !== "production") { 7 | Impl.validate(this); 8 | } 9 | } 10 | 11 | componentDidUpdate() { 12 | if (process.env.NODE_ENV !== "production") { 13 | Impl.validate(this); 14 | } 15 | } 16 | 17 | request(action /* [, ...args] [, callback] */) { 18 | const args = []; 19 | const lastIndex = arguments.length - 1; 20 | let callback; 21 | if (lastIndex >= 1) { 22 | for (let i = 1; i < lastIndex; ++i) { 23 | args.push(arguments[i]); 24 | } 25 | if (typeof arguments[lastIndex] !== "function") { 26 | args.push(arguments[lastIndex]); 27 | } 28 | else { 29 | callback = arguments[lastIndex]; 30 | } 31 | } 32 | return Impl.sendAction(this, action, args, callback); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/AgentMixin.js: -------------------------------------------------------------------------------- 1 | import Impl from "./agent-impl"; 2 | 3 | export default { 4 | componentDidMount() { 5 | if (process.env.NODE_ENV !== "production") { 6 | Impl.validate(this); 7 | } 8 | }, 9 | 10 | componentDidUpdate() { 11 | if (process.env.NODE_ENV !== "production") { 12 | Impl.validate(this); 13 | } 14 | }, 15 | 16 | request(action /* [, ...args] [, callback] */) { 17 | const args = []; 18 | const lastIndex = arguments.length - 1; 19 | let callback; 20 | if (lastIndex >= 1) { 21 | for (let i = 1; i < lastIndex; ++i) { 22 | args.push(arguments[i]); 23 | } 24 | if (typeof arguments[lastIndex] !== "function") { 25 | args.push(arguments[lastIndex]); 26 | } 27 | else { 28 | callback = arguments[lastIndex]; 29 | } 30 | } 31 | return Impl.sendAction(this, action, args, callback); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/SendActionEvent.js: -------------------------------------------------------------------------------- 1 | // There are several cross-callings in this file. 2 | /*eslint no-use-before-define:[2,"nofunc"]*/ 3 | 4 | function invariant(condition, message) { 5 | if (!condition) { 6 | throw new Error(message); 7 | } 8 | } 9 | 10 | function isFunction(x) { 11 | return typeof x === "function"; 12 | } 13 | 14 | function isThenable(x) { 15 | return typeof x === "object" && 16 | typeof x.then === "function"; 17 | } 18 | 19 | function isGenerator(x) { 20 | return typeof x === "object" && 21 | typeof x.next === "function" && 22 | typeof x.throw === "function"; 23 | } 24 | 25 | function printError(error) { 26 | // This function is used in (process.env.NODE_ENV !== "production"). 27 | if (error != null) { 28 | console.error(error); //eslint-disable-line no-console 29 | } 30 | } 31 | 32 | // A handler that is used if value is not all of undefined, a function, a 33 | // promise, and a generator. 34 | // Just it sets the value to the component's stage value. 35 | function set(component, isInGenerator, value, resolve, reject) { 36 | try { 37 | component.setStageValue(value, resolve); 38 | } 39 | catch (err) { 40 | reject(err); 41 | } 42 | } 43 | 44 | // A handler that is used if value is a function. 45 | // It calls the function together with the component's stage value. 46 | // Then set the result to the component's stage value. 47 | function callAndSet(component, isInGenerator, func, resolve, reject) { 48 | let result; 49 | try { 50 | result = func(component.stageValue); 51 | } 52 | catch (error) { 53 | reject(error); 54 | return; 55 | } 56 | setUnified(component, isInGenerator, result, resolve, reject); 57 | } 58 | 59 | // A handler that is used if value is a promise. 60 | // It waits for the promise become fulfilled. 61 | // Then set the result to the component's stage value. 62 | // But if is while advancing a generator, it doesn't set to the stage value, 63 | // just returns the result. 64 | function waitAndSet(component, isInGenerator, promise, resolve, reject) { 65 | const promise2 = promise.then( 66 | result => { 67 | if (isInGenerator) { 68 | resolve(result); 69 | } 70 | else { 71 | setUnified(component, isInGenerator, result, resolve, reject); 72 | } 73 | }, 74 | reject 75 | ); 76 | 77 | if (process.env.NODE_ENV !== "production") { 78 | promise2.catch(printError); 79 | } 80 | } 81 | 82 | // A handler that is used if value is a generator. 83 | // Process a generator. ping-pong the component's stage value. 84 | function advanceToEnd(component, isInGenerator, generator, resolve, reject) { 85 | onFulfilled(undefined); 86 | 87 | function onFulfilled(stageValue) { 88 | let ret; 89 | try { 90 | ret = generator.next(stageValue); 91 | } 92 | catch (err) { 93 | reject(err); 94 | return; 95 | } 96 | 97 | next(ret); 98 | } 99 | 100 | function onRejected(err) { 101 | let ret; 102 | try { 103 | ret = generator.throw(err); 104 | } 105 | catch (err2) { 106 | reject(err2); 107 | return; 108 | } 109 | 110 | next(ret); 111 | } 112 | 113 | function next(ret) { 114 | if (ret.done) { 115 | setUnified(component, true, ret.value, resolve, reject); 116 | } 117 | else { 118 | setUnified(component, true, ret.value, onFulfilled, onRejected); 119 | } 120 | } 121 | } 122 | 123 | // Check type of the value, and handle the value. 124 | function setUnified(component, isInGenerator, value, resolve, reject) { 125 | if (value === undefined) { 126 | resolve(component.stageValue); 127 | return; 128 | } 129 | 130 | const handle = 131 | isFunction(value) ? callAndSet : 132 | isThenable(value) ? waitAndSet : 133 | isGenerator(value) ? advanceToEnd : 134 | /* otherwise */ set; 135 | 136 | handle(component, isInGenerator, value, resolve, reject); 137 | } 138 | 139 | /** 140 | * The event name for `SendActionEvent`. 141 | * @type {string} 142 | */ 143 | export const EVENT_NAME = "helix-sent-action"; 144 | 145 | /** 146 | * @param action {function} - A function to transform the state. 147 | * @param args {any[]} - Information for action. This value is given to the 148 | * second argument of action. 149 | * @return {SendActionEvent} - The created event object. 150 | */ 151 | export function createSendActionEvent(action, args, callback) { 152 | if (process.env.NODE_ENV !== "production") { 153 | invariant(typeof action === "function", "action should be a function."); 154 | invariant(Array.isArray(args), "args should be an array."); 155 | invariant(callback == null || typeof callback === "function", 156 | "callback should be a function or nothing."); 157 | 158 | if (callback == null) { 159 | callback = printError; 160 | } 161 | } 162 | 163 | let event = document.createEvent("CustomEvent"); 164 | let handled = false; 165 | 166 | event.initCustomEvent(EVENT_NAME, true, true, null); 167 | Object.defineProperties(event, { 168 | action: { 169 | value: action, 170 | configurable: true, 171 | enumerable: true, 172 | writable: true 173 | }, 174 | 175 | arguments: { 176 | value: args, 177 | configurable: true, 178 | enumerable: true, 179 | writable: true 180 | }, 181 | 182 | // This is internal method, called from StageMixin. 183 | applyTo: {value: function applyTo(component) { 184 | if (process.env.NODE_ENV !== "production") { 185 | const get = Object.getOwnPropertyDescriptor(component, "stageValue"); 186 | const set = Object.getOwnPropertyDescriptor(component, "setStageValue"); 187 | invariant(isFunction(get.get), 188 | "component.stageValue should be a getter property."); 189 | invariant(isFunction(set.value), 190 | "component.setStageValue should be a function."); 191 | invariant(handled === false, 192 | `this ${EVENT_NAME} event had been applied already.`); 193 | invariant(isFunction(this.action), 194 | "this.action should be a function."); 195 | invariant(Array.isArray(this.arguments), 196 | "this.arguments should be an array."); 197 | } 198 | handled = true; 199 | 200 | let value; 201 | try { 202 | value = this.action(component.stageValue, ...this.arguments); 203 | } 204 | catch (error) { 205 | if (callback != null) { 206 | callback(error); 207 | } 208 | return; 209 | } 210 | 211 | setUnified( 212 | component, 213 | false, 214 | value, 215 | result => callback && callback(null, result), 216 | callback); 217 | }}, 218 | 219 | // This is internal method, called from AgentMixin. 220 | rejectIfNotHandled: {value: function rejectIfNotHandled() { 221 | if (handled === false) { 222 | handled = true; 223 | if (callback != null) { 224 | callback(new Error("not handled")); 225 | } 226 | } 227 | }} 228 | }); 229 | 230 | return event; 231 | } 232 | -------------------------------------------------------------------------------- /src/StageComponent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Impl from "./stage-impl"; 3 | 4 | export default class StageComponent extends React.Component { 5 | constructor(props, stageValuePath = "") { 6 | super(props); 7 | Impl.initialize(this, stageValuePath); 8 | } 9 | 10 | componentDidMount() { 11 | Impl.setupHandler(this); 12 | } 13 | 14 | componentDidUpdate() { 15 | Impl.setupHandler(this); 16 | } 17 | 18 | componentWillUnmount() { 19 | Impl.teardownHandler(this); 20 | } 21 | 22 | componentWillUpdate() { 23 | Impl.teardownHandler(this); 24 | } 25 | 26 | filterAction(/* event */) { 27 | return true; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/StageMixin.js: -------------------------------------------------------------------------------- 1 | import Impl from "./stage-impl"; 2 | 3 | export default { 4 | componentWillMount() { 5 | Impl.initialize(this, this.stageValuePath || ""); 6 | }, 7 | 8 | componentDidMount() { 9 | Impl.setupHandler(this); 10 | }, 11 | 12 | componentDidUpdate() { 13 | Impl.setupHandler(this); 14 | }, 15 | 16 | componentWillUpdate() { 17 | Impl.teardownHandler(this); 18 | }, 19 | 20 | componentWillUnmount() { 21 | Impl.teardownHandler(this); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/agent-impl.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {createSendActionEvent} from "./SendActionEvent"; 3 | 4 | function invariant(condition, message) { 5 | if (!condition) { 6 | throw new Error(message); 7 | } 8 | } 9 | 10 | export default { 11 | validate(component) { 12 | if (process.env.NODE_ENV !== "production") { 13 | const node = React.findDOMNode(component); 14 | invariant(node != null, "AgentMixin: requires to be rendered."); 15 | } 16 | }, 17 | 18 | sendAction(component, action, args, callback) { 19 | const node = React.findDOMNode(component); 20 | const event = createSendActionEvent(action, args, callback); 21 | 22 | if (process.env.NODE_ENV !== "production") { 23 | invariant(node != null, "AgentMixin: requires to be rendered."); 24 | } 25 | node.dispatchEvent(event); 26 | event.rejectIfNotHandled(); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import AgentComponent from "./AgentComponent"; 2 | import AgentMixin from "./AgentMixin"; 3 | import StageComponent from "./StageComponent"; 4 | import StageMixin from "./StageMixin"; 5 | 6 | export default { 7 | AgentComponent, 8 | AgentMixin, 9 | StageComponent, 10 | StageMixin 11 | }; 12 | 13 | export { 14 | AgentComponent, 15 | AgentMixin, 16 | StageComponent, 17 | StageMixin 18 | }; 19 | -------------------------------------------------------------------------------- /src/stage-impl.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {EVENT_NAME} from "./SendActionEvent"; 3 | 4 | function invariant(condition, message) { 5 | if (!condition) { 6 | throw new Error(message); 7 | } 8 | } 9 | 10 | //------------------------------------------------------------------------------ 11 | function parseStageValuePath(value) { 12 | return (typeof value === "string" ? value.split(".").filter(Boolean) : []); 13 | } 14 | 15 | //------------------------------------------------------------------------------ 16 | function defineGetStageValue(parts) { 17 | let body; 18 | switch (parts.length) { 19 | case 0: 20 | body = "return this.state;"; 21 | break; 22 | 23 | default: 24 | const lastIndex = parts.length - 1; 25 | body = "var tmp0 = this.state;"; 26 | for (let i = 0; i < lastIndex; ++i) { 27 | body += ` 28 | if (tmp${i} == null) { 29 | return undefined; 30 | } 31 | var tmp${i + 1} = tmp${i}.${parts[i]};`; 32 | } 33 | body += ` 34 | return tmp${lastIndex} && tmp${lastIndex}.${parts[lastIndex]};`; 35 | break; 36 | } 37 | 38 | return Function(body); 39 | } 40 | 41 | //------------------------------------------------------------------------------ 42 | function defineSetStageValue(parts) { 43 | let body = `var cb2 = cb && function() { cb(this.stageValue); }.bind(this);`; 44 | 45 | switch (parts.length) { 46 | case 0: 47 | body += "\nthis.setState(value, cb2);"; 48 | break; 49 | 50 | case 1: 51 | body += `\nthis.setState({${parts[0]}: value}, cb2);`; 52 | break; 53 | 54 | default: 55 | const lastIndex = parts.length - 1; 56 | body += `\nvar tmp0 = this.state || {};`; 57 | for (let i = 0; i < lastIndex; ++i) { 58 | body += ` 59 | var tmp${i + 1} = tmp${i}.${parts[i]}; 60 | if (tmp${i + 1} == null) { 61 | tmp${i + 1} = tmp${i}.${parts[i]} = {}; 62 | }`; 63 | } 64 | body += ` 65 | tmp${lastIndex}.${parts[lastIndex]} = value; 66 | this.setState(tmp0, cb2);`; 67 | break; 68 | } 69 | 70 | return Function("value", "cb", body); 71 | } 72 | 73 | //------------------------------------------------------------------------------ 74 | function handleSendAction(event) { 75 | if (event.defaultPrevented) { 76 | return; 77 | } 78 | if (typeof this.filterAction === "function" && !this.filterAction(event)) { 79 | return; 80 | } 81 | event.stopPropagation(); 82 | event.applyTo(this); 83 | } 84 | 85 | //------------------------------------------------------------------------------ 86 | export default { 87 | initialize(component, stageValuePath) { 88 | if (component.stageMixinInitialized) { 89 | return; 90 | } 91 | 92 | const parts = parseStageValuePath(stageValuePath); 93 | const getStageValue = defineGetStageValue(parts); 94 | 95 | if (process.env.NODE_ENV !== "production") { 96 | invariant(stageValuePath == null || typeof stageValuePath === "string", 97 | "StageMixin: stageValuePath should be a string."); 98 | 99 | try { 100 | getStageValue.call(component); 101 | } 102 | catch (cause) { 103 | let err = new Error( 104 | `StageMixin: stageValuePath is invalid (${stageValuePath}).`); 105 | err.cause = cause; 106 | throw err; 107 | } 108 | } 109 | 110 | Object.defineProperties(component, { 111 | stageMixinInitialized: { 112 | value: true, 113 | configurable: true 114 | }, 115 | 116 | stageMixinHandleSendAction: { 117 | value: handleSendAction.bind(component), 118 | configurable: true 119 | }, 120 | 121 | stageValue: { 122 | get: getStageValue, 123 | configurable: true, 124 | enumerable: true 125 | }, 126 | 127 | setStageValue: { 128 | value: defineSetStageValue(parts).bind(component), 129 | configurable: true 130 | } 131 | }); 132 | }, 133 | 134 | setupHandler(component) { 135 | const node = React.findDOMNode(component); 136 | if (process.env.NODE_ENV !== "production") { 137 | invariant(node != null, "StageMixin: requires to be rendered."); 138 | } 139 | node.addEventListener(EVENT_NAME, component.stageMixinHandleSendAction); 140 | }, 141 | 142 | teardownHandler(component) { 143 | const node = React.findDOMNode(component); 144 | if (node != null) { 145 | node.removeEventListener( 146 | EVENT_NAME, 147 | component.stageMixinHandleSendAction); 148 | } 149 | } 150 | }; 151 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "rules": { 6 | "react/no-multi-comp": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/agent.js: -------------------------------------------------------------------------------- 1 | import assert from "power-assert"; 2 | import React from "react"; 3 | import {AgentComponent, AgentMixin} from "../src/index"; 4 | import {EVENT_NAME} from "../src/SendActionEvent"; 5 | 6 | function increaseValue(obj, amount) { 7 | return {value: (obj.value || 0) + amount}; 8 | } 9 | 10 | function doTest(Empty, Simple) { 11 | describe("if not rendered,", () => { 12 | let container; 13 | 14 | beforeEach(() => { 15 | container = document.createElement("div"); 16 | document.body.appendChild(container); 17 | }); 18 | afterEach(() => { 19 | React.unmountComponentAtNode(container); 20 | document.body.removeChild(container); 21 | container = null; 22 | }); 23 | 24 | it("should throw an error.", () => { 25 | assert.throws(() => { 26 | React.render(, container); 27 | }); 28 | }); 29 | }); 30 | 31 | describe("if rendered,", () => { 32 | let container, target; 33 | 34 | beforeEach(() => { 35 | container = document.createElement("div"); 36 | document.body.appendChild(container); 37 | target = React.render(, container); 38 | }); 39 | afterEach(() => { 40 | target = null; 41 | React.unmountComponentAtNode(container); 42 | document.body.removeChild(container); 43 | container = null; 44 | }); 45 | 46 | it("should have \"request\" method.", () => { 47 | assert(typeof target.request === "function"); 48 | }); 49 | 50 | describe("\"request\" should fire an " + EVENT_NAME + " event.", () => { 51 | let event, listener; 52 | 53 | beforeEach(() => { 54 | listener = (e) => { 55 | assert(event == null); 56 | event = e; 57 | }; 58 | addEventListener(EVENT_NAME, listener); 59 | }); 60 | afterEach(() => { 61 | removeEventListener(EVENT_NAME, listener); 62 | event = listener = null; 63 | }); 64 | 65 | it("with no arguments.", () => { 66 | target.request(increaseValue); 67 | 68 | assert(event instanceof Event); 69 | assert(event.action === increaseValue); 70 | assert(event.arguments.length === 0); 71 | }); 72 | 73 | it("with an argument.", () => { 74 | target.request(increaseValue, 777); 75 | 76 | assert(event instanceof Event); 77 | assert(event.action === increaseValue); 78 | assert(event.arguments.length === 1); 79 | assert(event.arguments[0] === 777); 80 | }); 81 | 82 | it("with multiple arguments.", () => { 83 | target.request(increaseValue, 777, 888); 84 | 85 | assert(event instanceof Event); 86 | assert(event.action === increaseValue); 87 | assert(event.arguments.length === 2); 88 | assert(event.arguments[0] === 777); 89 | assert(event.arguments[1] === 888); 90 | }); 91 | 92 | it("with a callback.", () => { 93 | function callback() { 94 | assert(callback.called === false); 95 | callback.called = true; 96 | } 97 | callback.called = false; 98 | 99 | target.request(increaseValue, callback); 100 | 101 | assert(callback.called); 102 | assert(event instanceof Event); 103 | assert(event.action === increaseValue); 104 | assert(event.arguments.length === 0); 105 | }); 106 | 107 | it("with a callback and arguments.", () => { 108 | function callback() { 109 | assert(callback.called === false); 110 | callback.called = true; 111 | } 112 | callback.called = false; 113 | 114 | target.request(increaseValue, 777, callback); 115 | 116 | assert(callback.called); 117 | assert(event instanceof Event); 118 | assert(event.action === increaseValue); 119 | assert(event.arguments.length === 1); 120 | assert(event.arguments[0] === 777); 121 | }); 122 | }); 123 | }); 124 | } 125 | 126 | describe("AgentComponent", () => { 127 | doTest( 128 | class Empty extends AgentComponent { 129 | render() { 130 | return null; 131 | } 132 | }, 133 | class Simple extends AgentComponent { 134 | constructor(props) { 135 | super(props); 136 | } 137 | 138 | render() { 139 | return
Hello!
; 140 | } 141 | } 142 | ); 143 | }); 144 | 145 | describe("AgentMixin", () => { 146 | doTest( 147 | React.createClass({ 148 | displayName: "Empty", 149 | mixins: [AgentMixin], 150 | 151 | render() { 152 | return null; 153 | } 154 | }), 155 | React.createClass({ 156 | displayName: "Simple", 157 | mixins: [AgentMixin], 158 | 159 | render() { 160 | return
Hello!
; 161 | } 162 | }) 163 | ); 164 | }); 165 | -------------------------------------------------------------------------------- /test/stage.js: -------------------------------------------------------------------------------- 1 | import assert from "power-assert"; 2 | import React from "react"; 3 | import {StageComponent, StageMixin} from "../src/index"; 4 | import {EVENT_NAME, createSendActionEvent} from "../src/SendActionEvent"; 5 | 6 | function delay(ms) { 7 | return new Promise(resolve => setTimeout(resolve, ms)); 8 | } 9 | 10 | function increaseValue(obj, amount1, amount2) { 11 | let value = (obj && obj.value) || 0; 12 | if (amount1 !== undefined) { 13 | value += amount1; 14 | } 15 | if (amount2 !== undefined) { 16 | value += amount2; 17 | } 18 | 19 | // special 20 | if (amount1 === undefined && amount2 === undefined) { 21 | value = 777; 22 | } 23 | 24 | return {value}; 25 | } 26 | 27 | function increaseValueLater(_, amount) { 28 | return delay(100).then(() => { 29 | return obj => { 30 | let value = (obj && obj.value) || 0; 31 | value += amount; 32 | return {value}; 33 | }; 34 | }); 35 | } 36 | 37 | function* increaseValue3times(obj, amount) { 38 | obj = yield increaseValue(obj, amount); 39 | obj = yield increaseValue(obj, amount); 40 | yield increaseValue(obj, amount); 41 | } 42 | 43 | function* multiplyValue3timesSlowly(_, k) { 44 | yield delay(100); 45 | yield obj => ({value: obj.value * k}); 46 | yield delay(100); 47 | yield obj => ({value: obj.value * k}); 48 | yield delay(100); 49 | yield obj => ({value: obj.value * k}); 50 | } 51 | 52 | function* promiseInGenerator() { 53 | const threeSevens = yield Promise.resolve(777); 54 | return obj => ({value: obj.value + threeSevens}); 55 | } 56 | 57 | function request(element, action, args, callback) { 58 | const node = React.findDOMNode(element); 59 | const event = createSendActionEvent(action, args, callback); 60 | node.dispatchEvent(event); 61 | event.rejectIfNotHandled(); 62 | } 63 | 64 | function doTest(Empty, Simple, WithValuePath, WithValuePath2) { 65 | describe("if not rendered,", () => { 66 | let container; 67 | 68 | beforeEach(() => { 69 | container = document.createElement("div"); 70 | document.body.appendChild(container); 71 | }); 72 | afterEach(() => { 73 | React.unmountComponentAtNode(container); 74 | document.body.removeChild(container); 75 | container = null; 76 | }); 77 | 78 | it("should throw an error.", () => { 79 | assert.throws(() => { 80 | React.render(, container); 81 | }); 82 | }); 83 | }); 84 | 85 | describe("if rendered,", () => { 86 | let container, target; 87 | 88 | beforeEach(() => { 89 | container = document.createElement("div"); 90 | document.body.appendChild(container); 91 | target = React.render(, container); 92 | }); 93 | afterEach(() => { 94 | target = null; 95 | React.unmountComponentAtNode(container); 96 | document.body.removeChild(container); 97 | container = null; 98 | }); 99 | 100 | it("should have \"stageValue\" getter property.", () => { 101 | const descriptor = Object.getOwnPropertyDescriptor(target, "stageValue"); 102 | assert(typeof descriptor.get === "function"); 103 | }); 104 | 105 | it("should have \"setStageValue\" method.", () => { 106 | assert(typeof target.setStageValue === "function"); 107 | }); 108 | 109 | describe("should handle " + EVENT_NAME + " events.", () => { 110 | it("with no arguments.", done => { 111 | request(target.refs.child, increaseValue, []); 112 | requestAnimationFrame(() => { 113 | assert(target.state.value === 777); 114 | done(); 115 | }); 116 | }); 117 | 118 | it("with an argument.", done => { 119 | request(target.refs.child, increaseValue, [1]); 120 | requestAnimationFrame(() => { 121 | assert(target.state.value === 1); 122 | done(); 123 | }); 124 | }); 125 | 126 | it("with multiple arguments.", done => { 127 | request(target.refs.child, increaseValue, [1, 2]); 128 | requestAnimationFrame(() => { 129 | assert(target.state.value === 3); 130 | done(); 131 | }); 132 | }); 133 | 134 | it("with a callback.", done => { 135 | request(target.refs.child, increaseValue, [], err => { 136 | assert(err === null); 137 | assert(target.state.value === 777); 138 | done(); 139 | }); 140 | }); 141 | 142 | it("with a callback and arguments.", done => { 143 | request(target.refs.child, increaseValue, [1], err => { 144 | assert(err === null); 145 | assert(target.state.value === 1); 146 | done(); 147 | }); 148 | }); 149 | 150 | it("action should be able to return promise.", done => { 151 | request(target.refs.child, increaseValueLater, [3], err => { 152 | assert(err === null); 153 | assert(target.state.value === 10); 154 | done(); 155 | }); 156 | request(target.refs.child, increaseValue, [7]); 157 | }); 158 | 159 | it("action should be able to return generator.", done => { 160 | request(target.refs.child, increaseValue3times, [3], err => { 161 | assert(err === null); 162 | assert(target.state.value === 9); 163 | done(); 164 | }); 165 | }); 166 | 167 | it("action should be able to conbinate promise and generator.", done => { 168 | request(target.refs.child, multiplyValue3timesSlowly, [2], err => { 169 | assert(err === null); 170 | assert(target.state.value === 56); 171 | done(); 172 | }); 173 | request(target.refs.child, increaseValue, [7]); 174 | }); 175 | 176 | it("promises in generators should not set to state.", done => { 177 | request(target.refs.child, promiseInGenerator, [], err => { 178 | assert(err === null); 179 | assert(target.state.value === 780); 180 | done(); 181 | }); 182 | request(target.refs.child, increaseValue, [3]); 183 | }); 184 | }); 185 | }); 186 | 187 | describe("if rendered, if has stage value path,", () => { 188 | let container, target; 189 | 190 | beforeEach(() => { 191 | container = document.createElement("div"); 192 | document.body.appendChild(container); 193 | target = React.render(, container); 194 | }); 195 | afterEach(() => { 196 | target = null; 197 | React.unmountComponentAtNode(container); 198 | document.body.removeChild(container); 199 | container = null; 200 | }); 201 | 202 | it("should handle " + EVENT_NAME + " events.", done => { 203 | request(target.refs.child, increaseValue, [1], () => { 204 | assert(target.state.stage.value === 1); 205 | request(target.refs.child, increaseValue, [2], () => { 206 | assert(target.state.stage.value === 3); 207 | done(); 208 | }); 209 | }); 210 | }); 211 | }); 212 | 213 | describe("if rendered, if has deep stage value path,", () => { 214 | let container, target; 215 | 216 | beforeEach(() => { 217 | container = document.createElement("div"); 218 | document.body.appendChild(container); 219 | target = React.render(, container); 220 | }); 221 | afterEach(() => { 222 | target = null; 223 | React.unmountComponentAtNode(container); 224 | document.body.removeChild(container); 225 | container = null; 226 | }); 227 | 228 | it("should handle " + EVENT_NAME + " events.", done => { 229 | request(target.refs.child, increaseValue, [1], () => { 230 | assert(target.state.s.t.o.r.e.value === 1); 231 | request(target.refs.child, increaseValue, [2], () => { 232 | assert(target.state.s.t.o.r.e.value === 3); 233 | done(); 234 | }); 235 | }); 236 | }); 237 | }); 238 | } 239 | 240 | describe("StageComponent", () => { 241 | doTest( 242 | class Empty extends StageComponent { 243 | render() { 244 | return null; 245 | } 246 | }, 247 | class Simple extends StageComponent { 248 | constructor(props) { 249 | super(props); 250 | this.state = {value: 0}; 251 | } 252 | 253 | render() { 254 | return
Hello!
; 255 | } 256 | }, 257 | class WithValuePath extends StageComponent { 258 | constructor(props) { 259 | super(props, "stage"); 260 | } 261 | 262 | render() { 263 | return
Hello!
; 264 | } 265 | }, 266 | class WithValuePath2 extends StageComponent { 267 | constructor(props) { 268 | super(props, "s.t.o.r.e"); 269 | } 270 | 271 | render() { 272 | return
Hello!
; 273 | } 274 | } 275 | ); 276 | }); 277 | 278 | describe("StageMixin", () => { 279 | doTest( 280 | React.createClass({ 281 | displayName: "Empty", 282 | mixins: [StageMixin], 283 | 284 | render() { 285 | return null; 286 | } 287 | }), 288 | React.createClass({ 289 | displayName: "Simple", 290 | mixins: [StageMixin], 291 | 292 | getInitialState() { 293 | return {value: 0}; 294 | }, 295 | 296 | render() { 297 | return
Hello!
; 298 | } 299 | }), 300 | React.createClass({ 301 | displayName: "WithValuePath", 302 | mixins: [StageMixin], 303 | stageValuePath: "stage", 304 | 305 | render() { 306 | return
Hello!
; 307 | } 308 | }), 309 | React.createClass({ 310 | displayName: "WithValuePath2", 311 | mixins: [StageMixin], 312 | stageValuePath: "s.t.o.r.e", 313 | 314 | render() { 315 | return
Hello!
; 316 | } 317 | }) 318 | ); 319 | }); 320 | --------------------------------------------------------------------------------