Charting used to be done on the server side, but now we have many client side charting libraries. The downside of client side charting always was converting to PDF. But with wkhtmltopdf, this is history: it parses the HTML with the webkit engine and then prints it to a PDF. It has to be installed on the webserver. It's a command line tool and can be used like this:
wkhtmltopdf www.myhomepage.com myhomepage.pdf
A simple way to use it from PHP:
$url = $_GET['url'];
$tmp = md5($url) . ".pdf";
$exec = shell_exec("wkhtmltopdf " . escapeshellarg($url) . " $tmp");
$str = file_get_contents($tmp);
//unlink($tmp);
header('Content-Type: application/pdf');
header('Content-Length: ' . strlen($str));
header('Content-Disposition: inline; filename="file.pdf"');
header('Cache-Control: private, max-age=0, must-revalidate');
//header('Pragma: public');
//ini_set('zlib.output_compression','0');
die($str);
Some other switches for this nice tool. The command below will write the PDF to standard out (-), creates no table of contents (--no-outline) and uses the CSS with media="print" (--print-media-type).
wkhtmltopdf -q --print-media-type --no-outline http://www.google.com -
So to write it without creating a temporary file:
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="test.pdf"');
header('Cache-Control: private, max-age=0, must-revalidate');
passthru("wkhtmltopdf -q --print-media-type --no-outline http://www.webessence.nl/ - 2>&1");
There are also some language bindings available if you want a more robust way to create PDFs.
A very nice client side chart plugin is the jQuery Visualize plugin from the Filament Group. It creates charts from HTML tables, so this is an accessible way to show charts (users without javascript still see the tables).
Some nice info about CSS selectors:
There are 2 types of properties: data properties and accessor properties. A data property is just a slot for some value and can be written and/or retrieved:
var item = {};
item.name = "Pete";
We could also wrote it like this:
var item = {};
Object.defineProperty(item, "name", {
value : "Pete",
writable : true,
enumerable : true,
configurable : true
});
But sometimes there is more work to be done when getting or setting a property. Think about the innerHTML property of HTML elements, that has to parse the DOM to get or set its value. In the example below I created a property named "appString". It can be used to "serialize" a person object into some format. The retrieved value consist of the id and name of the person separated by a ":", for example: "2:Pete". When setting this property, it parses the value and sets the id and name properties of the person.
var person = {};
Object.defineProperty(person, "appString", {
get : function() {
return this.id + ":" + this.name;
},
set : function(appString) {
if (!appString) {
throw new Error("Bad format");
}
var parts = appString.split(":");
this.id = parts[0].trim();
this.name = parts[1].trim();
},
configurable : false
});
var pete = Object.create(person);
pete.id = 1;
pete.name = "Pete";
console.log(pete.appString); // 1:Pete
pete.appString = "2:Jan";
console.log(pete.appString); // 2:Jan
console.log(pete.id); // 2
// Cannot delete it when configurable is false (default).
delete person.appString;
console.log(pete.appString); // 2:Jan
You see getters and setters are just a convenient way of getting and getting some value. You could also use methods to do this. The advantage of using an accessor property instead of a public method, is you can secure it, so it can be redifined or deleted.
Some nice links:
In the MSDN article, there are some nice examples of extending the DOM elements, like HTMLElement.prototype, so you can add new functionality or replace existing functionality, for example replace the innerHTML property of all HTML elements (don't know if this is a real good idea though).
There seems to be a bug in Firefox 10, where you cannot get the property descriptor for many native DOM elements, like:
var desc1 = Object.getOwnPropertyDescriptor(NodeList.prototype, "item");
var desc2 = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerHTML");
The first succeeds in Firefox, but the second raises an exception. Strange is, that in de webconsole you don't get an error.
A simple example of Classical vs Prototypal inheritance. We create an Employee "class" and a Manager class that inherits from Employee.
// The inherit function is necessary to create the prototype chain.
var inherit = function(Child, Parent) {
var F = function() {};
F.prototype = Parent.prototype;
Child.prototype = new F();
};
var Employee = function(id, name) {
this.id = id;
this.name = name;
};
Employee.prototype.toString = function() {
return this.name + " (" + this.id + ")";
};
Employee.prototype.getName = function() {
return this.name;
};
var Manager = function(id, name, car) {
Employee.call(this, id, name);
this.car = car;
this.employees = [];
};
inherit(Manager, Employee);
var pete = new Manager(2, "Pete", "Mercedes");
var employee = {
id : 0,
name : "",
toString : function() {
return this.name + " (" + this.id + ")";
},
getName : function() {
return this.name;
}
};
var manager = Object.create(employee);
manager.car = "Mercedes";
manager.toString = function() {
return "Manager " + this.name + " (" + this.id + ")";
};
var pete = Object.create(manager);
pete.id = 2;
pete.name = "Pete";
pete.car = "Mercedes";
Server Sent Events (SSE) can be used to get "push" notifications from the server. It is really a standardized way of polling the server for new notifications (COMET). The client uses the EventSource object for this. This object makes a request to the URL and when it receives data, it fires events.
var source = new EventSource("http://www.mysite.com/updates.php");
source.addEventListener("message", function(e) {
console.log(e.data);
}, false);
The server should not closes the connection, but keeps it open. When the connection is closed, the client will automatically reconnect. The server can send a "retry" command with the number of milliseconds that the client should wait to reconnect. Because the webserver should not closes the connection, Apache is not really suitable for this. Servers like node.js are better, because of the event loop.
Some good links:
Interesting links
At this point, WebSQL is supported by Safari, Chrome and Opera. Chrome also supports IndexedDB, but Safari doesn't, so this means if you develop for mobile or tablet, WebSQL cannot be ignored. The W3 has deprecated WebSQL in favour of IndexedDB, which has something strange in it, because the 2 storage engines don't have the same functionality.
The benefits of using WebSQL in relation to webstorage:
The benefits of using WebSQL in relation to IndexedDB:
Put simply, the steps you need to do are:
The application cache (appcache) makes it possible to run a website offline. It makes use of a manifest file with entries of files inside it. When an appcache exists, every call to the files inside it are served directly from this appcache - no network access is done and needed.
When a manifest attribute is encountered and the appcache does not already exists, the browsers creates a new appcache and downloads all entries inside this manifest file and adds these to the appcahe. This happens in the background. With event listeners you can see what's happening.
There are some caveats when dynamically adding SCRIPT tags. To know when the added script has been downloaded, you can use the following function. It makes use of the load event when available with a fallback to the readystatechange event. The callback has access to the SCRIPT tag (this) and the event object.
function includeScript(src, callback) {
var firstScript = document.getElementsByTagName("script")[0];
var script = document.createElement("script");
// IE will download the script as soon as the attribute src is set.
script.setAttribute("src", src);
if (typeof script.onload !== "undefined") {
// Chrome, Firefox and IE9
script.onload = callback;
// When the script could not be loaded, we want an error event.
// If added with script.onerror, in Firefox we don't get a good event parameter (no e.type).
script.addEventListener("error", callback, false);
} else if (typeof script.readyState !== "undefined") {
// IE < 9
// There is NO WAY to detect if a script failed loading,
// if you load a non existing script, you'll still get
// loaded or completed...
script.onreadystatechange = function() {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
callback.call(script, window.event);
}
};
}
firstScript.parentNode.insertBefore(script, firstScript);
}
// this = SCRIPT element.
// e = event object.
function scriptLoaded(e) {
if (e.type == "error") {
alert("Failed loading " + this.src);
} else {
alert("I downloaded " + this.src);
}
}
To add a script and execute some code after the script has been downloaded:
includeScript("test.js", scriptLoaded);
As in the article http://www.blaze.io/technical/ies-premature-execution-problem/ is explained, always add the created SCRIPT element to an element that is already attached to the DOM. The safest way is to add it to the parent of the SCRIPT tag, because this will always be present when this javascript code is executed.
With mysqldump you can only create a CSV file if you have permission (and access) to the database server filesystem. So if you have a remote database server, you can use the following:
mysql -u[username] -h[server] -p[password] -B
-e "select * from database.table"
--default-character-set=utf8 > dump.csvThe usage of pcntl_fork in a webserver environment is not encouraged according the PHP documentation. As an alternative, you can use the following code:
exec("/usr/bin/php some_script.php > /dev/null &");
// Here you can do some more actions, while some_script.php
// is still executing.
Even if the caller script has ended, the some_script.php will still run. A drawback certainly is the lack of return value.