convert javascript into a bookmarklet

by Laurens Maneschijn

HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<!-- https://jsfiddle.net/ElMoonLite/e9um30x7/ -->

<h1>Convert javascript to bookmarklet link.</h1>

<div class="block">
	<b>Input:</b>
	<small>
		<a href="#" data-demoinput>clear</a>
		demos:
		<a href="#" data-demoinput="demo0">hello world</a> ,
		<a href="#" data-demoinput="demo1">1</a> ,
		<a href="#" data-demoinput="demo2">2</a> ,
		<a href="#" data-demoinput="demo3">3</a>
	</small>

	<textarea id="ta_source" placeholder="paste or type javascript code here"></textarea>

	<div id="parse_log"></div>
</div>


<div class="block">
	<b>Output:</b>

	<div class="block">
		<b>As link:</b>
		<a id="ta_link" href="#">link</a><br>
		Click to quick test <small>(runs on current page, see console for issues)</small>.<br>
		Drag link to bookmarks to save and use on other pages.<br>
		<small>rename link before dragging:</small>
		<input id="link_rename" placeholder="link">
	</div>

	<div class="block">
		<b>As html:</b><br>
		For sharing as htmlescaped string. Copy paste elsewhere into e.g.
		&lt;a href="..."&gt; or &lt;code&gt;...&lt;/code&gt;.<br>
		escape what:
		<label><input type="checkbox" id="escape_amp"  checked> &amp;  &rArr; &amp;amp;  </label>
		<label><input type="checkbox" id="escape_quot" checked> &quot; &rArr; &amp;quot; </label>
		<label><input type="checkbox" id="escape_apos" checked> &apos; &rArr; &amp;apos; </label>
		<label><input type="checkbox" id="escape_lt"   checked> &lt;   &rArr; &amp;lt;   </label>
		<label><input type="checkbox" id="escape_gt"   checked> &gt;   &rArr; &amp;gt;   </label>
		<br>
		<textarea id="ta_1linehref" readonly></textarea><br>
	</div>

	<div class="block">
		<details>
			<summary><b>Debug:</b><br></summary>
			only (inline and block) comments stripped:<br>
			<small>(useful for debugging faulty comment stripping bugs)<br></small>
			<textarea id="ta_nocomments" readonly></textarea><br>

			<hr>

			1 line, and all<sup>*</sup> whitespace...

CSS

body {
	background: #fff;
	color: #000;
}
h1,h2,h3,h4,h5,h6 {
	font-size: 1em;
	padding: 0 0 0 0;
	margin: 2px 0 2px 0;
}
ul,li {
	margin: 0 0 0 0;
}
code {
	display: inline-block;
	outline: 1px dotted #888;
	padding: 1px;
	margin: 1px;
}
label {
	display: inline-block;
	border: 1px solid #888;
	border-radius: 4px;
	background: #8882;
	padding: 1px;
	margin: 1px;
	cursor: pointer;
}
label:hover {
	border-color: #000;
	background: #8884;
}
label:hover input {
	outline: 1px dashed #000;
}
label input {
	margin:0;
}
textarea {
	width: 100%;
	box-sizing: border-box;
	tab-size: 4;
	white-space: pre;
	margin: 4px 0;
}
textarea[readonly] {
	background: #8884;
}

textarea#ta_source {
	height: 100px;
}
#link_rename {
	width: 50px;
}
.block {
	display: block;
	margin: 12px 0;
	padding: 4px;
	border: 1px solid #888;
	border-radius: 4px;
	background: #8882;
}

JavaScript

$(document).ready(function(){
	$(document).on('change focus blur click keydown keyup', '#ta_source', function(e){
		update();
	});
	$(document).on('change','input[type=checkbox]', function(e){
		update();
	});
	update();
	$(document).on('change keyup focus blur','#link_rename', function(e){
		$('#ta_link').text(this.value || 'link');
	});
	$('[data-demoinput]').click(function(e){
		var d = e.currentTarget.getAttribute('data-demoinput');
		var s = demo_input[d] || d || '';
		$('#ta_source').val(s);
		update();
	});
});

function update() {
	var s = $('#ta_source').val();
	$('#ta_nocomments').val(parseJavascript(s, false));
	var s_bookmarklet = scriptToBookmarklet(s);
	$('#ta_1line').val(s_bookmarklet);
//	$('#ta_1linehref').val(htmlescapeforlink(s_bookmarklet));
	$('#ta_1linehref').val(htmlescape_optional(s_bookmarklet));
	$('#ta_link').attr('href', s_bookmarklet);

	$('#parse_log').empty();
	var m = detect_possible_missing_semicolons(s);
	if(m) {
		var ta = document.createElement('textarea');
		ta.setAttribute('readonly', true);
		ta.value = m.join('\n');
		$('#parse_log').append('<b style="color:#f00;">Possible missing EOL semicolon errors:</b><br>');
		$('#parse_log').append(ta);
	}
}

function parseJavascript(str, compact) {
	// basic javascript parser, and return given javascript string:
	// - strip comments, 
	// - if compact==true: remove double code whitespace (not inside strings).
	// NB: code might break if:
	// - ; (semicolon) is missing after a statement before a newline.
	// - regex using slashes is used, e.g. "/123/g"
	//   (but using: "new RegExp('...', '...')" is fine though).

	// Note on trying to parse regex, which is hard to implement:
	// Determining if / is a RegExp delimiter or a division character is hard.
	// e.g. "re = /123/g" vs "2 / 3"
	// We can work around this by (manually) replacing "/123/g" with:
	// "new RegExp('123', 'g')".
	// see:
	//...