<?php
// vim: ts=2 sw=2
set_include_path(get_include_path() . PATH_SEPARATOR . '../../app');
include_once "base_controller.php";
class DataServiceConfig extends BaseController {
protected function signinRequired() {
return true;
}
protected function adminRequired() {
return true;
}
public function doAjaxGet() {
$action = 'load_config';
if (isset($_GET['action']))
$action = $_GET['action'];
$response = array();
if ($action == "status")
$this->handleServiceStatusQuery();
else if ($action == 'load_config')
$this->handleDataMappingQuery();
else
$this->renderAjaxError($response, "invalid action: " . $action);
}
public function doAjaxPost() {
$response = array();
$action = "save_config";
if (isset($_POST['action']))
$action = $_POST['action'];
if ($action == "save_config") {
$curPath = dirname(__FILE__);
$config_data = $this->getInputData('config_data', false);
$filename = build_file_path($curPath, "data_mapping_config.json");
file_put_contents($filename, $config_data);
$mapping_data = $this->getInputData('mapping_data', false);
$filename = build_file_path($curPath, "data_mapping.json");
if (file_put_contents($filename, $mapping_data)) {
execCmd('ln -sf '.$filename.' /etc/mqtt-service.json')
$pfName = platformName();
if ($pfName == "FS") {
// if monit service exists, then use it; otherwise use init script
`[ -e /usr/bin/monit ] && /usr/bin/monit restart mqtt-service || /etc/init.d/S91mqtt-service restart`
} else if ($pfName == "FW") {
`[ -e /usr/bin/monit ] && /usr/bin/monit restart mqtt-service || /etc/init.d/mqtt-service restart`
} else {
$this->renderAjaxError($response, "current platform is not supported");
}
$this->renderAjaxSuccess($response);
}
else
$this->renderAjaxError($response, "can not write config data to file: " . $filename);
} else
$this->renderAjaxError($response, "invalid action: " . $action);
}
protected function handleServiceStatusQuery() {
$response = array();
$pfName = platformName();
if ($pfName == 'FS')
$cmd = "/bin/sh /etc/init.d/S91mqtt-service status";
else if ($pfName == 'FW')
$cmd = "/bin/sh -c 'source /etc/init.d/mqtt-service && service_status'";
else
$this->renderAjaxError($response, "platform is not supported");
$output = array();
$return = '';
$last_line = exec($cmd, $output, $return)
if ($return != '0')
$response['service_status'] = 'unknown';
else {
if (preg_match('/mqtt-service dead but pid file exists/', $last_line)
|| preg_match('/mqtt-service is stopped/', $last_line))
$response['service_status'] = 'stopped';
else if (preg_match('/mqtt-service \(pid \d+\) is running.../', $last_line))
$response['service_status'] = 'started';
else
$response['service_status'] = 'error';
}
$this->renderAjaxSuccess($response);
}
protected function handleDataMappingQuery() {
$response = array();
$response['data'] = array();
$response['data']['certs_dir'] = build_file_path(dirname(__FILE__), "uploads", "certs");
$filename = "data_mapping.json";
if (file_exists($filename))
$response['data']['mapping_data'] = file_get_contents($filename);
$filename = "data_mapping_config.json";
if (file_exists($filename))
$response['data']['config_data'] = file_get_contents($filename);
$this->renderAjaxSuccess2($response);
}
}
$controller = new DataServiceConfig();
$controller->run();
?>
<!DOCTYPE html>
<html>
<head>
<eta charset="utf-8" />
<title>Data Service Config</title>
<eta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../css/bootstrap.min.css" media="screen" />
<link rel="stylesheet" href="../../css/dropzone.css" media="screen" />
<link rel="stylesheet" href="../../js/jstree/themes/default/style.min.css" media="screen" />
<link rel="stylesheet" href="css/data_service_config.css" media="screen" />
</head>
<body>
<div class="container" id="data_service_config_content">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="/">EasyIO</a>
<ul class="nav">
<li class="divider-vertical"></li>
<li v-for="sec in sections" :class="{active:isSectionActive(sec)}"><a href="#" @click="active_sec_id=sec.sec_id">{{ sec.name }}</a></li>
</ul>
<service-status id="service_status" />
<!-- <button class="btn btn-primary pull-right">New Profile</button> -->
</div>
</div>
<!-- <h3 class='text-center'>Data Service Config</h3> -->
<div class="row">
<alert></alert>
<template v-for="sec in sections">
<component :is="sec.sec_id" class="tab-pane" :class="{hide: active_sec_id != sec.sec_id}" :ref="sec.sec_id"></component>
</template>
<button type="button" class="btn btn-primary btn-large pull-right" @click="saveConfig">Save</button>
</div>
<file-selector ref="file_selector_inst" server_url="file_manager.php" ></file-selector>
</div>
<!-- component templates -->
<script type="text/x-template" id="alert-template">
<div class="alert" :class="label_class" v-show="show">
<button type="button" class="close" @click="show=false">×</button>
<strong>{{ title }}</strong> {{ msg }}
</div>
</script>
<script type="text/x-template" id="gcp_iot_core_template">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label"><strong>GCP IoT Core</strong></label>
</div>
<div class="control-group" :class="{error: name_error.length > 0}">
<label class="control-label" for="inputName">Broker Name:</label>
<div class="controls">
<input type="text" id="inputName" placeholder="Broker Name" v-model.trim="name">
<span class="help-inline" v-show="name_error.length > 0">{{ name_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: project_id_error.length > 0}">
<label class="control-label" for="inputProjectID">Project ID:</label>
<div class="controls">
<input type="text" id="inputProjectID" placeholder="Project ID" v-model.trim="config.project_id">
<span class="help-inline" v-show="project_id_error.length > 0">{{ project_id_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: registry_id_error.length > 0}">
<label class="control-label" for="inputRegistryID">Registry ID:</label>
<div class="controls">
<input type="text" id="inputRegistryID" placeholder="Registry ID" v-model.trim="config.registry_id">
<span class="help-inline" v-show="registry_id_error.length > 0">{{ registry_id_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: device_id_error.length > 0}">
<label class="control-label" for="inputDeviceID">Device ID:</label>
<div class="controls">
<input type="text" id="inputDeviceID" placeholder="Device ID" v-model.trim="config.device_id">
<span class="help-inline" v-show="device_id_error.length > 0">{{ device_id_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: region_error.length > 0}">
<label class="control-label" for="inputRegion">Region:</label>
<div class="controls">
<select id="inputRegion" v-model="config.region">
<option value="us-central1">us-central1</option>
<option value="europe-west1">europe-west1</option>
<option value="asia-east1">asia-east1</option>
</select>
<span class="help-inline" v-show="region_error.length > 0">{{ region_error }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label"><strong>SSL/TLS</strong></label>
</div>
<div class="control-group" :class="{error: ca_file_error.length > 0}">
<label class="control-label" for="inputCAFile">CA File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputCAFile" placeholder="CA File (click to change)" v-model="config.ca_file" @click="changeCAFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.ca_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="ca_file_error.length > 0">{{ ca_file_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: cert_file_error.length > 0}">
<label class="control-label" for="inputCertFile">Certificate File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputCertFile" placeholder="Client Certificate File (click to change)" v-model="config.cert_file" @click="changeClientCertFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.cert_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="cert_file_error.length > 0">{{ cert_file_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: key_file_error.length > 0}">
<label class="control-label" for="inputKeyFile">Key File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputKeyFile" placeholder="Key File (click to change)" v-model="config.key_file" @click="changeKeyFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.key_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="key_file_error.length > 0">{{ key_file_error }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label"><strong>Publish Interval</strong></label>
</div>
<div class="control-group" :class="{error: events_interval_error.length > 0}">
<label class="control-label" for="inputEventInterval">Publish Interval:</label>
<div class="controls">
<input type="number" id="inputEventInterval" placeholder="How often to publish data" v-model.number="config.events_interval">
<span class="help-inline" v-show="events_interval_error.length > 0">{{ events_interval_error }}</span>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-success" @click.prevent="onSaveLocal">Ok</button>
<button class="btn" @click.prevent="onReset">Reset</button>
</div>
</div>
</form>
</script>
<script type="text/x-template" id="mqtt-template">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label"><strong>General MQTT</strong></label>
</div>
<div class="control-group" :class="{error: name_error.length > 0}">
<label class="control-label" for="inputName">Broker Name:</label>
<div class="controls">
<input type="text" id="inputName" placeholder="Broker Name" v-model.trim="name">
<span class="help-inline" v-show="name_error.length > 0">{{ name_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: host_error.length > 0}">
<label class="control-label" for="inputHost">Host:</label>
<div class="controls">
<input type="text" id="inputHost" placeholder="MQTT Broker Host" v-model.trim="config.mqtt_host">
<span class="help-inline" v-show="host_error.length > 0">{{ host_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: port_error.length > 0}">
<label class="control-label" for="inputPort">Port:</label>
<div class="controls">
<input type="text" id="inputPort" placeholder="MQTT Broker Port" v-model.number="config.mqtt_port">
<span class="help-inline" v-show="port_error.length > 0">{{ port_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: client_id_error.length > 0}">
<label class="control-label" for="inputClientId">Client ID:</label>
<div class="controls">
<input type="text" id="inputClientId" placeholder="Client ID" v-model.trim="config.mqtt_client_id">
<span class="help-inline" v-show="client_id_error.length > 0">{{ client_id_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: user_error.length > 0}">
<label class="control-label" for="inputUserName">User:</label>
<div class="controls">
<input type="text" id="inputUserName" placeholder="User" v-model.trim="config.mqtt_user" autocomplete="nope">
<span class="help-inline" v-show="user_error.length > 0">{{ user_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: passwd_error.length > 0}">
<label class="control-label" for="inputPassword">Password:</label>
<div class="controls">
<input id="inputPassword" type="password" placeholder="Password" v-model.trim="config.mqtt_passwd" autocomplete="nope">
<span class="help-inline" v-show="passwd_error.length > 0">{{ passwd_error }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputQoS">QoS:</label>
<div class="controls">
<select id="inputQoS" v-model="config.mqtt_qos">
<option value="0">QoS0</option>
<option value="1">QoS1</option>
<option value="2">QoS2</option>
</select>
</div>
</div>
<div class="control-group">
<label class="control-label"><strong>SSL/TLS</strong></label>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" id="inputEnableTls" v-model="tls_enabled">Enable TLS
</label>
</div>
</div>
<div class="control-group" :class="{error: ca_file_error.length > 0}">
<label class="control-label" for="inputCAFile">CA File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputCAFile" placeholder="CA File (click to change)" v-model="config.ca_file" @click="changeCAFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.ca_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="ca_file_error.length">Invalid CA File</span>
</div>
</div>
<div class="control-group" :class="{error: cert_file_error.length > 0}">
<label class="control-label" for="inputCertFile">Certificate File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputCertFile" placeholder="Client Certificate File (click to change)" v-model="config.cert_file" @click="changeClientCertFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.cert_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="cert_file_error.length > 0">{{ cert_file_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: key_file_error.length > 0}">
<label class="control-label" for="inputKeyFile">Key File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputKeyFile" placeholder="Key File (click to change)" v-model="config.key_file" @click="changeKeyFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.key_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="key_file_error.length">{{ key_file_error }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label"><strong>Publish Interval</strong></label>
</div>
<div class="control-group" :class="{error: events_interval_error.length > 0}">
<label class="control-label" for="inputEventInterval">Publish Interval:</label>
<div class="controls">
<input type="number" id="inputEventInterval" placeholder="How often to publish data" v-model.number="config.events_interval">
<span class="help-inline" v-show="events_interval_error.length">{{ events_interval_error }}</span>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-success" @click.prevent="onSaveLocal">Ok</button>
<button class="btn" @click.prevent="onReset">Reset</button>
</div>
</div>
</form>
</script>
<script type="text/x-template" id="aws_iot_core_template">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label"><strong>AWS IoT Core</strong></label>
</div>
<div class="control-group" :class="{error: name_error.length > 0}">
<label class="control-label" for="inputName">Broker Name:</label>
<div class="controls">
<input type="text" id="inputName" placeholder="Broker Name" v-model.trim="name">
<span class="help-inline" v-show="name_error.length > 0">{{ name_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: host_error.length > 0}">
<label class="control-label" for="inputHost">Endpoint:</label>
<div class="controls">
<input type="text" id="inputHost" placeholder="MQTT Endpoint" v-model.trim="config.mqtt_host">
<span class="help-inline" v-show="host_error.length > 0">{{ host_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: client_id_error.length > 0}">
<label class="control-label" for="inputClientId">Client ID:</label>
<div class="controls">
<input type="text" id="inputClientId" placeholder="Client ID" v-model.trim="config.mqtt_client_id">
<span class="help-inline" v-show="client_id_error.length > 0">{{ client_id_error }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputQoS">QoS:</label>
<div class="controls">
<select id="inputQoS" v-model="config.mqtt_qos">
<option value="0">QoS0</option>
<option value="1">QoS1</option>
</select>
</div>
</div>
<div class="control-group">
<label class="control-label"><strong>SSL/TLS</strong></label>
</div>
<div class="control-group" :class="{error: ca_file_error.length > 0}">
<label class="control-label" for="inputCAFile">CA File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputCAFile" placeholder="CA File (click to change)" v-model="config.ca_file" @click="changeCAFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.ca_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="ca_file_error.length > 0">{{ ca_file_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: cert_file_error.length > 0}">
<label class="control-label" for="inputCertFile">Certificate File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputCertFile" placeholder="Client Certificate File (click to change)" v-model="config.cert_file" @click="changeClientCertFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.cert_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="cert_file_error.length > 0">{{ cert_file_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: key_file_error.length > 0}">
<label class="control-label" for="inputKeyFile">Key File:</label>
<div class="controls">
<div class="input-append">
<input type="text" id="inputKeyFile" placeholder="Key File (click to change)" v-model="config.key_file" @click="changeKeyFile" readonly class='default-cursor span11'>
<button class="btn btn-link" type="button" @click="config.key_file=''"><i class="icon-remove"></i></button>
</div>
<span class="help-inline" v-show="key_file_error.length > 0">{{ key_file_error }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label"><strong>Publish Interval</strong></label>
</div>
<div class="control-group" :class="{error: events_interval_error.length > 0}">
<label class="control-label" for="inputEventInterval">Publish Interval:</label>
<div class="controls">
<input type="number" id="inputEventInterval" placeholder="How often to publish data" v-model.number="config.events_interval">
<span class="help-inline" v-show="events_interval_error.length > 0">{{ events_interval_error }}</span>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-success" @click.prevent="onSaveLocal">Ok</button>
<button class="btn" @click.prevent="onReset">Reset</button>
</div>
</div>
</form>
</script>
<script type="text/x-template" id="file_selector_template">
<div id="file_selector_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="file_selector_modal_label" aria-hidden="true" >
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="file_selector_modal_label">{{ title }}</h3>
</div>
<div class="modal-body">
<div id="file_upload_panel" style="overflow:scroll;">
<div class="dropzone needsclick dz-clickable">
<div class="dz-message needsclick">
Drag and Drop file here or click to upload.
</div>
</div>
<div class="dropzone dropzone-previews">
<div class="dz-message">{{ previewMsg }}</div>
<div class="table table-striped files dz-clickable">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" @click="saveFilePath">Ok</button>
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
</div>
</script>
<script type="text/x-template" id="file_preview_template">
<div class="file-row">
<!-- This is used as the file preview template -->
<div>
<span class="preview"><img data-dz-thumbnail /></span>
</div>
<div>
<p class="name" data-dz-name></p>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
<div>
<p class="size" data-dz-size></p>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="bar bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</div>
<div>
<!--
<button class="btn btn-primary start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start</span>
</button>
<button data-dz-remove class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
-->
<button data-dz-remove class="btn btn-danger delete">
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
</div>
</div>
</script>
<script type="text/x-template" id="device_data_panel_template">
<div id="deviceDataPanel" class='well well-small'>
<h4 class="center">Device Data Tree</h4>
<div id="deviceDataTree" class="row scrollable-content"></div>
<p class="tree-btns">
<button class="btn btn-mini" type="button" @click="addObjectToSchema">Add</button>
</p>
</div>
</script>
<script type="text/x-template" id="data_schema_panel_template">
<div class='well well-small'>
<h4 class="center">Cloud Data Tree</h4>
<div id="dataSchemaTree" class="row scrollable-content"></div>
<p class="tree-btns">
<button class="btn btn-mini" type="button" @click="newNode">New</button>
<button class="btn btn-mini" type="button" @click="newTopicTree">New Topic Tree</button>
<button class="btn btn-mini btn-info" type="button" @click="editNode">Edit</button>
<button class="btn btn-mini btn-danger" type="button" @click="removeNode">Delete</button>
</p>
<odal-dialog :title="newTopicTreeMode ? 'New Topic Tree' : 'Edit Topic'" v-on:submitChange="newTopicTreeMode ? createTopicTreeNode() : changeTopicTreeNode()">
<div v-if="availableTopicNames.length > 0" class="control-group">
<label class="control-label">Topic:</label>
<div class="controls">
<select v-model="selectedTopicName">
<option v-for="topicName in availableTopicNames" :value="topicName">{{topicName}}</option>
<!-- <option value="NoMQTTTopic">NoMQTTTopic</option> -->
</select>
</div>
</div>
<div v-else class="alert alert-block">
There is no topics available, please create more topics from TopicManager first.
</div>
</modal-dialog>
</div>
</script>
<script type="text/x-template" id="property_list_panel_template">
<div class="comp-data-box">
<button type="button" class="btn btn-fullwidth" data-toggle="collapse" :data-target="'#' + objDataUniqueDomID(objData.path)" @click="boxOpened = !boxOpened"><i :class="boxBtnClass"></i> {{ objData.path }}</button>
<div :id="objDataUniqueDomID(objData.path)" class="collapse in">
<table class="table table-striped table-hover table-condensed">
<thead>
<tr>
<th></th>
<th>Label</th>
<th>Value</th>
<th class="operation-box">Operations</th>
</tr>
</thead>
<tbody>
<tr v-for='(slot, index) in objData.slots'>
<td @click='slot.enabled = !slot.enabled'><input type='checkbox' v-model='slot.enabled'></td>
<td><input type='text' class='input-small' v-model='slot.label' :disabled="!slot.enabled"></td>
<td><input type='text' class='input-small' v-model='slot.value' :disabled="!slot.enabled"></td>
<td class="operation-box">
<button class="btn btn-mini btn-danger btn-link" type="button" @click="objData.slots.splice(index, 1)">Delete</button>
</td>
</tr>
</tbody>
</table>
<div class="comp-btn-group">
<button class="btn btn-mini btn-success" id="newPropertyBtn" type="button" @click="addProperty">New</button>
<button class="btn btn-mini" type="button" @click="selectAll">Select All</button>
<button class="btn btn-mini btn-inverse" type="button" @click="inverseSelect">Inverse Select</button>
<button class="btn btn-mini btn-warning" type="button" @click="selectNone">Select None</button>
<button class="btn btn-mini btn-danger pull-right" type="button" @click="deleteSelectedSlot">Del Selected</button>
<button id="delCompBtn" class="btn btn-mini btn-danger pull-right" type="button" @click="$emit('remove')">Del Comp</button>
</div>
</div>
</div>
</script>
<script type="text/x-template" id="node_schema_panel_template">
<div class='well well-small'>
<h4 class="center">Cloud Tree Node Schema</h4>
<div class="row scrollable-content">
<table class="table table-striped table-hover table-condensed">
<thead>
<tr>
<th></th>
<th>Label</th>
<th>Binding</th>
</tr>
</thead>
<tbody>
<template v-for='objData in objDataList'>
<tr v-for='(slotData, index) in objData.slots' v-show="slotData.visible">
<td><input type='radio' :value="slotData.uid" v-model='selectedSlotUID'></td>
<td><input type='text' class='input-small' v-model='slotData.label'
:disabled="selectedSlotUID!=slotData.uid" :class="{error: errorSlotUID == slotData.uid}"></td>
<td><a v-if="commonBindingPath.length > 0" href="#" data-toggle="tooltip" :title="bindingTooltip(slotData)">...</a>/{{ shortBindingPath(slotData) }}</td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="row" id="nodeBtnSection">
<!--
<button class="btn btn-mini" type="button" @click="selectAll">Select All</button>
<button class="btn btn-mini btn-inverse" type="button" @click="inverseSelect">Inverse Select</button>
<button class="btn btn-mini btn-warning" type="button" @click="selectNone">Select None</button>
<button class="btn btn-mini btn-danger pull-right" type="button" @click="deleteSelectedSlot">Del Selected</button>
-->
<transition name="fade" mode="out-in">
<button class="btn btn-mini" type="button" :key="editMode" @click="saveNodeSchema">{{ editMode ? 'Update' : 'Add' }}</button>
</transition>
</div>
</div>
</script>
<script type="text/x-template" id="name_replacement_preprocessor_template">
<div>
<form class="form-inline">
<label>When slot's name is :</label>
<input type='text' class='input-small' v-model='payload.old_name' :disabled="!enabled"></td>
<label> then rename it to :</label>
<input type='text' class='input-small' v-model='payload.new_name' :disabled="!enabled"></td>
<a v-if="tooltip && tooltip.length > 0" v-once class="pull-right" href="#" data-toggle="tooltip" data-placement="top" :title="tooltip"><i class="icon-question-sign"></i></a>
</form>
</div>
</script>
<script type="text/x-template" id="preselect_preprocessor_template">
<div>
<form class="form-inline">
<label>Preselect slot when its name match :</label>
<input type='text' class='input-small' :disabled="!enabled"></td>
<a v-if="tooltip && tooltip.length > 0" v-once class="pull-right" href="#" data-toggle="tooltip" data-placement="top" :title="tooltip"><i class="icon-question-sign"></i></a>
</form>
</div>
</script>
<script type="text/x-template" id="schema_preview_panel_template">
<div class='row' >
<div class="alert alert-info">
<button type='button' class='close' data-dismiss='alert'>x</button>
<strong>Note:</strong> all property values (number, boolean, string etc) are fake, only for demo purpose.
</div>
<schema-preview-entry v-for="(schema_json, index) in schema_jsons" :key='index' :topic='schema_json[0]' :json='schema_json[1]'></schema-preview-entry>
</div>
</script>
<script type="text/x-template" id="preprocessor_list_template">
<div class='row scrollable-content' >
<div id='preprocessorBtnGroup' class='btn-group dropdown pull-right'>
<a class='btn dropdown-toggle' data-toggle='dropdown' href='#'>
Add New Preprocessor
<span class='caret'></span>
</a>
<ul class="dropdown-menu">
<li v-for="preprocessor in type_list">
<a href="#" @click.prevent="addPreprocessorElem(preprocessor)"><i :class="preprocessor.icon"></i> {{ preprocessor.label }}</a>
</li>
</ul>
</div>
<table class="table table-striped table-hover">
<thead>
<tr>
<th></th>
<th></th>
<th>Preprocessor</th>
<th class="operation-box">Operation</th>
</tr>
</thead>
<tbody>
<tr v-for='(inst, index) in instance_list'>
<td @click='inst.enabled = !inst.enabled'>
<input type='checkbox' v-model='inst.enabled'>
</td>
<td> <i :class="iconFromComp(inst.component)"></i> </td>
<td>
<component :is="inst.component" ref="preprocessor" :payload="inst.payload" :enabled="inst.enabled"></component>
</td>
<td class="operation-box">
<button class="btn btn-mini btn-link" type="button" @click="instance_list.push(_.clone(inst))">Clone</button>
<button class="btn btn-mini btn-danger btn-link" type="button" @click="instance_list.splice(index, 1)">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</script>
<script type="text/x-template" id="data_mapping_template">
<div class='row tabbable'>
<ul class="nav nav-tabs">
<li v-for="tab in tabs" v-once :class="{ active : tab.id == active_tab_id }"><a :href="'#' + tab.id" :data-tab-id='tab.id' data-toggle='tab' @click='onShowTab'>{{ tab.label }}</a></li>
</ul>
<div class='row tab-content'>
<div :id='tabs[0].id' class='row tab-pane' :class="{ active: tabs[0].id == active_tab_id }" >
<data-schema-panel id="dataSchemaPanel" ref="dataSchemaPanel" class="split"
v-on:nodeChanged="onNodeChanged">
</data-schema-panel>
<node-schema-panel id="nodeSchemaPanel" ref="nodeSchemaPanel" class="split"
v-on:nodeSchemaUpdated="onNodeSchemaUpdated" v-on:objectAdded="onObjectAdded" >
</node-schema-panel>
<device-data-panel id="deviceDataPanel" ref="deviceDataPanel" class="split" v-on:objectsToSchema="addObjectsToSchema" ></device-data-panel>
</div>
<schema-preview-panel :id='tabs[1].id' ref="schemaPreviewPanel" class='tab-pane' :class="{ active: tabs[1].id == active_tab_id }" :schema_jsons="previewJsons"></schema-preview-panel>
<preprocess-list-panel :id='tabs[2].id' ref="preprocessorListPanel" class='tab-pane' :class="{ active: tabs[2].id == active_tab_id }"></preprocess-list-panel>
</div>
</div>
</script>
<script type="text/x-template" id="broker_manager_template">
<div class="row-fluid" v-cloak>
<div id="broker_list" class="span6">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<sortable-table-header :sortField="sortField" fieldName="name" @sortBy="sortBy"></sortable-table-header>
<sortable-table-header :sortField="sortField" fieldName="type" @sortBy="sortBy"></sortable-table-header>
</tr>
</thead>
<tbody>
<tr v-for="broker in brokerList" @click="activeBroker=broker" :class="{info:activeBroker && activeBroker.uniqueKey==broker.uniqueKey}">
<td>{{broker.name}}</td>
<td>{{broker.type | typeLabel}}</td>
</tr>
</tbody>
</table>
<div class="pull-right">
<button class="btn" type="button" @click="addBroker">Add</button>
<button class="btn" type="button" @click="cloneBroker" :disabled="activeBroker==null">Clone</button>
<button class="btn btn-danger" type="button" @click="deleteBroker" :disabled="activeBroker==null">Delete</button>
</div>
</div>
<div id="broker_detail" class="well span6">
<keep-alive>
<component v-if="activeBroker != null" :is="activeBroker.type" :key="activeBroker.uniqueKey"
:brokerName="activeBroker.name" :configData="activeBroker.config" :nameError="nameError"
@brokerUpdated="onBrokerUpdated" ></component>
</keep-alive>
</div>
<odal-dialog title="New Broker" :error="newBroker.error.length != 0" v-on:submitChange="createBroker">
<div class="control-group" :class="{error: !isNewNameValid(newBroker.name)}">
<label class="control-label" for="inputBrokerName">Name:</label>
<div class="controls">
<input type="text" id="inputBrokerName" placeholder="Broker's Name" v-model.trim="newBroker.name" autofocus>
<span class="help-inline" v-show="newBroker.error.length != 0">{{newBroker.error}}</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Type:</label>
<div class="controls">
<select v-model="newBroker.type">
<option v-for="type in brokerTypeList" :value="type">{{type | typeLabel}}</option>
</select>
</div>
</div>
</modal-dialog>
</div>
</script>
<script type="text/x-template" id="sortable_table_header_template">
<th @click="ascendingSort = !ascendingSort">
<slot>
{{fieldName | capitalize}}
</slot>
<a v-show="sortField == fieldName" class="pull-right" href="#"><i :class="[ascendingSort ? 'icon-chevron-down' : 'icon-chevron-up']"></i></a>
</th>
</script>
<script type="text/x-template" id="gcp_iot_core_topic_template">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label"><strong>GCP IoT Core Topic</strong></label>
</div>
<div class="control-group" :class="{error: name_error.length > 0}">
<label class="control-label" for="inputTopicName">Topic Name:</label>
<div class="controls">
<input type="text" id="inputTopicName" placeholder="Topic Name" v-model.trim="name">
<span class="help-inline" v-show="name_error.length > 0">{{ name_error }}</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Topic Path:</label>
<div class="controls">
<input type="text" placeholder="Topic Path" :value="path" readonly>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-success" @click.prevent="onSaveLocal">Ok</button>
<button class="btn" @click.prevent="onReset">Reset</button>
</div>
</div>
</form>
</script>
<script type="text/x-template" id="mqtt_topic_template">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label"><strong>MQTT Topic</strong></label>
</div>
<div class="control-group" :class="{error: name_error.length > 0}">
<label class="control-label" for="inputTopicName">Topic Name:</label>
<div class="controls">
<input type="text" id="inputTopicName" placeholder="Topic Name" v-model.trim.lazy="name">
<span class="help-inline" v-show="name_error.length > 0">{{ name_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: path_error.length > 0}">
<label class="control-label" for="inputTopicPath">Topic Path:</label>
<div class="controls">
<input type="text" id="inputTopicPath" placeholder="Topic Path" v-model.trim="config.path">
<span class="help-inline" v-show="path_error.length > 0">{{ path_error }}</span>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-success" @click.prevent="onSaveLocal">Ok</button>
<button class="btn" @click.prevent="onReset">Reset</button>
</div>
</div>
</form>
</script>
<script type="text/x-template" id="aws_iot_core_topic_template">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label"><strong>AWS IoT Core Topic</strong></label>
</div>
<div class="control-group" :class="{error: name_error.length > 0}">
<label class="control-label" for="inputTopicName">Topic Name:</label>
<div class="controls">
<input type="text" id="inputTopicName" placeholder="Topic Name" v-model.trim.lazy="name">
<span class="help-inline" v-show="name_error.length > 0">{{ name_error }}</span>
</div>
</div>
<div class="control-group" :class="{error: path_error.length > 0}">
<label class="control-label" for="inputTopicPath">Topic Path:</label>
<div class="controls">
<input type="text" id="inputTopicPath" placeholder="Topic Path" v-model.trim="config.path">
<span class="help-inline" v-show="path_error.length > 0">{{ path_error }}</span>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-success" @click.prevent="onSaveLocal">Ok</button>
<button class="btn" @click.prevent="onReset">Reset</button>
</div>
</div>
</form>
</script>
<script type="text/x-template" id="topic_manager_template">
<div class="row-fluid" v-cloak>
<div id="topic_list" class="span6">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<sortable-table-header :sortField="sortField" fieldName="name" @sortBy="sortBy"></sortable-table-header>
<sortable-table-header :sortField="sortField" fieldName="broker" @sortBy="sortBy"></sortable-table-header>
</tr>
</thead>
<tbody>
<tr v-for="topic in topicList" @click="activeTopic=topic" :class="{info: activeTopic && activeTopic.uniqueKey==topic.uniqueKey}">
<td>{{topic.name}}</td>
<td>{{topic.broker}}</td>
</tr>
</tbody>
</table>
<div class="pull-right">
<button class="btn" type="button" @click="addTopic">Add</button>
<button class="btn btn-danger" type="button" @click="deleteTopic">Delete</button>
</div>
</div>
<div id="topic_detail" class="well span6">
<keep-alive>
<component v-if="activeTopic != null && activeTopic.broker != null" :is="activeTopicType" :key="activeTopic.uniqueKey"
:topicName="activeTopic.name" :configData="activeTopic.config" :nameError="nameError" :broker="activeTopic.broker"
@topicUpdated="onTopicUpdated" ></component>
</keep-alive>
</div>
<odal-dialog title="New Topic" :error="newTopic.error.length != 0" v-on:submitChange="createTopic">
<div v-if="brokerList.length > 0">
<div class="control-group" :class="{error: !isNewNameValid(newTopic.name)}">
<label class="control-label" for="inputNewTopicName">Name:</label>
<div class="controls">
<input type="text" id="inputNewTopicName" placeholder="Topic's Name" v-model.trim="newTopic.name" autofocus>
<span class="help-inline" v-show="newTopic.error.length != 0">{{newTopic.error}}</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Broker:</label>
<div class="controls">
<select v-model="newTopic.broker">
<option v-for="broker in brokerList" :value="broker.name">{{broker.name}}</option>
</select>
</div>
</div>
</div>
<div v-else class="alert alert-block">
There is no broker available, please create some broker from BrokerManager first.
</div>
</modal-dialog>
</div>
</script>
<script type="text/x-template" id="modal_dialog_template">
<div class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>{{title}}</h3>
</div>
<div class="modal-body">
<form class="form-horizontal" @submit.prevent="onSubmit">
<slot></slot>
</form>
</div>
<div class="modal-footer">
<slot name="buttons">
<button class="btn btn-primary" @click="onSubmit">Save</button>
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
</slot>
</div>
</div>
</script>
<script type="text/javascript" src="../../js/jquery-1.10.0.min.js"></script>
<script type="text/javascript" src="../../js/bootstrap.min.js"></script>
<script type="text/javascript" src="../../js/underscore-min.js"></script>
<script type="text/javascript" src="../../js/dropzone.js"></script>
<script type="text/javascript" src="../../js/spin.min.js"></script>
<script type="text/javascript" src="../../js/renderjson.js"></script>
<script type="text/javascript" src="../../js/vue.js"></script>
<script type="text/javascript" src="../../js/plugin-utils.js"></script>
<script type="text/javascript" src="../../js/vue-alert-component.js"></script>
<script type="text/javascript" src="../../js/vue-file-selector-component.js"></script>
<!-- used by data mapping -->
<script type="text/javascript" src="../../js/split.min.js"></script>
<script type="text/javascript" src="../../js/jstree/jstree.min.js"></script>
<script type="text/javascript" src="js/data_service_config.js"></script>
</body>
</html>