Joho the Blog » javascript

July 5, 2014

Convert Javascript to an HTML table

I was writing a long-ish tutorial about how to use LibraryCloud that refers to bunches of Javascript. I wanted a way to refer to that code in the HTML document. So I wrote a converter that turns Javascript code (and maybe others) into HTML tables you can just plunk into your document.

Here’s an example of some code:

function init(){
// get textarea to accept tab key
// thank you http://stackoverflow.com/questions/6140632/how-to-handle-tab-in-textarea
	$("#rawjs").keydown(function(e) {
		if(e.keyCode === 9) { // tab was pressed
			// get caret position/selection
			var start = this.selectionStart;
			var end = this.selectionEnd;

			var $this = $(this);
			var value = $this.val();

			// set textarea value to: text before caret + tab + text after caret
			$this.val(value.substring(0, start)
						+ "\t"
						+ value.substring(end));

			// put caret at right position again (add one for the tab)
			this.selectionStart = this.selectionEnd = start + 1;

			// prevent the focus lose
			e.preventDefault();
		}
	});
}

And here’s what it looks like once converted into an HTML table:

1

function init(){

2

// get textarea to accept tab key

3

// thank you http://stackoverflow.com/questions/6140632/how-to-handle-tab-in-textarea

4

$(“#rawjs”).keydown(function(e) {

5

if(e.keyCode === 9) { // tab was pressed

6

// get caret position/selection

7

var start = this.selectionStart;

8

var end = this.selectionEnd;

 

9

var $this = $(this);

10

var value = $this.val();

 

11

// set textarea value to: text before caret + tab + text after caret

12

$this.val(value.substring(0, start)

13

+ “\t”

14

+ value.substring(end));

 

15

// put caret at right position again (add one for the tab)

16

this.selectionStart = this.selectionEnd = start + 1;

 

17

// prevent the focus lose

18

e.preventDefault();

19

}

20

});

21

}

There’s definitely some clunkiness to this. For example, if you click on the “toggle line numbers” button it hides or shows the line numbers for every such table in your document. (I’ll fix this eventually.) By the way, the “Toggle” button is there so your readers can select the code and not get the lline numbers.

Also, in order to get the formatting totally encapsulated within your document, I clunkily add a “<style>” section before each table. I couldn’t figure out any other way to do it without making your HTML document dependent on my server being up.

The utility is here: http://hyperorg.com/programs/convertJStoTable/convertJStoTable.html. The code itself is up at Github (but not quite up to date at the moment).

Please let me know all the ways it’s broken and sucks, although I don’t promise to do anything about it. It worked for me.

1 Comment »

September 9, 2012

Beginner2Beginner: Javascript multi-file upload + PHP to process it

Much as I love being a hobbyist programmer, it can sometimes be frustrating as all get-out. Sometimes it’s just a bug because I made a dumb error (getting a variable’s scope wrong) or because I made an assumption about how something works (BBedit‘s hex dump does not show you the content of the file on the disk, but of the file in memory, including the line endings it’s transformed). But then there are the frustrations that come from not having the slightest idea of the basics. The worst are the basics that are so basic that the explanations of them assume you already know more than you do.

Welcome to the world of pain known as uploading multiple files using Javascript. For example, suppose you are writing an app that lets users take notes on an article using a plain old text processor. They can then upload those note files to some code that processes them, perhaps turning them into a database. Rather than having users upload one file at a time, you want to let them upload a bunch.

There are plenty of slick, free examples on the Web that provide beautiful front ends for doing this. Many of them I could get to work, but not exactly the way that I wanted. Besides, I was really really really confused about what happened after the front end.

So, after a lot of skullpounding and forehead slapping, here’s a guide. But please take seriously this warning: I barely know what I’m doing, and I’m undoubtedly doing this in the clunkiest fashion possible. I am very likely getting important things wrong. Some of them may be fatal, at least in the programmatic sense. (If you have a correction, please let me know. Note I may fix the code that follows, rather than doing the usual — and proper — Web thing of striking through the errors, etc.) Here goes….

