Simple Inheritance on Javascript Objects

I’ve been playing a lot with javascript lately and needed to extend an object, retaining all the existing methods but also adding more.

This simple script allows exactly that:

Object.prototype.implement = Function.prototype.implement = function(props) {
  for (var key in props) {
    this[key] = props[key];
  }
  
  return true;
}

Object.prototype.extend = Function.prototype.extend = function(props) {
  var obj = new this.constructor();
  obj.prototype = this.prototype;
  obj.implement(this);
  
  if (props) {
    obj.implement(props);
  }

  return obj;
}

It should be used similar to how you would extend a class in PHP i guess:

var Class = function() {
}

Class.prototype.test = function() {
  alert('test');
}

var Class2 = Class.extend();

Class2.prototype.test2 = function() {
  alert('test2');
}

var obj = new Class();

var obj2 = new Class2();

obj.test();
// alerts test
obj.test2();
// throws an exception
obj2.test();
// alerts test
obj2.test2();
// alerts test2

I know there are many frameworks that probably already do this better, but if you just need a simple object extension method, this should work.

jsInflections – Ruby style inflections for Javascript

Small class, that handles inflections like ruby for javascript strings, eg:

alert('cow'.pluralize());  // alerts cows
alert('cows'.singularize()); // alerts cow

Pretty simple, all methods except constantize have been replicated.

This file is probably mostly the work of the rails team, so the same license applies to this, as rails itself.

Download (3.3KB)

Webkit (Safari) – Speed Dial bookmarklet

Since chrome (well I know opera did it way back when… but anyway) the speed dial feature of a browser has become very popular and I thought that since bookmarklets are cool and webkit has a database engine, surely these ideas could all be combined, so I’ve built a simple speed dial page written totally in javascript with webkit’s SQL engine.

It’s not super pretty, surely has some bugs (if you load the bookmarklet from anything other than a new tab, you’ll have no favourites and also it can’t be your homepage, for some reason), and you need to sign up for a snapcasa.com account (it is free!) for the prettiness, but it’s mainly proof-of-concept.

speedDial bookmarklet for WebKit
speedDial bookmarklet for WebKit

To enjoy previews of the sites you’ve added, you’ll need to sign up for a free account at http://snapcasa.com/Register.aspx after which you will have to add your IP address (hopefully it’s static… sorry if not, but there are other solutions… http://www.webresourcesdepot.com/10-free-website-thumbnail-generation-services/)

You can download the package with a YUI compressed copy of the code and a fully expanded version below, or alternatively, you can copy this code, remembering to replace the SNAPCASA_CODE with your own code:

