New web UI. (#176)
* remove node.js webui * temp commit * flesh out web ui * add documentation * add ray timeline * Small changes to documentation and formatting.
@@ -8,7 +8,7 @@ else
|
||||
base_commit="$TRAVIS_BRANCH"
|
||||
echo "Running clang-format against branch $base_commit, with hash $(git rev-parse $base_commit)"
|
||||
fi
|
||||
output="$(.travis/git-clang-format --binary clang-format-3.8 --commit $base_commit --diff --exclude '(.*thirdparty/|.*redismodule.h)')"
|
||||
output="$(.travis/git-clang-format --binary clang-format-3.8 --commit $base_commit --diff --exclude '(.*thirdparty/|.*redismodule.h|.*webui*)')"
|
||||
if [ "$output" == "no modified files to format" ] || [ "$output" == "clang-format did not modify any files" ] ; then
|
||||
echo "clang-format passed."
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": ["eslint:recommended", "google"],
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"plugins": [
|
||||
"html"
|
||||
],
|
||||
"rules": {
|
||||
"no-var": "off",
|
||||
"new-cap": ["error", { "capIsNewExceptions": ["Polymer"] }]
|
||||
},
|
||||
"globals": {
|
||||
"Polymer": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
* text=auto
|
||||
@@ -0,0 +1,33 @@
|
||||
<!-- Instructions: https://github.com/PolymerElements/polymer-starter-kit/CONTRIBUTING.md#filing-issues -->
|
||||
### Description
|
||||
<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
|
||||
|
||||
### Expected outcome
|
||||
|
||||
<!-- Example: The page stays the same color. -->
|
||||
|
||||
### Actual outcome
|
||||
|
||||
<!-- Example: The page turns pink. -->
|
||||
|
||||
### Live Demo
|
||||
<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
<!-- Example
|
||||
1. Put a `paper-foo` element in the page.
|
||||
2. Open the page in a web browser.
|
||||
3. Click the `paper-foo` element.
|
||||
-->
|
||||
|
||||
### Browsers Affected
|
||||
<!-- Check all that apply -->
|
||||
- [ ] Chrome
|
||||
- [ ] Firefox
|
||||
- [ ] Safari 9
|
||||
- [ ] Safari 8
|
||||
- [ ] Safari 7
|
||||
- [ ] Edge
|
||||
- [ ] IE 11
|
||||
- [ ] IE 10
|
||||
@@ -0,0 +1,3 @@
|
||||
bower_components/
|
||||
build/
|
||||
node_modules/
|
||||
@@ -0,0 +1,19 @@
|
||||
language: node_js
|
||||
sudo: required
|
||||
dist: trusty
|
||||
addons:
|
||||
firefox: latest
|
||||
apt:
|
||||
sources:
|
||||
- google-chrome
|
||||
packages:
|
||||
- google-chrome-stable
|
||||
node_js:
|
||||
- '6'
|
||||
- '5'
|
||||
- '4'
|
||||
before_script:
|
||||
- npm install -g bower polymer-cli
|
||||
- bower install
|
||||
script:
|
||||
- xvfb-run npm test
|
||||
@@ -0,0 +1,86 @@
|
||||
# The Ray Web UI
|
||||
|
||||
This is Ray's Web UI. It consists of two components:
|
||||
|
||||
* The **frontend** is a [Polymer](https://www.polymer-project.org/1.0/) app that
|
||||
uses [google-charts](https://elements.polymer-project.org/elements/google-chart)
|
||||
for visualization.
|
||||
* The **backend** is a Python 3 websocket server (see `backend/ray_ui.py`) that
|
||||
connects to Redis and potentially Ray.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
The Ray Web UI requires Python 3.
|
||||
|
||||
Install [polymer-cli](https://github.com/Polymer/polymer-cli):
|
||||
|
||||
pip install aioredis websockets
|
||||
npm install -g polymer-cli
|
||||
|
||||
### Setup
|
||||
|
||||
The following must be done once.
|
||||
|
||||
cd webui
|
||||
bower install
|
||||
|
||||
### Start the backend
|
||||
|
||||
First start Ray and note down the address of the Redis server. Then run
|
||||
|
||||
cd webui/backend
|
||||
python ray_ui.py --redis-address 127.0.0.1:6379 --port 8888
|
||||
|
||||
where you substitute your Redis address appropriately.
|
||||
|
||||
### Start the frontend development server
|
||||
|
||||
To start the front end, run the following.
|
||||
|
||||
cd webui
|
||||
polymer serve --open
|
||||
|
||||
The web UI can then be accessed at `http://localhost:8080`.
|
||||
|
||||
### Build
|
||||
|
||||
This command performs HTML, CSS, and JS minification on the application
|
||||
dependencies, and generates a service-worker.js file with code to pre-cache the
|
||||
dependencies based on the entrypoint and fragments specified in `polymer.json`.
|
||||
The minified files are output to the `build/unbundled` folder, and are suitable
|
||||
for serving from a HTTP/2+Push compatible server.
|
||||
|
||||
In addition the command also creates a fallback `build/bundled` folder,
|
||||
generated using fragment bundling, suitable for serving from non
|
||||
H2/push-compatible servers or to clients that do not support H2/Push.
|
||||
|
||||
polymer build
|
||||
|
||||
### Preview the build
|
||||
|
||||
This command serves the minified version of the app at `http://localhost:8080`
|
||||
in an unbundled state, as it would be served by a push-compatible server:
|
||||
|
||||
polymer serve build/unbundled
|
||||
|
||||
This command serves the minified version of the app at `http://localhost:8080`
|
||||
generated using fragment bundling:
|
||||
|
||||
polymer serve build/bundled
|
||||
|
||||
### Run tests
|
||||
|
||||
This command will run
|
||||
[Web Component Tester](https://github.com/Polymer/web-component-tester) against
|
||||
the browsers currently installed on your machine.
|
||||
|
||||
polymer test
|
||||
|
||||
### Adding a new view
|
||||
|
||||
You can extend the app by adding more views that will be demand-loaded e.g.
|
||||
based on the route, or to progressively render non-critical sections of the
|
||||
application. Each new demand-loaded fragment should be added to the list of
|
||||
`fragments` in the included `polymer.json` file. This will ensure those
|
||||
components and their dependencies are added to the list of pre-cached components
|
||||
(and will have bundles created in the fallback `bundled` build).
|
||||
@@ -0,0 +1,115 @@
|
||||
import aioredis
|
||||
import argparse
|
||||
import asyncio
|
||||
import binascii
|
||||
from collections import defaultdict
|
||||
import json
|
||||
import websockets
|
||||
|
||||
parser = argparse.ArgumentParser(description="parse information for the web ui")
|
||||
parser.add_argument("--port", type=int, help="port to use for the web ui")
|
||||
parser.add_argument("--redis-address", required=True, type=str, help="the address to use for redis")
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
IDENTIFIER_LENGTH = 20
|
||||
|
||||
def hex_identifier(identifier):
|
||||
return binascii.hexlify(identifier).decode()
|
||||
|
||||
def identifier(hex_identifier):
|
||||
return binascii.unhexlify(hex_identifier)
|
||||
|
||||
def key_to_hex_identifier(key):
|
||||
return hex_identifier(key[(key.index(b":") + 1):(key.index(b":") + IDENTIFIER_LENGTH + 1)])
|
||||
|
||||
def key_to_hex_identifiers(key):
|
||||
# Extract worker_id and task_id from key of the form prefix:worker_id:task_id.
|
||||
offset = key.index(b":") + 1
|
||||
worker_id = hex_identifier(key[offset:(offset + IDENTIFIER_LENGTH)])
|
||||
offset += IDENTIFIER_LENGTH + 1
|
||||
task_id = hex_identifier(key[offset:(offset + IDENTIFIER_LENGTH)])
|
||||
return worker_id, task_id
|
||||
|
||||
|
||||
async def hello(websocket, path):
|
||||
conn = await aioredis.create_connection((redis_host, redis_port), loop=loop)
|
||||
|
||||
while True:
|
||||
command = json.loads(await websocket.recv())
|
||||
print("received command {}".format(command))
|
||||
|
||||
if command["command"] == "get-workers":
|
||||
result = []
|
||||
workers = await conn.execute("keys", "WorkerInfo:*")
|
||||
for key in workers:
|
||||
content = await conn.execute("hgetall", key)
|
||||
worker_id = key_to_hex_identifier(key)
|
||||
result.append({"worker": worker_id, "export_counter": int(content[1])})
|
||||
await websocket.send(json.dumps(result))
|
||||
elif command["command"] == "get-clients":
|
||||
result = []
|
||||
clients = await conn.execute("keys", "CL:*")
|
||||
for key in clients:
|
||||
content = await conn.execute("hgetall", key)
|
||||
result.append({"client": hex_identifier(content[1]),
|
||||
"node_ip_address": content[3].decode(),
|
||||
"client_type": content[5].decode()})
|
||||
await websocket.send(json.dumps(result))
|
||||
elif command["command"] == "get-objects":
|
||||
result = []
|
||||
objects = await conn.execute("keys", "OI:*")
|
||||
for key in objects:
|
||||
content = await conn.execute("hgetall", key)
|
||||
result.append({"object_id": hex_identifier(content[1]),
|
||||
"hash": hex_identifier(content[3]),
|
||||
"data_size": content[5].decode()})
|
||||
await websocket.send(json.dumps(result))
|
||||
elif command["command"] == "get-object-info":
|
||||
# TODO(pcm): Get the object here (have to connect to ray) and ship content
|
||||
# and type back to webclient. One challenge here is that the naive
|
||||
# implementation will block the web ui backend, which is not ok if it is
|
||||
# serving multiple users.
|
||||
await websocket.send(json.dumps({"object_id": "none"}))
|
||||
elif command["command"] == "get-tasks":
|
||||
result = []
|
||||
tasks = await conn.execute("keys", "TT:*")
|
||||
for key in tasks:
|
||||
content = await conn.execute("hgetall", key)
|
||||
result.append({"task_id": key_to_hex_identifier(key),
|
||||
"state": int(content[1]),
|
||||
"node_id": hex_identifier(content[3])})
|
||||
await websocket.send(json.dumps(result))
|
||||
elif command["command"] == "get-timeline":
|
||||
tasks = defaultdict(list)
|
||||
for key in await conn.execute("keys", "event_log:*"):
|
||||
worker_id, task_id = key_to_hex_identifiers(key)
|
||||
content = await conn.execute("lrange", key, "0", "-1")
|
||||
data = json.loads(content[0].decode())
|
||||
begin_and_end_time = [timestamp for (timestamp, task, kind, info) in data if task == "ray:task"]
|
||||
tasks[worker_id].append({"task_id": task_id,
|
||||
"start_task": min(begin_and_end_time),
|
||||
"end_task": max(begin_and_end_time)})
|
||||
await websocket.send(json.dumps(tasks))
|
||||
elif command["command"] == "get-events":
|
||||
result = []
|
||||
for key in await conn.execute("keys", "event_log:*"):
|
||||
worker_id, task_id = key_to_hex_identifiers(key)
|
||||
answer = await conn.execute("lrange", key, "0", "-1")
|
||||
assert len(answer) == 1
|
||||
events = json.loads(answer[0].decode())
|
||||
result.extend([{"worker_id": worker_id,
|
||||
"task_id": task_id,
|
||||
"time": event[0],
|
||||
"type": event[1]} for event in events])
|
||||
await websocket.send(json.dumps(result))
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
redis_address = args.redis_address.split(":")
|
||||
redis_host, redis_port = redis_address[0], int(redis_address[1])
|
||||
|
||||
start_server = websockets.serve(hello, "localhost", args.port)
|
||||
|
||||
loop.run_until_complete(start_server)
|
||||
loop.run_forever()
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "polymer-starter-kit",
|
||||
"authors": [
|
||||
"The Polymer Authors"
|
||||
],
|
||||
"license": "https://polymer.github.io/LICENSE.txt",
|
||||
"dependencies": {
|
||||
"app-layout": "PolymerElements/app-layout#^0.10.0",
|
||||
"app-route": "PolymerElements/app-route#^0.9.0",
|
||||
"iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
|
||||
"iron-icon": "PolymerElements/iron-icon#^1.0.0",
|
||||
"iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
|
||||
"iron-localstorage": "PolymerElements/iron-localstorage#^1.0.0",
|
||||
"iron-media-query": "PolymerElements/iron-media-query#^1.0.0",
|
||||
"iron-pages": "PolymerElements/iron-pages#^1.0.0",
|
||||
"iron-selector": "PolymerElements/iron-selector#^1.0.0",
|
||||
"paper-icon-button": "PolymerElements/paper-icon-button#~1.1.0",
|
||||
"polymer": "Polymer/polymer#^1.6.0",
|
||||
"vaadin-grid": "^1.2.1",
|
||||
"google-chart": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"web-component-tester": "^4.0.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
@@ -1,305 +0,0 @@
|
||||
import React from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import {AutoSizer, InfiniteLoader, List} from 'react-virtualized';
|
||||
import classNames from 'classnames/bind';
|
||||
import io from 'socket.io-client';
|
||||
import scrollbarSize from 'dom-helpers/util/scrollbarSize';
|
||||
|
||||
class RayUI extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
table_one: "object_table",
|
||||
table_two: "task_table",
|
||||
table_three: "failure_table",
|
||||
table_four: "Remote_table",
|
||||
table_one_channel:"object",
|
||||
table_two_channel:"task",
|
||||
table_three_channel:"failure",
|
||||
table_four_channel:"remote",
|
||||
websocket_connection: io()
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<TableView table={this.state.table_one} channel={this.state.table_one_channel} socket={this.state.websocket_connection}/>
|
||||
<TableView table={this.state.table_two} channel={this.state.table_two_channel} socket={this.state.websocket_connection}/>
|
||||
<TableView table={this.state.table_three} channel={this.state.table_three_channel} socket={this.state.websocket_connection}/>
|
||||
<TableView table={this.state.table_four} channel={this.state.table_four_channel} socket={this.state.websocket_connection}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
class TableView extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state= {
|
||||
press: false
|
||||
};
|
||||
this._toggle=this._toggle.bind(this)
|
||||
}
|
||||
|
||||
_toggle(e){
|
||||
this.setState({press: !this.state.press});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this._toggle}>{this.props.table}</button>
|
||||
<TableScroll press={this.state.press} table={this.props.table} channel={this.props.channel} socket={this.props.socket}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
class TableScroll extends React.Component{
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
messages: [],
|
||||
filteredmsg: [],
|
||||
loadedRowsMap: {},
|
||||
content: "This is the view pane.",
|
||||
atEnd: true,
|
||||
currentpos: 0,
|
||||
filter: ""
|
||||
};
|
||||
this._onfilter = this._onfilter.bind(this);
|
||||
this.filterdata = this.filterdata.bind(this);
|
||||
this._isRowLoaded = this._isRowLoaded.bind(this);
|
||||
this._rowRenderer = this._rowRenderer.bind(this);
|
||||
this._loadMoreRows = this._loadMoreRows.bind(this);
|
||||
this.objectselect = this.objectselect.bind(this);
|
||||
this._objectrenderer = this._objectrenderer.bind(this);
|
||||
this.taskselect = this.taskselect.bind(this);
|
||||
this._taskrenderer = this._taskrenderer.bind(this);
|
||||
this.computationselect = this.computationselect.bind(this);
|
||||
this._computationrenderer = this._computationrenderer.bind(this);
|
||||
this.failureselect = this.failureselect.bind(this);
|
||||
this._failurerenderer = this._failurerenderer.bind(this);
|
||||
this.remoteselect = this.remoteselect.bind(this);
|
||||
this._remoterenderer = this._remoterenderer.bind(this);
|
||||
this.scrollcontroller = this.scrollcontroller.bind(this);
|
||||
switch (this.props.channel) {
|
||||
case "object": this.renderfunction = this._objectrenderer;
|
||||
this.header = (<div className={classNames("evenRow", "cell", "centeredCell")}>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Object ID"}</div>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Plasma Store ID"}</div>
|
||||
</div>);
|
||||
break;
|
||||
case "failure": this.renderfunction = this._failurerenderer;
|
||||
this.header = (<div className={classNames("evenRow", "cell", "centeredCell")}>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Failed Function"}</div>
|
||||
</div>);
|
||||
break;
|
||||
case "computation": this.renderfunction = this._computationrenderer;
|
||||
this.header = (<div className={classNames("evenRow", "cell", "centeredCell")}>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Task iid"}</div>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Function_id"}</div>
|
||||
</div>);
|
||||
break;
|
||||
case "task": this.renderfunction = this._taskrenderer;
|
||||
this.header = (<div className={classNames("evenRow", "cell", "centeredCell")}>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Node id"}</div>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Function_id"}</div>
|
||||
</div>);
|
||||
break;
|
||||
case "remote": this.renderfunction = this._remoterenderer;
|
||||
this.header = (<div className={classNames("evenRow", "cell", "centeredCell")}>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Function id"}</div>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Module"}</div>
|
||||
<div className={classNames("evenRow", "cell", "centeredCell")}>{"Function Name"}</div>
|
||||
</div>);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
var self = this;
|
||||
console.log("port" + location.port);
|
||||
this.props.socket.emit('getall', {table:this.props.channel});
|
||||
var arr = [];
|
||||
this.props.socket.on(this.props.channel, function(message) {
|
||||
console.log("got message " + message);
|
||||
var filteredarray = self.state.filteredmsg;
|
||||
message.forEach(function(msg,i){
|
||||
// console.log("Content is " + JSON.stringify(msg));
|
||||
arr.push(msg);
|
||||
if (self.filterdata(msg, self.state.filter)) {filteredarray.push(msg);}
|
||||
});
|
||||
self.setState({messages: arr, filteredmsg: filteredarray});
|
||||
});
|
||||
}
|
||||
filterdata(data, filter) {
|
||||
console.log(data);
|
||||
var self = this;
|
||||
return filter != "" ? Object.values(data).filter(function(data){return data === Object(data) ? self.filterdata(data, filter) : String(data).includes(filter)}).length != 0 : true;
|
||||
}
|
||||
_onfilter(e) {
|
||||
var self = this;
|
||||
this.setState({filteredmsg: e.target.value != "" ? self.state.messages.filter(function(data){return self.filterdata(data, e.target.value)}) : self.state.messages, filter:e.target.value});
|
||||
}
|
||||
_isRowLoaded({ index }) {
|
||||
return !!this.state.loadedRowsMap[index];
|
||||
}
|
||||
objectselect(data, e) {
|
||||
console.log(data);
|
||||
this.setState({content:JSON.stringify(data)})
|
||||
}
|
||||
_objectrenderer(record, key, style) {
|
||||
const className = classNames("evenRow", "cell", "centeredCell");
|
||||
return (
|
||||
<button
|
||||
onClick={this.objectselect.bind(null, record)}
|
||||
className={classNames("evenRow", "cell", "rowbutton", "centeredCell")}
|
||||
key={key}
|
||||
style={style}
|
||||
>
|
||||
<div className={className}>{record.ObjectId}</div>
|
||||
<div className={className}>{record.PlasmaStoreId}</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
}
|
||||
failureselect(data, e) {
|
||||
console.log(data);
|
||||
this.setState({content:data.error})
|
||||
}
|
||||
_failurerenderer(record, key, style) {
|
||||
const className = classNames("evenRow", "cell", "centeredCell");
|
||||
return (
|
||||
<button
|
||||
onClick={this.failureselect.bind(null, record)}
|
||||
className={classNames("evenRow", "cell", "rowbutton", "centeredCell")}
|
||||
key={key}
|
||||
style={style}
|
||||
>
|
||||
<div className={className}>{record.functionname}</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
}
|
||||
computationselect(data, e) {
|
||||
console.log(data);
|
||||
this.setState({content: JSON.stringify});
|
||||
}
|
||||
|
||||
_computationrenderer(record, key, style) {
|
||||
const className = classNames("evenRow", "cell", "centeredCell");
|
||||
return (
|
||||
<button
|
||||
onClick={this.computationselect.bind(null, record)}
|
||||
className={classNames("evenRow", "cell", "rowbutton", "centeredCell")}
|
||||
key={key}
|
||||
style={style}
|
||||
>
|
||||
<div className={className}>{record.task_iid.toString().substring(0,8)}</div>
|
||||
<div className={className}>{record.function_id.toString().substring(0,8)}</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
taskselect(data, e) {
|
||||
console.log(data);
|
||||
this.setState({content: JSON.stringify(data)});
|
||||
}
|
||||
|
||||
_taskrenderer(record, key, style) {
|
||||
const className = classNames("evenRow", "cell", "centeredCell");
|
||||
return (
|
||||
<button
|
||||
onClick={this.taskselect.bind(null, record)}
|
||||
className={classNames("evenRow", "cell", "rowbutton", "centeredCell")}
|
||||
key={key}
|
||||
style={style}
|
||||
>
|
||||
<div className={className}>{record.node_id}</div>
|
||||
<div className={className}>{record.function_id.toString().substring(0,8)}</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
}
|
||||
remoteselect(data, e) {
|
||||
console.log(data);
|
||||
this.setState({content: JSON.stringify(data)});
|
||||
}
|
||||
|
||||
_remoterenderer(record, key, style) {
|
||||
const className = classNames("evenRow", "cell", "centeredCell");
|
||||
return (
|
||||
<button
|
||||
onClick={this.remoteselect.bind(null, record)}
|
||||
className={classNames("evenRow", "cell", "rowbutton", "centeredCell")}
|
||||
key={key}
|
||||
style={style}
|
||||
>
|
||||
<div className={className}>{record.function_id}</div>
|
||||
<div className={className}>{record.module}</div>
|
||||
<div className={className}>{record.name}</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
_rowRenderer({ index, key, style, isScrolling}){
|
||||
const record = this.state.filteredmsg[index];
|
||||
return this.renderfunction(record, key, style);
|
||||
}
|
||||
|
||||
_loadMoreRows({ startIndex, stopIndex }) {
|
||||
for (var i = startIndex; i <= stopIndex; i++) {
|
||||
this.state.loadedRowsMap[i] = 1;
|
||||
}
|
||||
let promiseResolver;
|
||||
return new Promise(resolve => {
|
||||
promiseResolver = resolve;
|
||||
})
|
||||
}
|
||||
|
||||
scrollcontroller({clientHeight, scrollHeight, scrollTop}){
|
||||
this.setState({atEnd:scrollTop >= scrollHeight- clientHeight, currentpos: Math.floor(scrollTop/20)-1});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.press) {
|
||||
var style = {display:'none'}
|
||||
}
|
||||
var self = this;
|
||||
return (
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
<div style={style}>
|
||||
<input type="text" onChange={this._onfilter} />
|
||||
<div style={{width:width-2*scrollbarSize(), height:20}}>{this.header}</div>
|
||||
<InfiniteLoader
|
||||
isRowLoaded={this._isRowLoaded}
|
||||
loadMoreRows={this._loadMoreRows}
|
||||
rowCount={this.state.filteredmsg.length}
|
||||
style={{display: "inline-block"}}>
|
||||
{({ onRowsRendered, registerChild }) => (
|
||||
<List
|
||||
ref={registerChild}
|
||||
rowRenderer={this._rowRenderer}
|
||||
onRowsRendered={onRowsRendered}
|
||||
className={"BodyGrid"}
|
||||
width={width}
|
||||
height={300}
|
||||
onScroll={this.scrollcontroller}
|
||||
overscanRowCount={20}
|
||||
scrollToIndex={this.state.atEnd ? this.state.filteredmsg.length-1: this.currentpos}
|
||||
scrollToAlignment={"end"}
|
||||
rowCount={this.state.filteredmsg.length}
|
||||
rowHeight={20}
|
||||
/>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
<textarea style={{width:width, height:300, display: "inline-block"}} value={this.state.content}></textarea>
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);}
|
||||
}
|
||||
render(<RayUI/>, document.getElementById('mount-point'));
|
||||
@@ -1,10 +0,0 @@
|
||||
<head>
|
||||
<title>Ray UI</title>
|
||||
</head>
|
||||
|
||||
<link rel=stylesheet type=text/css href="/static/rayui.css">
|
||||
<body>
|
||||
<div id="mount-point"></div>
|
||||
<script src="/public/bundle.js" type="text/javascript"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
.GridContainer {
|
||||
margin-top: 15px;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.GridRow {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.GridColumn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.LeftSideGridContainer {
|
||||
flex: 0 0 50px;
|
||||
}
|
||||
|
||||
.BodyGrid {
|
||||
width: 100%;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.evenRow,
|
||||
.oddRow {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
.oddRow {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.cell,
|
||||
.headerCell {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 0 .5em;
|
||||
}
|
||||
.cell {
|
||||
border-right: 1px solid #e0e0e0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: white;
|
||||
}
|
||||
.rowbutton:hover {
|
||||
background-color: blue;
|
||||
}
|
||||
.rowbutton:hover *{
|
||||
background-color: gold;
|
||||
}
|
||||
.headerCell {
|
||||
font-weight: bold;
|
||||
border-right: 1px solid #e0e0e0;
|
||||
}
|
||||
.centeredCell {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.table {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.letterCell {
|
||||
font-size: 1.5em;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.noCells {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1em;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,115 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="Polymer Starter Kit">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
|
||||
|
||||
<title>Ray UI</title>
|
||||
<meta name="description" content="A web UI for Ray">
|
||||
|
||||
<link rel="icon" href="/images/favicon.ico">
|
||||
|
||||
<!-- See https://goo.gl/OOhYW5 -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<!-- See https://goo.gl/qRE0vM -->
|
||||
<meta name="theme-color" content="#3f51b5">
|
||||
|
||||
<!-- Add to homescreen for Chrome on Android. Fallback for manifest.json -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="application-name" content="Ray UI">
|
||||
|
||||
<!-- Add to homescreen for Safari on iOS -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Ray UI">
|
||||
|
||||
<!-- Homescreen icons -->
|
||||
<link rel="apple-touch-icon" href="/images/manifest/icon-48x48.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/images/manifest/icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="96x96" href="/images/manifest/icon-96x96.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/images/manifest/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/images/manifest/icon-192x192.png">
|
||||
|
||||
<!-- Tile icon for Windows 8 (144x144 + tile color) -->
|
||||
<meta name="msapplication-TileImage" content="/images/manifest/icon-144x144.png">
|
||||
<meta name="msapplication-TileColor" content="#3f51b5">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
|
||||
<script>
|
||||
// Setup Polymer options
|
||||
window.Polymer = {
|
||||
dom: 'shadow',
|
||||
lazyRegister: true,
|
||||
};
|
||||
|
||||
// Load webcomponentsjs polyfill if browser does not support native
|
||||
// Web Components
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var onload = function() {
|
||||
// For native Imports, manually fire WebComponentsReady so user code
|
||||
// can use the same code path for native and polyfill'd imports.
|
||||
if (!window.HTMLImports) {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent('WebComponentsReady', {bubbles: true})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var webComponentsSupported = (
|
||||
'registerElement' in document
|
||||
&& 'import' in document.createElement('link')
|
||||
&& 'content' in document.createElement('template')
|
||||
);
|
||||
|
||||
if (!webComponentsSupported) {
|
||||
var script = document.createElement('script');
|
||||
script.async = true;
|
||||
script.src = '/bower_components/webcomponentsjs/webcomponents-lite.min.js';
|
||||
script.onload = onload;
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
onload();
|
||||
}
|
||||
})();
|
||||
|
||||
// Load pre-caching Service Worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function() {
|
||||
navigator.serviceWorker.register('/service-worker.js');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<link rel="import" href="/src/ray-app.html">
|
||||
<link rel="import" href="/bower_components/google-chart/google-chart.html">
|
||||
<link rel="import" href="/bower_components/vaadin-grid/vaadin-grid.html">
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Roboto', 'Noto', sans-serif;
|
||||
line-height: 1.5;
|
||||
min-height: 100vh;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<ray-app></ray-app>
|
||||
<!-- Built with love using Polymer Starter Kit -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,112 +0,0 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var http = require('http').Server(app);
|
||||
var io = require('socket.io')(http);
|
||||
var redis = require("redis");
|
||||
var task = require('./task.js');
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
var port = process.argv[2];
|
||||
var sub = redis.createClient(port, {return_buffers: true});
|
||||
var db = redis.createClient(port, {return_buffers: true});
|
||||
} else {
|
||||
var sub = redis.createClient({return_buffers: true});
|
||||
var db = redis.createClient({return_buffers: true});
|
||||
}
|
||||
const assert = require('assert');
|
||||
|
||||
app.use(express.static(__dirname + '/client'));
|
||||
app.get('/', function(req, res) {
|
||||
res.sendFile(__dirname + '/client/index.html');
|
||||
});
|
||||
|
||||
sub.config("SET", "notify-keyspace-events", "AKE");
|
||||
sub.psubscribe("task_log:*");
|
||||
sub.psubscribe("__keyspace@0__:obj:*");
|
||||
sub.psubscribe("__keyspace@0__:Failures*");
|
||||
sub.psubscribe("__keyspace@0__:RemoteFunction*");
|
||||
io.on('connection', function(socket) {
|
||||
console.log('a user connected');
|
||||
socket.on('disconnect', function() { console.log('user disconnected'); });
|
||||
sub.on('psubscribe', function(channel, count) { console.log("Subscribed"); });
|
||||
});
|
||||
|
||||
backlogobject = [];
|
||||
backlogtask = [];
|
||||
backlogfailures = [];
|
||||
backlogremotefunction = [];
|
||||
var failureindex;
|
||||
db.llen("Failures", function(err, result) { failureindex = result; });
|
||||
sub.on('pmessage', function(pattern, channel, message) {
|
||||
if (channel.toString().split(":")[0] === "__keyspace@0__") {
|
||||
console.log(channel.toString());
|
||||
switch (channel.toString().split(":")[1]) {
|
||||
case "Failures":
|
||||
db.lindex("Failures", failureindex++, function(err, result) {
|
||||
backlogfailures.push({
|
||||
"functionname": result.toString().split(" ")[2].slice(5, -5),
|
||||
"error": result.toString()
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "obj":
|
||||
db.smembers(channel.slice(15), function(err, result) {
|
||||
console.log(result);
|
||||
backlogobject.push({
|
||||
"ObjectId": channel.slice(19).toString('hex'),
|
||||
"PlasmaStoreId": result[0].toString()
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "RemoteFunction":
|
||||
db.hgetall(channel.slice(15), function(err, result) {
|
||||
backlogremotefunction.push({
|
||||
"function_id": result.function_id.toString('hex'),
|
||||
"module": result.module.toString(),
|
||||
"name": result.name.toString()
|
||||
});
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log(channel.toString());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
backlogtask.push(task.parse_task_instance(message));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
setInterval(function() {
|
||||
if (backlogfailures.length > 0) {
|
||||
console.log("Sending ", backlogfailures.length, " objects on failure");
|
||||
console.log(backlogfailures);
|
||||
io.sockets.emit('failure', backlogfailures);
|
||||
}
|
||||
backlogfailures = [];
|
||||
}, 30);
|
||||
setInterval(function() {
|
||||
if (backlogobject.length > 0) {
|
||||
console.log("Sending ", backlogobject.length, " objects on object");
|
||||
console.log(backlogobject);
|
||||
io.sockets.emit('object', backlogobject);
|
||||
}
|
||||
backlogobject = [];
|
||||
}, 30);
|
||||
setInterval(function() {
|
||||
if (backlogtask.length > 0) {
|
||||
console.log("Sending ", backlogtask.length, " objects on task");
|
||||
io.sockets.emit('task', backlogtask);
|
||||
}
|
||||
backlogtask = [];
|
||||
}, 30);
|
||||
setInterval(function() {
|
||||
if (backlogremotefunction.length > 0) {
|
||||
console.log("Sending ", backlogremotefunction.length, " objects on remote");
|
||||
console.log(backlogremotefunction);
|
||||
io.sockets.emit('remote', backlogremotefunction);
|
||||
}
|
||||
backlogremotefunction = [];
|
||||
}, 30);
|
||||
http.listen(3000, function() { console.log('listening on *:3000'); });
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "My App",
|
||||
"short_name": "My App",
|
||||
"start_url": "/?homescreen=1",
|
||||
"display": "standalone",
|
||||
"theme_color": "#3f51b5",
|
||||
"background_color": "#3f51b5",
|
||||
"icons": [
|
||||
{
|
||||
"src": "images/manifest/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/manifest/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,32 +1,13 @@
|
||||
{
|
||||
"name": "RayWebUI",
|
||||
"version": "0.0.1",
|
||||
"description": "A Web UI for Ray",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/ray-project/ray.git"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"babel-core": "~6.17.0",
|
||||
"babel-loader": "~6.2.5",
|
||||
"babel-preset-es2015": "~6.16.0",
|
||||
"babel-preset-react": "~6.16.0",
|
||||
"classnames": "~2.2.5",
|
||||
"express": "^4.10.2",
|
||||
"jbinary": "^2.1.3",
|
||||
"react": "~15.3.2",
|
||||
"react-addons-shallow-compare": "~15.3.2",
|
||||
"react-dom": "~15.3.2",
|
||||
"react-virtualized": "~8.0.12",
|
||||
"redis": "^2.6.2",
|
||||
"socket.io": "^1.5.0",
|
||||
"socket.io-client": "~1.5.0",
|
||||
"webpack": "~1.13.2",
|
||||
"dom-helpers": "~3.0.0",
|
||||
"jsesc": "~2.2.0"
|
||||
},
|
||||
"name": "polymer-starter-kit",
|
||||
"license": "BSD-3-Clause",
|
||||
"devDependencies": {
|
||||
"webpack": "~1.13.3"
|
||||
"eslint": "^3.12.0",
|
||||
"eslint-config-google": "^0.7.1",
|
||||
"eslint-plugin-html": "^1.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext js,html; exit 0;",
|
||||
"test": "npm run lint && polymer test"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"entrypoint": "index.html",
|
||||
"shell": "src/my-app.html",
|
||||
"fragments": [
|
||||
"src/my-overview.html",
|
||||
"src/my-objects.html",
|
||||
"src/my-view3.html",
|
||||
"src/my-view404.html"
|
||||
],
|
||||
"sourceGlobs": [
|
||||
"src/**/*",
|
||||
"images/**/*",
|
||||
"bower.json"
|
||||
],
|
||||
"includeDependencies": [
|
||||
"manifest.json",
|
||||
"bower_components/webcomponentsjs/webcomponents-lite.min.js"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/* eslint no-console: ["error", { allow: ["info"] }] */
|
||||
|
||||
console.info(
|
||||
'Service worker disabled for development, will be generated at build time.'
|
||||
);
|
||||
@@ -0,0 +1,142 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../bower_components/app-layout/app-drawer/app-drawer.html">
|
||||
<link rel="import" href="../bower_components/app-layout/app-drawer-layout/app-drawer-layout.html">
|
||||
<link rel="import" href="../bower_components/app-layout/app-header/app-header.html">
|
||||
<link rel="import" href="../bower_components/app-layout/app-header-layout/app-header-layout.html">
|
||||
<link rel="import" href="../bower_components/app-layout/app-scroll-effects/app-scroll-effects.html">
|
||||
<link rel="import" href="../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
||||
<link rel="import" href="../bower_components/app-route/app-location.html">
|
||||
<link rel="import" href="../bower_components/app-route/app-route.html">
|
||||
<link rel="import" href="../bower_components/iron-pages/iron-pages.html">
|
||||
<link rel="import" href="../bower_components/iron-selector/iron-selector.html">
|
||||
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
|
||||
<link rel="import" href="ray-icons.html">
|
||||
|
||||
<dom-module id="ray-app">
|
||||
<template>
|
||||
<style>
|
||||
:host {
|
||||
--app-primary-color: #4285f4;
|
||||
--app-secondary-color: black;
|
||||
|
||||
display: block;
|
||||
}
|
||||
|
||||
app-header {
|
||||
color: #fff;
|
||||
background-color: var(--app-primary-color);
|
||||
}
|
||||
app-header paper-icon-button {
|
||||
--paper-icon-button-ink-color: white;
|
||||
}
|
||||
|
||||
.drawer-list {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.drawer-list a {
|
||||
display: block;
|
||||
padding: 0 16px;
|
||||
text-decoration: none;
|
||||
color: var(--app-secondary-color);
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.drawer-list a.iron-selected {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-location route="{{route}}"></app-location>
|
||||
<app-route
|
||||
route="{{route}}"
|
||||
pattern="/:page"
|
||||
data="{{routeData}}"
|
||||
tail="{{subroute}}"></app-route>
|
||||
|
||||
<app-drawer-layout fullbleed>
|
||||
<!-- Drawer content -->
|
||||
<app-drawer id="drawer">
|
||||
<app-toolbar>Menu</app-toolbar>
|
||||
<iron-selector selected="[[page]]" attr-for-selected="name" class="drawer-list" role="navigation">
|
||||
<a name="overview" href="/overview">Overview</a>
|
||||
<a name="objects" href="/objects">Objects</a>
|
||||
<a name="tasks" href="/tasks">Tasks</a>
|
||||
<a name="events" href="/events">Events</a>
|
||||
<a name="timeline" href="/timeline">Timeline</a>
|
||||
</iron-selector>
|
||||
</app-drawer>
|
||||
|
||||
<!-- Main content -->
|
||||
<app-header-layout has-scrolling-region>
|
||||
|
||||
<app-header condenses reveals effects="waterfall">
|
||||
<app-toolbar>
|
||||
<paper-icon-button icon="ray-icons:menu" drawer-toggle></paper-icon-button>
|
||||
<div main-title>Ray UI</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<iron-pages
|
||||
selected="[[page]]"
|
||||
attr-for-selected="name"
|
||||
fallback-selection="view404"
|
||||
role="main">
|
||||
<ray-overview name="overview"></ray-overview>
|
||||
<ray-objects name="objects"></ray-objects>
|
||||
<ray-tasks name="tasks"></ray-tasks>
|
||||
<ray-events name="events"></ray-events>
|
||||
<ray-timeline name="timeline"></ray-timeline>
|
||||
<ray-view404 name="view404"></ray-view404>
|
||||
</iron-pages>
|
||||
</app-header-layout>
|
||||
</app-drawer-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ray-app',
|
||||
|
||||
properties: {
|
||||
page: {
|
||||
type: String,
|
||||
reflectToAttribute: true,
|
||||
observer: '_pageChanged',
|
||||
},
|
||||
},
|
||||
|
||||
observers: [
|
||||
'_routePageChanged(routeData.page)',
|
||||
],
|
||||
|
||||
_routePageChanged: function(page) {
|
||||
this.page = page || 'overview';
|
||||
|
||||
if (!this.$.drawer.persistent) {
|
||||
this.$.drawer.close();
|
||||
}
|
||||
},
|
||||
|
||||
_pageChanged: function(page) {
|
||||
// Load page import on demand. Show 404 page if fails
|
||||
var resolvedPageUrl = this.resolveUrl('ray-' + page + '.html');
|
||||
this.importHref(resolvedPageUrl, null, this._showPage404, true);
|
||||
},
|
||||
|
||||
_showPage404: function() {
|
||||
this.page = 'view404';
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,57 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="shared-styles.html">
|
||||
|
||||
<dom-module id="ray-events">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="card">
|
||||
<h1>Events</h1>
|
||||
<vaadin-grid id="events">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col name="worker_id" sortable="" sort-direction="desc"/>
|
||||
<col name="task_id" sortable="" sort-direction="desc"/>
|
||||
<col name="time" sortable="" sort-direction="desc"/>
|
||||
<col name="type" sortable="" sort-direction="desc"/>
|
||||
</colgroup>
|
||||
</table>
|
||||
</vaadin-grid>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var backend_address = "ws://127.0.0.1:8888";
|
||||
Polymer({
|
||||
is: 'ray-events',
|
||||
ready: function() {
|
||||
var eventSocket = new WebSocket(backend_address);
|
||||
var events = Polymer.dom(this.root).querySelector("#events");
|
||||
|
||||
eventSocket.onopen = function() {
|
||||
eventSocket.send(JSON.stringify({"command": "get-events"}));
|
||||
}
|
||||
eventSocket.onmessage = function(answer) {
|
||||
console.dir(answer.data);
|
||||
events.items = JSON.parse(answer.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/iron-icon/iron-icon.html">
|
||||
<link rel="import" href="../bower_components/iron-iconset-svg/iron-iconset-svg.html">
|
||||
|
||||
<iron-iconset-svg name="my-icons" size="24">
|
||||
<svg>
|
||||
<defs>
|
||||
<g id="arrow-back">
|
||||
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
|
||||
</g>
|
||||
<g id="menu">
|
||||
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />
|
||||
</g>
|
||||
<g id="chevron-right">
|
||||
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
||||
</g>
|
||||
<g id="close">
|
||||
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
|
||||
</g>
|
||||
</defs>
|
||||
</svg>
|
||||
</iron-iconset-svg>
|
||||
@@ -0,0 +1,67 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="shared-styles.html">
|
||||
|
||||
<dom-module id="ray-objects">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="card">
|
||||
<h1>Objects</h1>
|
||||
<vaadin-grid id="objects">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col name="object_id" sortable="" sort-direction="desc"/>
|
||||
<col name="data_size" sortable="" sort-direction="desc"/>
|
||||
<col name="hash" sortable="" sort-direction="desc"/>
|
||||
</colgroup>
|
||||
</table>
|
||||
</vaadin-grid>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var backend_address = "ws://127.0.0.1:8888";
|
||||
Polymer({
|
||||
is: 'ray-objects',
|
||||
ready: function() {
|
||||
var objectSocket = new WebSocket(backend_address);
|
||||
var objectInfoSocket = new WebSocket(backend_address);
|
||||
var objects = Polymer.dom(this.root).querySelector("#objects");
|
||||
|
||||
objectSocket.onopen = function() {
|
||||
objectSocket.send(JSON.stringify({"command": "get-objects"}));
|
||||
}
|
||||
objectSocket.onmessage = function(answer) {
|
||||
objects.items = JSON.parse(answer.data);
|
||||
}
|
||||
|
||||
objects.addEventListener('selected-items-changed', function() {
|
||||
var index = this.selection.selected();
|
||||
if (index != undefined && this.items != undefined && this.items[index] != undefined) {
|
||||
var id = this.items[index]["object_id"];
|
||||
objectInfoSocket.send(JSON.stringify({"command": "get-object-info", "object_id": id}));
|
||||
objectInfoSocket.onmessage = function(answer) {
|
||||
console.log(answer.data);
|
||||
}
|
||||
}
|
||||
}.bind(objects));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,75 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="shared-styles.html">
|
||||
|
||||
<dom-module id="ray-overview">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="card">
|
||||
<h1>Overview</h1>
|
||||
<h2>Clients</h2>
|
||||
<vaadin-grid id="clients">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col name="client_type" sortable="" sort-direction="desc"/>
|
||||
<col name="client" sortable="" sort-direction="desc"/>
|
||||
<col name="node_ip_address" sortable="" sort-direction="desc"/>
|
||||
</colgroup>
|
||||
</table>
|
||||
</vaadin-grid>
|
||||
<h2>Workers</h2>
|
||||
<vaadin-grid id="workers">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col name="worker" sortable="" sort-direction="desc"/>
|
||||
<col name="export_counter" sortable="" sort-direction="desc"/>
|
||||
</colgroup>
|
||||
</table>
|
||||
</vaadin-grid>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var backend_address = "ws://127.0.0.1:8888";
|
||||
Polymer({
|
||||
is: 'ray-overview',
|
||||
ready: function() {
|
||||
var workerSocket = new WebSocket(backend_address);
|
||||
var workers = Polymer.dom(this.root).querySelector("#workers");
|
||||
workerSocket.onopen = function() {
|
||||
workerSocket.send(JSON.stringify({"command": "get-workers"}));
|
||||
}
|
||||
workerSocket.onmessage = function(answer) {
|
||||
workers.items = JSON.parse(answer.data);
|
||||
}
|
||||
|
||||
var clientSocket = new WebSocket(backend_address);
|
||||
var clients = Polymer.dom(this.root).querySelector("#clients");
|
||||
clientSocket.onopen = function() {
|
||||
clientSocket.send(JSON.stringify({"command": "get-clients"}));
|
||||
}
|
||||
clientSocket.onmessage = function(answer) {
|
||||
var result = JSON.parse(answer.data);
|
||||
console.dir(result);
|
||||
clients.items = result;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,55 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="shared-styles.html">
|
||||
|
||||
<dom-module id="ray-tasks">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="card">
|
||||
<h1>Tasks</h1>
|
||||
<vaadin-grid id="tasks">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col name="task_id" sortable="" sort-direction="desc"/>
|
||||
<col name="state" sortable="" sort-direction="desc"/>
|
||||
<col name="node_id" sortable="" sort-direction="desc"/>
|
||||
</colgroup>
|
||||
</table>
|
||||
</vaadin-grid>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var backend_address = "ws://127.0.0.1:8888";
|
||||
Polymer({
|
||||
is: 'ray-tasks',
|
||||
ready: function() {
|
||||
var taskSocket = new WebSocket(backend_address);
|
||||
var tasks = Polymer.dom(this.root).querySelector("#tasks");
|
||||
|
||||
taskSocket.onopen = function() {
|
||||
taskSocket.send(JSON.stringify({"command": "get-tasks"}));
|
||||
}
|
||||
taskSocket.onmessage = function(answer) {
|
||||
tasks.items = JSON.parse(answer.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,81 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="shared-styles.html">
|
||||
|
||||
<dom-module id="ray-timeline">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
google-chart {
|
||||
height: 600px;
|
||||
width: 95%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="card">
|
||||
<h1>Ray Timeline</h1>
|
||||
<figure id="timeline-container"></figure>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var redis_address = "ws://127.0.0.1:8888";
|
||||
|
||||
Polymer({
|
||||
is: 'ray-timeline',
|
||||
ready: function() {
|
||||
var loader = document.createElement('google-chart-loader');
|
||||
var timelineContainer = Polymer.dom(this.root).querySelector('figure#timeline-container');
|
||||
var timelineChart = document.createElement('google-chart');
|
||||
timelineChart.setAttribute('type', 'timeline');
|
||||
timelineChart.options = {
|
||||
height: 400,
|
||||
gantt: {
|
||||
trackHeight: 30
|
||||
}
|
||||
};
|
||||
timelineContainer.appendChild(timelineChart);
|
||||
|
||||
loader.dataTable().then(function(table) {
|
||||
table.addColumn({ type: 'string', id: 'Worker' });
|
||||
table.addColumn({ type: 'string', id: 'Task'});
|
||||
table.addColumn({ type: 'date', id: 'Start' });
|
||||
table.addColumn({ type: 'date', id: 'End' });
|
||||
|
||||
var socket = new WebSocket(redis_address);
|
||||
|
||||
socket.onopen = function() {
|
||||
socket.send(JSON.stringify({"command": "get-timeline"}));
|
||||
}
|
||||
|
||||
socket.onmessage = function(messageEvent) {
|
||||
var tasks = JSON.parse(messageEvent.data);
|
||||
for (var key in tasks) {
|
||||
for (var index in tasks[key]) {
|
||||
var task = tasks[key][index];
|
||||
var d1 = new Date(task["start_task"] * 1000 * 1000);
|
||||
var d2 = new Date(task["end_task"] * 1000 * 1000);
|
||||
table.addRows([[key, task["task_id"], d1, d2]]);
|
||||
}
|
||||
}
|
||||
console.dir(table);
|
||||
timelineChart.data = table;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
|
||||
<dom-module id="ray-view404">
|
||||
<template>
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
|
||||
padding: 10px 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
If deploying in a folder replace href="/" with the full path to your site.
|
||||
Such as: href=="http://polymerelements.github.io/polymer-starter-kit"
|
||||
-->
|
||||
Oops you hit a 404. <a href="/">Head back to home.</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'ray-view404',
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
|
||||
<!-- shared styles for all views -->
|
||||
<dom-module id="shared-styles">
|
||||
<template>
|
||||
<style>
|
||||
.card {
|
||||
margin: 24px;
|
||||
padding: 16px;
|
||||
color: #757575;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.circle {
|
||||
display: inline-block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
text-align: center;
|
||||
color: #555;
|
||||
border-radius: 50%;
|
||||
background: #ddd;
|
||||
font-size: 30px;
|
||||
line-height: 64px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 16px 0;
|
||||
color: #212121;
|
||||
font-size: 22px;
|
||||
}
|
||||
</style>
|
||||
</template>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = {
|
||||
staticFileGlobs: [
|
||||
'/index.html',
|
||||
'/manifest.json',
|
||||
'/bower_components/webcomponentsjs/webcomponents-lite.min.js',
|
||||
],
|
||||
navigateFallback: 'index.html',
|
||||
};
|
||||
@@ -1,68 +0,0 @@
|
||||
var jb = require('jbinary');
|
||||
var stream = require('stream');
|
||||
|
||||
var task_argument = {
|
||||
'jBinary.littleEndian': true,
|
||||
is_ref: ['enum', 'int8', [true, false]],
|
||||
padding: ['array', 'int8', 7],
|
||||
reference: [
|
||||
'if', 'is_ref',
|
||||
{object_id: ['array', 'uint8', 20], padding: ['array', 'int8', 4]}
|
||||
],
|
||||
value: [
|
||||
'if_not', 'is_ref',
|
||||
{offset: 'int64', length: 'int64', padding: ['array', 'int8', 8]}
|
||||
]
|
||||
};
|
||||
|
||||
var task_spec_header = {
|
||||
'jBinary.littleEndian': true,
|
||||
function_id: ['array', 'uint8', 20],
|
||||
padding: ['array', 'uint8', 4],
|
||||
num_args: ['int64'],
|
||||
arg_index: ['int64'],
|
||||
num_returns: ['int64'],
|
||||
args_value_size: ['int64'],
|
||||
args_value_offset: ['int64'],
|
||||
arguments: ['array', task_argument, 'num_args']
|
||||
};
|
||||
|
||||
var task_instance = {
|
||||
'jBinary.littleEndian': true,
|
||||
task_iid: ['array', 'uint8', 20],
|
||||
state: 'int32',
|
||||
node_id: ['array', 'uint8', 20],
|
||||
padding: ['array', 'uint8', 4],
|
||||
task_spec_header: ['object', task_spec_header],
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Convert string or byte buffer of an object ID to hex string.
|
||||
function id_to_hex(id) {
|
||||
return new Buffer(id).toString('hex');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parse_task_instance: function(buffer) {
|
||||
var binary = new jb(buffer, task_instance);
|
||||
binary.read('padding');
|
||||
var task_spec = binary.read('task_spec_header');
|
||||
var arguments = [];
|
||||
for (var i = 0; i < task_spec['arguments'].length; i++) {
|
||||
var arg = task_spec['arguments'][i];
|
||||
if (arg['is_ref']) {
|
||||
console.log(arg['reference']['object_id']);
|
||||
arguments.push(id_to_hex(arg['reference']['object_id']));
|
||||
} else {
|
||||
arguments.push("value");
|
||||
}
|
||||
}
|
||||
var state = binary.read('state');
|
||||
var node_id = binary.read('node_id');
|
||||
return {
|
||||
state: state, node_id: id_to_hex(node_id),
|
||||
function_id: id_to_hex(task_spec['function_id']), arguments: arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"env": {
|
||||
"mocha": true
|
||||
},
|
||||
"globals": {
|
||||
"assert": false,
|
||||
"fixture": false,
|
||||
"WCT": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
|
||||
|
||||
<title>Tests</title>
|
||||
|
||||
<script src="../bower_components/web-component-tester/browser.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
WCT.loadSuites([
|
||||
'my-view1.html?dom=shady',
|
||||
'my-view1.html?dom=shadow',
|
||||
]);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,47 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
|
||||
|
||||
<title>my-view1</title>
|
||||
|
||||
<script src="../bower_components/webcomponentsjs/webcomponents-lite.js"></script>
|
||||
<script src="../bower_components/web-component-tester/browser.js"></script>
|
||||
|
||||
<!-- Import the element to test -->
|
||||
<link rel="import" href="../src/my-view1.html">
|
||||
</head>
|
||||
<body>
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<my-view1></my-view1>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('my-view1 tests', function() {
|
||||
var home;
|
||||
|
||||
setup(function() {
|
||||
home = fixture('basic');
|
||||
});
|
||||
|
||||
test('Number in circle should be 1', function() {
|
||||
var circle = Polymer.dom(home.root).querySelector('.circle');
|
||||
assert.equal(circle.textContent, '1');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
var webpack = require('webpack');
|
||||
var path = require('path');
|
||||
|
||||
var BUILD_DIR = path.resolve(__dirname, 'client/public');
|
||||
var APP_DIR = path.resolve(__dirname, 'client/app');
|
||||
var config = {
|
||||
entry: APP_DIR + '/index.jsx',
|
||||
output: {path: BUILD_DIR, filename: 'bundle.js'},
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.jsx?/,
|
||||
include: APP_DIR,
|
||||
loader: 'babel',
|
||||
query: {presets: ['react']}
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||