Current UIComposer implementation is still lacking in generalization and extensibility. Therefore adding a new protocol view takes quite some work and is error-prone. However, this document attempts to describe the steps.
An important thing to note is that the existing implementation holds its state on the client-side, making the implementation more complicated.
The parts described here are expected to undergo a complete redesign and refactoring in the next major (2.0) release of the UIComposer.
index.html
...
<div id="tabs">
...
<li>
<a href="#x10_tab" class="has_icon">X10</a><span id="create_x10_icon" title="Create X10" class="ui-icon ui-icon-plusthick"></span>
</li>
...
<div id="x10_tab">
<p> Click the plus image to create x10 button.</p>
<div id="x10_container" class="item_container">
<div class="clear"></div>
</div>
<div class="clear"></div>
</div>
...
<div id="create_x10_dialog">
<form id="x10_form">
<fieldset>
<label for="x10_label_input">Label</label>
<input type="text" id="x10_label_input" name="x10_label_input"/>
<label for="x10_address_input">Address</label>
<input type="text" id="x10_address_input" name="x10_address_input"/>
<label for="x10_command_input">Command</label>
<input type="text" id="x10_command_input" name="x10_command_input"/>
</fieldset>
</form>
</div>
model/x10.js
var X10 = function() {
function X10() {
var self = this;
Model.call(self);
//text ui interface display
self.label = "";
self.address = "";
self.command = "";
/**
* Get HTML getElementId
*/
self.getElementId = function() {
return "x10"+self.id;
};
self.inspectViewTemplate = "template/_x10Inspect.ejs";
}
/**
* Create new instance from flat model (which have no private method).
* @param model flat model (which have no private method).
* @returns created new instance.
*/
X10.init = function(model) {
var x10 = new X10();
x10.id = model.id ;
x10.label = model.label ;
x10.address = model.address;
x10.command = model.command;
return x10;
};
return X10;
}();
template/_x10Inspect.ejs
<label for="inspect_x10_label">Label</label>
<input type="text" class="text ui-widget-content ui-corner-all" value="<%=label %>" id="inspect_x10_label">
<label for="inspect_x10_address">Address</label>
<input type="text" class="text ui-widget-content ui-corner-all" value="<%=address %>" id="inspect_x10_address">
<label for="inspect_x10_command">Command</label>
<input type="text" class="text ui-widget-content ui-corner-all" value="<%=command %>" id="inspect_x10_command">
<%InspectView.getModel= function() { %>
<% var model = $("#inspect_tool_bar").data("model"); %>
<% var label = $.trim($("#inspect_x10_label").val()); %>
<% var command = $.trim($("#inspect_x10_command").val()); %>
<% model.label = label;%>
<% model.command = command;%>
<% return model;%>
<% }; %>
view/protocolView.js
...
var X10View = function() {
function X10View(x10) {
var self = this;
var _model = x10;
self.getModel = function() {
return _model;
};
self.getElement = function() {
return $("#" + self.getModel().getElementId());
};
var init = function() {
var btn = HTMLBuilder.X10BtnBuilder(self.getModel());
var info = $("#x10_tab p");
if (info.size() != 0) {
info.remove();
}
btn.prependTo($("#x10_tab .item_container"));
};
init();
self.deleteView = function() {
self.getElement().remove();
};
self.updateView = function () {
var x10 = self.getModel();
var btn = $("#"+x10.getElementId());
btn.interceptStr({
text:x10.label,
max:14
});
};
}
return X10View;
} ();
htmlBuilder.js
...
X10BtnBuilder: function(x10) {
var button = HTMLBuilder.blueBtnBuilder(x10.label);
button.addClass("x10_btn");
button.addClass("iphone_element");
button.attr("id", x10.getElementId());
button.data("model", x10);
button.attr("title", x10.label);
return button;
},
...
template/_controllerXML.js
Template for generating the controller.xml configuration file. Modify to add an entry to a new protocol event you plan on adding. Each event type has its own top-level event group (for example, <x10Events>, <knxEvents>).
...
(1) <x10Events>
(2) <% for (var index in events.x10Events.x10Event) {%>
(3) <% var x10Event = events.x10Events.x10Event[index]; %>
(4) <x10Event id="<%=x10Event.id %>" label="<%=x10Event.label %>" address="<%=x10Event.address %>" command="<%=x10Event.command %>"/>
(5) <% } %>
(6) </x10Events>
...
A code snippet from template/_controllerXML.js in UIComposer codebase.
In the code snippet above, line #1 shows the top-level <x10Events> XML tag. Line #2 starts a loop to go through all the X10 events (user interface buttons) the user has added to his model. Line #3 declares a x10Event variable to hold the next X10 event instance locally. On next line #4 a <x10Event> XML element is added with attributes whose values are fetched from the local x10Event variable fields (id, label, address and command). At the end, lines #5 and #6 close off the loop and XML <x10Events> element.
controller/protocolController.js
...
var X10Controller = function() {
return {
/**
* Show create x10 button dialog.
*/
showCreateX10Dialog:function () {
$("#create_x10_dialog").showModalForm("Create X10", {
buttons:{
'Create': X10Controller.confirmCreate
},
confirmButtonName:'Create',
width:350
});
},
/**
* Invoked when user confirm create x10 button.
*/
confirmCreate:function () {
var label = $("#x10_label_input");
var address = $("#x10_address_input");
var command = $("#x10_command_input");
$("#x10_form").validate({
invalidHandler:function(form, validator) {
$("#create_x10_dialog").errorTips(validator);
},
showErrors:function(){},
rules: {
x10_label_input: {
required: true,
maxlength: 50
},
x10_address_input: {
required:true,
maxlength: 50
},
x10_command_input: {
required:true,
maxlength: 50
}
},
messages:{
x10_label_input: {
required: "Please input a label",
maxlength: "Please input a label no more than 50 charactors"
},
x10_address_input: {
required: "Please input a address",
maxlength: "Please input a address no more than 50 charactors"
},
x10_command_input: {
required: "Please input a command",
maxlength: "Please input a command no more than 50 charactors"
}
}
});
if ($("#x10_form").valid()) {
var x10 = new X10();
x10.id = global.BUTTONID++;
x10.label = label.val();
x10.address = address.val();
x10.command = command.val();
X10Controller.createX10(x10);
$("#create_x10_dialog").closeModalForm();
}
},
createX10: function(x10){
var x10View = new X10View(x10);
x10.addDeleteListener(x10View);
x10.addUpdateListener(x10View);
var btn = x10View.getElement();
makeBtnDraggable(btn);
btn.inspectable();
}
};
}();
...
controller/tabController.js
...
// Static methods -----------------------------------------------------------------------------
TabController.init = function() {
$("#tabs").tabs();
$("#create_knx_icon").unbind().bind("click", KNXController.showCreateKNXDialog);
$("#create_x10_icon").unbind().bind("click", X10Controller.showCreateX10Dialog);
$("#create_http_icon").unbind().bind("click", HTTPController.showCreateHTTPDialog);
$("#create_tcp_icon").unbind().bind("click", HTTPController.showCreateTCPDialog);
$("#create_telnet_icon").unbind().bind("click", HTTPController.showCreateTelnetDialog);
$("#select_command_icon").unbind().bind("click", selectCommand);
};
return TabController;
controller/downloadController.js
...
/**
* Gets all X10 events user has added.
*
* @returns x10Event array
*/
function parseX10() {
(1) var x10Events = new Array();
(2) $("#x10_container").find(".x10_btn").each(function() {
(3) var x10Event = new Object();
(4) var model = $(this).data("model");
(5) x10Event.id = model.id;
(6) x10Event.address = model.address;
(7) x10Event.command = model.command;
(8) x10Event.label = model.label;
(9) x10Events.push(x10Event);
});
return x10Events;
}
...
...
/*-------------------- generate irb file --------------------------------------*/
/**
* Generate UI Interface description file.
*/
function generatePanelDesc() {
var screens = getStoredScreens();
var macroBtns = new Array();
$("#macro .macro_btn_defination").each(function() {
var model = $(this).data("model");
var btnModels = model.getSubModels();
model.buttons = new Array();
for (var index in btnModels) {
model.buttons.push(btnModels[index]);
}
macroBtns.push(model);
});
var knxBtns = new Array();
$("#knx_container").find(".knx_btn").each(function() {
knxBtns.push($(this).data("model"));
});
var x10Btns = new Array();
$("#x10_container").find(".x10_btn").each(function() {
x10Btns.push($(this).data("model"));
});
var httpBtns = new Array();
$("#http_container").find(".http_btn").each(function() {
httpBtns.push($(this).data("model"));
});
var tcpBtns = new Array();
$("#tcp_container").find(".tcp_btn").each(function() {
tcpBtns.push($(this).data("model"));
});
var panel = {
screens: screens,
knxBtns: knxBtns,
x10Btns: x10Btns,
httpBtns: httpBtns,
tcpBtns: tcpBtns,
macroBtns: macroBtns,
maxId: global.BUTTONID
};
var data = JSON.stringify({
panel: panel
});
return data;
}
...
controller-1.0-M3.xsd
<xsd:element name="events">
<xsd:annotation>
<xsd:documentation><![CDATA[
The parent element of all kinds of event tags.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="irEvents"/>
<xsd:element ref="knxEvents"/>
<xsd:element ref="x10Events"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
<xsd:element name="x10Events">
<xsd:annotation>
<xsd:documentation><![CDATA[
The parent element of <x10Event> tags.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="x10Event" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="x10Event">
<xsd:annotation>
<xsd:documentation><![CDATA[
The event to control a device with X10 protocol.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attributeGroup ref="idAttr"/>
<xsd:attribute name="label" type="xsd:string" use="optional"/>
<xsd:attribute name="address" type="xsd:string" use="required"/>
<xsd:attribute name="command" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
controller/importController.js
...
/**
* Invoked after upload success.
* @param responseText responseText
* @param statusText statusText
*/
function uploadSuccess(responseText, statusText) {
ImportController.cleanUp();
var data = responseText;
// notice: revert order is very important, don't change it if you are clear with it.
revertKnxBtns(data.panel.knxBtns);
revertX10Btns(data.panel.x10Btns);
//revertHTTPBtns(data.panel.httpBtns); // TODO !!
revertMacroBtns(data.panel.macroBtns);
revertMacroSubBtns(data.panel.macroBtns);
revertScreens(data.panel.screens);
global.BUTTONID = data.panel.maxId;
$("#upload_form_container").closeModalForm();
$.hideLoading();
}
...
/**
* Revert X10 buttons
* @param x10Btns x10Btns object from description file
*/
function revertX10Btns(x10Btns) {
for (var index in x10Btns) {
var btn = x10Btns[index];
var model = ImportController.buildModel(btn);
X10Controller.createX10(model);
}
}
...