| 
22
 | 
     1 <!doctype html>
 | 
| 
 | 
     2 <html>
 | 
| 
 | 
     3 	<head>
 | 
| 
 | 
     4 		<meta name="viewport" content="width=device-width, initial-scale=1">
 | 
| 
35
 | 
     5 		<script src="/site.js"></script>
 | 
| 
24
 | 
     6 		<script src="http://tinymce.luan.software/tinymce.min.js" xreferrerpolicy="origin"></script>
 | 
| 
22
 | 
     7 		<style>
 | 
| 
35
 | 
     8 			input[type="file"] {
 | 
| 
 | 
     9 				display: none;
 | 
| 
 | 
    10 			}
 | 
| 
22
 | 
    11 		</style>
 | 
| 
 | 
    12 		<script>
 | 
| 
28
 | 
    13 			function videoIframe(url) {
 | 
| 
 | 
    14 				return '<iframe data-video="'+url+'" width="560" height="315" frameborder="0" allowfullscreen src="'+url+'"></iframe>';
 | 
| 
 | 
    15 			}
 | 
| 
 | 
    16 
 | 
| 
30
 | 
    17 			// fucking moronic javascript doesn't have \Q \E in regex
 | 
| 
28
 | 
    18 			var videoHandlers = {};
 | 
| 
 | 
    19 			{
 | 
| 
30
 | 
    20 				let ptn1 = /^https:\/\/youtu\.be\/([a-zA-Z0-9_-]+)(?:\?t=([0-9]+))?/;
 | 
| 
 | 
    21 				let ptn2 = /^https:\/\/www\.youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)(?:&t=([0-9]+)s)?/;
 | 
| 
28
 | 
    22 				videoHandlers.youtube = function(url) {
 | 
| 
 | 
    23 					let result = url.match(ptn1) || url.match(ptn2);
 | 
| 
 | 
    24 					if( result ) {
 | 
| 
 | 
    25 						url = 'https://www.youtube.com/embed/' + result[1];
 | 
| 
 | 
    26 						if( result[2] )
 | 
| 
 | 
    27 							url += '?start=' + result[2];
 | 
| 
 | 
    28 						return videoIframe(url);
 | 
| 
 | 
    29 					}
 | 
| 
 | 
    30 				}
 | 
| 
 | 
    31 			}
 | 
| 
 | 
    32 			{
 | 
| 
30
 | 
    33 				let ptn = /^https:\/\/rumble\.com\/embed\/[a-z0-9]+\/\?pub=[a-z0-9]+/;
 | 
| 
28
 | 
    34 				videoHandlers.rumble = function(url) {
 | 
| 
 | 
    35 					if( url.match(ptn) ) {
 | 
| 
 | 
    36 						return videoIframe(url);
 | 
| 
 | 
    37 					}
 | 
| 
 | 
    38 				}
 | 
| 
 | 
    39 			}
 | 
| 
 | 
    40 			{
 | 
| 
30
 | 
    41 				let ptn = /^https:\/\/www\.bitchute\.com\/video\/([a-zA-Z0-9]+)\//;
 | 
| 
28
 | 
    42 				videoHandlers.bitchute = function(url) {
 | 
| 
 | 
    43 					let result = url.match(ptn);
 | 
| 
 | 
    44 					if( result ) {
 | 
| 
 | 
    45 						url = 'https://www.bitchute.com/embed/' + result[1];
 | 
| 
 | 
    46 						return videoIframe(url);
 | 
| 
 | 
    47 					}
 | 
| 
 | 
    48 				}
 | 
| 
 | 
    49 			}
 | 
| 
 | 
    50 			{
 | 
| 
30
 | 
    51 				let ptn = /^https:\/\/vimeo\.com\/([0-9]+)/;
 | 
| 
28
 | 
    52 				videoHandlers.vimeo = function(url) {
 | 
| 
 | 
    53 					let result = url.match(ptn);
 | 
| 
 | 
    54 					if( result ) {
 | 
| 
 | 
    55 						url = 'https://player.vimeo.com/video/' + result[1];
 | 
| 
 | 
    56 						return videoIframe(url);
 | 
| 
 | 
    57 					}
 | 
| 
 | 
    58 				}
 | 
| 
 | 
    59 			}
 | 
