Monday, April 14, 2008

PHP: Parsing firefox bookmarks.html

In this post I will cover how to parse firefox bookmarks.html file.

I am sure many of you have been turned off by the way firefox stores the bookmarks. I personally find it not pretty at all. My main complaint is that it uses old datalist format that somewhat resembles html, but when you start parsing it, it turns out nowhere near. One of the main features(?) of this format is that tags are not closed unless they contain nested children, so you have to rely on newline characters to figure out what the document is trying to tell you.

Why, tell me, why not just use xml like every normal application?
Beats me.

Anyways, I had to write a parser in PHP to accommodate that ugly format. It consists of two functions the starter and the recursive-helper. To get a nested Array of bookmarks just call FF_parseBookmarks($s) where $s is the text your code have read from the bookmarks.html.


<?php
// Firefox library
function FF_parseBookmarks_($aLines,&$i=0,$aProps=NULL) {
if(!$aProps) { $aProps = array(); }
if($i == 0) {
$bBeginning = true;
}
$a = array_merge($aProps,array(
'nodes' => array()
));
$sDTMode = 'folder';
$sNewFolderProps = array();
for(;$i<count($aLines);$i++) {
$s = trim($aLines[$i]);
// print $s."\n";
if(eregi('^<DL>',$s)) {
$i++;
$a['nodes'][] = FF_parseBookmarks_($aLines,$i,$sNewFolderProps);
$sNewFolderProps = array();
} else if(eregi('^<DT><H3([^>]+)>([^<]+)',$s,$aRegs)) {
$sDTMode = 'folder';
$sCreated = $sModified = '';
if(eregi('ADD_DATE="([^"]+)"',$aRegs[1],$aRegs1)) {
$sCreated = $aRegs1[1];
}
if(eregi('LAST_MODIFIED="([^"]+)"',$aRegs[1],$aRegs1)) {
$sModified = $aRegs1[1];
}
if(eregi('PERSONAL_TOOLBAR_FOLDER="true"',$aRegs[1])) {
$sNewFolderProps['is_toolbar'] = '1';
}
$sNewFolderProps['title'] = $aRegs[2];
$sNewFolderProps['created'] = $sCreated;
$sNewFolderProps['modified'] = $sModified;

} else if(eregi('^<DD>([^>]+)',$s,$aRegs)) {
if($sDTMode=='folder') {
$sNewFolderProps['descrption'] = $aRegs[1];
} else {
$a['nodes'][count($a['nodes'])-1]['description'] = $aRegs[1];
}
} else if(eregi('^<DT><A([^>]+)>([^<]+)',$s,$aRegs)) {
$sDTMode = 'link';
$sURL = $sCreated = $sModified = $sTags = '';
if(eregi('href="([^"]+)"',$aRegs[1],$aRegs1)) {
$sURL = $aRegs1[1];
}
if(eregi('ADD_DATE="([^"]+)"',$aRegs[1],$aRegs1)) {
$sCreated = $aRegs1[1];
}
if(eregi('LAST_MODIFIED="([^"]+)"',$aRegs[1],$aRegs1)) {
$sModified = $aRegs1[1];
}
if(eregi('SHORTCUTURL="([^"]+)"',$aRegs[1],$aRegs1)) {
$sTags = $aRegs1[1];
}

$a['nodes'][] = array(
'url' => $sURL,
'title' => $aRegs[2],
'created' => $sCreated,
'modified' => $sModified,
'tags' => $sTags
);
} else if(eregi('^</DL>',$s,$aRegs)) {
return $a;
}
}
if($bBeginning) {
return $a['nodes'];
}
return $a;
}
function FF_parseBookmarks($s) {
$s = ereg_replace('ICON="[^"]+"','',$s);
$a = explode('<DL>',$s,2);
$s = '<DL>'.$a[1];
//$s = str_replace('<HR>',"</DL>\n<DL>\n",$s);
$aLines = explode("\n",$s);
return FF_parseBookmarks_($aLines);
}

?>

Sunday, April 13, 2008

XUL Tree: a javascript widget

Here is a piece of code I wrote to be able to assemble XUL Tree from a given javascript object.

