diff --git a/js/ui/botui/build/botui.js b/js/ui/botui/build/botui.js
new file mode 100644
index 0000000..77afb46
--- /dev/null
+++ b/js/ui/botui/build/botui.js
@@ -0,0 +1,435 @@
+/*
+ * botui 0.3.9M1
+ * A JS library to build the UI for your bot
+ * https://botui.org
+ *
+ * Copyright 2019, Moin Uddin
+ * Released under the MIT license.
+ */
+
+(function(root, factory) {
+ "use strict";
+ if (typeof define === 'function' && define.amd) {
+ define([], function() {
+ return (root.BotUI = factory(root));
+ });
+ } else {
+ root.BotUI = factory(root);
+ }
+}(typeof window !== 'undefined' ? window : this, function(root, undefined) {
+ "use strict";
+
+ var BotUI = (function(id, opts) {
+
+ opts = opts || {};
+
+ if (!id) {
+ throw Error('BotUI: Container id is required as first argument.');
+ }
+
+ if (!document.getElementById(id)) {
+ throw Error('BotUI: Element with id #' + id + ' does not exist.');
+ }
+
+ if (!root.Vue && !opts.vue) {
+ throw Error('BotUI: Vue is required but not found.');
+ }
+
+ var _botApp, // current vue instance.
+ _options = {
+ debug: false,
+ fontawesome: true,
+ searchselect: true
+ },
+ _container, // the outermost Element. Needed to scroll to bottom, for now.
+ _interface = {}, // methods returned by a BotUI() instance.
+ _actionResolve,
+ _markDownRegex = {
+ icon: /!\(([^\)]+)\)/igm, // !(icon)
+ image: /!\[(.*?)\]\((.*?)\)/igm, // 
+ link: /\[([^\[]+)\]\(([^\)]+)\)(\^?)/igm // [text](link) ^ can be added at end to set the target as 'blank'
+ },
+ _fontAwesome = 'https://use.fontawesome.com/ea731dcb6f.js',
+ _esPromisePollyfill = 'https://cdn.jsdelivr.net/es6-promise/4.1.0/es6-promise.min.js', // mostly for IE
+ _searchselect = "https://unpkg.com/vue-select@2.4.0/dist/vue-select.js";
+
+ root.Vue = root.Vue || opts.vue;
+
+ // merge opts passed to constructor with _options
+ for (var prop in _options) {
+ if (opts.hasOwnProperty(prop)) {
+ _options[prop] = opts[prop];
+ }
+ }
+
+ if (!root.Promise && typeof Promise === "undefined" && !opts.promise) {
+ loadScript(_esPromisePollyfill);
+ }
+
+ function _linkReplacer(match, $1, $2, $3) {
+ var _target = $3 ? 'blank' : ''; // check if '^' sign is present with link syntax
+ return "" + $1 + "";
+ }
+
+ function _parseMarkDown(text) {
+ return text
+ .replace(_markDownRegex.image, "
")
+ .replace(_markDownRegex.icon, "")
+ .replace(_markDownRegex.link, _linkReplacer);
+ }
+
+ function loadScript(src, cb) {
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = src;
+
+ if (cb) {
+ script.onload = cb;
+ }
+
+ document.body.appendChild(script);
+ }
+
+ function _handleAction(text) {
+ if (_instance.action.addMessage) {
+ _interface.message.human({
+ delay: 100,
+ content: text
+ });
+ }
+ _instance.action.show = !_instance.action.autoHide;
+ }
+
+ var _botuiComponent = {
+ template: '
', // replaced by HTML template during build. see Gulpfile.js
+ data: function() {
+ return {
+ action: {
+ text: {
+ size: 30,
+ placeholder: 'Write here ..'
+ },
+ button: {},
+ show: false,
+ type: 'text',
+ autoHide: true,
+ addMessage: true
+ },
+ messages: []
+ };
+ },
+ computed: {
+ isMobile: function() {
+ return root.innerWidth && root.innerWidth <= 768;
+ }
+ },
+ methods: {
+ handle_action_button: function(button) {
+ for (var i = 0; i < this.action.button.buttons.length; i++) {
+ if (this.action.button.buttons[i].value == button.value && typeof(this.action.button.buttons[i].event) == 'function') {
+ this.action.button.buttons[i].event(button);
+ if (this.action.button.buttons[i].actionStop) return false;
+ break;
+ }
+ }
+
+ _handleAction(button.text);
+
+ var defaultActionObj = {
+ type: 'button',
+ text: button.text,
+ value: button.value
+ };
+
+ for (var eachProperty in button) {
+ if (button.hasOwnProperty(eachProperty)) {
+ if (eachProperty !== 'type' && eachProperty !== 'text' && eachProperty !== 'value') {
+ defaultActionObj[eachProperty] = button[eachProperty];
+ }
+ }
+ }
+
+ _actionResolve(defaultActionObj);
+ },
+ handle_action_text: function() {
+ if (!this.action.text.value) return;
+ _handleAction(this.action.text.value);
+ _actionResolve({
+ type: 'text',
+ value: this.action.text.value
+ });
+ this.action.text.value = '';
+ },
+ handle_action_select: function() {
+ if (this.action.select.searchselect && !this.action.select.multipleselect) {
+ if (!this.action.select.value.value) return;
+ _handleAction(this.action.select.value[this.action.select.label]);
+ _actionResolve({
+ type: 'text',
+ value: this.action.select.value.value,
+ text: this.action.select.value.text,
+ obj: this.action.select.value
+ });
+ }
+ if (this.action.select.searchselect && this.action.select.multipleselect) {
+ if (!this.action.select.value) return;
+ var values = new Array();
+ var labels = new Array();
+ for (var i = 0; i < this.action.select.value.length; i++) {
+ values.push(this.action.select.value[i].value);
+ labels.push(this.action.select.value[i][this.action.select.label]);
+ }
+ _handleAction(labels.join(', '));
+ _actionResolve({
+ type: 'text',
+ value: values.join(', '),
+ text: labels.join(', '),
+ obj: this.action.select.value
+ });
+ } else {
+ if (!this.action.select.value) return;
+ for (var i = 0; i < this.action.select.options.length; i++) { // Find select title
+ if (this.action.select.options[i].value == this.action.select.value) {
+ _handleAction(this.action.select.options[i].text);
+ _actionResolve({
+ type: 'text',
+ value: this.action.select.value,
+ text: this.action.select.options[i].text
+ });
+ }
+ }
+ }
+ }
+ }
+ };
+
+ root.Vue.directive('botui-markdown', function(el, binding) {
+ if (binding.value == 'false') return; // v-botui-markdown="false"
+ el.innerHTML = _parseMarkDown(el.textContent);
+ });
+
+ root.Vue.directive('botui-scroll', {
+ inserted: function(el) {
+ _container.scrollTop = _container.scrollHeight;
+ el.scrollIntoView(true);
+ }
+ });
+
+ root.Vue.directive('focus', {
+ inserted: function(el) {
+ el.focus();
+ }
+ });
+
+ root.Vue.directive('botui-container', {
+ inserted: function(el) {
+ _container = el;
+ }
+ });
+
+ _botApp = new root.Vue({
+ components: {
+ 'bot-ui': _botuiComponent
+ }
+ }).$mount('#' + id);
+
+ var _instance = _botApp.$children[0]; // to access the component's data
+
+ function _addMessage(_msg) {
+
+ if (!_msg.loading && !_msg.content) {
+ throw Error('BotUI: "content" is required in a non-loading message object.');
+ }
+
+ if (opts.callback) opts.callback(_msg);
+
+ _msg.type = _msg.type || 'text';
+ _msg.visible = (_msg.delay || _msg.loading) ? false : true;
+ var _index = _instance.messages.push(_msg) - 1;
+
+ return new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ if (_msg.delay) {
+ _msg.visible = true;
+
+ if (_msg.loading) {
+ _msg.loading = false;
+ }
+ }
+ resolve(_index);
+ }, _msg.delay || 0);
+ });
+ }
+
+ function _checkOpts(_opts) {
+ if (typeof _opts === 'string') {
+ _opts = {
+ content: _opts
+ };
+ }
+ return _opts || {};
+ }
+
+ _interface.clear = function() {
+ var _out = document.getElementById(id);
+ var CHILDREN_TO_PRESERVE = 0;
+ while (_out.childNodes[CHILDREN_TO_PRESERVE])
+ _out.removeChild(_out.childNodes[CHILDREN_TO_PRESERVE]);
+ _out.appendChild(document.createElement("bot-ui"));
+ },
+ _interface.removeAction = function() {
+ _instance.action.show = false;
+ },
+ _interface.message = {
+ add: function(addOpts) {
+ return _addMessage(_checkOpts(addOpts));
+ },
+ bot: function(addOpts) {
+ addOpts = _checkOpts(addOpts);
+ return _addMessage(addOpts);
+ },
+ human: function(addOpts) {
+ addOpts = _checkOpts(addOpts);
+ addOpts.human = true;
+ return _addMessage(addOpts);
+ },
+ get: function(index) {
+ return Promise.resolve(_instance.messages[index]);
+ },
+ remove: function(index) {
+ _instance.messages.splice(index, 1);
+ return Promise.resolve();
+ },
+ update: function(index, msg) { // only content can be updated, not the message type.
+ var _msg = _instance.messages[index];
+ _msg.content = msg.content;
+ _msg.visible = !msg.loading;
+ _msg.loading = !!msg.loading;
+ return Promise.resolve(msg.content);
+ },
+ removeAll: function() {
+ _instance.messages.splice(0, _instance.messages.length);
+ return Promise.resolve();
+ }
+ };
+
+ function mergeAtoB(objA, objB) {
+ for (var prop in objA) {
+ if (!objB.hasOwnProperty(prop)) {
+ objB[prop] = objA[prop];
+ }
+ }
+ }
+
+ function _checkAction(_opts) {
+ if (!_opts.action && !_opts.actionButton && !_opts.actionText) {
+ throw Error('BotUI: "action" property is required.');
+ }
+ }
+
+ function _showActions(_opts) {
+
+ _checkAction(_opts);
+
+ mergeAtoB({
+ type: 'text',
+ cssClass: '',
+ autoHide: true,
+ addMessage: true
+ }, _opts);
+
+ _instance.action.type = _opts.type;
+ _instance.action.cssClass = _opts.cssClass;
+ _instance.action.autoHide = _opts.autoHide;
+ _instance.action.addMessage = _opts.addMessage;
+
+ return new Promise(function(resolve, reject) {
+ _actionResolve = resolve; // resolved when action is performed, i.e: button clicked, text submitted, etc.
+ setTimeout(function() {
+ _instance.action.show = true;
+ }, _opts.delay || 0);
+ });
+ };
+
+ _interface.action = {
+ show: _showActions,
+ hide: function() {
+ _instance.action.show = false;
+ return Promise.resolve();
+ },
+ text: function(_opts) {
+ _checkAction(_opts);
+ _instance.action.text = _opts.action;
+ return _showActions(_opts);
+ },
+ button: function(_opts) {
+ _checkAction(_opts);
+ _opts.type = 'button';
+ _instance.action.button.buttons = _opts.action;
+ return _showActions(_opts);
+ },
+ select: function(_opts) {
+ _checkAction(_opts);
+ _opts.type = 'select';
+ _opts.action.label = _opts.action.label || 'text';
+ _opts.action.value = _opts.action.value || '';
+ _opts.action.searchselect = typeof _opts.action.searchselect !== 'undefined' ? _opts.action.searchselect : _options.searchselect;
+ _opts.action.multipleselect = _opts.action.multipleselect || false;
+ if (_opts.action.searchselect && typeof(_opts.action.value) == 'string') {
+ if (!_opts.action.multipleselect) {
+ for (var i = 0; i < _opts.action.options.length; i++) { // Find object
+ if (_opts.action.options[i].value == _opts.action.value) {
+ _opts.action.value = _opts.action.options[i]
+ }
+ }
+ } else {
+ var vals = _opts.action.value.split(',');
+ _opts.action.value = new Array();
+ for (var i = 0; i < _opts.action.options.length; i++) { // Find object
+ for (var j = 0; j < vals.length; j++) { // Search values
+ if (_opts.action.options[i].value == vals[j]) {
+ _opts.action.value.push(_opts.action.options[i]);
+ }
+ }
+ }
+ }
+ }
+ if (!_opts.action.searchselect) {
+ _opts.action.options.unshift({
+ value: '',
+ text: _opts.action.placeholder
+ });
+ }
+ _instance.action.button = _opts.action.button;
+ _instance.action.select = _opts.action;
+ return _showActions(_opts);
+ },
+ buttontext: function(_opts) {
+ _checkAction(_opts);
+ _opts.type = 'buttontext';
+ _instance.action.button.buttons = _opts.actionButton;
+ _instance.action.text = _opts.actionText;
+ return _showActions(_opts);
+ }
+ };
+
+ if (_options.fontawesome) {
+ loadScript(_fontAwesome);
+ }
+
+ if (_options.searchselect) {
+ loadScript(_searchselect, function() {
+ Vue.component('v-select', VueSelect.VueSelect);
+ });
+ }
+
+ if (_options.debug) {
+ _interface._botApp = _botApp; // current Vue instance
+ }
+
+ return _interface;
+ });
+
+ return BotUI;
+
+}));