`;
76 | chat.appendChild(bubble);
77 |
78 | scroll = window.setInterval(scrollDown, 16);
79 | setTimeout(() => {
80 | window.clearInterval(scroll);
81 | chat.scrollTop = chat.scrollHeight;
82 | }, 300);
83 |
84 | setTimeout(() => {
85 | bubble.classList.add('show');
86 | }, 10);
87 |
88 | if (type === 'user') {
89 | let animate = chat.querySelectorAll('button:not(:disabled)');
90 | for (let i = 0; i < animate.length; i += 1) {
91 | slideIn(animate[i], i);
92 | }
93 | bubble.classList.add('active');
94 | }
95 | };
96 |
97 | const randomReply = replies => replies[Math.floor(Math.random() * replies.length)];
98 |
99 | const checkUp = () => {
100 | let lastMessage = document.querySelector('.active'),
101 | idleReplies = [
102 | 'Did you fall asleep? 😴',
103 | 'Coffee break? ☕',
104 | 'Still there?',
105 | '❄ 🌱 🌞 🍂'
106 | ];
107 | if (lastMessage) {
108 | lastMessage.parentNode.removeChild(lastMessage);
109 | }
110 | newMessage(randomReply(idleReplies), 'bot');
111 | setTimeout(() => {
112 | let helpReplies = [
113 | 'Don\'t like this conversation? Send an email to mike@redvolume.com if you want a real one. 🚀',
114 | 'Not finding what you\'re looking for? Send an email to mike@redvolume.com with any questions...',
115 | 'Wanna talk to a real person? 💬 Fire off an email to mike@redvolume.com. 🔥'
116 | ];
117 | newMessage(randomReply(helpReplies), 'bot');
118 | setTimeout(() => {
119 | let knowMoreReplies = [
120 | 'Where\'s the normal web page? 😞',
121 | 'I want a regular web page 😱',
122 | 'Do you have a regular website? 😻'
123 | ],
124 | menuAgainReplies = [
125 | 'Show me the options again please ✅',
126 | 'Ok, go! 🚗',
127 | 'I wanna check something 👍'
128 | ];
129 | newMessage(` `);
130 | }, 300);
131 | }, 500);
132 | };
133 |
134 | const init = () => {
135 | let welcomeReplies = [
136 | 'Hello! 👋 I\'m Mike, a medical librarian working at Karolinska Institutet. I try to create the best user experiences possible, both physical and virtual, using humour, friendliness, web technologies and good design.'
137 | ];
138 | idle = window.setInterval(() => {
139 | window.clearInterval(idle);
140 | checkUp();
141 | }, idletime);
142 | newMessage(randomReply(welcomeReplies), 'bot');
143 | setTimeout(() => {
144 | let startReplies = [
145 | 'Hey, tell me more! 🚀',
146 | 'Hi! 👀',
147 | 'I\'ll bite 😄'
148 | ];
149 | newMessage(``);
150 | }, 300);
151 | };
152 |
153 | const makeUserBubble = el => {
154 | el.parentNode.parentNode.classList.add('selected');
155 | el.parentNode.parentNode.classList.remove('active');
156 | el.parentNode.parentNode.innerHTML = `
${el.textContent}
`;
157 | };
158 |
159 | const showMenu = again => {
160 | let menu = '',
161 | goBack = chat.querySelector('button.newmenu'),
162 | againReplies = [
163 | 'Here\'s a few things that I can tell you about... 🎤',
164 | 'Ok, check this out! 👇',
165 | 'Anything else you\'re interested in? 🙏'
166 | ],
167 | replies = [
168 | 'What would you like to know more about? 💡',
169 | 'Can I interest you in any of this? 💯',
170 | 'Select something of the following... 👇'
171 | ];
172 | if (goBack) {
173 | makeUserBubble(goBack);
174 | }
175 | setTimeout(() => {
176 | again ? newMessage(randomReply(againReplies), 'bot') : newMessage(randomReply(replies), 'bot');
177 | structure.menu.forEach((val, index) => {
178 | menu += ``;
179 | });
180 | setTimeout(() => {
181 | newMessage(menu);
182 | }, 300);
183 | }, 500);
184 | idle = window.setInterval(() => {
185 | window.clearInterval(idle);
186 | checkUp();
187 | }, idletime);
188 | };
189 |
190 | const menuClick = clicked => {
191 | let submenu = '',
192 | menuChoice = structure.menu[clicked.getAttribute('data-submenu')],
193 | replies = [
194 | '👍 Here\'s what I have on that...',
195 | 'See anything interesting? 🙈',
196 | 'Any of this cool?'
197 | ],
198 | userReplies = [
199 | `I wanna read about something other than ${menuChoice.title.toLowerCase()} 😜`,
200 | 'Show me the menu again 😋',
201 | `${menuChoice.title} was interesting, but show me something else... 😒`
202 | ];
203 | menuChoice.submenu.forEach(val => {
204 | let id = `${menuChoice.id}-${val.id}`;
205 | submenu += ``;
206 | });
207 | submenu += ` `;
208 | setTimeout(() => {
209 | newMessage(randomReply(replies), 'bot');
210 | setTimeout(() => {
211 | newMessage(submenu);
212 | }, 300);
213 | }, 500);
214 | };
215 |
216 | const toggleContent = article => {
217 | let buttons = chat.querySelectorAll('button');
218 | if (article) {
219 | article.classList.add('show');
220 | chat.setAttribute('aria-hidden', 'true');
221 | content.setAttribute('aria-hidden', 'false');
222 | content.tabIndex = '0';
223 | content.focus();
224 | } else {
225 | content.setAttribute('aria-hidden', 'true');
226 | content.tabIndex = '-1';
227 | chat.setAttribute('aria-hidden', 'false');
228 | if (history.state && history.state.id === 'content') {
229 | history.back();
230 | }
231 | setTimeout(() => {
232 | let active = document.querySelector('.content article.show');
233 | if (active) {
234 | active.classList.remove('show');
235 | chat.querySelector(`button[data-content="${active.id}"]`).focus();
236 | }
237 | }, 300);
238 | }
239 | for (let i = 0; i < buttons.length; i += 1) {
240 | buttons[i].tabIndex = article ? '-1' : '0';
241 | }
242 | };
243 |
244 | const subMenuClick = clicked => {
245 | if (clicked.classList.contains('newmenu')) {
246 | showMenu(true);
247 | } else {
248 | toggleContent(document.getElementById(clicked.getAttribute('data-content')));
249 | history.pushState({'id': clicked.getAttribute('data-content')}, '', `#${clicked.getAttribute('data-content')}`);
250 | }
251 | };
252 |
253 | document.addEventListener('click', e => {
254 | if (e.target.classList.contains('choice')) {
255 | window.clearInterval(idle);
256 | if (!e.target.classList.contains('submenu')) {
257 | makeUserBubble(e.target);
258 | }
259 |
260 | if (e.target.classList.contains('menu')) {
261 | menuClick(e.target);
262 | }
263 |
264 | if (e.target.classList.contains('submenu')) {
265 | subMenuClick(e.target);
266 | }
267 |
268 | if (e.target.classList.contains('start')) {
269 | showMenu();
270 | }
271 |
272 | if (e.target.classList.contains('showmenu')) {
273 | showMenu(true);
274 | }
275 |
276 | if (e.target.classList.contains('showinfo')) {
277 | let infoReplies = [
278 | 'Here\'s my website ',
279 | 'Here you go: ',
280 | 'Check this one out '
281 | ],
282 | okReplies = [
283 | 'OK 😎',
284 | 'How do I get back? 🌒',
285 | 'Ok, thanks! 👌'
286 | ];
287 | setTimeout(() => {
288 | newMessage(`${randomReply(infoReplies)} https://librarian.codes`, 'bot');
289 | setTimeout(() => {
290 | newMessage(``);
291 | }, 300);
292 | }, 500);
293 | }
294 | }
295 | if (e.target.classList.contains('close')) {
296 | e.preventDefault();
297 | history.back();
298 | }
299 | });
300 |
301 | document.addEventListener('keydown', e => {
302 | if (e.keyCode === 27 && content.getAttribute('aria-hidden') === 'false') {
303 | history.back();
304 | }
305 | });
306 |
307 | window.addEventListener('popstate', e => {
308 | toggleContent(e.state && e.state.id && document.getElementById(e.state.id) ? document.getElementById(e.state.id) : '');
309 | });
310 |
311 | setTimeout(() => {
312 | init();
313 | }, 500);
314 |
315 | if (document.getElementById(window.location.hash.split('#')[1])) {
316 | let contentId = window.location.hash.split('#')[1];
317 | history.replaceState('', '', '.');
318 | setTimeout(() => {
319 | toggleContent(document.getElementById(contentId));
320 | history.pushState({'id': contentId}, '', `#${contentId}`);
321 | }, 10);
322 | }
323 | }
324 |
--------------------------------------------------------------------------------
/js/app.min.js:
--------------------------------------------------------------------------------
1 | "use strict";!function(){var t={menu:[{title:"Projects",id:"projects",submenu:[{title:"Project 1",id:"p1"},{title:"Project 2",id:"p2"},{title:"Project 3",id:"p3"}]},{title:"Writing",id:"writing",submenu:[{title:"Articles",id:"articles"},{title:"Theses",id:"theses"}]},{title:"Contact",id:"contact",submenu:[{title:"Social media",id:"socialmedia"},{title:"Email",id:"email"},{title:"Address",id:"address"}]}]},e=void 0,n=45e3,o=document.querySelector(".chat"),i=document.querySelector(".content"),a=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"user",n=document.createElement("li"),i=function(t,e){setTimeout(function(){t.classList.add("show")},150*e?150*e:10)},a=void 0,s=function(){o.scrollTop+=Math.floor(n.offsetHeight/18)};if(n.classList.add("message"),n.classList.add(e),n.innerHTML="user"===e?"":"
"+t+"
",o.appendChild(n),a=window.setInterval(s,16),setTimeout(function(){window.clearInterval(a),o.scrollTop=o.scrollHeight},300),setTimeout(function(){n.classList.add("show")},10),"user"===e){for(var r=o.querySelectorAll("button:not(:disabled)"),c=0;cmike@redvolume.com if you want a real one. 🚀','Not finding what you\'re looking for? Send an email to mike@redvolume.com with any questions...','Wanna talk to a real person? 💬 Fire off an email to mike@redvolume.com. 🔥'];a(s(t),"bot"),setTimeout(function(){var t=["Where's the normal web page? 😞","I want a regular web page 😱","Do you have a regular website? 😻"],e=["Show me the options again please ✅","Ok, go! 🚗","I wanna check something 👍"];a(' ")},300)},500)},c=function(){var t=["Hello! 👋 I'm Mike, a medical librarian working at Karolinska Institutet. I try to create the best user experiences possible, both physical and virtual, using humour, friendliness, web technologies and good design."];e=window.setInterval(function(){window.clearInterval(e),r()},n),a(s(t),"bot"),setTimeout(function(){var t=["Hey, tell me more! 🚀","Hi! 👀","I'll bite 😄"];a('")},300)},u=function(t){t.parentNode.parentNode.classList.add("selected"),t.parentNode.parentNode.classList.remove("active"),t.parentNode.parentNode.innerHTML="
"+t.textContent+"
"},l=function(i){var c="",l=o.querySelector("button.newmenu"),d=["Here's a few things that I can tell you about... 🎤","Ok, check this out! 👇","Anything else you're interested in? 🙏"],m=["What would you like to know more about? 💡","Can I interest you in any of this? 💯","Select something of the following... 👇"];l&&u(l),setTimeout(function(){i?a(s(d),"bot"):a(s(m),"bot"),t.menu.forEach(function(t,e){c+='"}),setTimeout(function(){a(c)},300)},500),e=window.setInterval(function(){window.clearInterval(e),r()},n)},d=function(e){var n="",o=t.menu[e.getAttribute("data-submenu")],i=["👍 Here's what I have on that...","See anything interesting? 🙈","Any of this cool?"],r=["I wanna read about something other than "+o.title.toLowerCase()+" 😜","Show me the menu again 😋",o.title+" was interesting, but show me something else... 😒"];o.submenu.forEach(function(t){var e=o.id+"-"+t.id;n+='"}),n+=' ",setTimeout(function(){a(s(i),"bot"),setTimeout(function(){a(n)},300)},500)},m=function(t){var e=o.querySelectorAll("button");t?(t.classList.add("show"),o.setAttribute("aria-hidden","true"),i.setAttribute("aria-hidden","false"),i.tabIndex="0",i.focus()):(i.setAttribute("aria-hidden","true"),i.tabIndex="-1",o.setAttribute("aria-hidden","false"),history.state&&"content"===history.state.id&&history.back(),setTimeout(function(){var t=document.querySelector(".content article.show");t&&(t.classList.remove("show"),o.querySelector('button[data-content="'+t.id+'"]').focus())},300));for(var n=0;nhttps://librarian.codes',"bot"),setTimeout(function(){a('")},300)},500)}()),t.target.classList.contains("close")&&(t.preventDefault(),history.back())}),document.addEventListener("keydown",function(t){27===t.keyCode&&"false"===i.getAttribute("aria-hidden")&&history.back()}),window.addEventListener("popstate",function(t){m(t.state&&t.state.id&&document.getElementById(t.state.id)?document.getElementById(t.state.id):"")}),setTimeout(function(){c()},500),document.getElementById(window.location.hash.split("#")[1])&&!function(){var t=window.location.hash.split("#")[1];history.replaceState("","","."),setTimeout(function(){m(document.getElementById(t)),history.pushState({id:t},"","#"+t)},10)}()}();
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "conversational-ui",
3 | "version": "1.0.0",
4 | "description": "Conversational UI example",
5 | "main": "gulpfile.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/dermike/conversational-ui.git"
12 | },
13 | "keywords": [
14 | "ui"
15 | ],
16 | "author": "Mikael Jergefelt",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/dermike/conversational-ui/issues"
20 | },
21 | "homepage": "https://github.com/dermike/conversational-ui#readme",
22 | "devDependencies": {
23 | "babel-preset-es2015": "^6.9.0",
24 | "gulp": "^3.9.1",
25 | "gulp-babel": "^6.1.2",
26 | "gulp-rename": "^1.2.2",
27 | "gulp-uglify": "^1.5.3"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------