PreParse is a library designed to simplify web development. It does this by running javascript and css through a preprocessor, which will rewrite standards compliant style rules to work in any browser, and extend and standardise javascript, resulting in faster load times, and quicker execution.
To use PreParse all that is strictly necessary is this line in the <head> of your html page:
<script type="text/javascript" src="pp_ss/preparse.php?script"></script>
This will automatically run any css through the preprocessor, however PreParse does not work on inline css:
<div style="rule:value;"></div>
It only works on css from linked style sheets, or inside <style> tags. This is not likely to change.
This will also give you access to the PreParse javascript object, however it will not run any javascript through the preprocessor. To have javascript parsed you will need a small amount of javascript after the above mentioned line to tell PreParse what to do:
<script type="text/javascript">
/*
* (optional) path to the pp_js and pp_ss directories
*/
pp.path = '/preparse';
/*
* scripts you wish to have included on the page
* the path is relative to the SCRIPT_PATH_PREFIX constant in
* pp_ss/preparse.php
*
* can either be a single function call, with each script as an
* argument
*/
pp.require('myscript.js','myscript2.js');
/*
* or one function call per script
*/
pp.require('myscript.js');
pp.require('myscript2.js');
</script>
To have inline scripting parsed, don't use script tags. Instead use the following:
<section class="preparse_script"> <!-- /* place any scripting here */ --> </section>
It must be a <section> tag, and must have preparse_script as it's only class name. The html comments aren't necessary, but are recommended for hiding script from view. You can style the element however you like, although display:none; will probably be all you really want to do.
In css the following elements get modified:
For Javascript, please see the Javascript section of this document.
Both style and scripting will be minified before sending to the client.
As seen above in What Gets Parsed? CSS will get updated without you having to do anything. Just write your standards compliant CSS, and let PreParse worry about browsers doing weird things. Additional css parsing will be added in time, however this will be to improve cross-browser support, rather than adding obtrusive code to your stylesheets
While PreParse is passive in it's css functionality, it is not when it comes to javascript. While it will work on ordinary javascript, this will effectively just result in the script being minimised, and will miss out on the advanced features and extensions that PreParse adds to the javascipt language, such as:
Why does PreParse modify javascript so much? PreParse is designed to improve the speed of javascript, both in loading, and in execution. It does this not only by minifying the script before outputting it, but by removing blocks of code that will not be used by the client's browser.
Preprocessor Directives allow us to modify the code before it gets to the browser. It's probably easier to see it in action than to describe, so let's jump straight into it and do something relatively simple:
We'll just pop up an alert box when a user clicks on a page, and tell them the position of the mouse. In regular javascript that would look something like this:
document.body.onclick = function(e) {
if (!e) var e = window.event;
alert(e.clientX+':'+e.clientY);
}
All very simple really, you check to see if e is defined and if not, then define it using the global event object. This will be common fare to most coders out there.
But, that's an extra line of code, and an extra instruction, which most browsers won't need. So why is it there? Cross-browser compatibility of course, but wouldn't it be better if you could remove that line completely if it's not needed? After all most browsers don't require it, but they still have the extra overhead of downloading a few extra bytes of code (and those bytes will add up in a long script), and parsing an instruction that's not needed. We can speed things up by removing that line, but then the code won't work in older browsers.
This is where PreParse's preprocessor directives are handy, the above code can now look like this:
document.body.onclick = function(e) {
#ifndef localevent
var e = window.event;
#endif
alert(e.clientX+':'+e.clientY);
}
At first glance this looks like more code than the first example, but when the code is sent to the browser it will be less, and that's what counts right? Smaller code means faster loading, and less instructions means faster execution.
So when PreParse sees this code it will compare the directive against what the browser is capable of, in this case:
#ifndef means 'if not defined' or "does the browser not have support for"
The definition to check against is 'localevent' or local event objects in functions
This is the same as 'if (!e)' in the regular javascript example, but the check is performed before sending the code to the browser
If this evaluates to true "the browser does not support local event objects" then the code between the directives will be included and sent to the browser, otherwise, the code will not be sent to the browser
So if a browser supports local event objects, the code will look like this:
document.body.onclick = function(e) {
alert(e.clientX+':'+e.clientY);
}
And if the browser does not support local event objects, the code will look like this:
document.body.onclick = function(e) {
var e = window.event;
alert(e.clientX+':'+e.clientY);
}
In both cases, this is less code sent to the browser, and less instuctions to execute, resulting in faster load times for any browser, whether it has support for local event objects or not.
Get the idea? Preprocessor Directives allow code to be modified to suit the browser, before sending it to the client.
At present the available directives are:
You can of course nest directives, you might want to check if ajax is supported, then check if xmlHttpRequest or ActiveX should be used:
#ifdef ajax
#ifdef xmlhttp
var xr = new xmlHttpRequest();
#endif
#ifndef xmlhttp
#ifdef activex
#ifdef msxml2
var xr = new ActiveXObject("Msxml2.XMLHTTP");
#endif
#ifdef microsoftxml
var xr = new ActiveXObject("Microsoft.XMLHTTP");
#endif
#endif
#endif
#endif
Worked that out yet? Here's some pseudo code that might help explain it:
if (browser supports ajax) {
if (browser supports xmlhttp) {
var xr = new xmlHttpRequest();
}
if (browser doesn't support xmlhttp) {
if (browser supports activex) {
if (browser supports msxml2) {
var xr = new ActiveXObject("Msxml2.XMLHTTP");
}
if (browser supports microsoftxml) {
var xr = new ActiveXObject("Microsoft.XMLHTTP");
}
}
}
}
This is a pretty heavily nested example, but if you've tried coding ajax from scratch, you'll know it's incredibly usefull! In addition to nesting though, you can also test for multiple things at once by seperating tags with a comma:
#ifdef cookie,domid
alert('This browser supports cookies and getElementById!');
#endif
#ifndef docall,domtag
alert('This browser supports neither document.all nor getElementsByTagName!')
#endif
The directive will only be true if all tags are defined (or not defined for ifndef), so if cookie is defined, but domid is not, the first alert won't show.
Javascipt objects and classes are a common stumbling block, even the most experienced coder can get lost in trying to do things which are common in other languages. To start with there are 3 ways to create an object, either by declaring objects directly, or declaring a constructor function and instantiating a new object:
Examples of javascript object declaration:
var myObject = new Object();
myObject.text = 'blah';
var myObject = {
text : 'blah'
}
Example of javascript object created using a constructor function
var myObjectConstructor = function() {
this.text = 'blah';
return this;
}
var myObject = new myObjectConstructor();
All 3 of the above result in the same thing, an object called myObject with a public variable accessable from myObject.text - they all work, but only the last method allows for private variables and functions. In all cases extending a function becomes messy, there are ways to do it, and if you look around on the net you'll find plenty of blog posts on the subject.
With PreParse, you can still use the above methods if you wish, but you also now have access to a more understandable (and common) method of defining a class:
Class myClass {
public var text = 'blah';
}
And instantiate an object from the class in the usual way:
var myObject = new myClass();
This should make a lot more sense to coders familiar with other languages, you declare a class, and give it public and private functions and variables:
Class myClass {
private var secret = 0;
public var notSecret = 1;
private function myFunc() {
return secret+self.notSecret;
}
public function getTotal() {
return myFunc();
}
}
If you instantiate the above class as myObject you can call myObject.getTotal() and read/write from/to myObject.notSecret, but secret and myFunc are inaccessable: myObject.myFunc() will throw an error saying that it is undefined. Just as it should be. As can be seen above, a function inside a class can call and access private functions and variables.
Also note that you can have a private and public function with the same name, they will remain seperate functions, therefore this is perfectly valid:
Class myClass {
private var secret = 0;
public var notSecret = 1;
private function myFunc() {
return secret+self.notSecret;
}
public function myFunc() {
return myFunc();
}
}
PreParse also adds support for constructors and deconstructors:
Class myClass {
public function __construct() {
}
public function __destruct() {
}
}
The constructor will be called when you instantiate an object from the class. The deconstructor will be called when you delete an object.
var myObject = new myClass(); // creates an object and calls the constructor delete myObject; // calls the deconstructor then deletes the object
You can of course use arguments in the constructor:
Class myClass {
public function __construct(name,type) {
}
public function __destruct() {
}
}
/*
* create an object, and call the constructor with the supplied arguments
*/
var myObject = new myClass('cola','beverage');
Deconstructors do not accept any arguments, as they are called automatically when deleting the object, therefore there's no way to pass the arguments in.
Another feature added by PreParse is extending classes, which allows:
Class myClass {
public function hello() {
alert('hello');
}
}
Class myNewClass Extends myClass {
public function helloWorld() {
alert('hello world');
}
}
var myObject = new myNewClass();
myObject.hello();
myObject.helloWorld();
This gives myNewClass all of the public variables and functions of myClass, as well as any public or private variables defined within itself. Note however that private functions and variables are not inherited by the new class, this is a problem with the underlying javascript object system we hope to resolve in the future.
Need to include a really long string in Javascript? Including newlines, and other special characters? Try this:
var text = <<<EOT Put any text you want in here, javascript will see it as one string, regardless of what characters you use. Including newlines/linebreaks... and tabs. EOT;
The string will start after the newline trailing <<<EOT, and end before the newline that preceeds EOT;. Don't put anything (including tabs) before that last EOT, or you'll have problems!
There are times when including the output of a function from serverside language in Javascript is useful, that's where PHP() can be used.
var text = 'PHP(myfunction,arg1,arg2,arg3)';
The above will replace PHP(myfunction,arg1,arg2,arg3) with the output of myfunction(arg1,arg2,arg3) from PHP.
Because it's just so useful, PreParse will ensure that document.querySelector() and document.querySelectorAll() works in all browsers, this lets you use css selectors to find elements within the DOM tree.
Other things not directly related to javascript and css that PreParse can do are:
Let you know if html5's <canvas> tag is supported. While not technically a javascript issue, you can use this to exclude canvas related scripting from browsers that don't support it.
#ifdef canvas // canvas code here #endif #ifndef canvas // maybe some script to replace the canvas with a static image? #endif
Let you know if Java is supported: I'm sure you can work out what to do with this.
| NAME | MEANING | |
| BROWSERS | ||
| khtml | browser is most likely khtml based | |
| mozilla | browser is most likely mozilla based | |
| msie | browser is most likely Microsoft Internet Explorer | |
| opera | browser is most likely opera | |
| webkit | browser is most likely webkit based | |
| CSS | ||
| opacity | browser supports css opacity | |
| filter | browser supports filter | |
| border-radius | browser supports css border-radius and derivatives | |
| moz-border-radius | browser supports -moz-border-radius and derivatives | |
| webkit-border-radius | browser supports -webkit-border-radius and derivatives | |
| text-shadow | browser supports css text-shadow | |
| max-width | browser supports css max-width | |
| min-width | browser supports css min-width | |
| min-height | browser supports css min-height | |
| max-height | browser supports css max-height | |
| khtml-opacity | browser supports -khtml-opacity | |
| JAVASCRIPT | ||
| activeX | browser supports ActiveX | |
| ajax | browser supports ajax with either xmlHttpRequest or ActiveX | |
| array-concat | browser supports Array.concat | |
| array-copy | browser supports Array.copy | |
| array-each | browser supports Array.forEach | |
| array-index | browser supports Array.indexOf | |
| array-insert | browser supports Array.insert | |
| array-lastindex | browser supports Array.lastIndexOf | |
| callglobaleval | browser supports eval.call(window) | |
| cookie | browser supports cookies | |
| docall | browser supports document.all | |
| domchild | browser supports DOM childNodes | |
| domclass | browser supports getElementsByClassName | |
| domcreate | browser supports document.createElement | |
| domid | browser supports getElementById | |
| domname | browser supports getElementsByName | |
| domsel | browser supports querySelector | |
| domselall | browser supports querySelectorAll | |
| domtag | browser supports getElementsByTagName | |
| exec | browser supports window.execScript | |
| globaleval | browser supports window.eval | |
| iframe-contentdocument | browser uses contentDocument to access an iframe's content | |
| innerhtml | browser supports innerHTML access to element contents | |
| localevent | browser supports local event objects in functions | |
| microsoftxml | browser supports the Microsoft.XMLHTTP ActiveX object for xmlHttpRequests | |
| msxml2 | browser supports the Msxml2.XMLHTTP ActiveX object for xmlHttpRequests | |
| prototype | browser supports Object.prototype | |
| string-capitalize | browser supports String.capitalize | |
| string-count | browser supports String.count | |
| string-trim | browser supports String.trim | |
| xmlhttp | browser supports xmlHttpRequest | |
| OTHER | ||
| canvas | browser supports the canvas element | |
| java | browser supports java applets |