product listing and pages
diff --git a/erpnext/website/js/product_category.js b/erpnext/website/js/product_category.js
index 8d3f898..bed4d24 100644
--- a/erpnext/website/js/product_category.js
+++ b/erpnext/website/js/product_category.js
@@ -18,7 +18,10 @@
 	wrapper.category_list = new wn.ui.Listing({
 		parent: $(wrapper).find('.more-categories').get(0),
 		query: 'select count(name) as items, item_group \
-			from tabItem where is_sales_item="Yes" \
+			from tabItem \
+			where is_sales_item="Yes" and \
+			ifnull(show_in_website, "No")="Yes" and \
+			docstatus = 0 \
 			group by item_group order by items desc',
 		hide_refresh: true,
 		render_row: function(parent, data) {
@@ -26,5 +29,6 @@
 				data);
 		}
 	});
-	wrapper.category_list.run();	
+	wrapper.category_list.run();
+	console.log('product categories made');
 }
diff --git a/erpnext/website/templates/product/product.html b/erpnext/website/templates/product/product.html
index 5a04e79..5d3c3da 100644
--- a/erpnext/website/templates/product/product.html
+++ b/erpnext/website/templates/product/product.html
@@ -9,9 +9,11 @@
 				<h1>{{ item_name }}</h1>
 				<div style="float: left;">
 					<br><br>
+					{% if website_image %}
 					<image src="files/{{ website_image }}" style="width: 300px; 
 						margin-left: 15px;" />
 					<br><br>
+					{% endif %}
 					{{ web_description_html }}
 					<button class="btn primary product-inquiry" 
 						data-product="{{ name }}"
diff --git a/erpnext/website/templates/product/product.js b/erpnext/website/templates/product/product.js
index ee73e7f..3475204 100644
--- a/erpnext/website/templates/product/product.js
+++ b/erpnext/website/templates/product/product.js
@@ -1,6 +1,7 @@
-{% extends "page.html" %}
+{% extends "product/product_category.js" %}
 
 {% block javascript %}
+{{ super() }}
 // ERPNext - web based ERP (http://erpnext.com)
 // Copyright (C) 2012 Web Notes Technologies Pvt Ltd
 // 
