File: 0.01.0a/js/freedesk.js (View as HTML)

  1: /* -------------------------------------------------------------
  2: This file is part of FreeDESK
  3: 
  4: FreeDESK is (C) Copyright 2012 David Cutting
  5: 
  6: FreeDESK is free software: you can redistribute it and/or modify
  7: it under the terms of the GNU General Public License as published by
  8: the Free Software Foundation, either version 3 of the License, or
  9: (at your option) any later version.
 10: 
 11: FreeDESK is distributed in the hope that it will be useful,
 12: but WITHOUT ANY WARRANTY; without even the implied warranty of
 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14: GNU General Public License for more details.
 15: 
 16: You should have received a copy of the GNU General Public License
 17: along with FreeDESK.  If not, see www.gnu.org/licenses
 18: 
 19: For more information see www.purplepixie.org/freedesk/
 20: -------------------------------------------------------------- */
 21: 
 22: /**
 23:  * The main FreeDESK JavaScript client-side code
 24: **/
 25: 
 26: function FreeDESK()
 27: {
 28: 	this.sid = ""; // Session ID
 29: 	
 30: 	// Statuses of requests (text)
 31: 	this.requestStatus = new Array();
 32: 	// Priorities of requests (text)
 33: 	this.requestPriority = new Array();
 34: 	// List of display fields for request list
 35: 	this.fieldList = new Array();
 36: 
 37: 	// XML of last request list fetched (for redisplay)
 38: 	this.lastListXML = null;
 39: 	
 40: 	// Last Request Details
 41: 	this.lastTeam = 0;
 42: 	this.lastUser = "";
 43: 	
 44: 	// Last subpage
 45: 	this.lastSubpage = "";
 46: 	this.lastSubpageOpts = "";
 47: 	
 48: 	// Sort Criteria
 49: 	this.sortField = "requestid";
 50: 	this.sortOrder = "D";
 51: 	
 52: 	// Refresh Event
 53: 	this.refreshEvent = null;
 54: 
 55: 	// Login Support
 56: 	this.login_action = function(responseXML)
 57: 	{
 58: 		//alert(responseXML);
 59: 		//var txt = document.createTextNode(responseXML);
 60: 		//document.getElementById("login_content").appendChild(txt);
 61: 		if (DESK.isError(responseXML))
 62: 		{
 63: 			DESK.show_login(DESK.getError(responseXML));
 64: 		}
 65: 		else
 66: 		{
 67: 			var newsid = responseXML.getElementsByTagName("sid")[0].childNodes[0].nodeValue;
 68: 			if (DESK.sid == "") // no current session so reload to index
 69: 			{
 70: 				//var loc = "./?sid="+newsid;
 71: 				//window.location.href = loc;
 72: 				document.forms['login_sid_form'].elements['sid'].value = newsid;
 73: 				document.forms['login_sid_form'].submit();
 74: 			}
 75: 			else
 76: 			{
 77: 				DESK.sid = newsid;
 78: 				DESK.hide_login();
 79: 				// Any other actions?
 80: 			}
 81: 		}
 82: 	}
 83: 
 84: 	this.login_click=function()
 85: 	{
 86: 		var req = new ServerRequest();
 87: 		req.url = "api.php?mode=login&type=user&username="
 88: 			+document.getElementById("login_username").value
 89: 			+"&password="
 90: 			+document.getElementById("login_password").value;
 91: 		req.callback = DESK.login_action;
 92: 		req.Get();
 93: 	}
 94: 	
 95: 	this.show_login=function(errormsg)
 96: 	{
 97: 		this.backdrop(true);
 98: 		var txt = "";
 99: 		if (errormsg !== undefined)
100: 			txt=errormsg;
101: 		document.getElementById("login_message").innerHTML = txt;
102: 		document.getElementById("login_form").style.display = "block";
103: 	}
104: 	
105: 	this.hide_login=function()
106: 	{
107: 		document.getElementById("login_form").style.display = "none";
108: 		document.getElementById("login_message").style.display = "none";
109: 		this.backdrop(false);
110: 	}
111: 
112: 	// Logout
113: 	this.logout_click=function()
114: 	{
115: 		var req = new ServerRequest();
116: 		req.url="api.php?mode=logout&sid="+this.sid;
117: 		req.callback = DESK.logout_action;
118: 		req.Get();
119: 	}
120: 	
121: 	this.logout_action=function()
122: 	{
123: 		window.location.href="./";
124: 	}
125: 
126: 	// Show/Hide Backdrop
127: 	this.backdrop = function(set)
128: 	{
129: 		var bd = document.getElementById("screen_backdrop");
130: 		if (set === undefined) // toggle
131: 		{
132: 			if (bd.style.display == "block")
133: 				set = false;
134: 			else
135: 				set = true;
136: 		}
137: 		
138: 		if (set)
139: 			bd.style.display = "block";
140: 		else
141: 			bd.style.display = "none";
142: 	}
143: 	
144: 	// Check for errors
145: 	this.isError = function(xml)
146: 	{
147: 		//alert(xml);
148: 		//alert(xml.documentElement);
149: 		//alert(xml.documentElement.tagName);
150: 		//alert(xml.getElementsByTagName("error")[0].childNodes[0].nodeValue);
151: 		if (xml.documentElement.tagName == "error")
152: 		//if (xml.getElementsByTagName("error").length > 0)
153: 			return true;
154: 		return false;
155: 	}
156: 	
157: 	this.getError = function(xml)
158: 	{
159: 		var out = xml.getElementsByTagName("code")[0].childNodes[0].nodeValue;
160: 		out += ": ";
161: 		out += xml.getElementsByTagName("text")[0].childNodes[0].nodeValue;
162: 		return out;
163: 	}
164: 	
165: 	this.getErrorCode = function(xml)
166: 	{
167: 		return xml.getElementsByTagName("code")[0].childNodes[0].nodeValue;
168: 	}
169: 	
170: 	// Display main or sub page (true for main, false for sub)
171: 	this.displayMain = function(disp)
172: 	{
173: 		if (disp)
174: 		{
175: 			document.getElementById("subpage").style.display="none";
176: 			document.getElementById("mainpage").style.display="block";
177: 		}
178: 		else
179: 		{
180: 			document.getElementById("mainpage").style.display="none";
181: 			document.getElementById("subpage").style.display="block";
182: 		}
183: 	}
184: 	
185: 	// Load sub-pages
186: 	this.displaySubpage = function(text)
187: 	{
188: 		document.getElementById("subpage").innerHTML = text;
189: 		DESK.displayMain(false);
190: 	}
191: 	
192: 	this.loadSubpage = function(page, opts)
193: 	{
194: 		if (opts == undefined)
195: 			var opts = "";
196: 		this.lastSubpage = page;
197: 		this.lastSubpageOpts = opts;
198: 		var sr = new ServerRequest();
199: 		sr.xmlrequest=false;
200: 		sr.url = "page.php?page="+page;
201: 		if (opts != "")
202: 			sr.url += "&"+opts;
203: 		sr.url += "&sid="+this.sid;
204: 		
205: 		sr.callback = DESK.displaySubpage;
206: 		sr.Get();
207: 	}
208: 	
209: 	// Refresh the subpage
210: 	this.refreshSubpage = function()
211: 	{
212: 		DESK.loadSubpage(DESK.lastSubpage, DESK.lastSubpageOpts);
213: 	}
214: 	
215: 	// Load a Request List to the Main Pane
216: 	this.mainPane = function(teamid, username)
217: 	{
218: 		if (teamid == undefined)
219: 			var teamid = 0;
220: 		if (username == undefined)
221: 			var username="";
222: 		//alert(teamid+" "+username);
223: 		var sr = new ServerRequest();
224: 		this.lastTeam = teamid;
225: 		this.lastUser = username;
226: 		
227: 		sr.url = "api.php?mode=requests_assigned&teamid="+teamid+"&username="+username;
228: 		if (this.sortField != "")
229: 		{
230: 			sr.url += "&sort="+this.sortField;
231: 			sr.url += "&order="+this.sortOrder;
232: 		}
233: 		sr.url += "&sid="+this.sid;
234: 		sr.callback = DESK.mainPaneDisplay;
235: 		sr.Get();
236: 	}
237: 	
238: 	// Refresh the Main Pane
239: 	this.mainPaneRefresh = function()
240: 	{
241: 		DESK.mainPane(DESK.lastTeam, DESK.lastUser);
242: 	}
243: 	
244: 	// Display a request list in the main pane
245: 	this.mainPaneDisplay = function(xml)
246: 	{
247: 		DESK.lastListXML = xml;
248: 		var table = document.createElement("table"); // table for results
249: 		table.border=0;
250: 		table.width="100%";
251: 		table.className="requestList";
252: 		
253: 		var container = document.getElementById('mainright');
254: 		
255: 		if (container.hasChildNodes())
256: 		{
257: 			while(container.childNodes.length >= 1)
258: 				container.removeChild(container.firstChild);
259: 		}
260: 		
261: 		var requests = xml.getElementsByTagName("request");
262: 		
263: 		if (requests.length <= 0)
264: 		{
265: 			container.innerHTML = "<h3>No Requests Found</h3>";
266: 			return;
267: 		}
268: 		
269: 		container.appendChild(table);
270: 		
271: 		var title = table.insertRow(0);
272: 		title.className = "requestListTitle";
273: 		for (var i=0; i<DESK.fieldList.length; ++i)
274: 		{
275: 			if (DESK.fieldList[i][1] == 1) // displayed field
276: 			{
277: 				var cell = title.insertCell(-1);
278: 				
279: 				var fieldTitle = DESK.fieldList[i][0];
280: 				
281: 				var link = "<a href=\"#\" onclick=\"DESK.mainPaneSort('"+DESK.fieldList[i][2]+"');\">";
282: 				link += fieldTitle;
283: 				
284: 				if (DESK.sortField == DESK.fieldList[i][2])
285: 					if (DESK.sortOrder == "D")
286: 						link+=" &gt;";
287: 					else
288: 						link+=" &lt;";
289: 						
290: 				link+"</a>";
291: 				
292: 				cell.innerHTML = link;
293: 			}
294: 		}
295: 		
296: 		for (var req=0; req<requests.length; ++req)
297: 		{
298: 			var request = requests[req];
299: 			var row = table.insertRow(table.getElementsByTagName("tr").length);
300: 			row.className="requestList";
301: 			
302: 			for (var fc=0; fc<DESK.fieldList.length; ++fc)
303: 			{
304: 				var field = DESK.fieldList[fc];
305: 				if (field[1] == 1) // display this field
306: 				{
307: 					var contents = "";
308: 					var data = request.getElementsByTagName(field[2])[0];
309: 					if (!data) // no field data of this form returned
310: 					{
311: 						contents="&nbsp;-";
312: 					}
313: 					else
314: 					{
315: 						contents = (data.textContent == undefined) ? data.firstChild.nodeValue : data.textContent;
316: 						if (field[2]=="status")
317: 							contents = DESK.requestStatus[contents];
318: 						else if (field[2]=="priority")
319: 							contents = DESK.requestPriority[contents];
320: 						else if (field[2]=="requestid")
321: 						{
322: 							var id = contents;
323: 							contents = "<a href=\"#\" onclick=\"DESK.displayRequest("+contents+");\">"+contents+"</a>";
324: 							// row.onclick = function(){ return DESK.displayRequest(id); }; // Always uses last - TODO fix it
325: 						}
326: 					}
327: 					
328: 					var cell = row.insertCell(-1);
329: 					cell.innerHTML = contents;
330: 				}
331: 			}
332: 		}
333: 	}
334: 	
335: 	// Set main pane sort
336: 	this.mainPaneSort = function(field)
337: 	{
338: 		if (DESK.sortField == field)
339: 		{
340: 			if (DESK.sortOrder == "D")
341: 				DESK.sortOrder = "A";
342: 			else
343: 				DESK.sortOrder = "D";
344: 		}
345: 		else
346: 		{
347: 			DESK.sortField = field;
348: 			DESK.sortOrder = "D";
349: 		}
350: 		DESK.mainPane(DESK.lastTeam, DESK.lastUser);
351: 	}
352: 	
353: 	// Option Displays for Main Page
354: 	this.optionDisplay = function(opt)
355: 	{
356: 		if (opt == 1)
357: 		{
358: 			document.getElementById('option_select').style.display = "none";
359: 			
360: 			var container = document.getElementById('option_dialog');
361: 			
362: 			if (container.hasChildNodes())
363: 			{
364: 				while(container.childNodes.length >= 1)
365: 					container.removeChild(container.firstChild);
366: 			}
367: 			
368: 			for (var i=0; i<this.fieldList.length; ++i)
369: 			{
370: 				var displayed = false;
371: 				if (this.fieldList[i][1]==1)
372: 					displayed=true;
373: 				var a = "";
374: 				if (displayed)
375: 					a += "<b>";
376: 				a += "<a href=\"#\" onclick=\"DESK.setFieldDisplay("+i+",";
377: 				if (displayed)
378: 					a+="0";
379: 				else
380: 					a+="1";
381: 				a+="); DESK.optionDisplay(1);\">";
382: 				//container.innerHTML += a;
383: 				a += this.fieldList[i][0];
384: 				a += "</a>";
385: 				if (displayed)
386: 					a += "</b>";
387: 				container.innerHTML += a;
388: 				container.innerHTML += "<br />";
389: 			}
390: 				
391: 				
392: 			container.innerHTML += "<br /><a href=\"#\" onclick=\"DESK.optionDisplay(0); DESK.mainPaneDisplay(DESK.lastListXML);\">Close and Apply</a>";
393: 			
394: 			container.style.display = "block";
395: 		}
396: 		else
397: 		{
398: 			document.getElementById('option_dialog').style.display = "none";
399: 			document.getElementById('option_select').style.display = "block";
400: 		}
401: 	}
402: 		
403: 	// Set a fieldDisplay property
404: 	this.setFieldDisplay = function(index, setting)
405: 	{
406: 		this.fieldList[index][1]=setting;
407: 	}
408: 	
409: 	// Display a Request
410: 	this.displayRequest = function(id)
411: 	{
412: 		var url = "request.php?id="+id+"&sid="+DESK.sid;
413: 		DESK.openWindow("FreeDESK Request", url);
414: 	}
415: 	
416: 	// Open a Window
417: 	this.openWindow = function(windowname, url, xsize, ysize, resizeable)
418: 	{
419: 		//alert(url);
420: 		if (xsize == undefined)
421: 			var xsize = 700;
422: 		if (ysize == undefined)
423: 			var ysize = 500;
424: 			
425: 		if (resizeable == undefined)
426: 			var resizeable = 1;
427: 		else if(resizeable)
428: 			resizeable=1;
429: 		else if(!resizeable)
430: 			resizable=0;
431: 		
432: 		var windowopts = "location=0,status=0,scrollbars=1,toolbar=0,width="+xsize+",height="+ysize+",resizeable="+resizeable;
433: 		
434: 		window.open(url, '', windowopts);
435: 	}
436: 	
437: 	// Perform an entity search
438: 	this.entitySearch = function(entity, callback, fields)
439: 	{
440: 		var url = "entity.php?mode=search&entity="+entity;
441: 		
442: 		if (callback != undefined)
443: 			url += "&callback="+callback;
444: 		if (fields != undefined)
445: 		{
446: 			for (var i=0; i<fields.length; ++i)
447: 			{
448: 				url += "&" + fields[i][0] + "=" + fields[i][1]; // escape?
449: 			}
450: 		}
451: 		url += "&sid=" + this.sid;
452: 		
453: 		this.openWindow("Search "+entity, url);
454: 	}
455: 	
456: 	// Open Edit Entity
457: 	this.editEntity = function(entity, keyfield, keyfieldValue)
458: 	{
459: 		var url = "entity.php?mode=edit&entity="+entity+"&keyfield="+keyfield+"&value="+keyfieldValue;
460: 		url += "&sid=" + this.sid;
461: 	
462: 		this.openWindow("Edit "+entity, url);
463: 	}
464: 	
465: 	// Perform an entity creation
466: 	this.entityCreate = function(entity, callback)
467: 	{
468: 		var url = "entity.php?mode=create&entity="+entity;
469: 		
470: 		if (callback != undefined)
471: 			url += "&callback="+callback;
472: 			
473: 		url += "&sid=" + this.sid;
474: 		
475: 		this.openWindow("Create "+entity, url);
476: 	}
477: 	
478: 	// Convert form to query string
479: 	this.formToQuery = function(formid)
480: 	{
481: 		var data = "";
482: 		
483: 		function add(name, value)
484: 		{
485: 			if (value == undefined)
486: 				var value = "";
487: 			
488: 			data += (data.length > 0 ? "&" : ""); // add & if required
489: 		
490: 			data += escape(name).replace(/\+/g, "%2B") + "=";
491: 		
492: 			data += escape(value).replace(/\+/g, "%2B");
493: 		}
494: 		
495: 		var form = document.forms[formid];
496: 		if (!form)
497: 			return "";
498: 		var elements = form.elements;
499: 		
500: 		for (var i=0; i<elements.length; ++i)
501: 		{
502: 			var element = elements[i];
503: 			var type = element.type.toLowerCase();
504: 			var name = element.name;
505: 			
506: 			if (name)
507: 			{
508: 				if (	type == "text" || type == "password" ||
509: 						type == "button" || type == "reset" ||
510: 						type == "file" || type == "submit" ||
511: 						type == "image" || type == "hidden"	|| 
512: 						type == "textarea" )
513: 					add(name, element.value);
514: 					
515: 				else if ( type == "checkbox" && element.checked )
516: 					add(name, element.value ? element.value : "On");
517: 				
518: 				else if ( type == "radio" && element.checked)
519: 					add(name, element.value);
520: 				
521: 				else if ( type.indexOf("select") != -1 )
522: 				{
523: 					for (var x=0; x<element.options.length; ++x)
524: 					{
525: 						var opt = element.options[x];
526: 						if (opt.selected)
527: 							add(name, opt.value ? opt.value : opt.text);
528: 					}
529: 				}
530: 			}
531: 		}
532: 		
533: 		return data;
534: 	}
535: 	
536: 	// API Form Action e.g. save entity
537: 	this.formAPI = function(formid, closeOnComplete, reloadOnComplete, callbackOnComplete)
538: 	{
539: 		if (closeOnComplete == undefined)
540: 			var closeOnComplete = false;
541: 		if (reloadOnComplete == undefined)
542: 			var reloadOnComplete = false;
543: 		if (callbackOnComplete == undefined)
544: 			var callbackOnComplete = false;
545: 		
546: 		
547: 		var q = DESK.formToQuery(formid);
548: 		
549: 		q += "&sid=" + DESK.sid;
550: 		
551: 		var sr = new ServerRequest();
552: 		sr.url = "api.php";
553: 		sr.callback = DESK.formAPIcallback;
554: 		sr.additional = new Array();
555: 		sr.additional[0] = closeOnComplete;
556: 		sr.additional[1] = reloadOnComplete;
557: 		sr.additional[2] = callbackOnComplete;
558: 		sr.Post(q);
559: 	}
560: 	
561: 	this.formAPIcallback = function(xml, additional)
562: 	{
563: 		if (DESK.isError(xml))
564: 		{
565: 			Alerts.add(DESK.getError(xml), 2, 10);
566: 		}
567: 		else
568: 		{
569: 			// We got this far - no DESK error or XML error so we can say success!
570: 			Alerts.add("Operation Successful", 0);
571: 			
572: 			if (additional[2]) // do the javascript first if there is any
573: 			{	
574: 				additional[2](xml);
575: 			}
576: 			
577: 			if (additional[0])
578: 				window.close();
579: 			else if (additional[1])
580: 				window.location.reload();
581: 		}
582: 	}
583: 	
584: 	// Switch a pane
585: 	this.paneSwitch = function(pid, oid)
586: 	{
587: 		var pane = document.getElementById("pane_"+pid);
588: 		var header = document.getElementById("pane_"+pid+"_header");
589: 		
590: 		var child = header.firstChild;
591: 		
592: 		var spans = header.getElementsByTagName("SPAN");
593: 		
594: 		for (var i=0; i<spans.length; ++i)
595: 		{
596: 			var arr = spans[i].id.split("_");
597: 			var opt = arr[arr.length-1];
598: 			
599: 			var contentid = "pane_"+pid+"_"+opt+"_content";
600: 			
601: 			if (oid == opt)
602: 				spans[i].className = "pane_option_selected";
603: 			else
604: 				spans[i].className = "pane_option";
605: 			// Always hide the divs to avoid duplicate display
606: 			document.getElementById(contentid).className = "pane_content_hidden";
607: 		}
608: 		
609: 		var contentid = "pane_"+pid+"_"+oid+"_content";
610: 		document.getElementById(contentid).className = "pane_content";
611: 		
612: 	}
613: 	
614: 	// Open new create request window
615: 	this.createRequest = function(reqclass)
616: 	{
617: 		if (reqclass == undefined)
618: 			var reqclass = "";
619: 		
620: 		var url = "request.php?";
621: 		if (reqclass != "")
622: 			url += "class="+reqclass+"&";
623: 		url += "sid="+DESK.sid;
624: 		
625: 		DESK.openWindow("FreeDESK Request", url);
626: 	}
627: 	
628: 	// Debug data output
629: 	this.debugData = function(container, session)
630: 	{
631: 		var out = "<b>Client-Side JavaScript Debug</b><br /><br />";
632: 		if (session == DESK.sid)
633: 			out += "Session IDs match server and client side<br /><br />";
634: 		else
635: 			out += "Session ID mis-match between client and server<br /><br />";
636: 			
637: 		document.getElementById(container).innerHTML = out;
638: 	}
639: 	
640: 	// Relogin (use current SID and re-post login form)
641: 	this.relogin = function()
642: 	{
643: 		document.forms['login_sid_form'].elements['sid'].value = DESK.sid;
644: 		document.forms['login_sid_form'].submit();
645: 	}
646: 	
647: 	// Start Auto-refreshing
648: 	this.startRefresh = function(interval)
649: 	{
650: 		if (interval == undefined)
651: 			var interval = 30;
652: 		interval = interval * 1000;
653: 		
654: 		DESK.refreshEvent = setInterval(
655: 			function(){ DESK.mainPaneRefresh(); },
656: 			interval );
657: 	}
658: 	
659: 	// Stop Auto-refreshing
660: 	this.stopRefresh = function()
661: 	{
662: 		clearInterval(DESK.refreshEvent);
663: 	}
664: 	
665: 	this.toSeconds = function(hours, minutes, seconds)
666: 	{
667: 		var totalSeconds = (hours * 60 * 60);
668: 		totalSeconds += (minutes * 60);
669: 		totalSeconds += (seconds);
670: 		return totalSeconds;
671: 	}
672: 	
673: 	this.toHMS = function(totalSeconds)
674: 	{
675: 		var hours = 0;
676: 		var minutes = 0;
677: 		var seconds = 0;
678: 		
679: 		if (totalSeconds >= 60*60)
680: 		{
681: 			hours = (totalSeconds/(60*60));
682: 			totalSeconds -= (hours*60*60);
683: 		}
684: 		
685: 		if (totalSeconds >= 60)
686: 		{
687: 			minutes = (totalSeconds/60);
688: 			totalSeconds -= (minutes*60);
689: 		}
690: 		
691: 		seconds = totalSeconds;
692: 		
693: 		var out = new Array();
694: 		
695: 		out[0] = hours;
696: 		out[1] = minutes;
697: 		out[2] = seconds;
698: 		
699: 		return out;
700: 	}
701: 		
702: 	
703: 	// Convert Form H:M:S fields to seconds field
704: 	this.formToSeconds = function(formid, hField, mField, sField, secondsField)
705: 	{
706: 		var hours = (document.forms[formid][hField].value == "") ? 0 : parseInt(document.forms[formid][hField].value);
707: 		var minutes = (document.forms[formid][mField].value == "") ? 0 : parseInt(document.forms[formid][mField].value);
708: 		var seconds = (document.forms[formid][sField].value == "") ? 0 : parseInt(document.forms[formid][sField].value);
709: 		document.forms[formid][secondsField].value = this.toSeconds(hours, minutes, seconds);
710: 	}
711: 	
712: 	// Convert Form Seconds to H:M:S
713: 	this.formToHMS = function(formid, hField, mField, sField, secondsField)
714: 	{
715: 		var hms = this.toHMS(parseInt(document.forms[formid][secondsField].value));
716: 		document.forms[formid][hField].value = hms[0];
717: 		document.forms[formid][mField].value = hms[1];
718: 		document.forms[formid][sField].value = hms[2];
719: 	}
720: 	
721: 	// Go to the mobile interface
722: 	this.goMobile = function()
723: 	{
724: 		window.location.href = "mobile/?sid="+DESK.sid;
725: 	}
726: }
727: 
728: var DESK = new FreeDESK();
729: 
730: