#!/usr/bin/python
# Usage: fox2html [options] [<foxmarks JSON file>] > bookmarks.html
#
# This script reads 'foxmarks.json' and print out a html file
# It's partly based on the index.py by David Blume @
# http://wiki.foxmarks.com/wiki/Talk:Foxmarks:_Frequently_Asked_Questions
# as well as xbel.xsl and xbel.css related to old Firefox Bookmarks
# Synchronizer plugin
#
# Author: Chun-Chung Chen <cjj@u.washington.edu>
# Release: 2008-12-24
# URL: http://ccdw.org/~cjj/prog/#fox2html
#
# Copyright 2008 Chun-Chung Chen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

from optparse import OptionParser
import simplejson
import os
import time

parser = OptionParser(usage = "%prog [options] [<foxmarks JSON file>] > bookmarks.html")
parser.add_option("-t", "--title", dest = "title", default = "Live Bookmarks", help = "set page title to TXT", metavar = "TXT")
parser.add_option("-H", "--hide", action = "store_true", dest = "hide", default = False, help = "hide folders with names begin with '-'")
parser.add_option("-u", "--uhide", action = "store_true", dest = "uhide", default = False, help = 'hide "Unsorted Bookmarks" folder')
parser.add_option("-s", "--shide", action = "store_true", dest = "shide", default = False, help = "hide separators")
parser.add_option("-X", "--nospec", action = "store_false", dest = "special", default = True, help = "turn off some special treatments")
parser.add_option("", "--foot", dest = "foot", default = "", help = "append HTML_TEXT to the bottom of the page", metavar = "HTML_TEXT")
(options, args) = parser.parse_args()
if len(args) > 1: raise RuntimeError("expected 0 or 1 argument")

json_file = 'foxmarks.json'
if len(args) > 0: json_file = args[0]

def fix(str):
	s = str.replace('&', '&amp;').replace('"', '&quot;').replace('<', '&lt;').replace('>', '&gt;').replace(' ','&nbsp;')
	return s.encode('utf-8', 'replace')

def do_insert(folders, command):
	if 'nid' in command:
		args = command['args']
		if args.has_key('ntype'):
			if args.has_key('name'): name = fix(args['name'])
			else: name = ''
			if args['ntype'] == 'folder':
				my_folder = []
				folders[command['nid']] = my_folder
				if command['nid'] != 'ROOT':
					parent = folders[args['pnid']]
					if options.special and name == 'Bookmarks&nbsp;Toolbar':
						parent.insert(0, (command['nid'], 'Toolbar', my_folder))
					else:
						parent.append((command['nid'], name, my_folder))
			elif args['ntype'] == 'separator':
				if options.shide: return
				parent = folders[args['pnid']]
				parent.append((command['nid'], '<hr />'))
			elif args['url'][:6] == 'place:': return # skip special folders
			else: # 'bookmark'
				parent = folders[args['pnid']]
				line = '<li style="list-style-image:'
				if 'icon' in args: line += 'url(' + args['icon'] + ')'
				else: line += 'none'
				line += ';"><a href="%s" target="_blank"' % fix(args['url'])
				if 'description' in args: line += ' title="%s"' % fix(args['description'])
				if len(name) == 0:
					name = '<img src="data:image/gif;base64,R0lGODlhEAAQAKECAABkAADDAP///////yH5BAEAAAIALAAAAAAQABAAAAIilI+pyxetwoNOUiNtzFyjDiYgByTAeZJUVq5Bu8KUfNVUAQA7" />'
				line += '>%s</a></li>' % name
				parent.append((command['nid'], line))