/*****************************************************
EXAMPLE OF CONFIG OBJECT
/*****************************************************
var oConfig = {
id: "mytree",
className: "classname"
treecols: [{
label: "Column 1",
primary: true,
id: "col1"
},{
label: "Column 2",
id: "col2",
}
]
}
/*****************************************************
EXAMPLE OF DATA OBJECT
/*****************************************************
var oData = {
open: true,
cols: [
{ label: 'node1' },
{ label: 'node2' }
],
nodes: [
{
cols: [
{ label: 'node3' },
{ label: 'node4' }
],
nodes: []
},{
cols: [
{ label: 'node5' },
{ label: 'node6' }
],
nodes: []
}
]
};
/*****************************************************/
function tree(sContainer, oConfig, oData) {
this.container = document.getElementById(sContainer);
this.data = oData;
this.config = oConfig;
// create tree
this.tree = document.createElement('tree');
this.tree.setAttribute('flex','1');
// create treecols
this.treecols = document.createElement('treecols');
this.cols = [];
var nOrdinal = 1;
for(var i in this.config.cols) {
var oCol = document.createElement('treecol');
oCol.setAttribute('label',this.config.cols[i].label);
oCol.setAttribute('primary',this.config.cols[i].primary);
oCol.setAttribute('id',this.config.cols[i].id);
oCol.setAttribute('ordinal',nOrdinal++);
oCol.setAttribute('flex','1');
this.cols.push(oCol);
var oSplitter = document.createElement('splitter');
oSplitter.setAttribute('ordinal',nOrdinal++);
this.treecols.appendChild(oCol);
if(i < this.config.cols.length - 1) {
this.treecols.appendChild(oSplitter);
}
}
this.tree.appendChild(this.treecols);
this.container.appendChild(this.tree);

this.root = document.createElement('treechildren');
this.createNode(this.root, this.data);
this.tree.appendChild(this.root);
}
/*****************************************************/
tree.prototype.createNode = function(oParent, oNodeData) {
var oTreeItem = document.createElement('treeitem');
if(oNodeData.open) {
oTreeItem.setAttribute('open',oNodeData.open);
}
var oTreeRow = document.createElement('treerow');
for(var i=0; i<oNodeData.cols.length; i++) {
var oTreeCell = document.createElement('treecell');
oTreeCell.setAttribute('label',oNodeData.cols[i].label);
oTreeRow.appendChild(oTreeCell);
}
oTreeItem.appendChild(oTreeRow);
if(oNodeData.nodes) {
oTreeItem.setAttribute('container','true');
var oTreeChildren = document.createElement('treechildren');
for(var i=0; i<oNodeData.nodes.length; i++) {
this.createNode(oTreeChildren,oNodeData.nodes[i]);
}
oTreeItem.appendChild(oTreeChildren);
}
oParent.appendChild(oTreeItem);
}
/*****************************************************

XUL: nesting trees

I have posted a piece of code on Mozilla Dev site that demonstrates how to nest nodes in the XUL tree. I will also post it here:

<tree id="myTree" flex="1" hidecolumnpicker="false" seltype="single" class="tree">
<treecols id="myTree2-treeCols">
<treecol id="myTree2-treeCol0" primary="true" flex="2" label="Column A" persist="width" ordinal="1"/>
<splitter class="tree-splitter" ordinal="2"/>
<treecol id="myTree2-treeCol1" flex="1" label="Column B" persist="width" ordinal="3"/>
</treecols>
<treechildren>
<treeitem>
<treerow>
<treecell label="1"/>
<treecell label="a"/>
</treerow>
</treeitem>
<treeitem container="true" open="true"> <!-- Make sure to set container="true" -->
<treerow>
<treecell label="2"/>
<treecell label="b"/>
</treerow>
<treechildren>
<treeitem>
<treerow>
<treecell label="2a"/>
<treecell label="ba"/>
</treerow>
</treeitem>
</treechildren>
</treeitem>
</treechildren>
</tree>
 
SQLITE | CROSSBROWSER CSS | MAC HOWTO | WEB WITHOUT ADS | DOT PHP | DOT JS | FIREFOX DEVELOPMENT