forked from om/chatcontrol-live-demo
Compare commits
16 commits
asbjørn-ad
...
master
Author | SHA1 | Date | |
---|---|---|---|
f4afe0fc3e | |||
74ed61169c | |||
3f2575e04e | |||
om | 56f1e2f9a4 | ||
om | 9533513e5f | ||
AsbjornOlling | 9c5df72a8d | ||
AsbjornOlling | a5c970db11 | ||
AsbjornOlling | 5a3bc05a70 | ||
911a58bd6b | |||
om | e0b37b2012 | ||
aca88b346a | |||
om | 385475e951 | ||
c51cb58b93 | |||
om | b6d9dc6e4c | ||
496a6b7caf | |||
om | 7a1c75aa99 |
667
chatapp.svg
667
chatapp.svg
|
@ -5,30 +5,71 @@
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
width="300"
|
width="300"
|
||||||
height="500"
|
height="600"
|
||||||
viewBox="0 0 300 500"
|
viewBox="0 0 300 600"
|
||||||
onload="start_chat('Ven')"
|
onload="start_chat('Ven')"
|
||||||
overflow="auto"
|
overflow="auto"
|
||||||
|
style="font-family: sans; border-radius: 10px;"
|
||||||
>
|
>
|
||||||
<defs>
|
<defs>
|
||||||
<symbol id="left_chat_message"><text style="text-align: left"></text></symbol>
|
<symbol id="kaereste_foto" width="60" height="200">
|
||||||
<symbol id="right_chat_message"><text style="text-align: right"></text></symbol>
|
<g style="fill: pink; stroke: pink;">
|
||||||
|
<circle cx="20" cy="20" r="20" />
|
||||||
|
<line x1="20" y1="40" x2="20" y2="60" />
|
||||||
|
<ellipse cx="20" cy="110" rx="10" ry="50" />
|
||||||
|
<!-- left arm -->
|
||||||
|
<polyline fill="none"
|
||||||
|
points="15,70 5,50 0,40" />
|
||||||
|
<!-- right arm -->
|
||||||
|
<polyline fill="none"
|
||||||
|
points="25,70 35,50 40,40" />
|
||||||
|
<!-- left leg -->
|
||||||
|
<polyline fill="none"
|
||||||
|
points="15,155 5,135 0,125" />
|
||||||
|
<!-- right leg -->
|
||||||
|
<polyline fill="none"
|
||||||
|
points="25,155 35,135 40,125" />
|
||||||
|
<!-- the thing -->
|
||||||
|
<text x="14" y="160" style="font: bold 20px sans-serif" fill="black">?</text>
|
||||||
|
</g>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="flag_denmark" width="20" height="15">
|
||||||
|
<path fill="#c8102e" d="M0,0H20V15H0Z"/>
|
||||||
|
<path fill="#fff" d="M0,6H6V0H8V6H20V8H8V15H6V8H0Z"/>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<symbol id="flag_uk" width="50" height="30">
|
||||||
|
<clipPath id="t">
|
||||||
|
<path d="M25,15h25v15zv15h-25zh-25v-15zv-15h25z"/>
|
||||||
|
</clipPath>
|
||||||
|
<path d="M0,0v30h50v-30z" fill="#012169"/>
|
||||||
|
<path d="M0,0 50,30M50,0 0,30" stroke="#fff" stroke-width="6"/>
|
||||||
|
<path d="M0,0 50,30M50,0 0,30" clip-path="url(#t)" stroke="#C8102E" stroke-width="4"/>
|
||||||
|
<path d="M-1 11h22v-12h8v12h22v8h-22v12h-8v-12h-22z" fill="#C8102E" stroke="#FFF" stroke-width="2"/>
|
||||||
|
</symbol>
|
||||||
</defs>
|
</defs>
|
||||||
<script type="application/ecmascript">
|
<script type="application/ecmascript">
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
'use strict';
|
||||||
|
|
||||||
const svgns = "http://www.w3.org/2000/svg";
|
const svgns = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
// must be the same as the SVG dimensions
|
// must be the same as the SVG dimensions
|
||||||
const width = 300;
|
const width = 300;
|
||||||
const height = 500;
|
const height = 550;
|
||||||
|
|
||||||
const line_height = 6;
|
const line_height = 14;
|
||||||
const max_visible_lines = 10;
|
const bubble_padding = 6;
|
||||||
const messages_y_offset = 20;
|
const bubble_spacing = 5;
|
||||||
const typing_speed = 70;
|
const visible_chat_area = {'top': 60,
|
||||||
|
'bottom': height - 100 // minus status & input areas
|
||||||
|
};
|
||||||
|
const typing_speed = 50;
|
||||||
|
|
||||||
var chat_lines_count = 0;
|
|
||||||
var conversation_count = 0;
|
var conversation_count = 0;
|
||||||
|
var current_dialog = null;
|
||||||
|
var current_language = "da";
|
||||||
|
var current_dialog_fn = null;
|
||||||
|
|
||||||
/// SVG 1.1 doesn't do proper text splitting into several lines.
|
/// SVG 1.1 doesn't do proper text splitting into several lines.
|
||||||
/// we need to do it ourselves.
|
/// we need to do it ourselves.
|
||||||
|
@ -61,94 +102,119 @@ function split_text_into_lines(text, upper_line_length) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A class holding a chat message.
|
function is_hyperlink(str) {
|
||||||
///
|
return str.startsWith('http');
|
||||||
/// ChatMessages's are owned by a Dialog.
|
}
|
||||||
function ChatMessage(message_text, is_myself) {
|
|
||||||
let lines = split_text_into_lines(message_text, 28);
|
|
||||||
let tspans = []
|
|
||||||
let bubbles = [];
|
|
||||||
|
|
||||||
|
function create_link_node(url) {
|
||||||
|
let link = create_svg_node('a', {target:'_blank', href: url});
|
||||||
|
link.appendChild(document.createTextNode(url));
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A class holding a text chat message.
|
||||||
|
///
|
||||||
|
/// *ChatMessages are owned by a Dialog.
|
||||||
|
function TextChatMessage(message_text, is_myself) {
|
||||||
|
let lines = split_text_into_lines(message_text, 28);
|
||||||
|
let bubble_color = (is_myself)? '#0084FF': '#E4E6EB';
|
||||||
|
let text_color = (is_myself)? 'white': 'black';
|
||||||
let container = document.getElementById('messages');
|
let container = document.getElementById('messages');
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
this.height = 0; // height on screen when fully visible
|
||||||
|
this.group = null; // a <g> with a transform=translate(0,y_shift) attribute
|
||||||
|
|
||||||
/// Render the chat message on the screen
|
/// Render the chat message on the screen
|
||||||
this.draw = function() {
|
this.draw = function(y_offset, y_shift) {
|
||||||
let text = document.createElementNS(svgns, 'text');
|
let group = create_svg_node('g', {'transform': `translate(0, ${y_shift})`});
|
||||||
let x = (is_myself ? 190 : 110);
|
|
||||||
text.setAttribute('x', `${x}%`);
|
|
||||||
text.setAttribute('font-size', '14px');
|
|
||||||
text.setAttribute('text-anchor', is_myself ? 'end' : 'start');
|
|
||||||
|
|
||||||
lines.forEach(function(line) {
|
let x = (is_myself ? 195 : 105);
|
||||||
let y = tspan_y_pos(chat_lines_count++);
|
let text = create_svg_node('text', {
|
||||||
let tspan = document.createElementNS(svgns, 'tspan');
|
'x': `${x}%`,
|
||||||
tspan.setAttribute('x', `${x}%`);
|
'font-size': `${line_height}px`,
|
||||||
tspan.setAttribute('y', `${y}%`); // important: y is lower text baseline
|
'text-anchor': is_myself ? 'end' : 'start'
|
||||||
tspan.setAttribute('z', 10);
|
|
||||||
tspan.appendChild(document.createTextNode(line));
|
|
||||||
tspan.setAttribute('fill', 'green');
|
|
||||||
tspan.setAttribute('width', '5%');
|
|
||||||
|
|
||||||
text.appendChild(tspan);
|
|
||||||
tspans.push(tspan);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// done making text
|
let height_so_far = y_offset;
|
||||||
// it must be in the DOM for getBBox() to work
|
|
||||||
container.appendChild(text);
|
|
||||||
|
|
||||||
// make chat bubble
|
lines.forEach(function(line) {
|
||||||
const padding = 6;
|
let y = height_so_far;
|
||||||
const textbox = text.getBBox();
|
let tspan = create_svg_node('tspan', {
|
||||||
const y_percent = Math.round(((textbox.y - padding) / height) * 100);
|
'x': `${x}%`,
|
||||||
const bubble = document.createElementNS(svgns, 'rect');
|
'y': `${y + line_height}`, // important: y is lower text baseline
|
||||||
bubble.setAttribute('x', textbox.x-padding);
|
'fill': text_color,
|
||||||
bubble.setAttribute('y', `${y_percent}%`);
|
'width': '5%'
|
||||||
bubble.setAttribute('width', textbox.width + 2*padding);
|
});
|
||||||
bubble.setAttribute('height', textbox.height + 2*padding);
|
enable_scrolling(tspan)
|
||||||
bubble.setAttribute('rx', 8);
|
|
||||||
bubble.setAttribute('fill', 'black');
|
|
||||||
bubbles.push(bubble);
|
|
||||||
|
|
||||||
// put stuff in dom in the right order
|
if(is_hyperlink(line)) {
|
||||||
container.appendChild(bubble);
|
tspan.appendChild(create_link_node(line));
|
||||||
container.removeChild(text);
|
} else {
|
||||||
container.appendChild(text);
|
tspan.appendChild(document.createTextNode(line));
|
||||||
|
}
|
||||||
|
text.appendChild(tspan);
|
||||||
|
|
||||||
|
height_so_far += line_height;
|
||||||
|
});
|
||||||
|
|
||||||
|
group.appendChild(text); // needs to be part of the DOM *now*
|
||||||
|
container.appendChild(group);
|
||||||
|
let bubble = create_bubble(text, bubble_color);
|
||||||
|
group.appendChild(bubble);
|
||||||
|
redraw_on_top(text);
|
||||||
|
|
||||||
|
this.height = bubble.getBBox().height + bubble_spacing;
|
||||||
|
this.group = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move a chat message on the screen (to simulate scrolling)
|
/// Move a chat message on the screen (to simulate scrolling)
|
||||||
this.shift_y_pos = function(by, lower_visibility_boundary) {
|
this.shift_y_pos = function(by) {
|
||||||
tspans.forEach(tspan => {
|
redraw_on_top(document.getElementById('contact_name_box'));
|
||||||
let is_already_hidden = tspan.getAttribute('visibility') == 'hidden';
|
this.group.setAttribute('transform', `translate(0, ${by})`);
|
||||||
let old = parseInt(tspan.getAttribute('y').split('%')[0]);
|
|
||||||
let new_value = old + by;
|
|
||||||
tspan.setAttribute('y', `${new_value}%`);
|
|
||||||
|
|
||||||
let visibility;
|
|
||||||
if(new_value < lower_visibility_boundary) {
|
|
||||||
visibility = 'hidden';
|
|
||||||
if(!is_already_hidden) {
|
|
||||||
chat_lines_count--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
visibility = 'visible';
|
|
||||||
if(is_already_hidden) {
|
|
||||||
chat_lines_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tspan.setAttribute('visibility', visibility);
|
|
||||||
});
|
|
||||||
bubbles.forEach(bubble => {
|
|
||||||
const old = parseInt(bubble.getAttribute('y').split('%')[0]);
|
|
||||||
const new_value = old + by;
|
|
||||||
bubble.setAttribute('y', `${new_value}%`);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to determine the position of a chat-tspan
|
/// A class holding a image-based chat message.
|
||||||
function tspan_y_pos(message_index) {
|
///
|
||||||
return messages_y_offset + message_index * line_height;
|
/// *ChatMessages are owned by a Dialog.
|
||||||
|
function ImageChatMessage(symbol_id, is_myself) {
|
||||||
|
let container = document.getElementById('messages');
|
||||||
|
let bubble_color = (is_myself)? 'white': '#DDFF66';
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
this.height = 0; // height on screen when fully visible
|
||||||
|
this.group = null; // a <g> with a transform=translate(0,y_shift) attribute
|
||||||
|
|
||||||
|
this.draw = function(y_offset, y_shift) {
|
||||||
|
let group = create_svg_node('g', {'transform': `translate(0, ${y_shift})`});
|
||||||
|
|
||||||
|
let x = (is_myself ? 190 : 110);
|
||||||
|
let img = create_svg_node('use', {
|
||||||
|
'href': symbol_id,
|
||||||
|
x: `${x}%`,
|
||||||
|
y: y_offset,
|
||||||
|
});
|
||||||
|
|
||||||
|
group.appendChild(img); // needs to be part of the DOM *now*
|
||||||
|
container.appendChild(group);
|
||||||
|
let bubble = create_bubble(img, bubble_color);
|
||||||
|
group.appendChild(bubble);
|
||||||
|
redraw_on_top(img);
|
||||||
|
|
||||||
|
this.height = bubble.getBBox().height + bubble_spacing;
|
||||||
|
this.group = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shift_y_pos = function(by) {
|
||||||
|
redraw_on_top(document.getElementById('contact_name_box'));
|
||||||
|
this.group.setAttribute('transform', `translate(0, ${by})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_chat_message(content, is_myself) {
|
||||||
|
let constr = (content.startsWith('#'))? ImageChatMessage: TextChatMessage;
|
||||||
|
return new constr(content, is_myself);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Promise-based version of setTimeout
|
/// Promise-based version of setTimeout
|
||||||
|
@ -183,6 +249,40 @@ function swipe_viewport() {
|
||||||
new Promise(animate);
|
new Promise(animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create a chat bubble around an element.
|
||||||
|
//
|
||||||
|
// The element must already be inside the DOM for this to work.
|
||||||
|
function create_bubble(inner_element, color) {
|
||||||
|
const bbox = inner_element.getBBox();
|
||||||
|
let bubble = create_svg_node('rect', {
|
||||||
|
'x': bbox.x - bubble_padding,
|
||||||
|
'y': bbox.y - bubble_padding,
|
||||||
|
'width': bbox.width + 2 * bubble_padding,
|
||||||
|
'height': bbox.height + 2 * bubble_padding,
|
||||||
|
'rx': 8,
|
||||||
|
'fill': color,
|
||||||
|
});
|
||||||
|
enable_scrolling(bubble);
|
||||||
|
return bubble;
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_svg_node(tag_name, attrs) {
|
||||||
|
let node = document.createElementNS(svgns, tag_name);
|
||||||
|
for(let attr in attrs) {
|
||||||
|
node.setAttribute(attr, attrs[attr]);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensure that an element is redrawn above all other elements
|
||||||
|
///
|
||||||
|
/// The element's children will also be redrawn on top
|
||||||
|
function redraw_on_top(element) {
|
||||||
|
let parent = element.parentNode;
|
||||||
|
parent.appendChild(parent.removeChild(element));
|
||||||
|
}
|
||||||
|
|
||||||
function removeAllChildren(parentNode) {
|
function removeAllChildren(parentNode) {
|
||||||
while(parentNode.firstChild) {
|
while(parentNode.firstChild) {
|
||||||
parentNode.removeChild(parentNode.lastChild);
|
parentNode.removeChild(parentNode.lastChild);
|
||||||
|
@ -193,46 +293,82 @@ function start_chat(who) {
|
||||||
let indicator = document.getElementById('contact_indicator');
|
let indicator = document.getElementById('contact_indicator');
|
||||||
indicator.childNodes[0].data = `Kontakt: ${who}`;
|
indicator.childNodes[0].data = `Kontakt: ${who}`;
|
||||||
removeAllChildren(document.getElementById('messages'));
|
removeAllChildren(document.getElementById('messages'));
|
||||||
chat_lines_count = 0;
|
|
||||||
swipe_viewport();
|
swipe_viewport();
|
||||||
|
|
||||||
switch(who) {
|
switch(who) {
|
||||||
case 'Ven':
|
case 'Ven':
|
||||||
dialog_ven();
|
current_dialog_fn = dialog_ven;
|
||||||
break;
|
break;
|
||||||
case 'Mor':
|
case 'Mor':
|
||||||
dialog_mor();
|
current_dialog_fn = dialog_mor;
|
||||||
break;
|
break;
|
||||||
case 'Kæreste':
|
case 'Kæreste':
|
||||||
dialog_kaereste();
|
current_dialog_fn = dialog_kaereste;
|
||||||
|
break;
|
||||||
|
case 'Politiven':
|
||||||
|
current_dialog_fn = dialog_politi;
|
||||||
|
break;
|
||||||
|
case 'ChatSupport':
|
||||||
|
current_dialog_fn = dialog_support;
|
||||||
|
break;
|
||||||
|
case 'Nabopige':
|
||||||
|
current_dialog_fn = dialog_nabopige;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
alert(`Unknown contact: ${who}`);
|
alert(`kontakten ${who} er ikke implementeret endnu`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
current_dialog_fn();
|
||||||
}
|
}
|
||||||
|
|
||||||
function Dialog(chat_partner) {
|
function Dialog() {
|
||||||
let conversation_id = ++conversation_count;
|
let conversation_id = ++conversation_count;
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
|
this.y_shift = visible_chat_area.top; // for scrolling
|
||||||
|
this.all_elements_height = 0; // also for scrolling
|
||||||
|
|
||||||
|
this.is_active = function() {
|
||||||
|
return conversation_id == conversation_count;
|
||||||
|
}
|
||||||
|
|
||||||
async function post_message(dialog, message, is_myself) {
|
async function post_message(dialog, message, is_myself) {
|
||||||
if(dialog.messages.length) await wait(message.length * typing_speed); // first message should be instant
|
if(!is_myself && dialog.messages.length) {
|
||||||
|
await wait(message.length * typing_speed);
|
||||||
|
} // first message should be instant
|
||||||
|
|
||||||
if(conversation_id != conversation_count) {
|
if(!dialog.is_active()) {
|
||||||
return; // Do not add messages to old conversation
|
return; // Do not add messages to old conversation
|
||||||
}
|
}
|
||||||
|
|
||||||
let chat_message = new ChatMessage(message, is_myself);
|
let chat_message = create_chat_message(message, is_myself);
|
||||||
chat_message.draw();
|
chat_message.draw(dialog.all_elements_height, // offset
|
||||||
|
dialog.y_shift);
|
||||||
|
dialog.all_elements_height += chat_message.height;
|
||||||
dialog.messages.push(chat_message);
|
dialog.messages.push(chat_message);
|
||||||
|
|
||||||
while(chat_lines_count > max_visible_lines) {
|
let bottom = dialog.all_elements_height + dialog.y_shift;
|
||||||
dialog.messages.forEach(msg => msg.shift_y_pos(-line_height, messages_y_offset));
|
let scroll = Math.max(0, bottom - visible_chat_area.bottom);
|
||||||
|
while(scroll > line_height) {
|
||||||
|
dialog.scroll_by(-line_height);
|
||||||
|
scroll -= line_height;
|
||||||
await wait(50);
|
await wait(50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.me = async function(message) {
|
this.scroll_by = function(change) {
|
||||||
|
this.y_shift += change;
|
||||||
|
this.messages.forEach(msg => msg.shift_y_pos(this.y_shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.me = async function(message, will_be_flagged) {
|
||||||
|
await wait(1500); // give me some time to read
|
||||||
|
if(!this.is_active()) return;
|
||||||
|
await type_message(this, message);
|
||||||
|
if(!this.is_active()) return;
|
||||||
|
hit_send_button();
|
||||||
|
await local_message_processing(will_be_flagged);
|
||||||
|
clear_input_field();
|
||||||
|
release_send_button();
|
||||||
await post_message(this, message, true);
|
await post_message(this, message, true);
|
||||||
}
|
}
|
||||||
this.you = async function(message) {
|
this.you = async function(message) {
|
||||||
|
@ -240,8 +376,94 @@ function Dialog(chat_partner) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function type_message(dialog, message) {
|
||||||
|
clear_input_field(); // just to be sure
|
||||||
|
let text = document.getElementById('message_input_data').firstChild;
|
||||||
|
|
||||||
|
let lines = split_text_into_lines(message, 22);
|
||||||
|
for(let line of lines) {
|
||||||
|
text.data = '';
|
||||||
|
for(let c of line) {
|
||||||
|
await wait(typing_speed);
|
||||||
|
text.data += c;
|
||||||
|
}
|
||||||
|
if(!dialog.is_active()) break;
|
||||||
|
await wait(20); // otherwise it's too fast
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hit_send_button() {
|
||||||
|
document.getElementById('message_submit_button').setAttribute('fill', 'white');
|
||||||
|
document.getElementById('message_submit_box').setAttribute('fill', '#888888');
|
||||||
|
}
|
||||||
|
|
||||||
|
function release_send_button() {
|
||||||
|
document.getElementById('message_submit_button').setAttribute('fill', 'black');
|
||||||
|
document.getElementById('message_submit_box').setAttribute('fill', '#CCCCCC');
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear_input_field() {
|
||||||
|
let element = document.getElementById('message_input_data')
|
||||||
|
removeAllChildren(element);
|
||||||
|
element.appendChild(document.createTextNode(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function local_message_processing(will_be_flagged) {
|
||||||
|
let text_element = document.getElementById('message_status');
|
||||||
|
let indicator = text_element.childNodes[0];
|
||||||
|
indicator.data = 'scanning for illegal content';
|
||||||
|
await wait(800);
|
||||||
|
if(will_be_flagged) {
|
||||||
|
let box = document.getElementById('message_status_box');
|
||||||
|
text_element.setAttribute('fill', 'red');
|
||||||
|
box.setAttribute('fill', 'yellow');
|
||||||
|
indicator.data = 'Reporting suspicious content';
|
||||||
|
await wait(1000);
|
||||||
|
text_element.setAttribute('fill', 'white');
|
||||||
|
box.setAttribute('fill', '#FF7B00');
|
||||||
|
}
|
||||||
|
indicator.data = 'encrypting message';
|
||||||
|
await wait(550);
|
||||||
|
indicator.data = 'sending message';
|
||||||
|
await wait(500);
|
||||||
|
indicator.data = 'message was sent';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function scroll_up() {
|
||||||
|
current_dialog.scroll_by(line_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function scroll_down() {
|
||||||
|
current_dialog.scroll_by(-line_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
const on_wheel = (e) => { e.deltaY < 0 ? scroll_up() : scroll_down() }
|
||||||
|
|
||||||
|
const enable_scrolling = (elem) => elem.addEventListener('wheel', on_wheel);
|
||||||
|
|
||||||
|
// enable scrolling
|
||||||
|
document.addEventListener(
|
||||||
|
"DOMContentLoaded",
|
||||||
|
() => document.getElementById("message_box").addEventListener('wheel', on_wheel)
|
||||||
|
)
|
||||||
|
|
||||||
|
async function switch_language(language_code) {
|
||||||
|
current_language = language_code;
|
||||||
|
removeAllChildren(document.getElementById('messages'));
|
||||||
|
await current_dialog_fn();
|
||||||
|
}
|
||||||
|
|
||||||
async function dialog_ven() {
|
async function dialog_ven() {
|
||||||
let d = new Dialog('Ven');
|
if(current_language == "da") {
|
||||||
|
await dialog_ven_da();
|
||||||
|
} else {
|
||||||
|
await dialog_ven_en();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_ven_da() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
await d.me("hej");
|
await d.me("hej");
|
||||||
await d.you("tak for sidst!");
|
await d.you("tak for sidst!");
|
||||||
await d.you('Har du hørt om den nye EU lov "ChatControl"?');
|
await d.you('Har du hørt om den nye EU lov "ChatControl"?');
|
||||||
|
@ -253,46 +475,263 @@ async function dialog_ven() {
|
||||||
await d.me('Det lyder overhovedet ikke rart. Hvorfor vil de gøre det?');
|
await d.me('Det lyder overhovedet ikke rart. Hvorfor vil de gøre det?');
|
||||||
await d.you('De siger at det er for at beskytte børn på nettet.');
|
await d.you('De siger at det er for at beskytte børn på nettet.');
|
||||||
await d.you('Men det giver ikke meget mening');
|
await d.you('Men det giver ikke meget mening');
|
||||||
await d.me('Alle vores beskeder skal scannes på grund af børn på nettet? Det lyder dumt!');
|
await d.me('Alle vores beskeder skal scannes på grund af børn på nettet? Det lyder dumt!', true);
|
||||||
await d.you('Her kan du læse mere om det:');
|
await d.you('Her kan du læse mere om det:');
|
||||||
await d.you('https://chatcontrol.eu');
|
await d.you('https://chatcontrol.dk');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_ven_en() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.me("hi");
|
||||||
|
await d.you("hi, what's up?");
|
||||||
|
await d.you('Have you heard about the new EU law "ChatControl"?');
|
||||||
|
await d.me("No, I haven't");
|
||||||
|
await d.me("What is it about?");
|
||||||
|
await d.you('The EU commission plans to read all chat messages in the EU');
|
||||||
|
await d.me('Yeah? But we encrypt all of our chats? Tough luck!');
|
||||||
|
await d.you('That has been taken into account. The eavesdropping will happen before the encryption step on your phone!');
|
||||||
|
await d.me('That sounds stupid. Why do they want to do that?');
|
||||||
|
await d.you('They claim that this is for protecting children on the internet');
|
||||||
|
await d.you("But that doesn't make any sense.");
|
||||||
|
await d.me('All of our messages will be scanned because of children on the internet? That sounds really stupid!', true);
|
||||||
|
await d.you('Here you can read more about it:');
|
||||||
|
await d.you('https://chatcontrol.dk');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dialog_mor() {
|
async function dialog_mor() {
|
||||||
let d = new Dialog('Mor');
|
if(current_language == "da") {
|
||||||
|
await dialog_mor_da();
|
||||||
|
} else {
|
||||||
|
await dialog_mor_en();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_mor_da() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
await d.you("Jeg har fundet nogle gamle familiebilleder fra vores ferie 10 år siden");
|
await d.you("Jeg har fundet nogle gamle familiebilleder fra vores ferie 10 år siden");
|
||||||
await d.you("Her leger du på stranden");
|
await d.you("Her leger du på stranden");
|
||||||
await d.you("(naked stick figure image here)");
|
await d.you("(naked stick figure image here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function dialog_mor_en() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.you("I have found some old family photos from our holiday from 10 years ago");
|
||||||
|
await d.you("This is you playing on the beach");
|
||||||
|
await d.you("(naked stick figure image here)");
|
||||||
|
}
|
||||||
|
|
||||||
async function dialog_kaereste() {
|
async function dialog_kaereste() {
|
||||||
let d = new Dialog('Kæreste');
|
if(current_language == "da") {
|
||||||
|
await dialog_kaereste_da();
|
||||||
|
} else {
|
||||||
|
await dialog_kaereste_en();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_kaereste_da() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
await d.me('hej smukkeste');
|
await d.me('hej smukkeste');
|
||||||
await d.you('hej, jeg har lige tænkt på dig!');
|
await d.you('hej, jeg har lige tænkt på dig!');
|
||||||
await d.you('og derfor har jeg taget et sexet billede');
|
await d.you('og derfor har jeg taget et sexet billede');
|
||||||
await d.you('(silly naked stick figure image here)')
|
await d.you('#kaereste_foto');
|
||||||
|
await d.me('tak. Det har jeg savnet!', true);
|
||||||
|
await d.me('du ser stadig så ung ud.');
|
||||||
|
await d.me('Jeg tror at vi skal være forsigtige, fordi der er en overvågning af vores beskeder');
|
||||||
|
await d.you('What the fuck??? Hvem overvågner os?????');
|
||||||
|
await d.me('Det kan være at udbyderen gør det, fordi de har lov til det');
|
||||||
|
await d.me("Men i fremtiden vil det faktisk blive forpligtende at scanne beskeder i EU'en");
|
||||||
|
await d.me('Og hvis der er beskeder som "ligner" børneborno får politiet en kopi af dem');
|
||||||
|
await d.you('Jeg vil hellere ikke at politiet får mine nøgenbilleder. Det er min krop og jeg vil selv bestemme over det!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function dialog_kaereste_en() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.me('Hi beautiful');
|
||||||
|
await d.you('hi, I just thought about you');
|
||||||
|
await d.you("and that's why I have taken a sexy picture");
|
||||||
|
await d.you('#kaereste_foto');
|
||||||
|
await d.me('Thanks! I missed that!', true);
|
||||||
|
await d.me('you still look so young.');
|
||||||
|
await d.me('I think we have to be careful, because our messages are being read');
|
||||||
|
await d.you('What the fuck??? Who is watching us?????');
|
||||||
|
await d.me('It can be that the chat provider does this, because they are allowed to search for illegal material');
|
||||||
|
await d.me('But in the future it will also become mandatory to scan chat messages in the EU');
|
||||||
|
await d.me('And if a message looks like child pornography then the police will get a copy of it');
|
||||||
|
await d.you("I don't want to share my nudes with the police. This is my body and I decide who I share it with!");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_politi() {
|
||||||
|
if(current_language == "da") {
|
||||||
|
await dialog_politi_da();
|
||||||
|
} else {
|
||||||
|
await dialog_politi_en();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_politi_da() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.me('Hej, jeg har et spørgsmål');
|
||||||
|
await d.me('Har du hørt om chatcontrollen og hvad tænker du på det?');
|
||||||
|
await d.you('Jeg er bekendt med det. Og det er faktisk et ret dårlig idé');
|
||||||
|
await d.you('Problemet er at der er så mange falsk positive resultater som skal analyseres');
|
||||||
|
await d.you('Og jeg gider ikke kigge på folkets private beskeder. Det er jo deres fortrolige samtaler');
|
||||||
|
await d.you('Jeg vil heller ikke at mine kolleger se hvad jeg skriver til min kæreste, kun fordi jeg hypotetisk kunne gøre noget illegalt');
|
||||||
|
await d.you('Tænk bare hvor absurd idén er: Hvorfor aflytter vi ikke bare alle telefoner? Der vil være nogle forbrydelser vi vil opdage!');
|
||||||
|
await d.you('Men så har vi den totale overvågning og ikke længere en fri samfund.');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_politi_en() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.me('Hi, I have a question');
|
||||||
|
await d.me('Have you heard about ChatControl and what do you think about it?');
|
||||||
|
await d.you('I know about it. It is actually a really bad idea');
|
||||||
|
await d.you('The problem is that there are many false positive hits that need to be analysed manually');
|
||||||
|
await d.you("And I do not want to read through people's private messages. These are confidential conversations");
|
||||||
|
await d.you("I don't like that my collegues can potentially see what I write to by partner, just because I could potentially do something illegal while chatting");
|
||||||
|
await d.you("Just think about how absurd the idea is: Why don't we wiretap all phones? There will be some crimes that we will detect!");
|
||||||
|
await d.you('But then we have total surveillance and no longer a free society.');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_support() {
|
||||||
|
if(current_language == "da") {
|
||||||
|
await dialog_support_da();
|
||||||
|
} else {
|
||||||
|
await dialog_support_en();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_support_da() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.you('Velkommen til supporten af ChatWorld. Hvad kan jeg hjælpe dig med?');
|
||||||
|
await d.me('Jeg mistænker at chatprogrammet scanner mine end-to-end krypterede beskeder. Er det rigtigt?');
|
||||||
|
await d.you('Ja, det er en feature som cheffen ville gerne have.');
|
||||||
|
await d.you('Der var nogle som har misbrugt chattjenesten til børneporno og det giver meget dårlig publicity og derfor har chefen valgt at scanne end-to-end krypterede beskeder');
|
||||||
|
await d.you('børneporno er jo en meget dårlig ting. Men jeg synes ikke at det giver mening at aflytte alle private beskeder.');
|
||||||
|
await d.you('og teknologien kan misbruges til mange ting. Jeg som tekniker kan ikke se hvad søgefunktionen faktisk leder efter.');
|
||||||
|
await d.you('Det kan være at det vil finde alternative politiske beskeder og markerer det som illegal selv om de ikke er.');
|
||||||
|
await d.you('Det er en fare for demokratiet hvis fortrolige beskeder bliver scannet for ting som den politiske elite ikke ønsker');
|
||||||
|
await d.you('Men det er desværre mit job at vedligeholde driften af overvågningen');
|
||||||
|
await d.me('Det løser ikke mit problem. Men tak for informationen. Jeg vil se hvad jeg kan gør for at undgår overvågningen i fremtiden', true);
|
||||||
|
await d.you('Beklager at jeg kan ikke tilbyde noget bedre. Men jeg håber at du forstår situationen');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_support_en() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.you('Welcome to the ChatWorld support. How can I help you?');
|
||||||
|
await d.me('I suspect that the chat program is scanning my end-to-end encrypted messages. Am I right?');
|
||||||
|
await d.you('Yes, that is a feature that our boss wanted to have');
|
||||||
|
await d.you('Some people have abused the chat service to share child pornography and that gives bad publicity. The boss has therefore chosen to scan all end-to-end encrypted messages');
|
||||||
|
await d.you("Child pornography is a really bad thing. But I don't think it is sensible to scan all messages.");
|
||||||
|
await d.you('and the scan technology can be abused for many things. I as a software engineer can not even see what the scanner is searching for');
|
||||||
|
await d.you("It can be that it will look for alternative political views and marks them as illegal even though they aren't");
|
||||||
|
await d.you('It is a danger to our democracy when confidential messages are scanned for things that the political elite does not like');
|
||||||
|
await d.you('But it is unfortunately my job to keep the surveillance working');
|
||||||
|
await d.me('That does not solve the problem. But thank you for the information. I will look for ways to avoid the surveillance in the future', true);
|
||||||
|
await d.you('I apologize that I can not offer something better. But I hope you understand the situation');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_nabopige() {
|
||||||
|
if(current_language == "da") {
|
||||||
|
await dialog_nabopige_da();
|
||||||
|
} else {
|
||||||
|
await dialog_nabopige_en();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_nabopige_da() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.you('Gæt hvem har snart fødselsdag!');
|
||||||
|
await d.me('Det er dig?');
|
||||||
|
await d.you('Rigtigt! Det var måske for nemt');
|
||||||
|
await d.me('Hvor gammel bliver du så?');
|
||||||
|
await d.you('ti');
|
||||||
|
await d.you('Jeg glæder mig meget til festen. Jeg har inviteret en masse mennesker');
|
||||||
|
await d.you('Du er også inviteret!');
|
||||||
|
await d.you('Hej, jeg vil gerne fortælle dig en hjemlighed');
|
||||||
|
await d.you('Men jeg kan desværre ikke gøre det over chatten');
|
||||||
|
await d.you('Fordi jeg ved at hvad vi skriver her bliver analyseret');
|
||||||
|
await d.you('Og det vil ikke være en hjemlighed hvis der er andre læser med');
|
||||||
|
await d.me('Du kan gerne fortælle mig hjemligheden i morgen, når jeg kommer forbi', true);
|
||||||
|
await d.me('Jeg er overrasket over at du ved noget om chatovervågningen');
|
||||||
|
await d.you('Ja, det er meget kedeligt. Jeg har læst noget om det og jeg kan overhovet ikke lide det');
|
||||||
|
await d.you('Det er bedre hvis man har gode voksne venner som man har tillid til end hvis der er en computer som læser alt hvad man skriver');
|
||||||
|
await d.me('helt enig');
|
||||||
|
await d.me('børn har også brug for hjemligheder og fortrolighed!');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dialog_nabopige_en() {
|
||||||
|
let d = new Dialog();
|
||||||
|
current_dialog = d;
|
||||||
|
await d.you("Guess who's birthday it is soon!");
|
||||||
|
await d.me("It's yours?");
|
||||||
|
await d.you('Right! That was maybe too easy');
|
||||||
|
await d.me('How old will you be?');
|
||||||
|
await d.you('ten');
|
||||||
|
await d.you('I am excited for the party. I have invited a lot of people');
|
||||||
|
await d.you('You are also invited!');
|
||||||
|
await d.you('Hi, I want to tell you a secret');
|
||||||
|
await d.you("But I just can't do that in the chat");
|
||||||
|
await d.you('Because what we write here is being analysed');
|
||||||
|
await d.you('And it is not going to be a secret when there are others who read the messages');
|
||||||
|
await d.me('You can tell me the secret tomorrow, when I come to your house', true);
|
||||||
|
await d.me('I am surprised that you know about the chat surveillance');
|
||||||
|
await d.you("Yes, it is stupid. I have read about it and I really don't like it");
|
||||||
|
await d.you('It is better to have adult friends whom I can trust that a computer who reads everything that I write.');
|
||||||
|
await d.me('Completely agree');
|
||||||
|
await d.me('Children also need their secrets and confidentiality');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
]]>
|
]]>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- [right] contact name view -->
|
<!-- [right] contact name view -->
|
||||||
<rect x="100%" y="0" width="100%" height="10%" fill="yellow" />
|
<g id="contact_name_box">
|
||||||
<polyline points="300,20 320,0 320,40" fill="blue" onclick="swipe_viewport()" /> <!-- XXX: these polyline points depend on the svg width -->
|
<rect x="300" y="0" width="300" height="50" fill="#F5F5F5"/>
|
||||||
<text x="125%" y="30" id="contact_indicator">loading...</text>
|
<polyline points="300,20 320,0 320,40" fill="#0084FF" onclick="swipe_viewport()" />
|
||||||
|
<polyline points="560,20 600,20 580,0" fill="#0084FF" onclick="scroll_up()" />
|
||||||
|
<polyline points="560,30 600,30 580,50" fill="#0084FF" onclick="scroll_down()" />
|
||||||
|
<text x="325" y="30" id="contact_indicator">loading...</text>
|
||||||
|
<use href="#flag_denmark" x="500" y="35" onclick="switch_language('da')" />
|
||||||
|
<g transform="scale(0.5,0.5)">
|
||||||
|
<use href="#flag_uk" x="1050" y="70" onclick="switch_language('en')" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
<!-- [right] messages view -->
|
<!-- [right] messages view -->
|
||||||
<rect x="100%" y="10%" width="100%" height="90%" style="stroke: green; stroke-width: 10px" fill="blue" />
|
<rect id="message_box" x="300" y="50" width="300" height="450" fill="white" />
|
||||||
<rect id="messages_background" x="105%" y="15%" z="2" width="90%" height="85%" fill="pink" />
|
|
||||||
<g id="messages"></g>
|
<g id="messages"></g>
|
||||||
|
|
||||||
<!-- [left] contact list -->
|
<!-- [right] message status -->
|
||||||
<rect x="0" y="0" width="100%" height="10%" fill="yellow" />
|
<rect id="message_status_box" x="300" y="520" fill="#FF7B00" width="300" height="30" />
|
||||||
<text x="20%" y="30">Dine kontakter</text>
|
<text id="message_status" x="310" y="540" fill="white">status</text>
|
||||||
<rect x="0" y="10%" width="100%" height="100%" style="stroke: green; stroke-width: 10px" fill="aqua" />
|
|
||||||
|
|
||||||
<text x="5%" y="15%" onclick="start_chat('Ven')">Ven</text>
|
<!-- [right] text input -->
|
||||||
<text x="5%" y="20%" onclick="start_chat('Mor')">Mor</text>
|
<rect id="message_input_box" x="300" y="550" stroke="black" fill="white" width="250" height="50" />
|
||||||
<text x="5%" y="25%" onclick="start_chat('Kæreste')">Kæreste❤</text>
|
<rect id="message_submit_box" x="550" y="550" stroke="black" fill="#CCCCCC" width="50" height="50" />
|
||||||
|
<text id="message_input_data" x="310" y="575" fill="black"></text>
|
||||||
|
<text id="message_submit_button" x="555" y="580" fill="black">SEND</text>
|
||||||
|
|
||||||
|
<!-- [left] contact list -->
|
||||||
|
<rect x="0" y="0" width="300" height="50" fill="#E4E6EB" />
|
||||||
|
<text x="60" y="30">Dine kontakter</text>
|
||||||
|
<rect x="0" y="50" width="300" height="550" style="stroke: green; stroke-width: 0px" fill="white" />
|
||||||
|
|
||||||
|
<text x="15" y="75" onclick="start_chat('Ven')">Ven</text>
|
||||||
|
<text x="15" y="100" onclick="start_chat('Mor')">Mor</text>
|
||||||
|
<text x="15" y="125" onclick="start_chat('Kæreste')">Kæreste❤</text>
|
||||||
|
<text x="15" y="150" onclick="start_chat('Politiven')">Politiven</text>
|
||||||
|
<text x="15" y="175" onclick="start_chat('ChatSupport')">Chat Support</text>
|
||||||
|
<text x="15" y="200" onclick="start_chat('Nabopige')">nabopige</text>
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 27 KiB |
Loading…
Reference in a new issue