Realtime Syntax Highlight Editor on Web, How Is That Possible?

Story

Although I have already played around some front-end techniques, I still think my webdev-fu is noob as hell, so maybe I should start it from scratch. How about basic DOM structure? Hmm, good. There are many realtime syntax highlight editor, I alway wonder how they work, and building one myself won’t be hard, right? Challenge accepted!

Google Hard

The quickest way to know how one thing works is to find one similar thing and study it. As far as I know, there are two editors with active communities — Code Mirror and Ace Edtior, but I am lazy as fuck, and reading other people’s code without any guides is really hard, so I decide to do some google about how it works.

Approach 1 – ContentEditable

Ace Editor is too modularized to make a quick understand. What I need is a one-filed, code-short example to make a quick structure overview. Code Mirror has some additional libraries, but the main program still remains in one file, so I think maybe it’s good for a starter.

While I was googling guides about Code Mirror, I happened to find this article about how Code Mirror 1 was made. This inspired me. Without knowing there was already an new version - Code Mirror 2, I started coding in this way.

The basic idea of this way is to get raw data string from parsing contentEditable div’s inner HTML, and trying to attach span tag with syntax. Basically, contentEditable way is possible, and Code Mirror 1 did it in this way, but to me, it’s a hard way. The main problem is, the behavior of key inputs differ from browser to browser, which means if you hope to get the same data from all platforms, you will be disappointed. In actually, you will get all different versions of data.

Sure you will say “Why don’t you change those behaviors to one? Problem solved!” Well, you won’t like this idea in the end, because you need to FUCKING REMAP ALL THE CONTROL KEY BEHAVIORS to act like the same, which is being said, you need to integrate all enter key versions into one, all shift-enter key versions into one, all cmd-enter key versions into one… and it’s only for enter keys, not to mention other keys’ combination, and… there are 5 mainstream browsers. Ouch, it hurts.

After one-week hard coding, it exhausted me. This is not a good idea. I need a better solution.

Approach 2 – Hidden Textarea

This is the way how Code Mioor 2 and Ace Editor were made. You can read this article if you already feel the pain from approach 1.

The key concept is that it hides a textarea from users’ eyes, and makes them believe they are typing into textarea. What they see, actually, is many divs pretending like a textarea.

The main challenge from this approach is that since you are not using default elements, you need to mimic your own textarea element — everything inside the textarea, including scrollbar, caret, selection and mouse events.

Though it’s not hard, I still want to find an easy way to achieve my goal because what I want to do is making a simple realtime syntax highlight editor, not another fully functional editor.

While I was struggling on approach 2 way, suddenly an idea came to my mind - “Hey, why don’t I just overlay a transparent textarea on the visual view? Using the default textarea can save a lot of works!”

So I started my overlay textarea journey.

Approach 3 – Overlay Textarea

Here is my final work.

Basic functions:

  • Support part of Markdown syntaxes.
  • Browser default key mapping.
  • Browser default mouse behaviors.

Tested on the latest version of Chrome, Firefox, Safari, Opera and IE 10, and be aware that this code is not optimized. If you paste a lot of text into textarea, I am sure it will be laggy as fuck.

On IE 10, user may experience the late response of caret because IE’s caret visual is not controllable in textarea. In order to show only one caret, I choose to hide my own caret to prevent from showing two carets at once.

What I do here is simply overlay a transparent textarea over a visual div. Basically, the concept is similar to approach 2 but sacrifices the possibility of further customizations due to lazy code. I don’t need to write mimic scrollbars and mouse event anymore.

Conclusion

To make a fully functional online text editor, programmer are bound to write a lot of codes to solve problems like right-to-left characters, line & word wrap, wide characters and blah blah blah tons of things.

If you are curious about how those realtime syntax highlight editors are possible, I hope this post can help.

Debug Pages On Phones With Weinre

Weinre stands for WEb INspector REmote. There are already numerous tutorials about this tool on the internet, but I still want to note it for myself or everyone else just want to get it work without getting details.

1 Install Node.js

Weinre depends on node.js, so we need to install node.js first.

There are two ways to install nodes.js. One is using homebrew or other CLI package management tools to install. On Mac is like this:

username$ brew install node

Or you can simply download and install package from the node.js site.

2 Install Weinre

After node.js is installed, now you can install weinre through this command:

username$ sudo npm -g install weinre

3 Run Weinre

In weinre, there are 3 terminologies. We will take a quick look:

  • Debug Server - just a…server, we will use our browser to connect to it.
  • Debug Client - web inspector, we will use in later in browser to inspect our page.
  • Debug Target - your web page, also refers to mobile device.

Time for running weinre. In most case we will only need to run:

username$ weinre --boundHost

Default option is localhost, but we won’t connect to localhost because that won’t do the debug thing for our page.

We use real ip address here. To lookup our computer’s real ip address:

On Mac open system preferences -> network

On Windows type cmd and run

> nslookup

So we finally run this command instead.

username$ weinre --boundHost [your ip address]

Now type [your ip address]:8080 in browser. You should see weinre page, and click link next to debug client user interface in Access Points to bring up our page’s inspector.

4 Inject Scripts

Before we start debugging we need to let our page response infomations which weinre needs through JavaScript. Weinre provides two ways to do:

  1. Inject script codes - once the page is ran, it will link to our server automatically, but only for specific pages we assigned to.
    <script src="http://[your ip address]:8080/target/target-script-min.js#anonymous"></script>
  2. Bookmarklet - simply make a bookmark on your phone browser device. I named it weinre debug on my iphone.
    javascript:(function(e){e.setAttribute("src","http://[your ip address]:8080/target/target-script-min.js#anonymous");document.getElementsByTagName("body")[0].appendChild(e);})(document.createElement("script"));void(0);

5 Debug Page

Our example target.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<title>index</title>
// decomment below if you are using method 1
// <script src="http://[your ip address]:8080/target/target-script-min.js#anonymous"></script>
</head>
<body>
<canvas id="canvas" width="200" height="200" style="background-color: green"> test </canvas>
</body>
<script>
(function(){
var canvas = document.getElementById("canvas");
canvas.addEventListener("touchstart", handleStart, false);

function handleStart(event){
event.preventDefault();
var touches = event.changedTouches;
console.log(touches);
}
})();
</script>
</html>

Run username$ python -m SimpleHTTPServer in this file’s directory, and use your phone to browse.

If you are using method 1 - inject script codes, you can start your testing immediately.

If you are using method 2 - bookmark, after pages is loaded, bring up bookmark page and run weinre deubg you just added, and finally, start testing.

Tap some on the little green rectangle will get you some responce.

6 Misc.

There is an online service will let you run weinre in an extreme simple fashion, but it seems broken at the time my post was written.

PhoneGap with Weinre can be a nice cooperation.

Since you can debug easily with weinre, everyhing shuold be able to work beautifully, right?

Nope.

Although weinre is convenience, the most common debug technique is not avaliable in weinre’s toolbelt:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<title>index</title>
// decomment below if you are using method 1
// <script src="http://[your ip address]:8080/target/target-script-min.js#anonymous"></script>
</head>
<body>
</body>
<script>
console.log("test"); // this won't work
</script>
</html>

It seems weinre loads a bunch of things before console is ready, but you can still use event register way to invoke console.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
<head>
<title>index</title>
// decomment below if you are using method 1
// <script src="http://[your ip address]:8080/target/target-script-min.js#anonymous"></script>
</head>
<body>
<button id="button">test</button>
</body>
<script>
(function(){
var button = document.getElementById("button");
button.onclick = function(){
console.log("test");
};
})();
</script>
</html>

Also weinre doesn’t support remote script debugger.