To enable uploads, you’ll be writing code that will live in two places. The user interface code is in the HTML running in the user’s browser. The files are going to be uploaded — copied — to a web server. The code in the browser is going to be Javascript. The code on the server is going to be PHP, because Javascript is for browsers. (Oversimplification noted.) Those two places can be physically the same machine. If you’re using a Mac, a web server comes standard; if you don’t have a web server handy, there are bunches of free ones; that’s beyond the scope of this post.

After experimenting with many of the beautiful packages that are available, I gave up and went with the HTML5 solution. Modern browsers will have no problem with this. If you need to design for the world of old-fashioned browsers, then you need to find a competent explanation. In short: I love you, but go away! (Apparently, if you use the HTML5 solution, it will still be usable in lesser browsers, although it will allow users to select only one file at a time.)

HTML5 makes it ridiculously easy to do the user interface bit: you just tell it you want to allow multiple selections within a file-chooser dialogue. This comes from a post by Tiffany Brown:

<form action="processThem.php" method="post" enctype="multipart/form-data">
<input type="file" value="" name="upload[]" multiple>
<button type="submit">Upload!</button>
</form>

This will create an input field that when clicked will launch a file-browsing dialogue box in which the user can select multiple files. It then sends it to the PHP script called “processThem.php” on your server. The two key points to note are that the first line does the work of allowing multiple choices, and the “[]” in the name turns that variable into an array that will pass the entire list of user choices to the PHP script waiting on your server.

Here’s what it looks like, although this particular example won’t actually upload anything:



Now you have to create the “processThem.php” script (or whatever name you’ve specified) on your server. The uploaded files get placed in an array called $_FILES. But, they don’t get stored on the server for long: they are stored in a temporary folder from which they are automatically deleted after a little while. So, you need to process them, and quite possibly move them for permanent storage to a folder of your choosing. Here’s some sample PHP code from an anonymous commenter (“Me”) on the Tiffany Brown post:


<?php
$error_message[0] = "Unknown problem with upload.";
$error_message[1] = "Uploaded file too large (load_max_filesize).";
$error_message[2] = "Uploaded file too large (MAX_FILE_SIZE).";
$error_message[3] = "File was only partially uploaded.";
$error_message[4] = "Choose a file to upload.";

$upload_dir  = './tmp/';
$num_files = count($_FILES['upload']['name']);

for ($i=0; $i < $num_files; $i++) {
    $upload_file = $upload_dir . urlencode(basename($_FILES['upload']['name'][$i]));

    if (!preg_match("/(gif|jpg|jpeg|png)$/",$_FILES['upload']['name'][$i])) {
        print "I asked for an image...";
    } else {
        if (@is_uploaded_file($_FILES['upload']['tmp_name'][$i])) {
            if (@move_uploaded_file($_FILES['upload']['tmp_name'][$i], 
                $upload_file)) {
                /* Great success... */
                echo "hooray";
                //$content = file_get_contents($upload_file);
                //print $content;
            } else {
                print $error_message[$_FILES['upload']['error'][$i]];
            }
        } else {
            print $error_message[$_FILES['upload']['error'][$i]];
        }    
    }
}
?>

Let’s walk through this.


$error_message[0] = "Unknown problem with upload.";
$error_message[1] = "Uploaded file too large (load_max_filesize).";
$error_message[2] = "Uploaded file too large (MAX_FILE_SIZE).";
$error_message[3] = "File was only partially uploaded.";
$error_message[4] = "Choose a file to upload.";

In the first few lines, Me does us the favor of providing non-technical explanations of possible errors, so that if something goes wrong, it will be easier to know exactly what it was.


$upload_dir  = './tmp/';

Then Me designates a particular folder as the container for the uploaded files. Me chooses one called “tmp” in the same directory as the PHP script. (Make sure you have such a folder and the permissions are set, or, of course, create one with whatever name you’d like.)


$num_files = count($_FILES['upload']['name']);

Then Me gets a count of how many files were uploaded, and stores it in the variable $num_files. You tell that variable that you want the number of files that were included in “upload[]” in the form on your HTML. (You can use whatever name you want on that form, so long as you use the same one in your PHP.)


