Skip to content

Instantly share code, notes, and snippets.

@blindman2k
Last active August 29, 2015 14:21
Show Gist options
  • Save blindman2k/609b165c5d134b555bab to your computer and use it in GitHub Desktop.
Save blindman2k/609b165c5d134b555bab to your computer and use it in GitHub Desktop.
#require "Rocky.class.nut:1.1.1"
class RockyII extends Rocky {
function _addAccessControl(res) {
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, PATCH, DELETE");
res.header("Access-Control-Expose-Headers", "X-ImpBase-Version");
}
}
//We have static variables, so only one version of ImpBase can be instantiated per agent
class ImpBase {
//static variables shared between all instances of ImpBase
_waiting = {};
_ib_data = {};
_rocky = null;
_root = null;
_path = null;
static version = [1, 0, 0];
constructor(rocky, urlbase = "/data", initialize = true) {
this._rocky = rocky;
this._path = urlbase;
this._root = this;
if(initialize) init();
}
function init() {
// Make sure Rocky is v1.1.1 or newer.
assert_version(_rocky, [1, 1, 1]);
//get impbase table from server
_ib_restore();
//set up rocky listeners
_rocky.on("GET", _path + "/impbase.js", _ib_javascript.bindenv(this));
_rocky.on("GET", _path, ib_get.bindenv(this)).onTimeout(_ib_get_timeout.bindenv(this), 60);
_rocky.on("GET", _path + "/(.*)", ib_get.bindenv(this)).onTimeout(_ib_get_timeout.bindenv(this), 60);
_rocky.on("POST", _path, ib_post.bindenv(this));
_rocky.on("POST", _path + "/.*", ib_post.bindenv(this));
_rocky.on("PUT", _path, ib_put.bindenv(this));
_rocky.on("PUT", _path + "/.*", ib_put.bindenv(this));
_rocky.on("PATCH", _path, ib_patch.bindenv(this));
_rocky.on("PATCH", _path + "/.*", ib_patch.bindenv(this));
_rocky.on("DELETE", _path, ib_delete.bindenv(this));
_rocky.on("DELETE", _path + "/.*", ib_delete.bindenv(this));
_rocky.on("OPTIONS", _path, function(context) { context.send("OK"); });
_rocky.on("OPTIONS", _path + "/.*", function(context) { context.send("OK"); });
}
function assert_version(object, version) {
assert(typeof version == "array" && version.len() == 3);
assert("version" in object);
assert( (object.version[0] > version[0])
|| (object.version[0] == version[0] && object.version[1] > version[1])
|| (object.version[0] == version[0] && object.version[1] == version[1] && object.version[2] >= version[2]));
}
//--------------------[ Remote handlers ]----------------------------------
// GET - Reading Data
function ib_get(context) {
local path = _map_path(context.req.path);
local last_version = null;
if ("version" in context.req.query) {
try {
if (context.req.query.version == "*") {
last_version = _ib_data.version;
} else {
last_version = context.req.query.version.tointeger();
}
} catch (e) {
// Do nothing. Just assume version 0.
}
}
if (last_version == null || last_version != _ib_data.version) {
// We don't have a version match so serve the results immediately
context.setHeader("X-ImpBase-Version", _ib_data.version);
context.send(200, _ib_get_path(path), true);
return;
}
}
// PUT - Writing Data
function ib_put(context) {
// Clobber the data store with the new data
child(context.req.path).set(context.req.body);
}
// PATCH - Updating Data
function ib_patch(context) {
if (context.req.body != null && typeof context.req.body != "table") {
// Check the input data is a table
context.send(400, "Body must be a JSON object");
} else {
local ptr = _ib_get_path(context.req.path);
if (ptr != null && typeof ptr != "table") {
context.send(400, "Existing data must be a table");
} else {
// Update the target node with the specified changes
child(context.req.path).update(context.req.body);
}
}
}
// DELETE - Removing Data
function ib_delete(context) {
// Delete the data store contents
child(context.req.path).remove();
}
// POST - Pushing Data
function ib_post(context) {
local autoid = child(context.req.path).push(context.req.body);
// Send a unique result to this context
context.setHeader("X-ImpBase-Version", _ib_data.version);
context.send(200, {"name": autoid}, true);
}
//--------------------[ Local handlers ]----------------------------------
// GET - Reading Data
function once(eventtype, callback) {
local path = _map_path(_path);
switch (eventtype) {
case "value" :
// Retrieve the data from the data store
local val = _ib_get_path(path);
callback(DataSnapshot(this, val));
break;
default:
callback(null);
server.error("event type not recognized");
}
}
function on(eventtype, callback) {
local path = _map_path(_path);
switch (eventtype) {
case "value" :
// Register the callback for future changes
if (!(path in _waiting)) _waiting[path] <- {};
if (!(eventtype in _waiting[path])) _waiting[path][eventtype] <- [];
_waiting[path][eventtype].push(callback);
// Call the callback for the initial value
local val = _ib_get_path(path);
callback(DataSnapshot(this, val));
break;
default:
callback(null);
server.error("event type not recognized");
}
// Returns the callback for off() requests
return callback;
}
function off(eventtype = null, callback = null) {
local path = _map_path(_path);
if (path == null) {
_waiting <- {};
} else if (eventtype == null) {
_waiting[path] <- {};
} else if (callback == null) {
_waiting[path][eventtype] <- [];
} else {
foreach (path, types in _waiting) {
foreach (type, callbacks in _waiting[path]) {
for (local i = callbacks.len()-1; i >= 0; i--) {
if (callbacks[i] == callback) {
callbacks.remove(i);
}
}
}
}
}
}
// PUT - Writing Data
function set(newData, callback=null) {
local path = _map_path(_path);
// Clobber the data store with the new data
_ib_set_path(path, newData);
// Persist to disk
_ib_persist();
// Distribute to all the waiting clients
_ib_broadcast(path);
if (callback) callback();
}
// UPDATE - Updating Data
function update(newData, callback=null) {
local path = _map_path(_path);
if (newData != null && typeof newData != "table") {
// Check the input data is a table
throw "Body must be a JSON object";
} else {
local ptr = _ib_get_path(path);
if (ptr == null || newData == null) {
// If the path isn't pointing to anything or the new data is null then treat it like a put/set
_ib_set_path(path, newData);
// Set the return value
ptr = newData;
} else if (typeof ptr == "table") {
// Patch the data store with the new changes
foreach (k,v in newData) ptr[k] <- v;
} else {
// The existing data isn't a table
throw "Existing data must be a table";
}
}
// Persist to disk
_ib_persist();
// Distribute to all the waiting clients
_ib_broadcast(path);
if (callback) callback();
}
// DELETE - Removing Data
function remove(callback=null) {
local path = _map_path(_path);
// Delete the data store contents
child(path).set(null);
if (callback) callback();
}
// POST - Pushing Data
function push(newData, callback=null) {
local path = _map_path(_path);
local ptr = _ib_get_path(path);
// Make sure the existing data is clean
if (typeof ptr != "table") {
ptr = {};
}
// Append the new data to the old
local autoid = format("~%08x-%04x", time(), math.rand()%0x10000);
ptr[autoid] <- newData;
// Update the data store
_ib_set_path(path, ptr);
// Persist to disk
_ib_persist();
// Distribute to all the waiting clients
_ib_broadcast(path);
if (callback) callback();
return autoid;
}
function root() {
return _root;
}
function child(path) {
if(path && path != "/") {
while (path.slice(0, 1) == "/") path = path.slice(1);
while (path.slice(-1) == "/") path = path.slice(0, -1);
if (path == "") return this;
local newPath = "/" + path;
local newRef = ImpBase(_rocky, newPath, false);
newRef._setRoot(_root);
return newRef;
} else {
return this;
}
}
function parent() {
if(_isRoot()) return this;
local lastNode = split(_path, "/").pop();
local newPath = _path.slice(0, _path.find(lastNode) - 1);
local newRef = ImpBase(_rocky, newPath, false);
newRef._setRoot(_root);
return newRef;
}
function key() {
if (_isRoot()) return null;
return split(_path, "/").slice(-1)[0];
}
function toString() {
return _path;
}
//--------------------[ Private functions ]---------------------------------
// Timeout handler for GET long polling requests
function _ib_get_timeout(context) {
// Send the latest, unchanged values
context.setHeader("X-ImpBase-Version", _ib_data.version);
context.send(204, "");
}
// Removes null nodes from the data tree
// This may become a memory/stack issue in larger trees. Redo this function
// when that happens.
function _ib_clean_data(ptr) {
local changed = false;
if (typeof ptr == "table" || typeof ptr == "array") {
foreach (k,v in ptr) {
if (v == null) {
// Remove this null node
changed = true;
delete ptr[k];
} else if ((typeof v == "table" || typeof v == "array") && v.len() == 0) {
// Remove this empty array/table
changed = true;
delete ptr[k];
} else if (typeof v == "array") {
// Convert this array into a table
changed = true;
local newtable = {};
for (local i = 0; i < v.len(); i++) {
newtable[i.tostring()] <- v[i];
}
ptr[k] <- newtable;
_ib_clean_data(ptr[k]);
} else {
// Move into the next node
changed = changed || _ib_clean_data(v);
}
}
}
return changed;
}
// Persist the current data to agent storage
function _ib_persist() {
// Remove null nodes for the tree
while (_ib_clean_data(_ib_data.root));
// Increment the version number
_ib_data.version++;
// Read the latest stored data and update it
local persist = server.load();
persist.impbase <- _ib_data;
server.save(persist);
}
// Restores the previously persisted data from disk
function _ib_restore() {
local persist = server.load();
if ("impbase" in persist) {
_ib_data.root <- persist.impbase.root;
_ib_data.version <- persist.impbase.version;
} else {
_ib_data.root <- null;
_ib_data.version <- -1;
}
}
// Set the subitem based on the path
function _ib_set_path(req_path, new_data) {
// Split out the path parts
local pathparts = split(req_path, "/");
// server.log("Setting " + req_path + " to " + new_data);
// Loop through the data searching for the node
local parent = _ib_data, ptr = _ib_data.root, p = 0, part = null, last_part = null;
for (p = 0; p < pathparts.len(); p++) {
part = pathparts[p];
// server.log(format("Stepping into %s/%s (%s)", (last_part ? last_part : "~"), part, typeof ptr));
if (new_data == null) {
// Handle deletions slightly differently
if (!(part in ptr)) {
// Can't delete a node that doesn't exist
return new_data;
} else if (typeof ptr[part] != "table" && p < pathparts.len()-1) {
// We aren't at the last node in the path but are at the last node of the data
return new_data;
}
} else {
if (typeof ptr != "table") {
if (last_part && parent) {
// We are missing or overriding a node
ptr = parent[last_part] <- {};
} else {
// We are missing or overriding the root node
ptr = _ib_data.root = {};
}
}
if (!(part in ptr) || typeof ptr[part] != "table") {
// But we can create a new node that doesn't exist
ptr[part] <- {};
}
}
parent = ptr;
ptr = ptr[part];
last_part = part;
}
if (parent == _ib_data) {
_ib_data.root = new_data;
} else {
parent[pathparts.pop()] <- new_data;
}
// We have set the item
return new_data;
}
// Extract the subitem based on the path
function _ib_get_path(req_path) {
// Parse out the path parts
local pathparts = split(req_path, "/");
// Loop through the data searching for the node
local ptr = _ib_data.root;
foreach (part in pathparts) {
if (part in ptr) ptr = ptr[part];
else return null;
}
// We have the item
return ptr;
}
// Broadcast the results specific to the caller's request
function _ib_broadcast(changedPath = null) {
imp.wakeup(0, function() {
// Broadcast to all waiting connections
// Clone the _contexts list as it will be changing
local contexts = clone Rocky.Context._contexts;
foreach (id, context in contexts) {
local path = _map_path(context.req.path);
if (path.find(changedPath) != null || changedPath.find(path) != null) {
context.setHeader("X-ImpBase-Version", _ib_data.version);
context.send(200, _ib_get_path(path), true);
}
}
// And to all waiting callback functions
foreach (path, types in _waiting) {
if (path.find(changedPath) != null || changedPath.find(path) != null) {
foreach (type, callbacks in _waiting[path]) {
foreach (callback in callbacks) {
local val = _ib_get_path(path);
callback(DataSnapshot(this, val));
}
}
}
}
}.bindenv(this))
}
// Converts a HTTP URL to a local path
function _map_path(path) {
local newPath = path;
if (path.find(_root._path) != null) newPath = path.slice(_root._path.len());
if (newPath == "") newPath = "/";
return newPath;
}
function _isRoot() {
return (_root.toString() == _path);
}
function _setRoot(newRoot) {
return _root = newRoot;
}
// Responds with the Javascript library
function _ib_javascript(context) {
context.setHeader("Content-Type", "application/javascript");
context.send(200, @"
function ImpBase (rootPath) {
// Constants
const MIN_TIMEOUT = 500;
const MAX_TIMEOUT = 5000;
// Properties
var _root = this;
var _path = null;
var _timeout = MIN_TIMEOUT;
// Private methods
var _constructor = function(rootPath) {
// Setup the static _requests object for holding the callbacks
if (typeof ImpBase._requests == 'undefined') {
ImpBase._requests = {};
}
if (!rootPath) {
// Use the current window URL as the basis for the path, assuming its running in an agent
_path = window.location.protocol + '//' + window.location.hostname + '/' + window.location.pathname.split('/')[1] + '/data';
} else {
// Strip any trailing slashes
while (rootPath.slice(-1) == '/') rootPath = rootPath.slice(0, -1);
_path = rootPath;
}
}
var _isRoot = function() {
return (_root.toString() == _path);
}
var _ajax = function(method, url, data, callback) {
if (typeof data == 'function') {
callback = data;
data = null;
}
var method = method.toUpperCase();
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function(){
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (typeof callback == 'function') {
var response = null;
if (xmlhttp.status == 200 && xmlhttp.getResponseHeader('Content-Type').indexOf('json') >= 0) {
response = JSON.parse(xmlhttp.responseText);
callback(xmlhttp.status, response, xmlhttp.getResponseHeader('X-ImpBase-Version'));
_timeout = MIN_TIMEOUT;
} else {
// console.log('Ajax request failed: ' + xmlhttp.status + ': ' + xmlhttp.responseText)
setTimeout(function() {
_ajax(method, url, data, callback);
}, _timeout);
_timeout = _timeout * 1.5;
if (_timeout > MAX_TIMEOUT) _timeout = MAX_TIMEOUT;
}
}
}
}
xmlhttp.open(method, url, true);
if (method == 'GET') {
xmlhttp.send();
} else {
xmlhttp.setRequestHeader('Content-Type', 'application/json');
// xmlhttp.setRequestHeader('Access-Control-Expose-Headers', 'X-ImpBase-Version');
xmlhttp.send(JSON.stringify(data));
}
return xmlhttp;
}
var _randomInt = function(min, max) {
return Math.random() * (max - min) + min;
}
// Protected methods
this._setRoot = function(newRoot) {
return _root = newRoot;
}
// Public methods
this.root = function() {
return _root;
}
this.child = function(path) {
if (path) {
// Strip the leading and trailing slashes off the new path and trailing slashes off the old path
while (path.slice(0, 1) == '/') path = path.slice(1);
while (path.slice(-1) == '/') path = path.slice(0, -1);
if (path == '') return this;
// Append the new path to the old path
var newPath = _path + '/' + path;
var newRef = new ImpBase(newPath);
newRef._setRoot(_root);
return newRef;
} else {
return this;
}
}
this.parent = function() {
if (_isRoot()) return this;
var newPath = _path.split('/').slice(0, -1).join('/');
var newRef = new ImpBase(newPath);
newRef._setRoot(_root);
return newRef;
}
this.key = function() {
if (_isRoot()) return null;
return _path.split('/').slice(-1)[0];
}
this.toString = function() {
return _path;
}
this.set = function(value, onComplete) {
_ajax('PUT', _path, value, onComplete);
}
this.update = function(value, onComplete) {
_ajax('PATCH', _path, value, onComplete);
}
this.remove = function(onComplete) {
_ajax('PUT', _path, null, onComplete);
}
this.push = function(value, onComplete) {
// Generate the autoid
var time = ('00000000' + Math.floor(Date.now() / 1000).toString(16)).slice(-8);
var rand = ('0000' + _randomInt(1, 0x10000).toString(16)).slice(-4);
var autoid = '~' + time + '-' + rand;
if (value != undefined) {
// Post the provided value as a new node
_ajax('PUT', _path + '/' + autoid, value, onComplete);
}
// Return an empty child node
return this.child(autoid);
}
this.on = function(eventtype, callback, cancelCallback, context, _version) {
var request = null;
switch (eventtype) {
case 'value':
var url = _path;
var context = context ? context : this;
if (_version != undefined) url += '?version=' + _version;
request = _ajax('GET', url, function(status, response, version) {
if (status == 200) {
callback.bind(context)(new DataSnapshot(this, response));
this.on(eventtype, callback, cancelCallback, context, version);
} else if (status == 204) {
this.on(eventtype, callback, cancelCallback, context, version);
} else if (cancelCallback) {
if (eventtype in ImpBase._requests && callback in ImpBase._requests[eventtype]) {
delete ImpBase._requests[eventtype][callback];
}
cancelCallback.bind(context)(status);
}
}.bind(this));
}
// Store the request for later cancelling with off()
if (request) {
if (!(eventtype in ImpBase._requests)) ImpBase._requests[eventtype] = {};
ImpBase._requests[eventtype][callback] = request;
}
return callback;
}
this.off = function(eventtype, callback) {
if (eventtype in ImpBase._requests && callback in ImpBase._requests[eventtype]) {
ImpBase._requests[eventtype][callback].abort();
}
}
this.once = function(eventtype, successCallback, failureCallback, context) {
var request = null;
switch (eventtype) {
case 'value':
var url = _path;
var context = context ? context : this;
request = _ajax('GET', url, function(status, response, version) {
if (status == 200) {
successCallback.bind(context)(new DataSnapshot(this, response));
} else if (failureCallback) {
failureCallback.bind(context)(status);
}
}.bind(this));
}
}
// Call the constructor
_constructor(rootPath);
}
function DataSnapshot(ref, val) {
// Properties
this._ref = ref;
this._val = val;
// Private methods
var _constructor = function() {
}
// Public methods
this.exists = function() {
return this._val != null;
}
this.val = function() {
return this._val;
}
this.child = function(childPath) {
var newRef = this._ref.child(childPath);
var nodes = childPath.split('/');
var newVal = this._val;
for (var nodeid in nodes) {
var node = nodes[nodeid];
if (node == '') {
continue;
} else if (newVal != null && typeof newVal == 'object' && node in newVal) {
newVal = newVal[node];
} else {
newVal = null;
break;
}
}
return new DataSnapshot(newRef, newVal);
}
this.forEach = function(childAction) {
for (var key in this._val) {
var ret = childAction(this.child(key));
if (ret === true) return true;
}
return false;
}
this.hasChild = function(childPath) {
return this.child(childPath).exists();
}
this.hasChildren = function() {
return this.numChildren() > 0;
}
this.numChildren = function() {
if ((this._val != null) && (typeof this._val == 'object')) {
return Object.keys(this._val).length;
} else {
return 0;
}
}
this.key = function() {
return this._ref.key();
}
this.ref = function() {
return this._ref;
}
// Call the constructor
_constructor();
}
");
}
}
class DataSnapshot {
_ref = null;
_val = null;
constructor(ref, val) {
this._ref = ref;
this._val = val;
}
function exists() {
return _val != null;
}
function val() {
return _val;
}
function child(childPath) {
local newRef = _ref.child(childPath);
local nodes = split(childPath, "/");
local newVal = _val;
foreach (node in nodes) {
if(newVal != null && typeof newVal == "table" && node in newVal ) {
newVal = newVal[node];
} else {
newVal = null;
break;
}
}
return DataSnapshot(newRef, newVal);
}
function forEach(childAction) {
foreach(key, value in _val) {
local ret = childAction(this.child(key))
if (ret == true) return true;
}
return false;
}
function hasChild(childPath) {
return this.child(childPath).exists();
}
function numChildren() {
if( (_val != null) && (typeof _val == "table") ) {
return _val.len();
} else {
return 0;
}
}
function hasChildren() {
return this.numChildren() > 0;
}
function key() {
return _ref.key();
}
function ref() {
return _ref;
}
}
server.log("AGENT RUNNING")
app <- RockyII(); //create an instance of rocky for impbase and webpage rendering
impbase <- ImpBase(app, "/data"); //create an instance of impbase - db for realtime functionality
led <- { "state" : 0 }; //sets default LED state to OFF
impbase.child("/led").on("value", function(snapshot) {
server.log("impbase change "+http.jsonencode(snapshot.val()));
if ("state" in snapshot.val()) {
device.send("state", snapshot.val().state);
} else {
impbase.child("/led").set(led); //stores default to impbase
}
});
device.on("getState" function(msg) {
impbase.child("/led/state").once("value", function(snapshot) {
server.log("in get state " + snapshot.val())
if(typeof snapshot.val() == "float" || typeof snapshot.val() == "integer") {
device.send("state", snapshot.val()); //sends state to device
}
})
});
impbase.child("/test").remove();
testOFF <- null;
imp.wakeup(5, function() {
server.log("5 sec")
impbase.child("/led/state").set(1);
// impbase.child("/test").set("setting");
// impbase.update({"test" : "updating"});
// impbase.child("/test").once("value", function(s) {
// server.log( "/test once " + http.jsonencode(s.val()) )
// })
testOFF = impbase.child("/test").on("value", function(snapshot) {
server.log( "/test on "+http.jsonencode(snapshot.val()) );
})
// impbase.child("/test").on("value", function(snapshot) {
// server.log( "/test on 2 "+http.jsonencode(snapshot.val()) );
// })
// impbase.update({"test" : {"update_table" : "updating"}});
})
imp.wakeup(10, function() {
server.log("10 sec")
impbase.child("/led/state").set(0);
// impbase.child("/led/state").parent().update({"state" : 0})
// impbase.child("/led").update({"state" : 0})
impbase.child("/test").push("pushing");
})
imp.wakeup(15, function() {
server.log("15 sec")
impbase.child("/test").push("pushing2");
impbase.child("/test").off("value", testOFF);
})
imp.wakeup(20, function() {
server.log("20 sec")
impbase.child("/test").push("pushing3");
})
// imp.wakeup(25, function() {
// server.log("25 sec")
// impbase.child("/test").push("pushing4");
// })
app.get("/", function(context) {
context.send(200, html); //render HTML to agent's URL
});
html <- @"
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta name='description' content>
<meta name='author' content>
<title>Test</title>
</head>
<style>
h1 {
text-align: center;
font-size: 36px;
}
button {
font-size: 36px;
border-radius: 4px;
padding: 2% 5%;
margin: 0 5%;
}
.buttons {
margin: auto;
width: 80%;
text-align: center;
}
#authorize-div {
margin: 5% auto;
width: 80%;
text-align: center;
}
#slider {
width: 60%;
margin: 2% auto 4%;
}
#output {
margin: 5% auto;
width: 80%;
text-align: center;
}
#output span {
font-size: 1.2em;
font-weight: bold;
}
.hidden {
display: none;
}
/*! jQuery UI - v1.11.4 - 2015-04-03
* http://jqueryui.com
* not whole lib - just the styles used by the slider
*/
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
border-top-left-radius: 4px;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
border-top-right-radius: 4px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
border-bottom-left-radius: 4px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
border-bottom-right-radius: 4px;
}
.ui-widget-content {
border: 1px solid #aaaaaa;
background: #ffffff;
color: #222222;
}
.ui-widget {
font-family: Verdana,Arial,sans-serif;
font-size: 1.1em;
}
.ui-slider-horizontal {
height: .8em;
}
.ui-slider-horizontal .ui-slider-handle {
top: -.3em;
margin-left: -.6em;
}
.ui-slider {
position: relative;
text-align: left;
}
.ui-slider .ui-slider-handle {
position: absolute;
z-index: 2;
width: 1.2em;
height: 1.2em;
cursor: default;
-ms-touch-action: none;
touch-action: none;
}
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
border: 1px solid #d3d3d3;
background: #e6e6e6;
font-weight: normal;
color: #555555;
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
border: 1px solid #aaaaaa;
background: #ffffff;
font-weight: normal;
color: #212121;
}
</style>
<body>
<h1>DIMMER CONTROL</h1>
<div id='slider'></div>
<br>
<div class='buttons'>
<button id='on' data-state='1'>ON</button>
<button id='off' data-state='0'>OFF</button>
<button id='stop' data-state='stop'>STOP</button>
</div>
<!-- ImpBase -->
<script>
var source = document.URL + '/data/impbase.js';
var script = document.createElement('script');
script.src = source;
document.getElementsByTagName('head')[0].appendChild(script);
</script>
<!-- jQuery -->
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js'></script>
<!-- JavaScript File -->
<script>
window.onload = function() {
var agentID = document.URL;
var IB = new ImpBase();
var sliderState = 0;
var testListener, ledListener
getState(initialzeSlider);
function getState(callback) {
IB.child('led/state').once('value', function(snapshot) {
console.log(snapshot.val());
callback(snapshot.val());
}, function(error) {
console.log('Error:', error);
})
}
function sendState(state) {
console.log('in send state')
if(state != sliderState) {
console.log('Sending '+ sliderState +' to IB at ' + new Date());
IB.child('/led/state').set(sliderState);
}
}
function initialzeSlider(state) {
$('#slider').slider({'value' : state*100});
openListeners();
}
function openListeners() {
$('#slider').on('slidechange', function(e, ui) {
sliderState = ui.value / 100;
getState(sendState);
});
$('button').on('click', translateButtonClick);
ledListener = IB.child('/led').on('value', handleIBChange);
// testListener = IB.child('/test').on('value', function(s){ console.log('in js test listener'); console.log(s.val()); })
}
function translateButtonClick(e){
var state = e.currentTarget.dataset.state;
if(state == 'stop') {
// IB.child('/test').set('pushing')
// IB.once('value', function(s) { console.log(s.child('/test')); console.log(s); })
} else {
setSlider(state);
}
}
function setSlider(state) {
$('#slider').slider('option', 'value', state*100);
}
function handleIBChange(snapshot) {
console.log('IB change registered. Snapshot value: ' + JSON.stringify(snapshot.val()) + ' Time Received: ' + new Date());
if(snapshot.val() && 'state' in snapshot.val() && snapshot.val().state != sliderState ) {
setSlider(snapshot.val().state);
}
}
}
</script>
</body>
</html>
"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment