tabtospaces

convert tabs to # spaces. old file using native JS.

by Laurens Maneschijn

HTML

<textarea id="ta1" rows="10" onchange="applytabtospaces_delayed();" onkeyup="applytabtospaces_delayed();"></textarea>
<textarea id="ta2" rows="10" readonly></textarea>
<br/>
convert tabs to <input id="tabspaces" type="number" value="4" size=2 min=1 max=100 step=1 style="width:36px;text-align:right;padding:0px;border:0px;" /> spaces<br/>
<span class="checkboxbox"><input id="updatescroll" type="checkbox" style="padding:0px;border:0px;background-color:#888;color:#f00;" checked /></span> updatescroll<br/>
<span class="checkboxbox"><input id="updateresize" type="checkbox" style="padding:0px;border:0px;background-color:#888;color:#f00;" checked /></span> updateresize<br/>

<h4 class="clickable" onclick="$('#exampleinpre').toggle();">+ example in &lt;pre&gt; tags:</h4>
<div id="exampleinpre" style="display:none;">
text below edited in a 4-space-tab editor, the 2 columns look odd in a default PRE or TEXTAREA container, because it will use 8-space-tabs:
<pre id="test1">
1234567890123456789012345678901234567890
0
	1
		2
			3
a	1	2
aa	1b	2
aaa	1bb	2
aaaa	2cc

example text, 2 columns edited with a 4-space-tab editor:
2 columns, seperated by 2 tabs (2nd column is at pos 8)

a		b
aa		b
aaa		b
aaaa	b
aaaaa	b
aaaaaa	b
aaaaaaa	b
aaaaaaaa	c
aaaaaaaaa	c
</pre>

same as above, but copy from above replacing tabs with 0-4 spaces (depending on where the tab is in the line), so everything is aligned in a "4-space-grid":
<pre id="test2">
</pre>


"&amp;emsp;" == "&emsp;" is still a fixed unit width, no tab magic happens:
<pre> 
&emsp;1
1&emsp;2
22&emsp;3
333&emsp;4

0
&emsp;1
&emsp;&emsp;2
&emsp;&emsp;&emsp;3
&emsp;&emsp;&emsp;&emsp;4
</pre>

[ <span class="checkboxbox"><input type="checkbox"/></span> checkbox styled border test ]<br/>

</div>

CSS

body{
	background-color:#000;
	color:#fff;
}

body, input, textarea, pre {
	font-family: 'monospace'; /* Lucida Console */
	font-size: 10px;
	line-height: 1;
}

input,textarea{
	background-color:#222;
	color:#fff;
	tab-size: 4;
}
pre{
	border:1px solid #888;
}
textarea{
	border:1px solid #888;
}
textarea#ta1{
	padding:1px;
	width:48%;
	height:50%;
	white-space:pre;
	word-wrap: normal;
	vertical-align:top;
}
textarea#ta2{
	padding:1px;
	width:49%;
	height:50%;
	white-space:pre;
	word-wrap: normal;
	vertical-align:top;
}
.clickable{
	cursor:pointer;
	border:2px outset #888;
	display:inline-block;
}
.clickable:hover{
	text-decoration:underline;
	border:2px outset #ccc;
}
.clickable:active{
	text-decoration:underline;
	border:2px inset #ccc;
}

input[type=radio],
input[type=checkbox]{
	margin:-2px -2px -2px -2px;
	vertical-align:middle;
}

.checkboxbox{
	display:inline-block;
	overflow:hidden;
/*
	border:2px inset #888;
	margin:-2px -2px -2px -2px;
	-webkit-box-shadow: 3px 3px 5px 1px rgba(120,120,120, 0.1);
	   -moz-box-shadow: 3px 3px 5px 1px rgba(120,120,120, 0.1);
	        box-shadow: 2px 2px 5px 1px rgba(120,120,120, 0.1);
	-webkit-border-radius: 2px;
	   -moz-border-radius: 2px;
	        border-radius: 2px;
*/
}
.checkboxbox input{
	margin:-2px -2px -2px -2px;
}

JavaScript

// string repeat implemented using Russian Peasant multiplication
String.prototype.repeat = function (n) {
    if (n<1) return '';
    var accum = '', c=this;
    for (; n; n >>=1) {
        if (1&n) accum += c;
        c += c;
    }
    return accum;
}
String.prototype.untabify = function(tabWidth) {
	// replace all starting tabs with tabWidth spaces each (for all tabs on the beginning of the line):
    tabWidth = tabWidth || 4;
    return this.replace(/^\t+/gm, function(tabs) { return ' '.repeat(tabWidth * tabs.length)} );
}
String.prototype.tabstospaces_replacefirsttabwithspaces = function(tabWidth){
	// replace the first tab
	var firsttabpos = this.indexOf('\t');
	if(firsttabpos < 0){
		return this;
	}else if(firsttabpos == 0){
		return ' '.repeat(tabWidth) + this.substr(1);
	}else{
		// replace a tab at pos x with y spaces:  y = 8 - (x % 8)
		var nspaces = tabWidth - (firsttabpos % tabWidth);
		return this.substr(0, firsttabpos) + ' '.repeat(nspaces) + this.substr(firsttabpos + 1);
	}
}
String.prototype.tabstospaces = function(tabWidth){
	// cut up string in lines and for each line:
	//  for each tab encountered, calculate the char position from the start of the line, and replace it with (tabWidth - (tabcharpos % tabWidth) ) spaces
	var firsttabpos, n,line,lines = this.split('\n');
	for(n = 0; n < lines.length ; n++){
		line = lines[n];
//		firsttabpos = line.indexOf('\t');
		while(line.indexOf('\t') >= 0){
			line = line.tabstospaces_replacefirsttabwithspaces(tabWidth);
		}
		lines[n] = line;
	}
	return lines.join('\n');
}


////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////

//	(function(window){
 
function bodyonload(){
	var p1,p2;
	p1 = document.getElementById('test1');
	p2 = document.getElementById('test2');
	p2.innerHTML = ('' + p1.innerHTML).tabstospaces(4);
/*
	var obj, objs =...