PageSourceSearch

https://www.webrtc-experiment.com/Canvas-Designer/

html webrtc-experiment.com collected 2026-10-01 12:18:04 UTC 20,221 bytes, 686 lines download raw bytes

1<!DOCTYPE html>
2<html itemscope itemtype="http://schema.org/WebPage">
3<head>
4	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8">
5	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
6	<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0, user-scalable=no">
7
8	<title>Canvas Designer supporting Collaboration | HTML5, WebRTC</title>
9	<meta name="description" content="HTML5 Canvas Designer ® Muaz Khan – A tool aimed to give you a full-fledged drawing surface and also auto generate appropriate code for you in different formats! Generator/Tool/Editor – 2D API/Context">
10</head>
11<body>
12<script src="canvas-designer-widget.js"></script>
12
13
14<style>
15* {
16    -webkit-user-select: none;
17    -moz-user-select: none;
18    -o-user-select: none;
19    -ms-user-select: none;
20    user-select: none;
21}
22input[type=text] {
23    -webkit-user-select: initial;
24    -moz-user-select: initial;
25    -o-user-select: initial;
26    -ms-user-select: initial;
27    user-select: initial;
28}
29
30button, input {
31    font-family: Myriad, Arial, Verdana;
32    font-weight: normal;
33    border-top-left-radius: 3px;
34    border-top-right-radius: 3px;
35    border-bottom-right-radius: 3px;
36    border-bottom-left-radius: 3px;
37    padding: 4px 12px;
38    text-decoration: none;
39    color: rgb(27, 26, 26);
40    display: inline-block;
41    box-shadow: rgb(255, 255, 255) 1px 1px 0px 0px inset;
42    text-shadow: none;
43    background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0.05, rgb(241, 241, 241)), to(rgb(230, 230, 230)));
44    font-size: 20px;
45    border: 1px solid red;
46    outline:none;
47}
48button:hover, button:hover, input:focus {
49    background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(5%, rgb(221, 221, 221)), to(rgb(250, 250, 250)));
50    border: 1px solid rgb(142, 142, 142);
51}
52button[disabled], input[disabled] {
53    background: rgb(249, 249, 249);
54    border: 1px solid rgb(218, 207, 207);
55    color: rgb(197, 189, 189);
56}
57
58input {
59    background: white;
60}
61
62.extra-controls {
63    position: fixed;
64    right: 21%;
65}
66
67#room-id {
68    width: 55%;
69    margin-right: 2px;
70}
71
72/* popup box */
73.black_overlay {
74    display: none;
75    position: absolute;
76    top: 0%;
77    left: 0%;
78    width: 100%;
79    height: 100%;
80    background-color: black;
81    z-index:1001;
82    -moz-opacity: 0.8;
83    opacity:.80;
84    filter: alpha(opacity=80);
85}
86.white_content {
87    display: none;
88    position: absolute;
89    top: 30%;
90    left: 40%;
91    width: 16%;
92    height: 15%;
93    padding: 16px;
94    border: 10px solid rgb(9, 159, 243);
95    background-color: rgba(255, 255, 255, 0.94);
96    z-index:1002;
97    overflow: hidden;
98    border-radius: 5px;
99    min-width: 300px;
100    min-height: 120px;
101}
102
103.white_content hr {
104    border-top: 4px solid rgb(9, 159, 243);
105    margin: 9px -20px;
106}
107
108#btn-undo, #btn-close-undo-popup, #btn-close-dataURL-popup, #btn-close-comments-popup, #btn-getDataURL {
109    font-size: 1.2em;
110    padding: 2px 9px;
111}
112
113#btn-close-undo-popup, #btn-close-dataURL-popup, #btn-close-comments-popup {
114    padding: 0 6px;
115    border-radius: 50%;
116    padding-bottom: 1px;
117    color: red;
118}
119
120.white_content select {
121    font-size: 1.2em;
122    border: 1px solid;
123    padding: 0;
124    outline: none!important;
125    border: 1px solid black !important;
126    float: left;
127}
128.white_content select option {
129    padding: 2px 5px;
130    border-bottom: 1px solid black;
131}
132.white_content select option:last-child {
133    border-bottom: 0;
134}
135
136.white_content option:checked, .white_content option[selected] {
137    background-color: #06CDFF!important;
138    background:linear-gradient(#06CDFF, #06CDFF)!important;
139    color: white!important;
140}
141
142.white_content select[disabled] {
143    background-color: rgba(232, 229, 229, 0.17);
144    color: rgb(84, 82, 82);
145}
146
147div#comments-popup {
148    width: 100%;
149    left: 0;
150    top: 0;
151    height: 100%;
152    border: 0;
153    padding: 0;
154	border-radius: 0;
155    overflow: auto;
156}
157
158#btn-comments {
159	color: red;
160	margin-top: 5px;
161	font-size: 24px;
162	text-shadow: 1px 1px white;
163}
164
165#h2-close-popup {
166	background: yellow;
167	padding: 5px 10px;
168	border-bottom: 1px solid red;
169	margin: 0;
170	color: red;
171}
172</style>
173
174<div id="widget-container" style="position: fixed;bottom: 0;right: 20%;left: 20%;height: 100%;border: 1px solid black; border-top:0; border-bottom: 0;"></div>
175
176<div id="fade" class="black_overlay"></div>
177
178<div id="action-controls" style="width: 19%; padding: 1%;position: absolute;left:0;">
179    <video controls autoplay playsinline style="width: 105%;margin-left: -15px;margin-top: -23px; display: none;"></video>
180
181    <div id="select-tools">
182    <h2>Select Tools</h2>
183    <input type="checkbox" id="pencil" checked>
184    <label for="pencil">pencil</label><br>
185
186    <input type="checkbox" id="marker" checked>
187    <label for="marker">marker</label><br>
188
189    <input type="checkbox" id="eraser" checked>
190    <label for="eraser">eraser</label><br>
191
192    <input type="checkbox" id="text" checked>
193    <label for="text">text</label><br>
194
195    <input type="checkbox" id="image" checked>
196    <label for="image">image</label><br>
197
198    <input type="checkbox" id="pdf" checked>
199    <label for="pdf">pdf</label><br>
200
201    <input type="checkbox" id="line" checked>
202    <label for="line">line</label><br>
203
204    <input type="checkbox" id="arrow" checked>
205    <label for="arrow">arrow</label><br>
206
207	<input type="checkbox" id="zoom">
208    <label for="zoom">zoom</label><br>
209
210    <input type="checkbox" id="dragSingle" checked>
211    <label for="dragSingle">dragSingle</label><br>
212
213    <input type="checkbox" id="dragMultiple" checked>
214    <label for="dragMultiple">dragMultiple</label><br>
215
216    <input type="checkbox" id="arc" checked>
217    <label for="arc">arc</label><br>
218
219    <input type="checkbox" id="rectangle" checked>
220    <label for="rectangle">rectangle</label><br>
221
222    <input type="checkbox" id="quadratic" checked>
223    <label for="quadratic">quadratic</label><br>
224
225    <input type="checkbox" id="bezier" checked>
226    <label for="bezier">bezier</label><br>
227
228    <input type="checkbox" id="undo" checked>
229    <label for="undo">undo</label><br>
230
231    <input type="checkbox" id="lineWidth" checked>
232    <label for="lineWidth">lineWidth</label><br>
233
234    <input type="checkbox" id="colorsPicker" checked>
235    <label for="colorsPicker">colorsPicker</label><br>
236
237    <input type="checkbox" id="extraOptions" checked>
238    <label for="extraOptions">extraOptions</label><br>
239
240    <input type="checkbox" id="code" checked>
241    <label for="code">code</label><br><br>
242    </div>
243
244    <hr>
245    <div>
246        Number of users connected with you: <b id="number-of-connected-users">0</b>
247
248        <br><br>
249
250        <div id="hide-on-datachannel-opened">
251            You can open a private WebRTC room to sync dashboard with private users.
252
253            <br><br>
254
255            <input type="text" id="room-id" placeholder="room-id">
256            <button id="open-room">Open</button>
257        </div>
258    </div>
259
260    <hr>
261    <h2>Shortcut Keys</h2>
262    ctrl+t (to display text-fonts box)<br>
263    ctrl+z (to undo last-single shape)<br>
264    ctrl+a (to select all shapes)<br>
265    ctrl+c (copy last-selected shape)<br>
266    ctrl+v (paste last-copied shape)<br><br>
267    ctrl+mousedown (to copy/paste all shapes)
268</div>
269
270<div class="extra-controls">
271    <button id="export-as-image">Export as Image</button>
272    <button id="btn-display-undo-popup">Undo</button>
273</div>
274
275<div id="light" class="white_content">
276    <button id="btn-close-undo-popup" style="float:right;">x</button>
277
278    <select id="undo-options" size="4">
279        <option>Last Shape</option>
280        <option>All Shapes</option>
281        <option>Last Multiple</option>
282        <option disabled>Specific Range</option>
283    </select>
284
285    <div style="display: none;margin-top: 20px;margin-bottom: 20px;margin-right: 12px;float: right;">
286        Digit/Number:<br>
287        <input type="text" id="number-of-shapes-to-undo" style="padding: 0;width: 90px;text-align: center;border-radius: 0;margin-top: 6px;">
288    </div>
289
290    <br><br><br><br><br>
291
292    <button id="btn-undo" style="float:right;">Undo</button>
293</div>
294
295<div id="dataURL-popup" class="white_content">
296    <button id="btn-close-dataURL-popup" style="float:right;">x</button>
297
298    <select id="data-url-format" size="4">
299        <option>image/png</option>
300        <option>image/jpeg</option>
301        <option>image/gif</option>
302        <option>image/webp</option>
303    </select>
304
305		<a id="link-to-image" target="_blank" download="image.png"></a>
306
307    <br><br><br><br><br>
308
309    <button id="btn-getDataURL" style="float:right;">Get DataURL</button>
310</div>
311
312<div id="comments-popup" class="white_content">
313    <button id="btn-close-comments-popup" style="float:right;">x</button>
314
315		<h2 id="h2-close-popup">Click right-side "x" button to close this popup.</h2>
316		<section class="experiment">
317        <h2 class="header" id="feedback">Feedback</h2>
318        <div>
319            <textarea id="message" style="height: 8em; margin: .2em; width: 98%; border: 1px solid rgb(189, 189, 189); outline: none; resize: vertical;" placeholder="Have any message? Suggestions or something went wrong?"></textarea>
320        </div>
321        <button id="send-message" style="font-size: 1em;">Send Message</button><small style="margin-left:1em;">Enter your email too; if you want "direct" reply!</small>
322    </section>
323</div>
324
325<div style="position: fixed; right: 0; width: 19%; padding: 1%;top:0; text-align: center;">
326    <h1>Canvas Designer!</h1>
327    <h2>
328        <a href="https://github.com/muaz-khan/Canvas-Designer">This tool is Open-Sourced!</a>
329    </h2>
330
331    Author: <a href="https://github.com/muaz-khan">Muaz Khan</a>
332
333    <hr>
334    <h2><a href="https://github.com/muaz-khan/Canvas-Designer#how-to-use">How to use in your own site?</a></h2>
335
336    <hr>
337
338    <h2><a href="https://muazkhan.com:9001/demos/dashboard/">WebRTC Dashboard</a></h2>
339
340    <br><br><br>
341
342		<div style="text-align:cener;margin-top:15px;">
343			<p style="font-size:22px;color:red;">Ask Questions Here:</p>
344			<button id="btn-comments">Comments<br>Discussion</button>
345		</div>
346</div>
347
348<script>
349document.getElementById('room-id').onkeyup = document.getElementById('room-id').onblur = function() {
350    localStorage.setItem('canvas-designer-roomid', this.value);
351};
352
353if (localStorage.getItem('canvas-designer-roomid')) {
354    document.getElementById('room-id').value = localStorage.getItem('canvas-designer-roomid');
355}
356
357document.getElementById('open-room').onclick = function() {
358    var roomid = document.getElementById('room-id').value;
359    if (!roomid.length) return alert('Please enter roomid.');
360
361    this.disabled = true;
362
363    connection.open(roomid, onOpenRoom);
364
365    this.parentNode.innerHTML = '<a href="#' + roomid + '" target="_blank">Please share this link</a>';
366};
367
368var designer = new CanvasDesigner();
369
370// you can place widget.html anywhere
371designer.widgetHtmlURL = 'widget.html';
372designer.widgetJsURL = 'widget.min.js'
373
374designer.addSyncListener(function(data) {
375    connection.send(data);
376});
377
378designer.setSelected('pencil');
379
380designer.setTools({
381    pencil: true,
382    text: true,
383    image: true,
384    pdf: true,
385    eraser: true,
386    line: true,
387    arrow: true,
388    dragSingle: true,
389    dragMultiple: true,
390    arc: true,
391    rectangle: true,
392    quadratic: true,
393    bezier: true,
394    marker: true,
395    zoom: false,
396    lineWidth: true,
397    colorsPicker: true,
398    extraOptions: true,
399    code: true,
400    undo: true
401});
402
403designer.appendTo(document.getElementById('widget-container'));
404
405Array.prototype.slice.call(document.getElementById('action-controls').querySelectorAll('input[type=checkbox]')).forEach(function(checkbox) {
406    checkbox.onchange = function() {
407        designer.destroy();
408
409        designer.addSyncListener(function(data) {
410            connection.send(data);
411        });
412
413        var tools = {};
414        Array.prototype.slice.call(document.getElementById('action-controls').querySelectorAll('input[type=checkbox]')).forEach(function(checkbox2) {
415            if (checkbox2.checked) {
416                tools[checkbox2.id] = true;
417            }
418        });
419        designer.setTools(tools);
420        designer.appendTo(document.getElementById('widget-container'));
421    };
422});
423
424var undoOptions = document.getElementById('undo-options');
425
426document.getElementById('btn-display-undo-popup').onclick = function() {
427    document.getElementById('light').style.display = 'block';
428    document.getElementById('fade').style.display = 'block';
429};
430
431var txtNumberOfShapesToUndo = document.getElementById('number-of-shapes-to-undo');
432txtNumberOfShapesToUndo.onkeyup = function() {
433    localStorage.setItem('number-of-shapes-to-undo', txtNumberOfShapesToUndo.value);
434}
435
436if (localStorage.getItem('number-of-shapes-to-undo')) {
437    txtNumberOfShapesToUndo.value = localStorage.getItem('number-of-shapes-to-undo');
438    txtNumberOfShapesToUndo.onkeyup();
439}
440
441undoOptions.onchange = function() {
442    txtNumberOfShapesToUndo.parentNode.style.display = 'none';
443
444    if (undoOptions.value === 'Specific Range') {
445        //
446    } else if (undoOptions.value === 'Last Multiple') {
447        txtNumberOfShapesToUndo.parentNode.style.display = 'block';
448    }
449
450    localStorage.setItem('undo-options', undoOptions.value);
451};
452
453undoOptions.onclick = undoOptions.onchange;
454
455if (localStorage.getItem('undo-options')) {
456    undoOptions.value = localStorage.getItem('undo-options');
457    undoOptions.onchange();
458}
459
460document.getElementById('btn-undo').onclick = function() {
461    if (undoOptions.value === 'All Shapes') {
462        designer.undo('all');
463    } else if (undoOptions.value === 'Specific Range') {
464        designer.undo({
465            specificRange: {
466                start: -1,
467                end: -1
468            }
469        });
470    } else if (undoOptions.value === 'Last Shape') {
471        designer.undo(-1);
472    } else if (undoOptions.value === 'Last Multiple') {
473        var numberOfLastShapes = txtNumberOfShapesToUndo.value;
474        numberOfLastShapes = parseInt(numberOfLastShapes || 0) || 0;
475        designer.undo({
476            numberOfLastShapes: numberOfLastShapes
477        });
478    }
479
480    closeUndoPopup();
481};
482
483function closeUndoPopup() {
484    document.getElementById('light').style.display = 'none';
485    document.getElementById('fade').style.display = 'none';
486
487    undoOptions.onchange();
488}
489document.getElementById('btn-close-undo-popup').onclick = closeUndoPopup;
490
491function closeDataURLPopup() {
492    document.getElementById('dataURL-popup').style.display = 'none';
493    document.getElementById('fade').style.display = 'none';
494
495    dataURLFormat.onchange();
496}
497document.getElementById('btn-close-dataURL-popup').onclick = closeDataURLPopup;
498
499document.getElementById('export-as-image').onclick = function() {
500    linkToImage.innerHTML = linkToImage.href = linkToImage.style = '';
501
502    document.getElementById('dataURL-popup').style.display = 'block';
503    document.getElementById('fade').style.display = 'block';
504
505    getDataURL();
506};
507
508function getDataURL(callback) {
509    callback = callback || function() {};
510    var format = dataURLFormat.value;
511    designer.toDataURL(format || 'image/png', function(dataURL) {
512        linkToImage.style = 'margin-left: 10px;display: block;text-align: center;margin-bottom: -50px;';
513        linkToImage.href = dataURL;
514        linkToImage.innerHTML = 'Click to Download Image';
515        linkToImage.download = 'image.' + (format || 'image/png').split('/')[1];
516
517        callback(dataURL, format);
518    });
519}
520
521var dataURLFormat = document.getElementById('data-url-format');
522var linkToImage = document.getElementById('link-to-image');
523
524dataURLFormat.onchange = function() {
525    localStorage.setItem('data-url-format', dataURLFormat.value);
526    getDataURL();
527};
528dataURLFormat.onclick = dataURLFormat.onchange;
529
530if (localStorage.getItem('data-url-format')) {
531    dataURLFormat.value = localStorage.getItem('data-url-format');
532    dataURLFormat.onchange();
533}
534
535document.getElementById('btn-getDataURL').onclick = function() {
536    getDataURL(function(dataURL, format) {
537        window.open(dataURL);
538    });
539
540    // closeDataURLPopup();
541};
542
543document.getElementById('btn-close-comments-popup').onclick = function() {
544    document.getElementById('comments-popup').style.display = 'none';
545    document.getElementById('fade').style.display = 'none';
546
547    dataURLFormat.onchange();
548};
549
550function showCommentsPopup(e) {
551    document.getElementById('comments-popup').style.display = 'block';
552    document.getElementById('fade').style.display = 'block';
553}
554document.getElementById('btn-comments').onclick = showCommentsPopup;
555if (location.hash.length && location.hash.indexOf('comment') !== -1) {
556    showCommentsPopup();
557}
558</script>
558
559
560<script src="https://muazkhan.com:9001/dist/RTCMultiConnection.min.js"></script>
vendor: 1 bytes, line 560
560
561<script src="https://muazkhan.com:9001/socket.io/socket.io.js"></script>
561
562
563<script>
564var connection = new RTCMultiConnection();
565
566connection.socketURL = 'https://muazkhan.com:9001/';
567connection.socketMessageEvent = 'canvas-designer';
568
569connection.enableFileSharing = false;
570connection.session = {
571    audio: true,
572    video: true,
573    data: true
574};
575connection.sdpConstraints.mandatory = {
576    OfferToReceiveAudio: true,
577    OfferToReceiveVideo: true
578};
579connection.dontCaptureUserMedia = true;
580if (location.hash.replace('#', '').length) {
581    var roomid = location.hash.replace('#', '');
582    connection.join(roomid);
583}
584
585connection.onUserStatusChanged = function(event) {
586    var infoBar = document.getElementById('hide-on-datachannel-opened');
587    if (event.status == 'online') {
588        infoBar.innerHTML = event.userid + ' is <b>online</b>.';
589    }
590
591    if (event.status == 'offline') {
592        infoBar.innerHTML = event.userid + ' is <b>offline</b>.';
593    }
594
595    numberOfConnectedUsers.innerHTML = connection.getAllParticipants().length;
596};
597
598var numberOfConnectedUsers = document.getElementById('number-of-connected-users');
599connection.onopen = function(event) {
600    var infoBar = document.getElementById('hide-on-datachannel-opened');
601    infoBar.innerHTML = '<b>' + event.userid + '</b> is ready to collaborate with you.';
602
603    if (designer.pointsLength <= 0) {
604        // make sure that remote user gets all drawings synced.
605        setTimeout(function() {
606            connection.send('plz-sync-points');
607        }, 1000);
608    }
609
610    numberOfConnectedUsers.innerHTML = connection.getAllParticipants().length;
611
612    if(connection.isInitiator) {
613        setTimeout(function() {
614            designer.renderStream();
615        }, 1000);
616    }
617};
618
619connection.onclose = connection.onerror = connection.onleave = function() {
620    numberOfConnectedUsers.innerHTML = connection.getAllParticipants().length;
621};
622
623connection.onmessage = function(event) {
624    if (event.data === 'plz-sync-points') {
625        designer.sync();
626        return;
627    }
628
629    designer.syncData(event.data);
630};
631</script>
631
632
633<script src="dev/webrtc-handler.js"></script>
vendor: 1 bytes, line 633
633
634<script>
635function onOpenRoom() {
636    // capture canvas-2d stream
637    // and share in realtime using RTCPeerConnection.addStream
638    // requires: dev/webrtc-handler.js
639    designer.captureStream(function(stream) {
640        connection.attachStreams = [stream];
641        connection.onstream({
642            stream: stream
643        });
644    });
645}
646
647var video = document.querySelector('video');
648
649connection.onstream = function(event) {
650    if (connection.isInitiator && event.mediaElement) return;
651
652    video.style.display = '';
653    video.srcObject = event.stream;
654
655    document.getElementById('select-tools').style.display = 'none';
656};
657
658connection.onstreamended = function() {
659    video.src = null;
660    video.style.display = 'none';
661};
662
663connection.iceServers = [];
664connection.iceServers.push({
665    urls: 'stun:muazkhan.com:3478',
666    credential: 'muazkh',
667    username: 'hkzaum'
668});
669connection.iceServers.push({
670    urls: 'turns:muazkhan.com:5349',
671    credential: 'muazkh',
672    username: 'hkzaum'
673});
674connection.iceServers.push({
675    urls: 'turn:muazkhan.com:3478',
676    credential: 'muazkh',
677    username: 'hkzaum'
678});
679</script>
679
680
681<script src="https://www.webrtc-experiment.com/common.js" async></script>
681
682
683<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5443396847776866"
684     crossorigin="anonymous"></script>
684
685</body>
686</html>

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.