for ($i=0; $i < $num_files; $i++) {
    $upload_file = $upload_dir . urlencode(basename($_FILES['upload']['name'][$i]));

Then Me loops through all the files, assigning each one in turn to the variable $upload_file. But notice the weirdness of this part of the line:

$upload_file = $upload_dir . urlencode(basename($_FILES[‘upload’][‘name’][$i]));

First the easy parts. The baseline function returns just a file’s name without any path information; we want that because the point of this line is build a path to where the file will be saved in the folder you’ve set up for it. Also, I added the urlencode function in case the name of the file your user uploaded contains spaces or other characters that makes your server barf.

Now consider $_FILES[‘upload’][‘name’][$i]. It’s got those weird bracketed terms because $_FILES is an associative array. You can think of the usual sort of arrays as pairing a value with a number; give the array the number and it returns the value. In an associative array, values are paired with arbitrary keys (i.e., a word); give it the key and it returns the value. Here are the pre-defined keys for the associative array that gets sent to the PHP script when a user uploads files:

  • name: The file name of the uploaded file
  • type: Is it an image? A music file? etc.
  • size: The size in bytes
  • tmp_name: The crazy-ass name of the copy being stored on the server
  • error: Any error codes resulting from the upload

So, suppose you’re cycling through the array of uploaded files as in our example, and you want to get the name of the current file (i.e., file $i in the sequence):


$fname = $_FILES['upload']['name'][$i];

The [‘upload’] designates the array of all uploaded files. The [$i] pulls out of that array all of the information about one particular uploaded file, just like with ordinary array. The [‘name’] gets the value associated with that key for that particular file. As $i is incremented, you get the name of each file. If you wanted the crazy-ass temporary name, you would put in tmp_name instead of name, and likewise for the other parameters.


if (!preg_match("/(gif|jpg|jpeg|png)$/",$_FILES['upload']['name'][$i])) {
        print "I asked for an image...";
    }

Next Me optionally checks the name of the uploaded file for a particular set of extensions in case you want to limit the uploads to images (as in Me’s example) or whatever. Me is using regex to do the work, a function for which I am doomed to a copy-and-paste understanding.


  if (@is_uploaded_file($_FILES['upload']['tmp_name'][$i])) {

Next Me does a check to make sure that the file was actually uploaded and is not the result of malicious code. is_uploaded_file is a built-in PHP function. The preceding “@” suppresses error messages; I don’t know why Me did that, but I’m confident it was the right thing to do. While you are debugging, you might want to take the @ out.


move_uploaded_file($_FILES['upload']['tmp_name'][$i], $upload_file)

If the file passes that test, then Me moves it to the folder s/he designated. Note that we’ve already put the pathname to the storage folder into $upload_file.

Obviously, where the script has the comment “Great Success” you would put the code that does what you want to do to the uploaded file. I have added two lines — commented out — that get and print the content of the file; that gets ugly if the file isn’t some type of text file.

So, please let me know what I’ve gotten wrong here. (And maybe sometime I’ll give you some truly embarrassing code for returning the results of the PHP on the page that has the upload form on it.)

7 Comments »

July 22, 2012

Capturing control keys in Chrome et al.

Hallelujah! For years — literally years — I’ve been limping along with a blender full of spaghetti to do something that should be really simple: capture control key combos (like CTRL-S or CTRL-I) via Javascript in all the major browsers. I finally found some simple code that seems to work beautifully.

The problem is that the browsers don’t agree about what’s going on when a user presses a control key and another key simultaneously, which is, after all, the usual thing people do with the control key. Some of the browsers think that it’s two events, so you have to record the control keypress, remember it, and treat the next keypress differently. Other browsers think of it as a single keypress that you can just process as a if CTRL-S were a unique key. Then, depending, you may or may not have to nullify the S press. The way I was doing it (cribbed from multiple sources, of course) involved first checking on which browser the Javascript was running in, and then process keystrokes, looking for an initial control press. Pain in the butt, and it was fragile.

I am certain that this is not a problem for actual developers. For example, jquery handles keystrokes, although I had trouble getting it to work (becaues, if it’s not clear, I am a ham-fisted hobbyist who mainly just copies in other people’s code. Thank you, other people!)

Today I had a a few minutes, so I went back to Google and found some simple code from Ganesh. Thank you!

Here’s what you do:

First, include jQuery. Place the following into your Javascript the following block. Put it toward the top, and don’t put inside a function. You want it to run whenever your Javascript loads. (Well, you could put it into an initialization function if you want.)

$.ctrl = function(key, callback, args) {
    $(document).keydown(function(e) {
        if(!args) args=[]; // IE barks when args is null 
        if(e.keyCode == key.charCodeAt(0) && e.ctrlKey) {
            callback.apply(this, args);
            return false;
        }
    });        
};

After that, you can bind a control combo, such as Control-S, to the function you want (e.g., “SaveMe()”) this way:

$.ctrl('S', function() {
    SaveMe();
});

By the way, Ganesh thanks an anonymous commenter for improving the code. God bless the iterative Web!

1 Comment »

August 1, 2010

Beginner to Beginner: When a Javascript program only works in a debugger

[LATER the same day:] Thanks to expert advice from some generous expert folks in the comments, I found the mistake I was making; I explain it in the comment #5. But, the mystery of why it worked only when run in debug mode remains. (Tom in comment #4 has a good hypothesis.) Nevertheless, I’m sticking with my overall point that for a home-made, one-user project like this, a brute-force, dumb-ass solution sometimes serves. I should maybe mention also that one reason I post about “programming” is precisely because I’m an idiot about it, and I think it’s good to be a beginner in public.

Aaarrrggghhh. I spent way too much time yesterday and today trying to fix a problem with a javascript program the right way. When I finally gave up and did it the wrong way, it worked fine…except for being embarrassingly inelegant.

It’s a mystery. The program doesn’t work ever except if you run it in a debugger and set a breakpoint anywhere in either of the two affected functions. It doesn’t even work if you put an “alert()” into it. It really really wants a breakpoint. This is consistent in both Firefox and Chrome, with Firebug and the integrated Chrome debugger. It only works if you set a breakpoint. It does not work if you do not set a breakpoint. Did I mention Aaaarrrrrrggggghhhhhh?

Some details: I added a function to my little all-crap blog editor. The function (call it FunctionA) gets called when the user (= me and only me) presses a button. FunctionA calls FunctionB to see if the selected text is listed in an array of terms. If so, then FunctionA inserts some text using associated text. (To be more concrete, although it doesn’t matter: If the selected text is the name of someone for whom I have a twitter name, FunctionA builds and inserts a link to the person’s Twitter address.) FunctionB calls one other function (that trims spaces and other cruft) and one method (to temporarily uppercase both the selected text and the name in the database, so that the search won’t be case sensitive, and yes, I’m sure there’s a better way to do that). Then it simply walks through the array, looking for a match.

I googled around and didn’t find much help with the mysterious fact that it only works if I set a breakpoint. People with similar problems can sometimes solve them by using the setTimeout() function, to let the script catch up with itself. But, that’s not really what setTimeout does, apparently. It doesn’t pause the script. Rather, it delays the execution of a particular function while the rest of the script continues merrily on. I also found that a common cause of a similar problem is that your script is trying to access a part of the document that hasn’t been loaded or created yet. But: (a) My script isn’t trying to do that, and (b) If that were the problem, then putting in an alert() would have worked for me, no? The cause might be some multithreaded mystery for which there is no known solution, but I really don’t know. (You might try this.)

So, after looking at some clever recursive setTimeout strategies that I couldn’t figure out how to adapt to my particular script, I had a stupid idea. I put the code from FunctionB — the one that looks the selected text up in an array — straight into FunctionA. So, FunctionA doesn’t call FunctionB. It has within it a copy of the code in FunctionB.

And it works.

It works even though it is just about the definition of “inelegant.” The whole point of every programming language since Turing first ran marked paper off a mental spool is to write functions for any process your script might use more than once. Not only does that save time writing, it makes debugging a billion times easier because you only have to fix it in one place. So my solution to my problem is like telling a chef to beat each egg separately or to suggest reloading a supersoaker after each and every squeeze.

But, dammit, it worked. I don’t know why, and I ‘d say that I don’t care, but, I do. Well, at least I can now get back to wasting my time in marginally less frustrating ways.

8 Comments »

July 9, 2010

Outlining in your browser

Dave Winer just linked to a bookmarklet — ijotEdit by Marc Barrot — that lets you write outlines in your browser. It’s OPML-based, of course. Very nice.

I have to admit, though, that I played with it with mixed emotions. I have nothing negative to say about it at all. On the contrary. And that’s the problem. For the past two months, my hobby has been writing an OPML-based, browser-based outliner because I couldn’t find free one that worked across Mac, Linux and Windows. I just two days ago got mine to the point of minimum usability, although it’s still full of bugs. But you can save and load the outlines you create with it, and it exports in RTF, HTML and text pretty reliably. The functionality is truly minimal: You can use the arrow keys to move lines or lines + children, but you can’t cut and paste structures; there are no tools for including links or graphics; it doesn’t do rich text; if you want to change the styles, you have to hand-edit the css file. But, hey, the little right and down arrows work! I could never get it to the point that I would share it with anyone else, and if I ever showed the code to an actual developer, the milk would come out her nose. But I got the arrow keys working in IE as well as Firefox and Chrome! (Well, the place I copied the key capture code from said that it would work in IE.)

So, why mixed feelings about Marc’s actual, real, working outliner? Simple: I lost the excuse for writing my own. I’m a hobbyist, so it shouldn’t matter. Oddly, it does.

8 Comments »

July 2, 2010

Beginner-to-beginner: Parsing OPML, including level (= indents)

I am 99.9% confident that I’ve spent many hours trying to do in a complex way something that javascript (perhaps with jquery) lets us do with a single line of compressed code. But, I couldn’t find that line. Besides, I’m a hobbyist which means the puzzle is usually more fun than the solution. But this one drove me crazy.

I’m trying to read an OPML file. OPML is an instance of XML designed to record outlines. I found plenty of scripts online (javascript and php) that would read the contents, but none that would tell me the indentation level of each line. I’m sure there’s some simple way of figuring that out, but it beat the heck out of me. So, I finally found some javascript by Vivin Pallath to which I could add a couple of lines (mainly by trial and error) to record indentation info.

Vivin’s script is, appropriately recursive. That’s because outlines are arbitrarily nested, so you have to be able to call a “get the children” function on each line, each of which may have its own children. A recursive function calls itself, in this case until it reaches the end of a branch, at which point it pops up to the next sibling branch and carries on. You can easily record each branch in a flat list, but I also need to know how far down the limb we are. I find recursion to be extraordinarily difficult for my brain to follow, so more or less by trial and error I added a counter (a global variable, although I’m not sure that’s required), and then tried to figure out when it needs to be reset to zero.

The way I have it set up, the tree walking function looks for XML elements called “outline,” because that’s the tag OPML uses. It records the text content of the line in the outline as the attribute “text.” (OPML also can encode stuff other than text, but I happen not to care about that.) At each line, it records the text and the level number in a global array. This is a rather silly way of recording the info, but that’s the easy part to fix.

The parseOpml function consists mainly of code from Phpied that opens an xml file and reads it into an xml document object that javascript knows how to work with. The parseOPML function calls the walkTree function that does the actual parsing. The walkTree function takes as parameters the element from which it should start walking (which, in this case, is the root of the xml object itself) and the starting level of indentation (0). It wouldn’t be hard to add to it the name of the element you want it to pay attention to (which, in this case, is specified in walkTree to be “outline”>

Now for the caveats: I’ve barely tested this. It may break under predictable circumstances. It is very likely wildly inefficient (which doesn’t matter for my petty uses). It will undoubtedly embarrass actual developers. But, maybe it will help some other hobbyist…

// globals
var treeArray = new Array(); // array to hold results
var glevel; // the indent level

function parseOpml(){
  // opens the xml file and calls the tree walker
  

var fname="/pathname/to/your/file.xml";

// open the xml file (from http://www.phpied.com/opml-to-html-via-javascript/) var xmlDoc = null; if (window.ActiveXObject) {// code for IE xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); } else // code for Mozilla, Firefox, Opera, etc. if (document.implementation.createDocument) { xmlDoc = document.implementation.createDocument("", "", null); } else { alert('Your browser cannot handle this script'); } if (xmlDoc != null) { xmlDoc.async = false; xmlDoc.load(fname); } // call the tree walker treeArray.length=0; // reset global array walkTree(xmlDoc,0);

} //--------------- Recursive tree walker --------------

function walkTree(node, glevel){ // walks tree looking for elements tagged "outline" // Replace the two instances of "outline" below to have it // look for other tags //Original function from: Vivin Pallath. Thanks! //http://vivin.net/2005/07/01/innerhtml-alternative-for-xhtml-documents-in-firefox/ //Modified to count indentation levels var n="d"; var a="";

if (node.hasChildNodes()) { node = node.firstChild; do{ // only return elements tagged "outline n = node.nodeName; if (n == "outline") { glevel++; // increase level // pushes the text and level as a string into a global array a = node.getAttribute("text"); // get the text treeArray.push(glevel + "=" + a); } walkTree(node,glevel); // recurse until no children node = node.nextSibling; // pop up to get next sibling // If this branch has no more children, reduce the level if ((n=="outline") && (!node.hasChildNodes())) { glevel = glevel - 1; } } while(node); } }

1 Comment »

January 16, 2010

Convert text with URLs to text with hyperlinks

Using a Javascript regex function written by Sam Hesler at StackOverflow (thanks!), I’ve posted a simple little page that lets you turn text that has URLs in it into text that has clickable, hyperlinked URLs. That is, you go from http://www.globalvoicesonline.org to
http://www.globalvoicesonline.org.

The page is: ConvertURLsToLinks.html. It’s quite bareboned, and I’m sure there are lots of sites that do a far better job with many more options. But, it worked well enough for the one job I wanted it for, so maybe it’ll work for you. At least it won’t destroy your original text (although keep the original just in case.)

The regex function I borrowed from Sam Hesler is:

function replaceURLWithHTMLLinks(text) {
var exp = /(b(https?|ftp|file)://[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/ig;
return text.replace(exp,”$1“);
}

7 Comments »

November 29, 2008

Beginner to Beginner: Splitting strings into arrays in Javascript

I mentioned to my nephew Joel Weinberger, a CS grad student at UC Berkeley, that I wished the Javascript “split” method took multiple delimiters, and within minutes, he wrote one for me. If you know what I’m talking about, you can click here to get a zip file with the code (including a function as well as a method) and a sample. If you don’t …

[Note: all explanations are approximate.] Javascript comes with a built-in method for converting a string (that is, what normally consists of letters and characters in quotes) into an array (that is, a data structure of numbered elements). So, if you have a string that’s really a list of elements, such as “monday, tuesday, wednesday” or “12-345-6,” the split method will automatically chop it up into an array, using a delimiter of your choice (a comma or a dash in the two examples given). This is very useful.

But suppose you have a string such as this: “beef OR chicken AND duck” You want to be able to chop it up at the ORs and the ANDs, but the split method only lets you specify one delimiter.

Enter Joel. His multiSplit method lets you specify an array of delimiters. It chops up the string and records the phrases and their delimiters. Very handy.

Thanks, Joel!

[Tags: ]

4 Comments »

July 29, 2008

Reason #12,563 I love the Web

I’m a-lovin’ Marijn Haverbeke’s Eloquent Javascript, an interactive javascript tutorial. It’s clear, nicely written, nice looking, handy (what with its embedded console for trying scripts out), free, and Creative Commons licensed. It’s easily downloadable so you can run/read it even when you don’t have any of that newfangled “broadband” the kids are so excited about.

Thank you, Marijn. [Tags: ]

Be the first to comment »