@@ -16,14 +17,13 @@
 // 
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-wn.require('js/product_category.js');
-
+wn.provide('erpnext.products');
 wn.pages['{{ name }}'].onload = function(wrapper) {
-	console.log('loaded page');
 	wrapper.product_group = "{{ item_group }}";
 	wrapper.product_name = "{{ name }}";
-	erpnext.make_product_categories(wrapper);
+	erpnext.products.make_product_categories(wrapper);
+	
+	// TODO make this working
 	$(wrapper).find('.product-inquiry').click(function() {
 		loadpage('contact', function() {
 			$('#content-contact-us [name="contact-message"]').val("Hello,\n\n\
@@ -43,23 +43,33 @@
 				cat: wrapper.product_group,
 				name: wrapper.product_name
 			};
-			return repl('select name, item_name, website_image, \
+			var query = repl('select name, item_name, website_image, \
 				page_name, description \
 				from tabItem \
+				where is_sales_item="Yes" \
 				and ifnull(show_in_website, "No")="Yes" \
-				and name != "%(name)s" \
+				and name != "%(name)s" and docstatus = 0 \
 				and item_group="%(cat)s" order by modified desc', args)
+			return query
 		},
 		render_row: function(parent, data) {
-			if(data.short_description.length > 100) {
-				data.short_description = data.short_description.substr(0,100) + '...';
+			if(data.description.length > 100) {
+				data.description = data.description.substr(0,100) + '...';
 			}
-			parent.innerHTML = repl('<div style="float:left; width: 60px;">\
-				<img src="files/%(website_image)s" style="width:55px;"></div>\
+			parent.innerHTML = repl('\
+				<div style="float:left; width: 60px; padding-bottom: 5px" class="img-area"></div>\
 				<div style="float:left; width: 180px">\
 					<b><a href="%(page_name)s.html">%(item_name)s</a></b>\
 					<p>%(description)s</p></div>\
 				<div style="clear: both; margin-bottom: 15px;"></div>', data);
+				
+			if(data.website_image) {
+				$(parent).find('.img-area').append(repl(
+					'<img src="files/%(website_image)s" style="width:55px;">', data))
+			} else {
+				$(parent).find('.img-area').append(wn.dom.placeholder(50, 
+					data.item_name));
+			}
 		}
 	});
 	wrapper.similar.run();
diff --git a/erpnext/website/templates/product/product_category.js b/erpnext/website/templates/product/product_category.js
new file mode 100644
index 0000000..d8b09b3
--- /dev/null
+++ b/erpnext/website/templates/product/product_category.js
@@ -0,0 +1,27 @@
+{% extends "page.html" %}
+
+{% block javascript %}
+wn.provide('erpnext.products');
+erpnext.products.make_product_categories = function(wrapper) {
+	if (!wrapper) { wrapper = erpnext.products.wrapper; }
+	if (!wrapper) { return; }
+
+	wrapper.category_list = new wn.ui.Listing({
+		parent: $(wrapper).find('.more-categories').get(0),
+		query: 'select count(name) as items, item_group \
+			from tabItem \
+			where is_sales_item="Yes" and \
+			ifnull(show_in_website, "No")="Yes" and \
+			docstatus = 0 \
+			group by item_group order by items desc',
+		hide_refresh: true,
+		render_row: function(parent, data) {
+			parent.innerHTML = repl(
+				'<a href="products.html#!products/%(item_group)s">%(item_group)s</a> (%(items)s)', 
+				data);
+		}
+	});
+	wrapper.category_list.run();
+}
+
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/website/templates/product/product_list.html b/erpnext/website/templates/product/product_list.html
index 416a2fe..100bfca 100644
--- a/erpnext/website/templates/product/product_list.html
+++ b/erpnext/website/templates/product/product_list.html
@@ -2,19 +2,34 @@
 
 {% block title %}Products{% endblock %}
 
+{% block css %}
+	<style>
+		h3 > a, h3 > a:link, h3 > a:visited, h3 > a:active,
+		h3 > a:hover, h3 > a:focus {
+			text-decoration: none;
+			color: inherit;
+		}
+	</style>
+{% endblock %}
+
+
 {% block content %}
 	<div class="layout-wrapper layout-wrapper-background">
 		<div class="web-content" id="content-products">
 			
 			<div class="layout-main-section">
 				<h1 class="products-category"></h1>
-				<div id="product-list">
+				<div class="products-search" style="margin-bottom: 15px;">
+					<input name="products-search" />
+					<button class="btn" style="margin-left: 7px">Search</button>
+				</div>
+				<div id="products-list">
 					<!-- product list will be generated dynamically -->
 				</div>
 			</div>
 			
 			<div class="layout-side-section">
-				<h3>Categories</h3>
+				<h3><a href="products.html">Categories</a></h3>
 				<div class="more-categories"></div>
 			</div>
 			<div style="clear: both"></div>
diff --git a/erpnext/website/templates/product/product_list.js b/erpnext/website/templates/product/product_list.js
index 82c7886..5166fed 100644
--- a/erpnext/website/templates/product/product_list.js
+++ b/erpnext/website/templates/product/product_list.js
@@ -1,6 +1,8 @@
-{% extends "page.html" %}
+{% extends "product/product_category.js" %}
 
 {% block javascript %}
+{{ super() }}
+
 // ERPNext - web based ERP (http://erpnext.com)
 // Copyright (C) 2012 Web Notes Technologies Pvt Ltd
 // 
@@ -18,23 +20,103 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 // js inside blog page
+wn.provide('erpnext.products');
+
 wn.pages['{{ name }}'].onload = function(wrapper) {
-	erpnext.product_list = new wn.ui.Listing({
-		parent: $(wrapper).find('#product-list').get(0),
-		query: "select name, item_code, item_name, description, page_name \
-				from `tabItem` \
-				where docstatus = 0 and ifnull(show_in_website, 'No')='Yes'\
-				order by item_name asc",
-		hide_refresh: true,
-		no_toolbar: true,
-		render_row: function(parent, data) {
-			if(data.description && data.description.length==1000) data.description += '... (read on)';
-			parent.innerHTML = repl('<h4><a href="%(page_name)s.html">%(item_name)s</a></h4>\
-				<p>%(description)s</p>', data);
-		},
-		page_length: 10
+	erpnext.products.wrapper = wrapper;
+	
+	// make product categories in the sidebar
+	erpnext.products.make_product_categories(wrapper);
+	
+	// make lists
+	erpnext.products.make_product_list(wrapper);
+	
+	// erpnext.products.product_list.run();
+	
+	// bind search button or enter key
+	$(wrapper).find('.products-search .btn').click(function() {
+		erpnext.products.product_list.run();
 	});
-	erpnext.product_list.run();
+	
+	$(wrapper).find('.products-search input').keypress(function(ev) {
+		if(ev.which==13) $(wrapper).find('.products-search .btn').click();
+	});
+}
+
+erpnext.products.make_product_list = function(wrapper) {
+	if (!wrapper) { wrapper = erpnext.products.wrapper; }
+	if (!wrapper) { return; }
+	
+	erpnext.products.product_list = new wn.ui.Listing({
+		parent: $(wrapper).find('#products-list').get(0),
+		run_btn: $(wrapper).find('.products-search .btn').get(0),
+		no_toolbar: true,
+		get_query: function() {
+			var srch = $('input[name="products-search"]').val()
+			var search_cond = 'and (description like "%%(srch)s%"\
+				or item_name like "%%(srch)s%")';
+			var product_group_cond = 'and item_group="%(group)s"';
+			var cur_group = erpnext.products.cur_group
+			args = {
+				search_cond: srch ? repl(search_cond, {srch:srch}) : '',
+				cat: cur_group ? repl(product_group_cond, {group: cur_group}) : '',
+			};
+			return repl('select name, item_name, website_image, \
+				description, page_name \
+				from tabItem \
+				where is_sales_item="Yes" \
+				%(cat)s \
+				and docstatus = 0 and ifnull(show_in_website, "No")="Yes"\
+				%(search_cond)s', args)
+		},
+		render_row: function(parent, data) {
+			parent.innerHTML = repl('\
+				<div style="float:left; width: 115px;" class="img-area"></div>\
+				<div style="float:left; width: 400px">\
+					<p><b><a href="%(page_name)s.html">%(item_name)s</a></b></p>\
+					<p>%(description)s</p></div>\
+				<div style="clear: both;"></div>', data);
+				
+			if(data.website_image) {
+				$(parent).find('.img-area').append(repl(
+					'<img src="files/%(website_image)s" style="width:100px;">', data))
+			} else {
+				$(parent).find('.img-area').append(wn.dom.placeholder(100, 
+					data.item_name));
+			}
+		}
+	});
+}
+
+wn.pages['{{ name }}'].onshow = function(wrapper) {
+	// show default product category
+	erpnext.products.set_group();
+}
+
+erpnext.products.set_group = function() {
+	var cat = erpnext.products.get_group();
+
+	// get erpnext.products.default_category
+	var wrapper = erpnext.products.wrapper;
+	
+	$(wrapper).find('h1').html(cat.label);
+	erpnext.products.product_list.run();
+}
+
+erpnext.products.get_group = function() {
+	route = wn.get_route();
+	if(route && route.length>1) {
+		// from url
+		var grp = route[1];
+		var label = route[1];
+		erpnext.products.cur_group = grp;
+	} else {
+		// default
+		var grp = 'Products';
+		var label = 'Products';
+		erpnext.products.cur_group = null;
+	}
+	return {grp:grp, label:label};
 }
 
 {% endblock %}
diff --git a/erpnext/website/web_cache.py b/erpnext/website/web_cache.py
index c9f196d..42bd1d1 100644
--- a/erpnext/website/web_cache.py
+++ b/erpnext/website/web_cache.py
@@ -15,7 +15,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # used by web.py
-def load_from_web_cache(page_name, comments, template):
+def load_from_web_cache(page_name, comments, template): #, script=None):
 	"""
 		* search for page in cache
 		* if html exists, return
@@ -45,6 +45,9 @@
 
 	from webnotes.utils import cstr
 	html += """\n<!-- %s -->""" % cstr(comments)
+	
+	# show error in error console
+	# if script: html += """\n\n<script>\n%s\n</script>""" % cstr(script)
 	return html
 
 def load_into_web_cache(page_name, template, doc_type, doc_name):
diff --git a/public/js/all-app.js b/public/js/all-app.js
index 026903b..30b3c96 100644
--- a/public/js/all-app.js
+++ b/public/js/all-app.js
@@ -252,8 +252,7 @@
 wn.route=function(){if(wn.re_route[window.location.hash]){var re_route_val=wn.get_route_str(wn.re_route[window.location.hash]);var cur_route_val=wn.get_route_str(wn._cur_route);if(decodeURIComponent(re_route_val)===decodeURIComponent(cur_route_val)){window.history.back();return;}else{window.location.hash=wn.re_route[window.location.hash];}}
 wn._cur_route=window.location.hash;route=wn.get_route();switch(route[0]){case"List":wn.views.doclistview.show(route[1]);break;case"Form":if(route.length>3){route[2]=route.splice(2).join('/');}
 wn.views.formview.show(route[1],route[2]);break;case"Report":wn.views.reportview.show(route[1],route[2]);break;case"Report2":wn.views.reportview2.show();break;default:wn.views.pageview.show(route[0]);}}
-wn.get_route=function(route){if(!wn.boot){return[window.page_name];}
-return $.map(wn.get_route_str(route).split('/'),function(r){return decodeURIComponent(r);});}
+wn.get_route=function(route){return $.map(wn.get_route_str(route).split('/'),function(r){return decodeURIComponent(r);});}
 wn.get_route_str=function(route){if(!route)
 route=window.location.hash;if(route.substr(0,1)=='#')route=route.substr(1);if(route.substr(0,1)=='!')route=route.substr(1);return route;}
 wn.set_route=function(){route=$.map(arguments,function(a){return encodeURIComponent(a)}).join('/');window.location.hash=route;wn.app.set_favicon();}
diff --git a/public/js/all-web.js b/public/js/all-web.js
index 9712ff6..b3ab4ed 100644
--- a/public/js/all-web.js
+++ b/public/js/all-web.js
@@ -139,8 +139,7 @@
 wn.route=function(){if(wn.re_route[window.location.hash]){var re_route_val=wn.get_route_str(wn.re_route[window.location.hash]);var cur_route_val=wn.get_route_str(wn._cur_route);if(decodeURIComponent(re_route_val)===decodeURIComponent(cur_route_val)){window.history.back();return;}else{window.location.hash=wn.re_route[window.location.hash];}}
 wn._cur_route=window.location.hash;route=wn.get_route();switch(route[0]){case"List":wn.views.doclistview.show(route[1]);break;case"Form":if(route.length>3){route[2]=route.splice(2).join('/');}
 wn.views.formview.show(route[1],route[2]);break;case"Report":wn.views.reportview.show(route[1],route[2]);break;case"Report2":wn.views.reportview2.show();break;default:wn.views.pageview.show(route[0]);}}
-wn.get_route=function(route){if(!wn.boot){return[window.page_name];}
-return $.map(wn.get_route_str(route).split('/'),function(r){return decodeURIComponent(r);});}
+wn.get_route=function(route){return $.map(wn.get_route_str(route).split('/'),function(r){return decodeURIComponent(r);});}
 wn.get_route_str=function(route){if(!route)
 route=window.location.hash;if(route.substr(0,1)=='#')route=route.substr(1);if(route.substr(0,1)=='!')route=route.substr(1);return route;}
 wn.set_route=function(){route=$.map(arguments,function(a){return encodeURIComponent(a)}).join('/');window.location.hash=route;wn.app.set_favicon();}
diff --git a/public/js/product_category.js b/public/js/product_category.js
index aac872f..da50d78 100644
--- a/public/js/product_category.js
+++ b/public/js/product_category.js
@@ -3,5 +3,8 @@
  *	erpnext/website/js/product_category.js
  */
 erpnext.make_product_categories=function(wrapper){wrapper.category_list=new wn.ui.Listing({parent:$(wrapper).find('.more-categories').get(0),query:'select count(name) as items, item_group \
-   from tabItem where is_sales_item="Yes" \
-   group by item_group order by items desc',hide_refresh:true,render_row:function(parent,data){parent.innerHTML=repl('<a href="#!products/%(item_group)s">%(item_group)s</a> (%(items)s)',data);}});wrapper.category_list.run();}
\ No newline at end of file
+   from tabItem \
+   where is_sales_item="Yes" and \
+   ifnull(show_in_website, "No")="Yes" and \
+   docstatus = 0 \
+   group by item_group order by items desc',hide_refresh:true,render_row:function(parent,data){parent.innerHTML=repl('<a href="#!products/%(item_group)s">%(item_group)s</a> (%(items)s)',data);}});wrapper.category_list.run();console.log('product categories made');}
\ No newline at end of file
diff --git a/public/web.py b/public/web.py
index 6f3569e..e70b61c 100755
--- a/public/web.py
+++ b/public/web.py
@@ -48,13 +48,26 @@
 	page_name = scrub_page_name(page_name)
 	
 	if page_name == '404':
-		comments = """error: %s""" % webnotes.getTraceback()
-		template = '404.html'
+		traceback = webnotes.getTraceback()
+		
+		# script is used to display traceback in error console
+		args = {
+			'comments': """error: %s""" % traceback,
+			'template': '404.html',
+		}
+		# 	'script': """(function() {
+		# 		var error = "ERROR: %s";
+		# 		console.log(error);
+		# 	})();""" % traceback.replace('"', '\\"').replace('\n', ' \\\n'),
+		# }
+				
 	else:
-		comments = """page: %s""" % page_name
-		template = 'page.html'
+		args = {
+			'comments': """page: %s""" % page_name,
+			'template': 'page.html',
+		}
 	
-	html = website.web_cache.load_from_web_cache(page_name, comments, template)
+	html = website.web_cache.load_from_web_cache(page_name, **args)
 	
 	return html