def print_folders(folders, indent):
	print indent + '<ul>'
	for item in folders:
		if len(item) > 2: # folders
			if len(item[1]) > 0:
				if options.hide and item[1][0] == '-': continue # hidden folders
				if options.uhide and item[1] == 'Unsorted&nbsp;Bookmarks': continue # hide Unsorted Bookmarks
				line = '<li class="folder"><span class="folderName" onclick="toggleDisplay(event);">'
				line += item[1] + '</span><div class="folderList" style="display:none;">'
			else:
				line = '<li class="folder"><span class="folderNoName" onclick="toggleDisplay(event);">'
				line += 'noname'
				line += '</span><div class="folderList" style="display:none;">'
			print indent + line
			print_folders(item[2], indent + '\t')
			print indent + '</div></li>'
		else: # bookmarks
			print indent + item[1]
	print indent + '</ul>'

zstr = time.strftime(' %z')
tstr = time.strftime('%Y-%m-%d %H:%M', time.localtime(os.stat(json_file).st_mtime)) + zstr[0:-2] + ':' + zstr[-2:]

print '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
''', '<title>%s</title>' % options.title, '''
<style type="text/css" media="all">
li[class="folder"]{
	list-style-image : url(data:image/gif;base64,R0lGODlhEAAOAOMEAJdaH+C6eP/inq1zLf/////Sg59oJMOHNP///////////////////////////////yH5BAEKAAgALAAAAAAQAA4AAAQ9EMlJq7336IPnICAxdIdgFkGwadQRviE3lWZtA9Rg78Lo8TafxFAoGo8GCuDILOAmgEHT+JwYrthsZ4uIAAA7);
	width:95%;
}
li[class="folderopen"]{
	list-style-image : url(data:image/gif;base64,R0lGODlhEAAOAOMEAJdaH+C6eP/inq1zLf/////Sg59oJMOHNP///////////////////////////////yH5BAEKAAgALAAAAAAQAA4AAARAEMlJq7336IPnICAxdIdgFkGwadQRviE3lWZtA9RgD3zvIzpT0HYaGQqFAXLJHCEASabU+VRKl1SJYcvtdr6ICAA7);
	width:95%;
}
li.bookmark{
	list-style-image : none;
	list-style-type:disc;
}
body{
	background-color: #e0e0FF;
	color: #000066;
}
h3{
	color: #000066;
	font-style:italic;
}
img{
	border: none;
}
span.spacer{
	min-width:4px;
}
div.booktree{
	border: 2px #F0F0FF solid;
	background-color: #F8F8F8;
	width:95%;
}
span.folderName{
	font-weight:bold;
}
span.folderName:hover
{
	cursor: pointer;
}
span.folderName:active
{
	color:red;
}
span.folderNoName{
	color: #AAAAFF;
}
span.folderNoName:hover
{
	cursor: pointer;
}
div.folderList{
	border: thin #CCCCFF solid;
	background-color: #F0F0F0;
	margin-bottom:4px;
	padding:4px 0px 4px 0px;
}
li.separator{
	list-style-type:none;
</style>
<script type="text/javascript" language="JavaScript">
// <![CDATA[
function toggleDisplay(aEvent)
{
	var target;

	if("fromElement" in aEvent){
		target=aEvent.srcElement.nextSibling;
	}
	else if("target" in aEvent){
		target=aEvent.target.nextSibling;
	}
	var i=0;
	while(target && target.nodeName.toUpperCase()!="DIV" && i++<100)
		target=target.nextSibling;
	if(!target || i>=100)
		return;
	var visible=target.style.display;
	if (!visible || visible=="block") {
		target.style.display="none";
		target.parentNode.className="folder";
	}
	else {
		target.style.display="block";
		target.parentNode.className="folderopen";
	}
}
// ]]>
</script>
</head>
<body>
''', '<h3>%s</h3>' % options.title,'''
''', '<div><small><i>Last sync: %s </i></small></div>' % tstr, '''
<div class="booktree">
''',
f = open(json_file)
json = simplejson.load(f)
f.close()
folders = {}
for command in json[u'commands']['set']:
	do_insert(folders, command)
print_folders(folders['ROOT'], '')
print '</div>'
if len(options.foot) > 0: print options.foot,
print '''
</body>
</html>
''',
