app.html HTML Source View


<html>
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
	<title>KumoApps</title>
	<link rel="stylesheet" type="text/css" href="styles/jquery.mobile.simpledialog.min.css" />
	<link rel="stylesheet" href="styles/jquery.mobile-1.0b2.min.css" />
	<link rel="stylesheet" href="styles/eth.css" />
	<link rel="stylesheet" href="styles/demo_table.css" />
	<style>
		table.display tr td:first-child{width: 150px;}
		.logtype-0 {color: black;}
		.logtype-1 {color: orange;}
		.logtype-2 {
			color: red; font-weight: bold;
		}
		.ui-content {
			padding: 25px 15px 5px 15px;
		}
	</style> 
	<script src="styles/jquery-1.6.4.min.js" type="text/javascript"></script> 
	<script src="styles/jquery.mobile-1.0b2.min.js?2" type="text/javascript"></script> 
	<script type="text/javascript" src="styles/jquery.mobile.simpledialog.min.js"></script> 
</head>
<body>
	<div data-role="page" id="appListPage">
		<div data-role="header" data-theme="f" data-position="inline">
			<a href="index.html" data-ajax="false" data-icon="home" data-iconpos="notext">Back</a>			
			<h1>Kumo Apps</h1>
			<a class="ui-btn-right" href="#addnewApp" data-rel="dialog" data-icon="plus">Install</a>
		</div>
		<div data-role="content" data-theme="c">
			
			<div id="applist" data-role="collapsible-set">
			</div>

		</div>
		<script src="styles/client.js?2" type="text/javascript"></script> 

		<div data-role="header" >
			<a href="http://wirelesstag.net/kumoapp/" data-icon="info" data-iconpos="notext" data-ajax="false">What is KumoApp?</a>

			<h1>CPU Used: <span id="cpuUsage">0%</span>&nbsp;&nbsp;  Memory Used: <span id="memoryUsage">0kB</span></h1>
			<a href="AppCoder.html" data-icon="gear" data-ajax="false"
				class="ui-btn-right">Write Your Own App</a>
		</div>
	</div>

	<div data-role="page" id="addnewApp">
		<div data-role="header" data-theme="d" data-position="inline">
			<h1>
				Install a new Kumo App
			</h1>
		</div>
		<div data-role="content" data-theme="c">
			<ul id="snippetList" data-role="listview" data-filter="true" data-filter-placeholder="Search description..." data-inset="true" data-dividertheme="f">
			</ul>
		</div>
	</div>
	<div data-role="page" id="configPage">
		<div data-role="header" data-theme="d" data-position="inline">
			<h1>
				Configure the App
			</h1>
		</div>
		<div data-role="content" data-theme="c">
			<form action="" method="post" id="configForm" data-ajax="false" class="ui-body ui-corner-all">
				<div data-role="fieldcontain">
					<label for="configAppName">
						<b>Give it a unique name:</b>
					</label>
					<input type="text" name="configAppName" id="configAppName" value="" required/>
				</div>
				<span id="add-tag-dialog"></span><span id="edit-region-dialog"></span>
				<div id="configContainer">

				</div>
				<button data-inline="1" data-icon="check" type="submit" data-theme="b" id="btnConfigSubmit">Apply</button>
				<a data-role="button" data-rel="back" data-inline="1" data-icon="delete" data-theme="c">Cancel</a>
			</form>
		</div>
	</div>

	<script type="text/javascript">
		function generatePhonesHtml(devices, i, prefix) {
			var ret = '<fieldset data-role="controlgroup"  id="{1}g-{0}">'.format(i, prefix);
			var uuid_done = {};
			devices.forEach(function (d) {
				if (uuid_done[d.uuid] == null) {
					ret += ('<input type="checkbox" name="{4}-{0}" data-uuid="{1}" id="{4}-{0}-{1}" class="custom" {2}/><label for="{4}-{0}-{1}">{3}</label>')
							.format(i, d.uuid, d.disabled ? '' : 'checked', d.name, prefix);
					uuid_done[d.uuid] = 1;
				}
			});
			ret+="</fieldset>"
			return ret;
		}
		function loadDevicesTemplate() {
			$.ajax({
				url: WSROOT + "ethSnippets.asmx/GetDevicesTemplate",
				data: "{}",
				success: function (retval, textStatus) {

					$(".need-devices-template").each(function () {
						$(this).append(generatePhonesHtml(retval.d, $(this).data("nth"), $(this).data("prefix"))).trigger('create');
					});
				},
				error: function (xhr, textStatus, exception) {
					popup_error(xhr, null);
				}
			});
		}
		var activeObj, isNew;

		function loadConfig(obj) {
			$.mobile.changePage($("#configPage"), { transition: device_transition, role: "dialog" });
			isNew = (obj.description != null);
			activeObj = obj;
			var name = obj.name ? obj.name : obj.description;
			$("#configAppName").val(name);
			var html = "";
			var assignments = isNew ? obj.placeHolders : obj.assignments;
			for (var i = 0; i < assignments.length; i++) {
				var assignment = assignments[i];
				html += ('<div data-role="fieldcontain" ><legend>{0}:</legend><ul id="assignment-{2}" data-role="listview" data-split-icon="delete" data-split-theme="d" data-inset="true"><li data-icon="plus"><a href="#" onclick="editTagAssignment({2});return false;">{1}...</a></li>').format(assignment.name, assignment.allowMultiple ? "Add" : "Specify", i);
				if (assignment.uuids.length == 0) {
					// pick the first tag with allowed type.
					for (var uuid in taglist_cache) {
						var tag = taglist_cache[uuid];
						if (assignment.supportedTypes.some(function (t) { return t == tag.tagType; })) {
							assignment.uuids.push(uuid);
							break;
						}
					}
				}
				html += generateAssignmentHtml(assignment.uuids, i);
				html += "</ul></div>";
			}
			for (var i = 0; i < obj.schedules.length; i++) {
				var schedule = obj.schedules[i];
				html += ('<fieldset class="ui-corner-all"><legend>{1}:</legend><label for="tod-{0}">Time of the day:</label><select data-inline=1 name="tod-{0}" id="tod-{0}">').format(i, schedule.name);
				for (var h = 0; h < 24; h++) {
					for (var q = 0; q < 4; q++) {
						var lit = (h > 12 ? h - 12 : h) + ":" + (q == 0 ? "00" : q * 15) + (h > 12 ? "PM" : "AM");
						var min_utc = h * 60 + q * 15; // + tzo;
						html += '<option value="{0}" {2}>{1}</option>'.format(min_utc, lit, min_utc == schedule.tod ? "selected" : "");
					}
				}
				html += '</select><div data-role="controlgroup" data-type="horizontal"><legend>Days of week:</legend>';
				html += '<input type="checkbox" data-dow=1 {1} name="dow-{0}" id="day{0}_sun" class="custom"/><label for="day{0}_sun">Sun</label>'.format(i, schedule.dow&1?"checked":"");
				html += '<input type="checkbox" data-dow=2 {1} name="dow-{0}" id="day{0}_mon" class="custom"/><label for="day{0}_mon">Mon</label>'.format(i, schedule.dow & 2 ? "checked" : "");
				html += '<input type="checkbox" data-dow=4 {1} name="dow-{0}"  id="day{0}_tue" class="custom"/><label for="day{0}_tue">Tue</label>'.format(i, schedule.dow & 4 ? "checked" : "");
				html += '<input type="checkbox" data-dow=8 {1} name="dow-{0}"  id="day{0}_wed" class="custom"/><label for="day{0}_wed">Wed</label>'.format(i, schedule.dow & 8 ? "checked" : "");
				html += '<input type="checkbox" data-dow=16 {1} name="dow-{0}"  id="day{0}_thu" class="custom"/><label for="day{0}_thu">Thu</label>'.format(i, schedule.dow & 16 ? "checked" : "");
				html += '<input type="checkbox" data-dow=32 {1} name="dow-{0}"  id="day{0}_fri" class="custom"/><label for="day{0}_fri">Fri</label>'.format(i, schedule.dow & 32 ? "checked" : "");
				html += '<input type="checkbox" data-dow=64 {1} name="dow-{0}"  id="day{0}_sat" class="custom"/><label for="day{0}_sat">Sat</label></div></fieldset>'.format(i, schedule.dow & 64 ? "checked" : "");
			}
			for (var i = 0; i < obj.literals.length; i++) {
				var literal = obj.literals[i];
				html += ('<div data-role="fieldcontain" ><legend>{1}:</legend><input id="literal-{0}" type="{2}" step="any" value="{3}" required/></div>').format(i, literal.name, literal.isString ? "text" : "number", literal.value);
			}

			var loadingDevicesTemplate = false;
			for (var i = 0; i < obj.phones.length; i++) {
				var ph = obj.phones[i];
				html += ('<div data-role="fieldcontain" ><legend>{0}:</legend>').format(ph.name);
				if(ph.devices==null || ph.devices.length==0){
					html += '<div class="need-devices-template" data-nth={0} data-prefix="ph"></div>'.format(i);

					if (!loadingDevicesTemplate) {
						loadingDevicesTemplate = true;
						loadDevicesTemplate();
					}

				} else {
					html += generatePhonesHtml(ph.devices, i, "ph");
				}
				html += '</div>';
			}

			for (var i = 0; i < obj.regions.length; i++) {
				var reg = obj.regions[i];
				html += ('<fieldset class="ui-corner-all"><legend>{0}:</legend>').format(reg.name);
				html += '<div style="border-radius: 10px; overflow: hidden;"><iframe onload="initRegionPicker({0})" id="regionPicker-{0}" height="350" width="100%" frameBorder="0" scrolling="no" id="mapRegionPicker" src="mapRegionPicker.html?26"></iframe></div>'.format(i);
				if (reg.devices==null || reg.devices.length == 0) {
					html += '<div class="need-devices-template" data-nth={0} data-prefix="reg"></div>'.format(i);

					if (!loadingDevicesTemplate) {
						loadingDevicesTemplate = true;
						loadDevicesTemplate();
					}

				} else {
					html += generatePhonesHtml(reg.devices, i, "reg");
				}
				html += '</fieldset>';
			}

			$("#configContainer").html(html).trigger("create");
		}

		function initRegionPicker(nth) {
			$("#regionPicker-" + nth).get(0).contentWindow.setRegion(activeObj.regions[nth]);
		}

		$("#configForm").submit(function (event) {
			event.preventDefault();
			var btn = $("#btnConfigSubmit");

			activeObj.name = $("#configAppName").val();
			if (activeObj.name.length == 0) {
				popup("Please specify name", btn);
				return;
			}
			var assignments = isNew ? activeObj.placeHolders : activeObj.assignments;
			for (var i = 0; i < assignments.length; i++) {
				var assignment = assignments[i];
				if (assignment.uuids.length == 0) {
					popup("Please pick at least one tag for " + assignment.name, btn);
					return;
				}
			}
			for (var i = 0; i < activeObj.schedules.length; i++) {
				var schedule = activeObj.schedules[i];
				schedule.tod = $("#tod-" + i).val();
				schedule.dow = 0;
				$('input[name=dow-{0}]:checked'.format(i)).each(function () {
					schedule.dow += $(this).data("dow");
				});
			}
			for (var i = 0; i < activeObj.literals.length; i++) {
				var literal = activeObj.literals[i];
				literal.value = $("#literal-" + i).val();
				if (literal.value.length == 0) {
					popup("Please specify " + literal.name, btn);
					return;
				}
			}
			for (var i = 0; i < activeObj.phones.length; i++) {
				var ph = activeObj.phones[i];
				ph.devices = [];
				$('input[name=ph-{0}]'.format(i)).each(function () {
					ph.devices.push({ uuid: $(this).data("uuid"), disabled: !$(this).is(":checked") });
				});
			}
			for (var i = 0; i < activeObj.regions.length; i++) {
				var reg = $("#regionPicker-" + i).get(0).contentWindow.getRegion();
				reg.devices = [];
				$('input[name=reg-{0}]'.format(i)).each(function () {
					reg.devices.push({ uuid: $(this).data("uuid"), disabled: !$(this).is(":checked") });
				});
				reg.name = activeObj.regions[i].name;
				activeObj.regions[i] = reg;
			}

			var oldhtml = show_finding(btn, "Saving...");
			var toSubmit = { name: activeObj.name, assignments: assignments, schedules: activeObj.schedules, regions: activeObj.regions, literals: activeObj.literals, phones: activeObj.phones };
			toSubmit[isNew ? "snippet_id" : "id"] = activeObj.id;
			$.ajax({
				url: WSROOT + "ethSnippets.asmx/" + (isNew ? "InsertScript3" : "ReconfigureScript3"),
				data: JSON.stringify(toSubmit),
				complete: function () { restore_finding(btn, oldhtml); },
				success: function (retval, textStatus) {
					$.mobile.changePage("#appListPage", { transition: device_transition }, true);
				},
				error: function (xhr, textStatus, exception) {
					popup_error(xhr, null);
				}
			});
		});

		function generateAssignmentHtml(uuids, nth) {
			var html = "";
			uuids.forEach(function (uuid) {
				html += ('<li class="tags-assignment-{1}"><a>{0}</a><a href="#" onclick="removeTagAssignment({1}, \'{2}\')">Remove</a></li>').format(taglist_cache[uuid].name, nth, uuid);
			});
			return html;
		}
		function removeTagAssignment(nth, uuid) {
			var assignments = isNew ? activeObj.placeHolders : activeObj.assignments;
			var assignment = assignments[nth];
			for (var i = 0; i < assignment.uuids.length; i++)
				if (assignment.uuids[i] == uuid)
					assignment.uuids.splice(i, 1);

			$(".tags-assignment-" + nth).remove();
			$("#assignment-" + nth).append(generateAssignmentHtml(assignment.uuids, nth)).listview("refresh");
		}
		function editTagAssignment(nth) {
			var assignments = isNew ? activeObj.placeHolders : activeObj.assignments;
			var assignment = assignments[nth];

			var html = ("<div style='padding: 15px'><b>Accepted types: {0}</b><fieldset data-role='controlgroup' id='edit-tag-choices'>").format(supportedTypesTitle(assignment));
			for (var uuid in taglist_cache) {
				var tag = taglist_cache[uuid];
				if (assignment.supportedTypes.some(function (t) { return t == tag.tagType; }))
					html += "<input type='checkbox' name='placeholder-tag' id='placeholder-tag-{0}' {2} value='{0}' /><label for='placeholder-tag-{0}'>{1}</label>"
						.format(uuid, tag.name, assignment.uuids.some(function (t) { return t == tag.uuid; }) ? "checked" : "");
			}
			html += "</fieldset></div>";

			var holder = $("#add-tag-dialog");
			if (holder.data('simpledialog')) {
				holder.data('simpledialog').options.fullHTML = html;
				holder.simpledialog('refresh').simpledialog('open');
			} else {
				holder.simpledialog({
					'mode': 'blank',
					'prompt': false,
					'forceInput': false,
					'useModal': true,
					pickPageTheme: 'c',
					'fullHTML': html,
					onClosed: function () {
						assignment.uuids = [];
						$(".tags-assignment-" + nth).remove();
						$('input[name=placeholder-tag]:checked').each(function () { assignment.uuids.push(this.value); });
						//lerma 949 724 7233
						$("#assignment-" + nth).append(generateAssignmentHtml(assignment.uuids, nth)).listview("refresh");
					}
				});
			}
		}

		function titleMsg(app) {
			var ret = app.lastError != null ? app.lastError : (app.logs.length > 0 ? app.logs[0].msg : "");
			if (ret.length > 0) return " (" + ret + ")";
			else return "";
		}
			var app_cache = {};
		function updateEntry(app) {
			if ($('#app-' + app.id).length == 0) {
				createEntry(app);
				app_cache[app.id] = app;
				$("#applist").resetRoundedEdges();
			} else {	
				$('#appname-' + app.id).text(app.name);
				setTheme($("#app-" + app.id).find("a:eq(0)").find(".ui-btn").andSelf(), app.lastError != null ? "e" : (app.running ? "f" : "c"));
				$('#appstat-' + app.id).text(titleMsg(app));

				var btn = $("#runstop-" + app.id);
				var btnText = app.running ?	 "Stop" : "Run";
				if (btn.is(":disabled"))
					restore_finding(btn, btnText);
				else
					btn.parent().find(".ui-btn-text")[0].innerHTML = btnText;

				setTheme(btn.parent(), app.running ? "c" : "f");
				if (app.running)
					btn.parent().find(".ui-icon").removeClass("ui-icon-check").addClass("ui-icon-delete");
				else
					btn.parent().find(".ui-icon").removeClass("ui-icon-delete").addClass("ui-icon-check");
				
				if (app.logs.length == 1 && app.logs[0].filetime > app_cache[app.id].logs[0].filetime) {
					for (var i = 0; i < Math.min(app_cache[app.id].logs.length, 19) ; i++)
						app.logs.push(app_cache[app.id].logs[i]);
				}
				else
						app.logs = app_cache[app.id].logs;
				
				app_cache[app.id] = app;

				$("#log-" + app.id).html(generateLogTableHtml(app));
			}
		}
		function generateLogTableHtml(app) {
			var html = "";
			for (var j = 0; j < app.logs.length; j++) {
				html += ('<tr class="{0}"><td>{1}</td><td class="logtype-{3}">{2}</td></tr>').format(j % 2 == 0 ? "even" : "odd", app.logs[j].time, app.logs[j].msg, app.logs[j].type);
			}
			return html;
		}
		
		function createEntry(app) {
			var html = "";
			html += ('<div id="app-{3}" data-role="collapsible" data-theme="{0}"><h3><span id=appname-{3}>{1}</span><span id=appstat-{3}>{2}</span></h3>')
						.format(app.lastError != null ? "e" : (app.running ? "f" : "c"), app.name, titleMsg(app), app.id);

			html += ('<button data-inline=1 id="runstop-{0}" data-corners=0 data-shadow=0 data-theme="{2}" onclick="togglerun($(this), \'{0}\'); return false;"  data-icon="{3}">{1}</button>')
						.format(app.id, app.running ? "Stop" : "Run", app.running ? "c" : "f", app.running ? "delete" : "check");

			html += ('<table class="display" id="log-{0}" cellspacing=0 cellpadding=0 border=0>').format(app.id);
			html += generateLogTableHtml(app);
			html += '</table>';
			html += ('<button data-inline=1 id="config-{0}" data-shadow=0 data-theme="b" onclick="configure($(this), \'{0}\'); return false;"  data-icon="gear">Configure...</button>').format(app.id);
			html += ('<button data-inline=1 id="remove-{0}" data-shadow=0 data-theme="d" onclick="uninstall($(this), \'{0}\'); return false;"  data-icon="delete">Uninstall</button>').format(app.id);
			html += ('<button data-inline=1 id="dowload-{0}" data-shadow=0 data-theme="d" onclick="downloadLog(\'{0}\'); return false;"  data-icon="arrow-d">Download Log</button>').format(app.id);
			html += ('<button data-inline=1 id="editcode-{0}" data-shadow=0 data-theme="d" onclick="openCode(\'{0}\'); return false;"  data-icon="gear">View Code</button>').format(app.snippetId);
			html += "</div>";
			$(html).appendTo("#applist").trigger('create');
			$("#app-" + app.id).collapsible();
		}
		function downloadLog(appid) {
			window.location = WSROOT + "ethDownloadAppLog.aspx?id=" + appid;
		}
		function openCode(snippetid) {
			window.open("AppCoder.html?" + snippetid, "_blank");
		}
		var getNextUpdate_xhr = null;
		function getNextUpdate() {
			if (getNextUpdate_xhr) return;

			getNextUpdate_xhr = $.ajax({
				url: WSROOT + "ethComet.asmx?op=GetNextAppUpdate",
				dataType: "xml",
				contentType: "text/xml; charset=\"utf-8\"",
				beforeSend: function (xhr) { xhr.setRequestHeader("SOAPAction", '"http://mytaglist.com/ethComet/GetNextAppUpdate"'); },
				data: '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetNextUpdate xmlns="http://mytaglist.com/ethComet" /></soap:Body></soap:Envelope>',
				error: function (xhr, textStatus, exception) {
					getNextUpdate_xhr = null;
					if (textStatus != "abort" && xhr.responseText)
						popup_error(xhr, $.mobile.pageContainer);
					else if (should_run_comet) {
						getNextUpdate();
					}
				},
				success: function (retval, textStatus, xhr) {
					getNextUpdate_xhr = null;
					getNextUpdate();

					var result = $(xhr.responseXML).find('GetNextAppUpdateResult');
					if (result.length && result.attr("xsi:nil") != "true") {
						var retval = eval("(" + result.text() + ")");
						if (retval != null) {
							$("#cpuUsage").text(Math.round(retval.cpuUsage * 100) + "%");
							$("#memoryUsage").text(Math.round(retval.memoryUsage/1000) + "kB");
							for (var i = 0; i < retval.updates.length; i++)
								updateEntry(retval.updates[i]);
						}
					}
				}
			});

		}
		$("#appListPage").live('pagehide', function () {
			should_run_comet = false;
			if (getNextUpdate_xhr) {
				getNextUpdate_xhr.abort();
				getNextUpdate_xhr = null;
			}
		});

		$("#appListPage").bind('pageshow', function () {
			should_run_comet = true;
			$.ajax({
				url: WSROOT + "ethSnippets.asmx/ScriptStatsForUser",
				data: "{}",
				success: function (retval, textStatus) {
					for (var i = 0; i < retval.d.entries.length; i++) {
						var app = retval.d.entries[i];
						updateEntry(app);
					}
					$("#cpuUsage").text(Math.round(retval.d.cpuUsage * 100) + "%");
					$("#memoryUsage").text(Math.round(retval.d.memoryUsage / 1000) + "kB");

					//$("#tagmanagerlist").html(html).trigger('create');
					$("#applist").resetRoundedEdges();
					getNextUpdate();
				},
				error: function (xhr, textStatus, exception) {
					popup_error(xhr, null);
				}
			});
		});

		function uninstall(btn, appid) {
			var oldhtml = show_finding(btn, "Removing...");
			$.ajax({
				url: WSROOT + "ethSnippets.asmx/RemoveScript",
				data: "{id: '" + appid + "'}",
				complete: function () { restore_finding(btn, oldhtml); },
				success: function (retval) {
					$('#app-' + appid).remove();
					$("#applist").resetRoundedEdges();
				}
			});
		}
		function togglerun(btn, appid) {
			var oldhtml = show_finding(btn, app_cache[appid].running?"Stopping...":"Starting...");
			$.ajax({
				url: WSROOT + "ethSnippets.asmx/EnableScript",
				data:  JSON.stringify({id: appid, enable: !app_cache[appid].running}),
				success: function (retval) {

				}
			});
		}
		function configure(btn, appid) {
			var oldhtml = show_finding(btn, "Loading...");
			$.ajax({
				url: WSROOT + "ethSnippets.asmx/LoadScriptID",
				data: "{scriptId: '" + appid + "'}",
				complete: function () { restore_finding(btn, oldhtml); },
				success: function (retval) {
					loadConfig(retval.d);
				}
			});
		}

		var taglist_cache = {};
		var available_types;
		$.ajax({
			url: WSROOT + "ethClient.asmx/GetTagListCached",
			data: "{}",
			success: function (retval, textStatus) {
				available_types = {};
				for (var i = 0; i < retval.d.length; i++) {
					var tag = retval.d[i];
					taglist_cache[tag.uuid] = tag;
					available_types[tag.tagType] = 1;
				}
			},
			error: function (xhr, textStatus, exception) {
				if (xhr.responseText.toLowerCase().indexOf("unauthorized")!=-1 || exception.toLowerCase().indexOf("unauthorized")!=-1 || xhr.responseText.toLowerCase().indexOf("authentication failed")!=-1)
					location.replace("signin.html?redirect=app.html");
				else
					popup_error(xhr, null);
			}
		});

		var typeNames = {
			12: "Motion Tag", 32: "Water Sensor", 33: "External Thermister", 42: "Current Sensor", 52: "Door/Window Sensor", 62: "Kumostat", 72: "PIR Sensor"
		};
		function requiredTagTypesTitle(snippet) {
			return snippet.requiredTypes.map(function (x) { return typeNames[x]; }).join(", ");
		}
		function supportedTypesTitle(assignment) {
			return assignment.supportedTypes.map(function (x) { return typeNames[x]; }).join(", ");
		}

		var snippets = {};
		$("#addnewApp").live('pagebeforeshow', function () {
			$.ajax({
				url: WSROOT + "ethSnippets.asmx/GetSnippets",
				data: JSON.stringify({ availableTypes: Object.keys(available_types) }),
				success: function (retval, textStatus) {
					var title, html = "";
					for (var i = 0; i < retval.d.length; i++) {
						var snippet = retval.d[i];
						snippets[snippet.id] = snippet;
						var newTitle = requiredTagTypesTitle(snippet);
						if (newTitle != title) {
							html += ('<li data-role="list-divider">{0}</li>').format(newTitle.length>0?"Requires "+newTitle:"Works with all types of tags");
							title = newTitle;
						}
						html += ('<li><a href="#" onclick="loadConfig(snippets[\'{4}\']);" data-rel="dialog" ><h2>{0}</h2><p>coded by {1} updated at {2}</p><p class="ui-li-aside"><strong>{3} installed</strong></p></a></li>')
							.format(snippet.description, snippet.author, snippet.updated, snippet.installed, snippet.id);
					}
					$("#snippetList").html(html).listview("refresh");
				},
				error: function (xhr, textStatus, exception) {
					popup_error(xhr, btn);
				}
			});
		});

	</script> 

</body>
</html>