Using Google Scripts to Host a Website

The Objective…

1.  Have a framework for hosting static HTML pages at no cost
2.  Linking between pages should still work

Where can I host some content?

There are probably lots of places out there where you can host for free.  Your internet service provider might have free space.  There might even be places in yahoo to do this.  And of course there are WordPress.org (this blog), blogger, etc…  These will allow you to get content online for free, but limits how you create the content and how you maintain it.

For me I was looking for a way to host my documentation (a continuation of this post).  So I wanted to use my documentation that is available on github.com, but be able to view it as a web page.  Really I was just playing around and having some fun.

Here is how I got it done

Setup your google scripts.  See my other post on this.

In order to host the github pages, we need to dynamically get the HTML we want to load.  In my example I will be using github.com for hosting, but you could use things like code.google.com or the like.  Google scripts gives an easy way of dynamically getting content.

UrlFetchApp.fetch(http://somedomain.com/path/to/file/to/download).getContentText();

However, following the steps from my previous blog post we can show the content, but linking will not work.  So we will have to parse the downloaded document.

Parsing the document and rewriting links

1.  Find “<a ”
2.  Check that there is an “href” attribute
3.  Parse the actual link
4.  Replace the link contents

The problems I came across

1. First pass, I didn’t account for links without “href” attribute
2.  Thought it was so easy I could just write it procedurely with no functions…that was a mistake.  Meaning, just because your playing around, doesn’t mean you should ignore breaking breaking a problem down into simpler parts.
3.   Its really hard to diagnose problems in google scripts…
3a.  Put all the code in your .gs files so you can step through the code.  This would have saved me a bunch of tme.

Finding the Next Valid “<a ” tag

/**
* Return the this position
* <a class=”someclass” href =”/some/url”
* ^
* pos
*
* @param String The raw HTML to parse
* @param int The start position to begin parsing of the rawHtml
* @return int The position or -1 on fail
*/
function getNextAtagUrlStart(rawHtml, pos){
var temp = rawHtml.substring(pos,rawHtml.length).indexOf(“<a “);
var start = pos + temp;

//no starting a tag
if(temp == -1)
return -1;

//the href.  We dont want to look too far ahead
var maxLen = Math.min(start+50,rawHtml.length);
var temp = rawHtml.substring(start,maxLen).indexOf(“href”);

//this atag has not href, so lets look again
if(temp == -1)
return getNextAtagUrlStart(rawHtml,start+2);

start += temp+4;//4 is for the “href” string

//ship whit spaces…href       =    ”
while(rawHtml[start] == ” “) start++;

//skip the  “=”
start++;

//skip till we get the url (actually the ” or ‘)
while(rawHtml[start] == ” “) start++;

//if this is an absolute linke skip it.  This assumes that internal links
//are relative.
if(rawHtml.substring(start+1,start+5) == “http”)
return getNextAtagUrlStart(rawHtml,start);

//return the position of the link including the leading ” or ‘
return start;
}

Get the End of the URL string

//skip the url portion of url
function getEndOfUrl(rawHtml, start){

//should give a ” or ‘
var wrapper = rawHtml[start];

//end tokens
var end1 = wrapper+”>”;
var end2 = wrapper+” “;

var token = function(pos){
return rawHtml.substring(pos,pos+2);
};

while(token(start) != end1 && token(start) != end2)
start++;

//this take use to the position after the closing ” or ‘
return start+1;
}

Get only the <body> Conetent

function getInnerText(rawHtml){
var start = rawHtml.indexOf(“<body”);
var end = rawHtml.indexOf(“</body>”)+6;

return rawHtml.substring(start,end);
}

Create your HTML Template File

<html>

<head>
<!–  Use the w3 default template so things look ok –>
<link rel=”stylesheet” href=”http://www.w3.org/StyleSheets/Core/Swiss&#8221; type=”text/css”>
</head>

<body>

<? output.append(“<div”+body.substring(5,body.length-7)+”</div>”); ?>

</body>
</html>

Put it All Together

So google scripts uses a defaul “main” function.
1.  doGet – This handles GET requests.  Just normal webbrowser
2.  doPost – This handles POST requests.  These more than likely come from form submits.

First I put some global parameters…

var base, domain, tempalte, file;

This doGet function will set these global variables…

function doGet(e) {
var tpl;

//need to just handle when we are missing template
if(typeof e == undefined || e.parameter.file == null || e.parameter.domain == null){

tpl = HtmlService.createTemplateFromFile(‘404’);//load the 404 html file
} else {

//choose template to load.  I have a base one described above and some others with specifc styleings
template = (typeof e.parameter.tpl == undefined || e.parameter.tpl == null) ? “base” : e.parameter.tpl;

base = ScriptApp.getService().getUrl();
domain = e.parameter.domain;
file = e.parameter.file;

tpl = HtmlService.createTemplateFromFile(template);

//dynamically load the html body
var response = UrlFetchApp.fetch(domain+file).getContentText();

var body = getInnerText(response); //get <body…</body>
var pos = getNextAtagUrlStart(body,0);
while( pos < body.length && pos != -1 ){
var endUrl = getEndOfUrl(body,pos);
var url = getUrl(body, pos+1,endUrl-1);

//splice in the url
body = body.substring(0,pos+1)+url+body.substring(endUrl-1,body.length);

pos = getNextAtagUrlStart(body,pos);

}

//make the body varaible available to the HTML template file we loaded
tpl.body = body;
}

//render the page
return tpl.evaluate();

}

Conclusion

In the end there are some short comings of my implementation.

1.  Doesn’t handle forms
2.  Doesn’t seem to handle anchor tags for bookmarking and internal references

Here is an example where I load my pages from https://github.com/Will-Smelser/openProjects

Advertisement

Jquery UI Select Box Plugin

 Jquery UI Select Box

So a couple of weeks ago I was working on a project for a customer and decided I needed to use JQuery UI since I was going to have some popup boxes and wanted some pretty buttons.  So I applied JQuery UI no problem, but forgot that it does not have a select box.  Yeah I could work around it using Autocomplete or just try and style the select menu up a little with some css, but that would’t be very cross browser consistent.

And then I thought how hard could it be?  I mean all I need to do is change the css and put an overlay to hide the drop down arrow.  And then I could simply trigger the drop down to show.  Something like this…

select_box

Basically I would just inspect the jquery css styles and apply some of the classes to the html to make a jquery styles selct box overlay.  So if the HTML would look something like this…

<div class=”my-ui-select”>
<span class=”ui-spinner ui-widget ui-widget-content ui-corner-all”>
<select style=”border:none;” >
<option>Option 1</option>
<option>Option 2</option>
</select>
<a class=”ui-spinner-button ui-spinner-down ui-state-default ui-corner-right”>
<span class=”ui-button-text”><span class=”ui-icon ui-icon-triangle-1-s”></span></span>
</a>
</span>
</div>

I figured it would be pretty simple to then bind a click event to my <a> tag.  Which is a custom drop down arrow using jquery styles.

<script>
//bind event to <a> tag
$(‘.my-ui-select a’).click(function(){
//trigger the select menu click event
$(this).parent().find(‘select’).first().trigger(‘click’);
});

</script>

Simple enough right?  Well in actually there is a huge difference in programmatically triggering an event versus a user’s mouse triggered event.  This is probably due to security concerns.

Possible Work Around

So in my searching I came across this entry in stack overflow.  One of the recommendations is to change the size of the select menu.  This would work if you absolutely position the element and use the wrapper description I gave above.

The javascript would look something like this…

<script>
$(‘.my-ui-select a’).click(function(){
//trigger the select menu click event
var select = $(this).parent().find(‘select’).first();
select.attr(‘size’,select.children().length);
});
</script>

Now, this is a very reasonable work around but it has its downsides.

  • The drop down list will not look like other JQuery UI elements when the drop down happens.  Some css styling might make this work alright though.
  • The size attribute can be a little tricky to get right.
    • If your select list has 2 elements, making it a size of 2 will give a lot of white space.
  • Positioning is tricky
    • The <a> tag has to be in-front of the default drop down arrow, but the wrapper which creates the border has to be below the select menu so the default actions work on the select menu.  Getting this position correct on all browsers will be tricky, but not impossible.
  • The scroll bar is default and cannot be removed (might be able to apply styleing, but this wont be corss 100% browsers).
  • In the end, it just doesn’t look quite right.  And this is just in Chrome.  Figuring out how to tweak styles to be good in all browsers will be a serious pain.

In the end you end up with something like this…
selec_using_size

Download the above example here.  Please keep in mind, this was only for Chrome and looks bad in some other browsers.  You will need to spend some time playing with the css, but in the end you should be able to get this working fairly nice.  Also might be a great lightweight solution.

My Solution

selec_using_final

So the above solution is pretty good and super simple.  You are using the native select menu with a little bit of a hack.  This is great, because all your events for the select menu make sense.  But the down side is it has some issues looking polished and the drop down doesn’t really follow the other Jquery UI.

So I needed to resolve these problems.  My solution was to combine the css styling used above coupled with Jquery  UI AutoComplete Widget.

How will this work…

  • Use above css to make something that used JQuery UI css to look nice.
  • Use jquery fn to create our own selector function
    •  Basically we will parse select elements, hide them and create our new css to make things look nice.
  • Apply AutoComplete Widget to our styled drop down menu to give the illusion of a select menu.

Why did I go this route?

  • Cross browser functionality
  • Cross browser looks
  • Maintain Jquery UI consistancy in my own widget
  • Use Jquery UI widget to hadle the heavy lifting of the select menu.  I just need to write an API wrapper for AutoComplete widget to make it do a little more.

In the end this was very simple to implament.  About 100 lines of code and very straight forward.  The real issues arose in event handling.  The AutoComplete Widget API is ok, but frankly a little lacking.  So working around its limitation and forcing it into my own needs took a little fiddling.

Here is the example page.  It takes a little while to load, but that is just the google scripts that I use to host it.  Look at my post about how to host stuff for free on google scripts.

If you have any questions let me know.  The code should be pretty simple to step through on your own, but I would be happy to explain what I can.

Free Hosting with Google Scripts

The Problem…

I have some projects that I work periodically and wanted to be able to host examples of on the web, but don’t want to pay for hosting (https://github.com/Will-Smelser/openProjects).

So the hosting needed the following

  • Insert my own javascript (So blogger, wordpress, etc… will not work)
  • Dynamically load content from external host (my source code on github)

The Solution…

I was playing around with google looking for a way to host content and came across Google Scripts.  The documentation can be a little bit of a mess, but after a couple hours of playing around I was able to figure out the basics.  A more detailed version of this will come later.

So for this I wanted to host my documentation for https://github.com/Will-Smelser/openProjects/tree/master/jquerySelect project.  I know I could have done this with github wiki, but this has limitation as I cannot actually insert example javascript.

Steps Taken…

1. Setup a project (http://script.google.com).

2. Create an HTML template file
Create HTML

3. Save this into the HTML template

<html>
<head></head>
<body>
<?  //dynamically load the html body
var response = UrlFetchApp.fetch(“https://raw.github.com/Will-Smelser/openProjects/master/jquerySelect/body.htm&#8221;);
output.append(response.getContentText());
?>
</body>
</html>

4. This uses the scripts API to grab the content. Then it uses the default global variable “output” to insert the loaded content.

5. Edit the Code.js and insert this content

// Script-as-app template.
function doGet() {
return HtmlService.createTemplateFromFile(‘selectbox2’).evaluate();
}

6.  Publish your project
deploy_webapp

Here is my example.

Conclusions

There are some downsides…
1.  The anchor tags dont work correctly.  I’ll look into fixing this.
2.  How I currently did this I would have to setup a template for each page.