1/* 2 * botui 0.3.8 3 * A JS library to build the UI for your bot 4 * https://botui.org 5 * 6 * Copyright 2019, Moin Uddin 7 * Released under the MIT license. 8*/ 9 10(function (root, factory) { 11 "use strict"; 12 if (typeof define === 'function' && define.amd) { 13 define([], function () { 14 return (root.BotUI = factory(root)); 15 }); 16 } else { 17 root.BotUI = factory(root); 18 } 19}(typeof window !== 'undefined' ? window : this, function (root, undefined) { 20 "use strict"; 21 22 var BotUI = (function (id, opts) { 23 24 opts = opts || {}; 25 26 if(!id) { 27 throw Error('BotUI: Container id is required as first argument.'); 28 } 29 30 if(!document.getElementById(id)) { 31 throw Error('BotUI: Element with id #' + id + ' does not exist.'); 32 } 33 34 if(!root.Vue && !opts.vue) { 35 throw Error('BotUI: Vue is required but not found.'); 36 } 37 38 var _botApp, // current vue instance. 39 _options = { 40 debug: false, 41 fontawesome: true, 42 searchselect: true 43 }, 44 _container, // the outermost Element. Needed to scroll to bottom, for now. 45 _interface = {}, // methods returned by a BotUI() instance. 46 _actionResolve, 47 _markDownRegex = { 48 icon: /!\(([^\)]+)\)/igm, // !(icon) 49 image: /!\[(.*?)\]\((.*?)\)/igm, //  50 link: /\[([^\[]+)\]\(([^\)]+)\)(\^?)/igm // [text](link) ^ can be added at end to set the target as 'blank' 51 }, 52 _fontAwesome = 'https://use.fontawesome.com/ea731dcb6f.js', 53 _esPromisePollyfill = 'https://cdn.jsdelivr.net/es6-promise/4.1.0/es6-promise.min.js', // mostly for IE 54 _searchselect = "https://unpkg.com/[email protected]/dist/vue-select.js"; 55 56 root.Vue = root.Vue || opts.vue; 57 58 // merge opts passed to constructor with _options 59 for (var prop in _options) { 60 if (opts.hasOwnProperty(prop)) { 61 _options[prop] = opts[prop]; 62 } 63 } 64 65 if(!root.Promise && typeof Promise === "undefined" && !opts.promise) { 66 loadScript(_esPromisePollyfill); 67 } 68 69 function _linkReplacer(match, $1, $2, $3) { 70 var _target = $3 ? 'blank' : ''; // check if '^' sign is present with link syntax 71 return "<a class='botui-message-content-link' target='" + _target + "' href='" + $2 +"'>" + $1 + "</a>"; 72 } 73 74 function _parseMarkDown(text) { 75 return text 76 .replace(_markDownRegex.image, "<img class='botui-message-content-image' src='$2' alt='$1' />") 77 .replace(_markDownRegex.icon, "<i class='botui-icon botui-message-content-icon fa fa-$1'></i>") 78 .replace(_markDownRegex.link, _linkReplacer); 79 } 80 81 function loadScript(src, cb) { 82 var script = document.createElement('script'); 83 script.type = 'text/javascript'; 84 script.src = src; 85 86 if(cb) { 87 script.onload = cb; 88 } 89 90 document.body.appendChild(script); 91 } 92 93 function _handleAction(text) { 94 if(_instance.action.addMessage) { 95 _interface.message.human({ 96 delay: 100, 97 content: text 98 }); 99 } 100 _instance.action.show = !_instance.action.autoHide; 101 } 102 103 var _botuiComponent = { 104 template: '<div class=\"botui botui-container\" v-botui-container><div class=\"botui-messages-container\"><div v-for=\"msg in messages\" class=\"botui-message\" :class=\"msg.cssClass\" v-botui-scroll><transition name=\"slide-fade\"><div v-if=\"msg.visible\"><div v-if=\"msg.photo && !msg.loading\" :class=\"[\'profil\', \'profile\', {human: msg.human, \'agent\': !msg.human}]\"> <img :src=\"msg.photo\" :class=\"[{human: msg.human, \'agent\': !msg.human}]\"></div><div :class=\"[{human: msg.human, \'botui-message-content\': true}, msg.type]\"><span v-if=\"msg.type == \'text\'\" v-text=\"msg.content\" v-botui-markdown></span><span v-if=\"msg.type == \'html\'\" v-html=\"msg.content\"></span> <iframe v-if=\"msg.type == \'embed\'\" :src=\"msg.content\" frameborder=\"0\" allowfullscreen></iframe></div></div></transition><div v-if=\"msg.photo && msg.loading && !msg.human\" :class=\"[\'profil\', \'profile\', {human: msg.human, \'agent\': !msg.human}]\"> <img :src=\"msg.photo\" :class=\"[{human: msg.human, \'agent\': !msg.human}]\"></div><div v-if=\"msg.loading\" class=\"botui-message-content loading\"><i class=\"dot\"></i><i class=\"dot\"></i><i class=\"dot\"></i></div></div></div><div class=\"botui-actions-container\"><transition name=\"slide-fade\"><div v-if=\"action.show\" v-botui-scroll><form v-if=\"action.type == \'text\'\" class=\"botui-actions-text\" @submit.prevent=\"handle_action_text()\" :class=\"action.cssClass\"><i v-if=\"action.text.icon\" class=\"botui-icon botui-action-text-icon fa\" :class=\"\'fa-\' + action.text.icon\"></i> <input type=\"text\" ref=\"input\" :type=\"action.text.sub_type\" v-model=\"action.text.value\" class=\"botui-actions-text-input\" :placeholder=\"action.text.placeholder\" :size=\"action.text.size\" :value=\" action.text.value\" :class=\"action.text.cssClass\" required v-focus/> <button type=\"submit\" :class=\"{\'botui-actions-buttons-button\': !!action.text.button, \'botui-actions-text-submit\': !action.text.button}\"><i v-if=\"action.text.button && action.text.button.icon\" class=\"botui-icon botui-action-button-icon fa\" :class=\"\'fa-\' + action.text.button.icon\"></i> <span>{{(action.text.button && action.text.button.label) || \'Go\'}}</span></button></form><form v-if=\"action.type == \'select\'\" class=\"botui-actions-select\" @submit.prevent=\"handle_action_select()\" :class=\"action.cssClass\"><i v-if=\"action.select.icon\" class=\"botui-icon botui-action-select-icon fa\" :class=\"\'fa-\' + action.select.icon\"></i><v-select v-if=\"action.select.searchselect && !action.select.multipleselect\" v-model=\"action.select.value\" :value=\"action.select.value\" :placeholder=\"action.select.placeholder\" class=\"botui-actions-text-searchselect\" :label=\"action.select.label\" :options=\"action.select.options\"></v-select><v-select v-else-if=\"action.select.searchselect && action.select.multipleselect\" multiple v-model=\"action.select.value\" :value=\"action.select.value\" :placeholder=\"action.select.placeholder\" class=\"botui-actions-text-searchselect\" :label=\"action.select.label\" :options=\"action.select.options\"></v-select> <select v-else v-model=\"action.select.value\" class=\"botui-actions-text-select\" :placeholder=\"action.select.placeholder\" :size=\"action.select.size\" :class=\"action.select.cssClass\" required v-focus><option v-for=\"option in action.select.options\" :class=\"action.select.optionClass\" v-bind:value=\"option.value\" :disabled=\"(option.value == \'\')?true:false\" :selected=\"(action.select.value == option.value)?\'selected\':\'\'\"> {{ option.text }}</option></select> <button type=\"submit\" :class=\"{\'botui-actions-buttons-button\': !!action.select.button, \'botui-actions-select-submit\': !action.select.button}\"><i v-if=\"action.select.button && action.select.button.icon\" class=\"botui-icon botui-action-button-icon fa\" :class=\"\'fa-\' + action.select.button.icon\"></i> <span>{{(action.select.button && action.select.button.label) || \'Ok\'}}</span></button></form><div v-if=\"action.type == \'button\'\" class=\"botui-actions-buttons\" :class=\"action.cssClass\"> <button type=\"button\" :class=\"button.cssClass\" class=\"botui-actions-buttons-button\" v-for=\"button in action.button.buttons\" @click=\"handle_action_button(button)\"><i v-if=\"button.icon\" class=\"botui-icon botui-action-button-icon fa\" :class=\"\'fa-\' + button.icon\"></i> {{button.text}}</button></div><form v-if=\"action.type == \'buttontext\'\" class=\"botui-actions-text\" @submit.prevent=\"handle_action_text()\" :class=\"action.cssClass\"><i v-if=\"action.text.icon\" class=\"botui-icon botui-action-text-icon fa\" :class=\"\'fa-\' + action.text.icon\"></i> <input type=\"text\" ref=\"input\" :type=\"action.text.sub_type\" v-model=\"action.text.value\" class=\"botui-actions-text-input\" :placeholder=\"action.text.placeholder\" :size=\"action.text.size\" :value=\"action.text.value\" :class=\"action.text.cssClass\" required v-focus/> <button type=\"submit\" :class=\"{\'botui-actions-buttons-button\': !!action.text.button, \'botui-actions-text-submit\': !action.text.button}\"><i v-if=\"action.text.button && action.text.button.icon\" class=\"botui-icon botui-action-button-icon fa\" :class=\"\'fa-\' + action.text.button.icon\"></i> <span>{{(action.text.button && action.text.button.label) || \'Go\'}}</span></button><div class=\"botui-actions-buttons\" :class=\"action.cssClass\"> <button type=\"button\" :class=\"button.cssClass\" class=\"botui-actions-buttons-button\" v-for=\"button in action.button.buttons\" @click=\"handle_action_button(button)\" autofocus><i v-if=\"button.icon\" class=\"botui-icon botui-action-button-icon fa\" :class=\"\'fa-\' + button.icon\"></i> {{button.text}}</button></div></form></div></transition></div></div>', // replaced by HTML template during build. see Gulpfile.js 105 data: function () { 106 return { 107 action: { 108 text: { 109 size: 30, 110 placeholder: 'Write here ..' 111 }, 112 button: {}, 113 show: false, 114 type: 'text', 115 autoHide: true, 116 addMessage: true 117 }, 118 messages: [] 119 }; 120 }, 121 computed: { 122 isMobile: function () { 123 return root.innerWidth && root.innerWidth <= 768; 124 } 125 }, 126 methods: { 127 handle_action_button: function (button) { 128 for (var i = 0; i < this.action.button.buttons.length; i++) { 129 if(this.action.button.buttons[i].value == button.value && typeof(this.action.button.buttons[i].event) == 'function') { 130 this.action.button.buttons[i].event(button); 131 if (this.action.button.buttons[i].actionStop) return false;
132 break; 133 } 134 } 135 136 _handleAction(button.text); 137 138 var defaultActionObj = { 139 type: 'button', 140 text: button.text, 141 value: button.value 142 }; 143 144 for (var eachProperty in button) { 145 if (button.hasOwnProperty(eachProperty)) { 146 if (eachProperty !== 'type' && eachProperty !== 'text' && eachProperty !== 'value') { 147 defaultActionObj[eachProperty] = button[eachProperty]; 148 } 149 } 150 } 151 152 _actionResolve(defaultActionObj); 153 }, 154 handle_action_text: function () { 155 if(!this.action.text.value) return; 156 _handleAction(this.action.text.value); 157 _actionResolve({ 158 type: 'text', 159 value: this.action.text.value 160 }); 161 this.action.text.value = ''; 162 }, 163 handle_action_select: function () { 164 if(this.action.select.searchselect && !this.action.select.multipleselect) { 165 if(!this.action.select.value.value) return; 166 _handleAction(this.action.select.value[this.action.select.label]); 167 _actionResolve({ 168 type: 'text', 169 value: this.action.select.value.value, 170 text: this.action.select.value.text, 171 obj: this.action.select.value 172 }); 173 } 174 if(this.action.select.searchselect && this.action.select.multipleselect) { 175 if(!this.action.select.value) return; 176 var values = new Array(); 177 var labels = new Array(); 178 for (var i = 0; i < this.action.select.value.length; i++) { 179 values.push(this.action.select.value[i].value); 180 labels.push(this.action.select.value[i][this.action.select.label]); 181 } 182 _handleAction(labels.join(', ')); 183 _actionResolve({ 184 type: 'text', 185 value: values.join(', '), 186 text: labels.join(', '), 187 obj: this.action.select.value 188 }); 189 } 190 else { 191 if(!this.action.select.value) return; 192 for (var i = 0; i < this.action.select.options.length; i++) { // Find select title 193 if (this.action.select.options[i].value == this.action.select.value) { 194 _handleAction(this.action.select.options[i].text); 195 _actionResolve({ 196 type: 'text', 197 value: this.action.select.value, 198 text: this.action.select.options[i].text 199 }); 200 } 201 } 202 } 203 } 204 } 205 }; 206 207 root.Vue.directive('botui-markdown', function (el, binding) { 208 if(binding.value == 'false') return; // v-botui-markdown="false" 209 el.innerHTML = _parseMarkDown(el.textContent); 210 }); 211 212 root.Vue.directive('botui-scroll', { 213 inserted: function (el) { 214 _container.scrollTop = _container.scrollHeight; 215 } 216 }); 217 218 root.Vue.directive('focus', { 219 inserted: function (el) { 220 el.focus(); 221 } 222 }); 223 224 root.Vue.directive('botui-container', { 225 inserted: function (el) { 226 _container = el; 227 } 228 }); 229 230 _botApp = new root.Vue({ 231 components: { 232 'bot-ui': _botuiComponent 233 } 234 }).$mount('#' + id); 235 236 var _instance = _botApp.$children[0]; // to access the component's data 237 238 function _addMessage(_msg) { 239 240 if(!_msg.loading && !_msg.content) { 241 throw Error('BotUI: "content" is required in a non-loading message object.'); 242 } 243 244 _msg.type = _msg.type || 'text'; 245 _msg.visible = (_msg.delay || _msg.loading) ? false : true; 246 var _index = _instance.messages.push(_msg) - 1; 247 248 return new Promise(function (resolve, reject) { 249 setTimeout(function () { 250 if(_msg.delay) { 251 _msg.visible = true; 252 253 if(_msg.loading) { 254 _msg.loading = false; 255 } 256 } 257 resolve(_index); 258 }, _msg.delay || 0); 259 }); 260 } 261 262 function _checkOpts(_opts) { 263 if(typeof _opts === 'string') { 264 _opts = { 265 content: _opts 266 }; 267 } 268 return _opts || {}; 269 } 270 271 _interface.message = { 272 add: function (addOpts) {
273 return _addMessage( _checkOpts(addOpts) ); 274 }, 275 bot: function (addOpts) { 276 addOpts = _checkOpts(addOpts); 277 return _addMessage(addOpts); 278 }, 279 human: function (addOpts) { 280 addOpts = _checkOpts(addOpts); 281 addOpts.human = true; 282 return _addMessage(addOpts); 283 }, 284 get: function (index) { 285 return Promise.resolve(_instance.messages[index]); 286 }, 287 remove: function (index) { 288 _instance.messages.splice(index, 1); 289 return Promise.resolve(); 290 }, 291 update: function (index, msg) { // only content can be updated, not the message type. 292 var _msg = _instance.messages[index]; 293 _msg.content = msg.content; 294 _msg.visible = !msg.loading; 295 _msg.loading = !!msg.loading; 296 return Promise.resolve(msg.content); 297 }, 298 removeAll: function () { 299 _instance.messages.splice(0, _instance.messages.length); 300 return Promise.resolve(); 301 } 302 }; 303 304 function mergeAtoB(objA, objB) { 305 for (var prop in objA) { 306 if (!objB.hasOwnProperty(prop)) { 307 objB[prop] = objA[prop]; 308 } 309 } 310 } 311 312 function _checkAction(_opts) { 313 if(!_opts.action && !_opts.actionButton && !_opts.actionText) { 314 throw Error('BotUI: "action" property is required.'); 315 } 316 } 317 318 function _showActions(_opts) { 319 320 _checkAction(_opts); 321 322 mergeAtoB({ 323 type: 'text', 324 cssClass: '', 325 autoHide: true, 326 addMessage: true 327 }, _opts); 328 329 _instance.action.type = _opts.type; 330 _instance.action.cssClass = _opts.cssClass; 331 _instance.action.autoHide = _opts.autoHide; 332 _instance.action.addMessage = _opts.addMessage; 333 334 return new Promise(function(resolve, reject) { 335 _actionResolve = resolve; // resolved when action is performed, i.e: button clicked, text submitted, etc. 336 setTimeout(function () { 337 _instance.action.show = true; 338 }, _opts.delay || 0); 339 }); 340 }; 341 342 _interface.action = { 343 show: _showActions, 344 hide: function () { 345 _instance.action.show = false; 346 return Promise.resolve(); 347 }, 348 text: function (_opts) { 349 _checkAction(_opts); 350 _instance.action.text = _opts.action; 351 return _showActions(_opts); 352 }, 353 button: function (_opts) { 354 _checkAction(_opts); 355 _opts.type = 'button'; 356 _instance.action.button.buttons = _opts.action; 357 return _showActions(_opts); 358 }, 359 select: function (_opts) { 360 _checkAction(_opts); 361 _opts.type = 'select'; 362 _opts.action.label = _opts.action.label || 'text'; 363 _opts.action.value = _opts.action.value || ''; 364 _opts.action.searchselect = typeof _opts.action.searchselect !== 'undefined' ? _opts.action.searchselect : _options.searchselect; 365 _opts.action.multipleselect = _opts.action.multipleselect || false; 366 if (_opts.action.searchselect && typeof(_opts.action.value) == 'string') { 367 if (!_opts.action.multipleselect) { 368 for (var i = 0; i < _opts.action.options.length; i++) { // Find object 369 if (_opts.action.options[i].value == _opts.action.value) { 370 _opts.action.value = _opts.action.options[i] 371 } 372 } 373 } 374 else { 375 var vals = _opts.action.value.split(','); 376 _opts.action.value = new Array(); 377 for (var i = 0; i < _opts.action.options.length; i++) { // Find object 378 for (var j = 0; j < vals.length; j++) { // Search values 379 if (_opts.action.options[i].value == vals[j]) { 380 _opts.action.value.push(_opts.action.options[i]); 381 } 382 } 383 } 384 } 385 } 386 if (!_opts.action.searchselect) { _opts.action.options.unshift({value:'',text : _opts.action.placeholder}); } 387 _instance.action.button = _opts.action.button; 388 _instance.action.select = _opts.action; 389 return _showActions(_opts); 390 }, 391 buttontext: function (_opts) { 392 _checkAction(_opts); 393 _opts.type = 'buttontext'; 394 _instance.action.button.buttons = _opts.actionButton; 395 _instance.action.text = _opts.actionText; 396 return _showActions(_opts); 397 } 398 }; 399 400 if(_options.fontawesome) { 401 loadScript(_fontAwesome); 402 } 403 404 if(_options.searchselect) { 405 loadScript(_searchselect, function() { 406 Vue.component('v-select', VueSelect.VueSelect); 407 }); 408 } 409 410 if(_options.debug) { 411 _interface._botApp = _botApp; // current Vue instance 412 } 413 414 return _interface; 415 }); 416 417 return BotUI; 418 419}
vendor: 4 bytes, line 419
419));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.