javascript:document.write('<html><head><title>Speed Dial</title></head><body><div id="body"></div></body></html>');window.speedDial={rows:3,cols:3,data:[""]};var body=document.getElementById("body");if(window.openDatabase){var database=window.openDatabase("SpeedDial","1.0","Speed Dial settings",200000);if(database){var loadSettings=function(){database.transaction(function(A){A.executeSql("SELECT rows, cols FROM SpeedDialSettings",[],function(C,B){for(var D=0;D<B.rows.length;++D){var E=B.rows.item(D);window.speedDial.rows=parseInt(E.rows);window.speedDial.cols=parseInt(E.cols)}},function(B,C){body.innerHTML=C.message;return})})};var loadData=function(){database.transaction(function(A){A.executeSql("SELECT * FROM SpeedDialSites",[],function(C,B){for(var D=0;D<B.rows.length;++D){var E=B.rows.item(D);window.speedDial.data[E.id]=E.url}},function(B,C){body.innerHTML=C.message;return})})};var changeSite=function(A){database.transaction(function(B){B.executeSql("SELECT * FROM SpeedDialSites WHERE id = ?",[A],function(D,C){if(C.rows.length==0){var E=checkURL(window.prompt("Enter the URL for cell "+A));if(E===false){return}if(E.length>0){D.executeSql("INSERT INTO SpeedDialSites (id, url) VALUES (?, ?)",[A,E],function(H,G){window.setTimeout(function(){render()},400)},function(G,H){alert(H.message)})}}else{if(C.rows.length==1){var F=C.rows.item(0);var E=checkURL(window.prompt("Enter the URL for cell "+A,F.url));if(E===false){return}if(E.length>0){D.executeSql("UPDATE SpeedDialSites SET id = ?, url = ?",[A,E],function(H,G){window.setTimeout(function(){render()},400)},function(G,H){alert(H.message)})}else{D.executeSql("DELETE FROM SpeedDialSites WHERE id = ?",[A],function(H,G){window.setTimeout(function(){render()},400)},function(G,H){alert(H.message)})}}else{alert("Too many rows!");return}}},function(C,D){alert("Error with SpeedDialSites table");return})})};var clearAll=function(){database.transaction(function(A){A.executeSql("DELETE FROM SpeedDialSites WHERE id > -1",[],function(C,B){},function(B,C){alert(C.message)})});window.setTimeout(function(){render()},400);return true};var changeSettings=function(){var A=parseInt(prompt("Please enter the number of rows you require"));var B=parseInt(prompt("Please enter the number of columns you require"));database.transaction(function(C){C.executeSql("UPDATE SpeedDialSettings SET rows = ?, cols = ? WHERE rows = ? AND cols = ?",[A,B,window.speedDial.rows,window.speedDial.cols],function(E,D){},function(D,E){alert(E.message)})});loadSettings();window.setTimeout(function(){render()},400)};var checkURL=function(A){if(A==null){return false}if(typeof A!="string"){return""}else{if(A.length>0){if(A.indexOf("http")==-1){return"http://"+A}else{return A}}else{return""}}};database.transaction(function(A){A.executeSql("SELECT * FROM SpeedDialSettings",[],function(C,B){if(B.rows.length==0){C.executeSql("INSERT INTO SpeedDialSettings (rows,cols) VALUES (?, ?)",[3,3])}else{}},function(B,C){B.executeSql("CREATE TABLE SpeedDialSettings (rows REAL, cols REAL)",[],function(E,D){E.executeSql("INSERT INTO SpeedDialSettings (rows,cols) VALUES (?, ?)",[3,3])})})});database.transaction(function(A){A.executeSql("SELECT COUNT(*) FROM SpeedDialSites",[],function(C,B){},function(B,C){B.executeSql("CREATE TABLE SpeedDialSites (id REAL UNIQUE, url TEXT)"),[],function(E,D){}})});var render=function(){loadSettings();loadData();body.innerHTML="";var G=document.createElement("table");G.cellSpacing="10px";G.style.width="90%";G.style.height="90%";G.style.marginTop="5%";G.style.marginLeft="5%";body.appendChild(G);for(var B=1;B<=window.speedDial.rows;B++){var I=document.createElement("tr");G.appendChild(I);for(var A=1;A<=window.speedDial.cols;A++){var C=(((B-1)*window.speedDial.cols)+A);var F=document.createElement("td");F.style.color="#ccc";F.style.border="8px solid #ccc";F.style.textAlign="center";F.style.fontFamily="Arial";F.style.width=parseInt(100/window.speedDial.cols)+"%";F.style.height=parseInt(100/window.speedDial.rows)+"%";F.num=C;F.onclick=function(){if(this.innerHTML==this.num||!this.url){changeSite(this.num)}else{if(event.altKey){changeSite(this.num)}else{window.location=this.url}}};F.onmouseover=function(){this.style.color="#999";this.style.border="8px solid #999"};F.onmouseout=function(){this.style.color="#ccc";this.style.border="8px solid #ccc"};if(typeof window.speedDial.data[C]=="undefined"){F.style.color="#ccc";F.style.fontSize="100px";F.innerHTML=C}else{F.url=window.speedDial.data[C];F.style.fontSize="15px";F.innerHTML='<img src="http://snapcasa.com/get.aspx?code=SNAPCASA_CODE&size=l&url='+F.url+'" alt=""/>'}I.appendChild(F)}}var D=document.createElement("a");D.href="#";D.onclick=function(){clearAll();return false};D.appendChild(document.createTextNode("Clear"));var E=document.createElement("a");E.href="#";E.onclick=function(){changeSettings();return false};E.appendChild(document.createTextNode("Settings"));D.style.color="#ccc";D.style.textDecoration="none";E.style.color="#ccc";E.style.textDecoration="none";var H=document.createElement("p");H.style.fontFamily="Arial";H.style.color="#ccc";H.appendChild(D);H.appendChild(document.createTextNode(" - "));H.appendChild(E);body.appendChild(H)};loadSettings();loadData();window.setTimeout(function(){render()},100)}else{body.innerHTML="Error opening database"}}else{body.innerHTML="Error accessing database"};

and paste in directly!

Download (3.2KB)

As ever, this snippet is under the Creative Commons ShareAlike Attribution license.

php-log: Process apache log files with PHP

I’ve recently had to quickly parse an Apache log file, to get basic usage statistics for a site over a month and wanted the ability to quickly process any general logs and store them in a MySQL table, so I’ve made a small package class.log.

Included in the package are:

  • class.log.php (the main class)
  • class.log.processor.php (an interface for a log processor, very basic!!)
  • class.log.output.php (a simple extension of the processor (outputs <p> tags with <span>s)
  • class.log.mysql.php (a simple mysql importer of the processed data)
  • parse.php (a simple implementation, bringing all the classes together)

The class is designed to use one line at a time from the log retrieved, and the regular expression specified in class.log.php can be modified to parse different types of logs as long as the matches array is also updated.

I’ve only used this for apache logs currently, which it managed quite well, I’m not sure if I’d use this script in an automated script, but I’ll leave that for you to decide.

As other scripts, this class is available under the Creative Commons Share Alike Attribution license.

Edit: Minor update, made class more generic and added more comments.

Download (3.7kB)