| 
 | 
    60 			{
 | 
| 
30
 | 
    61 				let ptn = /^https:\/\/dai\.ly\/([a-z0-9]+)/;
 | 
| 
28
 | 
    62 				videoHandlers.dailymotion = function(url) {
 | 
| 
 | 
    63 					let result = url.match(ptn);
 | 
| 
 | 
    64 					if( result ) {
 | 
| 
 | 
    65 						url = 'https://www.dailymotion.com/embed/video/' + result[1];
 | 
| 
 | 
    66 						return videoIframe(url);
 | 
| 
 | 
    67 					}
 | 
| 
 | 
    68 				}
 | 
| 
 | 
    69 			}
 | 
| 
 | 
    70 			{
 | 
| 
30
 | 
    71 				let ptn = /^https:\/\/www\.tiktok\.com\/[^/]+\/video\/([0-9]+)/;
 | 
| 
28
 | 
    72 				videoHandlers.tiktok = function(url) {
 | 
| 
 | 
    73 					let result = url.match(ptn);
 | 
| 
 | 
    74 					if( result ) {
 | 
| 
31
 | 
    75 						//let html = '<blockquote class="tiktok-embed" data-video="'+result[1]+'" style="max-width: 560px; margin-left: 0;"><section></section></blockquote>';
 | 
| 
28
 | 
    76 						//html += '<script async src="https://www.tiktok.com/embed.js"></'+'script>';
 | 
| 
31
 | 
    77 						let html = '<img data-video="'+result[1]+'" src="whataever">';
 | 
| 
28
 | 
    78 						return html;
 | 
| 
 | 
    79 					}
 | 
| 
 | 
    80 				}
 | 
| 
 | 
    81 			}
 | 
| 
 | 
    82 			{
 | 
| 
30
 | 
    83 				let ptn = /\.[a-zA-Z0-9]+$/;
 | 
| 
28
 | 
    84 				videoHandlers.file = function(url) {
 | 
| 
 | 
    85 					if( url.match(ptn) ) {
 | 
| 
 | 
    86 						return '<video controls width="560" height><source src="'+url+'"></video>';
 | 
| 
 | 
    87 					}
 | 
| 
 | 
    88 				}
 | 
| 
 | 
    89 			}
 | 
| 
 | 
    90 
 | 
| 
 | 
    91 			function videoUrlToHtml(url) {
 | 
| 
 | 
    92 				for (let key in videoHandlers) {
 | 
| 
 | 
    93 					let handle = videoHandlers[key];
 | 
| 
 | 
    94 					let html = handle(url);
 | 
| 
 | 
    95 					if(html) return html;
 | 
| 
 | 
    96 				}
 | 
| 
 | 
    97 				return '<a data-video="'+url+'" href="'+url+'">'+url+'</a>';
 | 
| 
 | 
    98 			}
 | 
| 
22
 | 
    99 
 | 
| 
35
 | 
   100 			function uploaded(input,url,filename) {
 | 
| 
 | 
   101 				input.parentNode.parentNode.querySelector('input[type="url"]').value = url;
 | 
| 
 | 
   102 			}
 | 
| 
 | 
   103 
 | 
| 
 | 
   104 			let imageFileHtml = '<input type=file accept="image/*" onchange="upload(this,uploaded)">';
 | 
| 
 | 
   105 			imageFileHtml += '<button class="tox-button tox-button--secondary" onclick="fileButtonClick(this)">Upload Image</button>';
 | 
| 
 | 
   106 
 | 
| 
 | 
   107 			let videoFileHtml = '<input type=file accept="video/*" onchange="upload(this,uploaded)">';
 | 
| 
 | 
   108 			videoFileHtml += '<button class="tox-button tox-button--secondary" onclick="fileButtonClick(this)">Upload Video</button>';
 | 
