Making line numbers uncopyable - javascript

I'm working on adding line number support to Rainbow, a syntax highlighter, but I can't figure out how to make the line numbers uncopyable.
Disabling selection via user-select: none; makes an element unhighlightable, but you can still copy its text by highlighting around it and then copying, which ends up copying the line numbers along with code.
Here is a working example of the problem: http://jsfiddle.net/CjJLv/8/
Any help would be appreciated. Thanks!

Okay, the easiest way in compliant browsers, and, sadly, not reliable cross-browser, is to use generated content (I've removed the various parts where index was being added to textual content in the plug-in, and used the following (at the end of the CSS) to implement un-copyable text:
table.rainbow {
counter-reset: line;
}
table.rainbow tbody tr td:first-child {
counter-increment: line;
}
table.rainbow tr td:first-child::before {
content: counter(line);
}
JS Fiddle demo.
This does, though, have some rather large flaws (the cross-browser unfriendly approach being the biggest), so I'll try for something better...

I would just add a regular list.
if (window.Rainbow) window.Rainbow.linecount = (function(Rainbow) {
Rainbow.onHighlight(function(block) {
var lines = $(block).text().split('\n');
var $lines = $('<ul class="lines"/>');
for (var i = 0, len = lines.length; i < len; i++) {
$lines.append('<li class="line"'+ i +'>'+ i +'</li>');
}
$(block).before($lines);
});
})(window.Rainbow);​
And CSS:
.lines {
float: left;
padding-right: 1.5em;
padding-left: .5em;
}
So now you can select just the code if you highlight carefully.
Demo: http://jsfiddle.net/elclanrs/CjJLv/18/

David Thomas's answer is perfect for line numbers. More generally, if you have other text you don't want to be copied, you can have it as generated content:
<style>#uniqueid::before { content: 'TEXT GOES HERE'; }</style>
<span id="uniqueid"></span>
But it's ugly to have to embed text in your CSS, so you can refine this using CSS attr() to read the text from an attribute in the HTML (as suggested by pimvdb):
<style>[data-nocopy]::before { content: attr(data-nocopy); }</style>
<span data-nocopy="TEXT GOES HERE"></span>
<span data-nocopy="AND HERE"></span>
Demo: http://jsbin.com/fob/1/edit
This works in Firefox, Safari, and Chrome due to 21-year-old(!) bugs in selecting generated content:
https://bugzilla.mozilla.org/show_bug.cgi?id=12460
https://bugs.webkit.org/show_bug.cgi?id=7562
https://bugs.chromium.org/p/chromium/issues/detail?id=80466
But in old IE (< 8) the text will be completely invisible; in newer IE it should be visible but may well be copyable. In general don't use this technique for anything critical, as these bugs might get fixed one day...
And use sparingly, as this can be very user-hostile.

You could display each line number as a sequence of <img>s.

Related

odd formatting when appending elements with both jQuery and javascript

I don't understand why my <li> within my <ol> are displaying in this peculiar way. It is happening in both firefox and chrome. Somehow they are not aligning properly. This is happening when I create the elements with both jQuery and JavaScript.
var limit = 10000, wrapper = document.getElementById("vanilla_wrapper"), el;
for (var i = 0; i <= limit; i++){
jq_wrapper.append("<li></li>");
}
for (var i = 0; i <= limit; i++){
el = document.createElement("li");
wrapper.appendChild(el);
}
Edit
JSBIN
Edit 2
Adjusting the padding as suggested still has very odd formatting:
JSBIN
That is because the ol element has a left-padding automatically applied which create the space for the numbering..
It has a default value of 40px so if your numbering text exceeds this, you get that problem..
You can increase it with
ol{
padding-left: 60px;
}
I think you need this
ol {margin: 0; list-style-position: inside; padding: 0;}
Try using this. Add your margin, then make sure it's vertically aligned to the top:
.jq { vertical-align: top }
ol li{
margin-left:20px;
}
Each browser dictates the formatting of the list marker, so you'll have to use browser specific properties to accomplish this. I'm looking into other browsers right now, but in the meantime, this is what I've found for Chrome (webkit):
td {
padding: 0px; /*Or just remove it...*/
}
ol {
list-style-position: inside;
-webkit-padding-start: 0px;
}
The default for -webkit-padding-start is 40px, which is what was throwing off your numbers above 1000. While others have suggested increasing the padding value, this will indent your numbers below 1000 too far and make them look odd. Using the -padding-start property will help keep the numbers in line. Stay tuned for more info on other browsers.
jsbin that works in chrome (I cut your limit down to 1500 just to reduce jsbin's lag): http://jsbin.com/uLabiHiY/18
Edit:
Here is the property for Firefox: -moz-padding-start
and Opera: -o-padding-start
It doesn't appear that IE has one (go figure...) but this link might provide a little insight on how to fix that: -webkit-padding-start: 40px; What it should be for IE and Firefox?

Wrapping a text on arbitrary non-whitespace characters

I have long namespaces to be displayed in my articles (like MyProject\Sub\Level) and I want them to be wrapped on a backslash character (\) if the window width is insufficient. How can I implement this using CSS or JS/jQuery?
To tell browsers that a line break is permitted after a character, insert the ZERO WIDTH SPACE (ZWSP) character, U+200B, after the character. In HTML markup, you can use the character reference `​', e.g.
MyProject\​Sub\​Level
If you add the characters via scripting, you can enter the character itself, using the string literal \u200b.
Some old browsers (IE 6) used to have problems with this, but now this approach seems to work better than the alternative, the old <wbr> tag (which was well-supported but has now been messed up in new versions of IE).
Use the following css in the element that contains the text:
word-wrap: break-word;
It's a css3 property supported in modern browsers: Google Chrome 6, Internet Explorer 8, Firefox 3, Opera 10 y Safari 5.
Acclaration: It won't break on / character only, it could break on any character of the word according to the stretching of container.
One option is to use the soft-hyphen (­) around the \ characters; the soft-hyphen allows a word to break at a given point, appearing only if the word does, in fact, break at that point.
It also doesn't appear in the text if it's copied and pasted (tested in Chromium 19/Ubuntu 11.04); this approach uses replace() and jQuery's html() method with the following HTML:
<ul>
<li>\some\text\possibly\a\path</li>
<li>\another\path</li>
<li>\something\else\also\quite\long</li>
</ul>​
And CSS:
li {
width: 8em;
border: 1px solid #000;
}​
jQuery:
$('li').html(function(i, h) {
return h.replace(/(\\)/g, '­$1­')
});​
JS Fiddle demo.
Obviously, you don't need to use the soft-hyphen on both sides of the \ character, that was just a demonstration for how it might be used.
An alternative, if you'd rather avoid the appearance of - characters at the break-point, is to use the same approach as above, but instead insert an empty span element, and give it the style display: inline-block;:
jQuery:
$('li').html(function(i, h) {
return h.replace(/(\\)/g, '$1<span></span>')
});​
CSS:
li {
width: 8em;
border: 1px solid #000;
}
li span {
display: inline-block;
}
JS Fiddle demo.
References:
html().
replace().
You could probably follow the example on jQuery - Find and replace text, after body was loaded and have it insert a break following every slash

How to show line numbers for a code block using JavaScript?

Here's the thing. I use 'Highlight.js' (a javascript-based automatic syntax highlighter) to syntax-highlight code on my website. But it doesn't support line numbers or zebra-striping (for alternate lines of code).
My code block is wrapped in <pre><code> blocks like this:
<pre><code>
<script type="text/javascript">
// Say hello world until the user starts questioning
// the meaningfulness of their existence.
function helloWorld(world) {
for (var i = 42; --i >= 0;) {
alert('Hello ' + String(world));
}
}
</script>
<style>
p { color: pink }
b { color: blue }
u { color: "umber" }
</style>
</code></pre>
And the output looks like this:
Now I want to show line numbers for the code block dynamically using JavaScript. How do I do that? (Also, if possible, how do I show zebra-striping?)
Thanks.
PS: I don't know JavaScript, so please try to be as clear as possible. I will try my best to understand. Thanks.
You could use an alternate framework such as http://alexgorbatchev.com/SyntaxHighlighter/
Or take a look here and find something that suites.
http://www.1stwebdesigner.com/css/16-free-javascript-code-syntax-highlighters-for-better-programming/
The basic steps would be:
Take the HTML inside the element.
Split by newline characters (\n).
For each string, add a number and a dot in front of it.
Combine the strings again with newline characters.
Set the string as the HTML of the element.
However, this would mess up the syntax highlighting of the syntax highlighter because it most likely won't recognize that the code has line numbers in front. So the syntax highlighter needs to provide the functionality of line numbers for you.
Adding a new answer to an old question.
I wanted to display line numbers in the left margin the way ace.js does.
My solution has some hacky details, but I wanted to share it anyway, because it turns out that absolute-positioned spans within relative-positioned spans work pretty well for this.
Encouraged by the above answers and this answer about relative positioning without taking up space, I used:
var line = 1;
code = code.replace(/^/gm, function() {
return '<span class="line-number-position">​<span class="line-number">' + line++ + '</span></span>';
});
The regular expression /^/gm "replaces" the beginning of each line with the span-within-span.
​ is a zero-width space, because apparently firefox seems to have trouble deciding whether to put a zero-height span at the top or the bottom of the character.
line-number-position and line-number are CSS classes like these:
.line-number-position {
position: relative;
top: 0;
}
.line-number {
position: absolute;
text-align: right;
right: 17px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
font-size: 12px;
}
Yes, there are some magic numbers in there to match ace formatting, but the point is to put a relative-positioned zero-sized span at the beginning of each line and use it as a reference point to add an absolute-positioned span out in the left margin.
Works on current Chrome, Safari, Firefox, and Opera.

JS/CSS/XHTML: Don't copy specific text during copy events

I'm looking for a way to disable the copying of a specific area of text when you use Ctrl + C, etc. Whether I have to write the text a different way or not.
http://gyazo.com/721a0a5b5af173beb1ad3305633beafb.png
Above is what this is for. It's a syntax highlighter I have been working on (3 languages supported so far). When the user selects ANY text in any way, I don't want the line numbers to be copied.
I can't think of a way to display line numbers, without them actually being there.
As long as the line numbers and the source code are mixed together, this is going to be tough to prevent programmatically, if not impossible.
The ideal way would be having the source code in an actual container of its own.
Open a document inspector and look at how Github do it, for example: https://github.com/jbrisbin/riak-exchange/blob/master/Makefile
they have a separate <pre> element containing the line numbers, and a <table> cell containing the code. (I assume selecting is a reason why they use tables here, but I do not know for sure.)
Give this a try...
Demo: http://jsfiddle.net/wdm954/UD8Dq/7
I layered the div so the code div is on top and the numbers are behind. When you copy and paste you should just get the code.
.lines {
position: absolute;
width: 80%;
color: #666;
}
.lines pre:nth-child(odd) {
background-color: #EEE;
}
.code {
position: absolute;
z-index: 2;
padding-left: 5%;
width: 80%;
}
<div class="box">
<div class="lines">
<pre>1</pre>
<pre>2</pre>
<pre>3</pre>
<pre>4</pre>
</div>
<div class="code">
<pre>
code
code
code
code
</pre>
</div>
</div>
Setting user-select, -moz-user-select, and -webkit-user-select to none might work. For IE, you will need to handle onselectstart and return false.
This will prevent people from selecting the text, but I don't know what happens when it's beside other text that you attempt to copy.
I know that this question is three years old, but with HTML5 you can store line numbers in a data attributes and use CSS2 to display the text. This should prevent line numbers from being copied.
HTML
<span data-line-number='1' class='line'></span>
CSS
.line:before {
content: attr(data-line-number);
}

Jquery attribute selector

I have a variable (div element) which contains some table html.
I can use this javascript to add a class to each cell that has a background set.
var tds = tempDiv.getElementsByTagName("TD");
for (var j = 0; j < tds.length; j++) {
var oTd = tds[j];
if (oTd.style.background.length > 0) {
oTd.className = 'faketh';
oTd.setAttribute('style', 'Clear');
} //if
}//for
what i'd like to do is do the same in jquery. Below is what i've come up with, and the second line works fine, but the first doesn't....
$(tempDiv).find("td[style*='background:']").addClass("faketh");
$(tempDiv).find("td").removeAttr('style');
Any help would be appreciated.
Edit:
Just to add; I'm using the code below without issue.
$(tempDiv).find("tr:odd").addClass('row0');
$(tempDiv).find("tr:even").addClass("row1");
So its not the adding of the class thats the problem... The issue is that i'm not finding any matching elements. Here is one of the td elements;
<td valign="top" class="faketd" style="border: 1pt solid windowtext; padding: 0cm 5.4pt; background: silver none repeat scroll 0% 0%; width: 131.4pt; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;">
<p style="margin: 0cm 0cm 0pt; text-align: justify;"><strong>VPN Name/Description:</strong></p>
</td>
I don't think you can do this using selectors natively in jQuery. The style attribute is not stored as a string by the browser, it's an object.
It has been implemented, however: http://code.google.com/p/aost/wiki/CustomJQuerySelectorInTellurium#:styles
Or you can use $.each in something like this:
$("img").each(function() {
if($(this).css('background').length > 0) {
$(this).addClass('faketh');
}
});
Or you can use the jQuery filter:
var x = $("#tempDiv td").filter(function(i){
return $(this).css("background").length > 0;
});
try this
$(tempDiv).find("td[style*=background]").addClass("faketh");
EDIT
to prevent selection of elements that have some kind of "background-" you could also do following
$(tempDiv).find("td[style*=background]:not(td[style*=background-])").addClass("faketh");
but if an element has both "background:blabla" and "background-color:#FFF", it won't be selected
A few warnings:
The contents of the style selector should be quoted (check the samples in the Jquery docs), as you have them in the question, not unquoted as others recommend.
Be careful with checking the style attribute in jQuery attribute selectors. The browser may modify the contents of the string (re-ordering, the spacing around the colon, etc.) for it's internal representation, and each browser does this slightly differently.
The most important bit:
Are you using Firefox? I've had trouble with attribute selectors in Firefox once or twice, so if you've only tested in Firefox, check Chrome/IE/Safari/Opera/etc. It won't solve the problem, but may give you a different scope for it.
Depending on what tempDiv is, you should also be able to shorten it up by doing:
$(tempDiv + " td[style*=background:]").addClass("faketh");
You are searching for the string 'background:' but in the example you gave, it's using 'background-color:'
So change it to either:
$(tempDiv).find("td[style*=background-color:]").addClass("faketh");
or:
$(tempDiv).find("td[style*=background]").addClass("faketh");

Categories

Resources