| 
24
 | 
   109 
 | 
| 
23
 | 
   110 			function tinymceSetup(editor) {
 | 
| 
24
 | 
   111 
 | 
| 
35
 | 
   112 				editor.ui.registry.addButton('insertLink', {
 | 
| 
 | 
   113 					icon: 'link',
 | 
| 
 | 
   114 					tooltip: 'Insert link',
 | 
| 
 | 
   115 					onAction: function(api) {
 | 
| 
 | 
   116 						editor.windowManager.open({
 | 
| 
 | 
   117 							title: 'Insert Link',
 | 
| 
 | 
   118 							body: {
 | 
| 
 | 
   119 								type: 'panel',
 | 
| 
 | 
   120 								items: [
 | 
| 
 | 
   121 									{
 | 
| 
 | 
   122 										type: 'urlinput',
 | 
| 
 | 
   123 										name: 'src',
 | 
| 
 | 
   124 										label: 'URL'
 | 
| 
 | 
   125 									},
 | 
| 
 | 
   126 									{
 | 
| 
 | 
   127 										type: 'input',
 | 
| 
 | 
   128 										name: 'text',
 | 
| 
 | 
   129 										label: 'Text to display'
 | 
| 
 | 
   130 									},
 | 
| 
 | 
   131 								]
 | 
| 
 | 
   132 							},
 | 
| 
 | 
   133 							buttons: [
 | 
| 
 | 
   134 								{
 | 
| 
 | 
   135 									type: 'cancel',
 | 
| 
 | 
   136 									text: 'Cancel'
 | 
| 
 | 
   137 								},
 | 
| 
 | 
   138 								{
 | 
| 
 | 
   139 									type: 'submit',
 | 
| 
 | 
   140 									text: 'Save',
 | 
| 
 | 
   141 									buttonType: 'primary'
 | 
| 
 | 
   142 								}
 | 
| 
 | 
   143 							],
 | 
| 
 | 
   144 							onSubmit: function(dialogApi) {
 | 
| 
 | 
   145 								let data = dialogApi.getData();
 | 
| 
 | 
   146 								let src = data.src.value;
 | 
| 
 | 
   147 								if(!src) return;
 | 
| 
 | 
   148 								src = tinymce.DOM.encode(src);
 | 
| 
 | 
   149 								let text = data.text;
 | 
| 
 | 
   150 								let html = '<a href="' + src + '">' + text + '</a>';
 | 
| 
 | 
   151 								dialogApi.close();
 | 
| 
 | 
   152 								editor.insertContent(html);
 | 
| 
 | 
   153 							}
 | 
| 
 | 
   154 						});
 | 
| 
 | 
   155 					},
 | 
| 
 | 
   156 				});
 | 
| 
 | 
   157 
 | 
| 
31
 | 
   158 				editor.ui.registry.addButton('insertImage', {
 | 
| 
 | 
   159 					icon: 'image',
 | 
| 
 | 
   160 					tooltip: 'Insert image',
 | 
| 
 | 
   161 					onAction: function(api) {
 | 
| 
 | 
   162 						editor.windowManager.open({
 | 
| 
 | 
   163 							title: 'Insert Image',
 | 
| 
 | 
   164 							body: {
 | 
| 
 | 
   165 								type: 'panel',
 | 
| 
 | 
   166 								items: [
 | 
| 
 | 
   167 									{
 | 
| 
 | 
   168 										type: 'urlinput',
 | 
| 
 | 
   169 										name: 'src',
 | 
| 
 | 
   170 										filetype: 'image',
 | 
| 
 | 
   171 										label: 'Source URL'
 | 
| 
 | 
   172 									},
 | 
| 
35
 | 
   173 									{
 | 
| 
 | 
   174 										type: 'htmlpanel',
 | 
| 
 | 
   175 										html: imageFileHtml
 | 
| 
 | 
   176 									},
 | 
| 
31
 | 
   177 								]
 | 
| 
 | 
   178 							},
 | 
| 
 | 
   179 							buttons: [
 | 
| 
 | 
   180 								{
 | 
| 
 | 
   181 									type: 'cancel',
 | 
| 
 | 
   182 									text: 'Cancel'
 | 
| 
 | 
   183 								},
 | 
| 
 | 
   184 								{
 | 
| 
 | 
   185 									type: 'submit',
 | 
| 
 | 
   186 									text: 'Save',
 | 
| 
 | 
   187 									buttonType: 'primary'
 | 
| 
 | 
   188 								}
 | 
| 
 | 
   189 							],
 | 
| 
 | 
   190 							onSubmit: function(dialogApi) {
 | 
| 
35
 | 
   191 								let src = dialogApi.getData().src.value;
 | 
| 
31
 | 
   192 								if(!src) return;
 | 
| 
 | 
   193 								src = tinymce.DOM.encode(src);
 | 
| 
 | 
   194 								let html = '<img src="' + src + '">';
 | 
| 
 | 
   195 								dialogApi.close();
 | 
| 
 | 
   196 								editor.insertContent(html);
 | 
| 
 | 
   197 							}
 | 
| 
 | 
   198 						});
 | 
| 
 | 
   199 					},
 | 
| 
 | 
   200 				});
 | 
| 
 | 
   201 
 | 
| 
 | 
   202 				editor.ui.registry.addButton('insertVideo', {
 | 
| 
 | 
   203 					icon: 'embed',
 | 
| 
 | 
   204 					tooltip: 'Insert video',
 | 
| 
 | 
   205 					onAction: function(api) {
 | 
| 
 | 
   206 						editor.windowManager.open({
 | 
| 
 | 
   207 							title: 'Insert Video',
 | 
| 
 | 
   208 							body: {
 | 
| 
 | 
   209 								type: 'panel',
 | 
| 
 | 
   210 								items: [
 | 
| 
 | 
   211 									{
 | 
| 
 | 
   212 										type: 'urlinput',
 | 
| 
 | 
   213 										name: 'src',
 | 
| 
35
 | 
   214 										filetype: 'media',
 | 
| 
31
 | 
   215 										label: 'Source URL'
 | 
| 
 | 
   216 									},
 | 
| 
35
 | 
   217 									{
 | 
| 
 | 
   218 										type: 'htmlpanel',
 | 
| 
 | 
   219 										html: videoFileHtml
 | 
| 
 | 
   220 									},
 | 
| 
31
 | 
   221 								]
 | 
| 
 | 
   222 							},
 | 
| 
 | 
   223 							buttons: [
 | 
| 
 | 
   224 								{
 | 
| 
 | 
   225 									type: 'cancel',
 | 
| 
 | 
   226 									text: 'Cancel'
 | 
| 
 | 
   227 								},
 | 
| 
 | 
   228 								{
 | 
| 
 | 
   229 									type: 'submit',
 | 
| 
 | 
   230 									text: 'Save',
 | 
| 
 | 
   231 									buttonType: 'primary'
 | 
| 
 | 
   232 								}
 | 
| 
 | 
   233 							],
 | 
| 
 | 
   234 							onSubmit: function(dialogApi) {
 | 
| 
35
 | 
   235 								let src = dialogApi.getData().src.value;
 | 
| 
31
 | 
   236 								if(!src) return;
 | 
| 
 | 
   237 								let html = videoUrlToHtml(src);
 | 
| 
 | 
   238 								//alert(html);
 | 
| 
 | 
   239 								dialogApi.close();
 | 
| 
 | 
   240 								editor.insertContent(html);
 | 
| 
 | 
   241 							}
 | 
| 
 | 
   242 						});
 | 
| 
 | 
   243 					},
 | 
| 
 | 
   244 				});
 | 
| 
 | 
   245 
 | 
| 
24
 | 
   246 				editor.ui.registry.addToggleButton('styleCode', {
 | 
| 
32
 | 
   247 					icon: 'code-sample',
 | 
| 
24
 | 
   248 					tooltip: 'Code',
 | 
| 
 | 
   249 					onAction: function(api) {
 | 
| 
31
 | 
   250 						editor.execCommand('mceToggleFormat', false, 'code');
 | 
| 
24
 | 
   251 					},
 | 
| 
 | 
   252 					onSetup: function(api) {
 | 
| 
 | 
   253 						api.setActive(editor.formatter.match('code'));
 | 
| 
 | 
   254 						let changed = editor.formatter.formatChanged('code', api.setActive);
 | 
| 
 | 
   255 						return function() { changed.unbind(); };
 | 
| 
 | 
   256 					}
 | 
| 
 | 
   257 				});
 | 
| 
 | 
   258 
 | 
| 
 | 
   259 				editor.ui.registry.addMenuButton('styleText', {
 | 
| 
 | 
   260 					icon: 'format',
 | 
| 
 | 
   261 					tooltip: 'Text',
 | 
| 
 | 
   262 					fetch: function(callback) {
 | 
| 
 | 
   263 						callback([
 | 
| 
 | 
   264 							'fontsize',
 | 
| 
 | 
   265 							'forecolor',
 | 
| 
 | 
   266 						])
 | 
| 
 | 
   267 					}
 | 
| 
23
 | 
   268 				});
 | 
| 
26
 | 
   269 
 | 
| 
31
 | 
   270 				editor.on( 'init', function(e) {
 | 
| 
 | 
   271 					editor.focus();
 | 
| 
 | 
   272 				} );
 | 
| 
23
 | 
   273 			}
 | 
| 
 | 
   274 
 | 
| 
22
 | 
   275 			tinymce.init({
 | 
| 
 | 
   276 				selector: 'textarea',
 | 
| 
23
 | 
   277 				setup: tinymceSetup,
 | 
| 
27
 | 
   278 				//menubar: false,
 | 
| 
26
 | 
   279 				statusbar: false,
 | 
| 
32
 | 
   280 				plugins: ['link', 'image', 'media', 'lists', 'code', 'autoresize'],
 | 
| 
35
 | 
   281 				toolbar: 'link insertLink insertImage insertVideo | styleCode bold italic underline strikethrough superscript styleText | blockquote numlist bullist',
 | 
| 
26
 | 
   282 				autoresize_bottom_margin: 0,
 | 
| 
22
 | 
   283 				link_target_list: false,
 | 
| 
 | 
   284 				link_title: false,
 | 
| 
 | 
   285 				object_resizing: false,
 | 
| 
 | 
   286 				contextmenu: false,
 | 
| 
 | 
   287 				text_patterns: false,
 | 
| 
26
 | 
   288 				content_style: 'img {max-width: 500px;} p {margin: 0}',
 | 
| 
22
 | 
   289 				extended_valid_elements: 'b,i',
 | 
| 
 | 
   290 				formats: {
 | 
| 
 | 
   291 					bold: { inline: 'b' },
 | 
| 
 | 
   292 					italic: {inline: 'i'},
 | 
| 
 | 
   293 					underline: {inline: 'u'},
 | 
| 
 | 
   294 				},
 | 
| 
 | 
   295 			});
 | 
| 
 | 
   296 
 | 
| 
 | 
   297 			function log() {
 | 
| 
 | 
   298 				console.log(tinymce.activeEditor.getContent());
 | 
| 
 | 
   299 			}
 | 
| 
 | 
   300 		</script>
 | 
| 
 | 
   301 	</head>
 | 
| 
 | 
   302 	<body>
 | 
| 
35
 | 
   303 		<p>
 | 
| 
 | 
   304 			<a href="https://www.tiny.cloud/">TinyMCE</a>
 | 
| 
 | 
   305 			<a href="https://github.com/tinymce/tinymce">source</a>
 | 
| 
 | 
   306 		</p>
 | 
| 
22
 | 
   307 		<textarea></textarea>
 | 
| 
 | 
   308 		<p><button onclick="log()">log</button></p>
 | 
| 
 | 
   309 	</body>
 | 
| 
 | 
   310 </html>
 |