summaryrefslogtreecommitdiff
path: root/framework/Web/Javascripts/prado
diff options
context:
space:
mode:
Diffstat (limited to 'framework/Web/Javascripts/prado')
-rw-r--r--framework/Web/Javascripts/prado/ajax.js425
-rw-r--r--framework/Web/Javascripts/prado/controls.js167
-rw-r--r--framework/Web/Javascripts/prado/datepicker.js656
-rw-r--r--framework/Web/Javascripts/prado/prado.js51
-rw-r--r--framework/Web/Javascripts/prado/validation.js751
-rw-r--r--framework/Web/Javascripts/prado/validators.js225
6 files changed, 2275 insertions, 0 deletions
diff --git a/framework/Web/Javascripts/prado/ajax.js b/framework/Web/Javascripts/prado/ajax.js
new file mode 100644
index 00000000..06c3d741
--- /dev/null
+++ b/framework/Web/Javascripts/prado/ajax.js
@@ -0,0 +1,425 @@
+/**
+ * Prado AJAX service. The default service provider is JPSpan.
+ */
+Prado.AJAX = { Service : 'Prototype' };
+
+/**
+ * Parse and execute javascript embedded in html.
+ */
+Prado.AJAX.EvalScript = function(output)
+{
+
+ var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
+ var scripts = output.match(match);
+ if (scripts)
+ {
+ match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
+ setTimeout((function()
+ {
+ for (var i = 0; i < scripts.length; i++)
+ eval(scripts[i].match(match)[1]);
+ }).bind(this), 50);
+ }
+}
+
+
+/**
+ * AJAX service request using Prototype's AJAX request class.
+ */
+Prado.AJAX.Request = Class.create();
+Prado.AJAX.Request.prototype = Object.extend(Ajax.Request.prototype,
+{
+ /**
+ * Evaluate the respond JSON data, override parent implementing.
+ * If default eval fails, try parsing the JSON data (slower).
+ */
+ evalJSON: function()
+ {
+ try
+ {
+ var json = this.transport.getResponseHeader('X-JSON'), object;
+ object = eval(json);
+ return object;
+ }
+ catch (e)
+ {
+ if(isString(json))
+ {
+ return Prado.AJAX.JSON.parse(json);
+ }
+ }
+ },
+
+ respondToReadyState: function(readyState) {
+ var event = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+
+ if(event == 'Complete' && transport.status)
+ Ajax.Responders.dispatch('on' + transport.status, this, transport, json);
+
+ (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + event, this, transport, json);
+
+ if (event == 'Complete')
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+
+
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+ if (event == 'Complete')
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+
+});
+
+Prado.AJAX.Error = function(e, code)
+{
+ e.name = 'Prado.AJAX.Error';
+ e.code = code;
+ return e;
+}
+
+/**
+ * Post data builder, serialize the data using JSON.
+ */
+Prado.AJAX.RequestBuilder = Class.create();
+Prado.AJAX.RequestBuilder.prototype =
+{
+ initialize : function()
+ {
+ this.body = '';
+ this.data = [];
+ },
+ encode : function(data)
+ {
+ return Prado.AJAX.JSON.stringify(data);
+ },
+ build : function(data)
+ {
+ var sep = '';
+ for ( var argName in data)
+ {
+ if(isFunction(data[argName])) continue;
+ try
+ {
+ this.body += sep + argName + '=';
+ this.body += encodeURIComponent(this.encode(data[argName]));
+ } catch (e) {
+ throw Prado.AJAX.Error(e, 1006);
+ }
+ sep = '&';
+ }
+ },
+
+ getAll : function()
+ {
+ this.build(this.data);
+ return this.body;
+ }
+}
+
+
+Prado.AJAX.RemoteObject = function(){};
+
+/**
+ * AJAX service request for Prado RemoteObjects
+ */
+Prado.AJAX.RemoteObject.Request = Class.create();
+Prado.AJAX.RemoteObject.Request.prototype = Object.extend(Prado.AJAX.Request.prototype,
+{
+ /**
+ * Initialize the RemoteObject Request, overrides parent
+ * implementation by delaying the request to invokeRemoteObject.
+ */
+ initialize : function(options)
+ {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.post = new Prado.AJAX.RequestBuilder();
+ },
+
+ /**
+ * Call the remote object,
+ * @param string the remote server url
+ * @param array additional arguments
+ */
+ invokeRemoteObject : function(url, args)
+ {
+ this.initParameters(args);
+ this.options.postBody = this.post.getAll();
+ this.request(url);
+ },
+
+ /**
+ * Set the additional arguments as post data with key '__parameters'
+ */
+ initParameters : function(args)
+ {
+ this.post.data['__parameters'] = [];
+ for(var i = 0; i<args.length; i++)
+ this.post.data['__parameters'][i] = args[i];
+ }
+});
+
+/**
+ * Base proxy class for Prado RemoteObjects via AJAX.
+ * e.g.
+ * <code>
+ * var TestObject1 = Class.create();
+ * TestObject1.prototype = Object.extend(new Prado.AJAX.RemoteObject(),
+ * {
+ * initialize : function(handlers, options)
+ * {
+ * this.__serverurl = 'http://127.0.0.1/.....';
+ * this.baseInitialize(handlers, options);
+ * }
+ *
+ * method1 : function()
+ * {
+ * return this.__call(this.__serverurl, 'method1', arguments);
+ * }
+ * });
+ *</code>
+ * And client usage,
+ * <code>
+ * var test1 = new TestObject1(); //create new remote object
+ * test1.method1(); //call the method, no onComplete hook
+ *
+ * var onComplete = { method1 : function(result){ alert(result) } };
+ * //create new remote object with onComplete callback
+ * var test2 = new TestObject1(onComplete);
+ * test2.method1(); //call it, on success, onComplete's method1 is called.
+ * </code>
+ */
+Prado.AJAX.RemoteObject.prototype =
+{
+ baseInitialize : function(handlers, options)
+ {
+ this.__handlers = handlers || {};
+ this.__service = new Prado.AJAX.RemoteObject.Request(options);
+ },
+
+ __call : function(url, method, args)
+ {
+ this.__service.options.onSuccess = this.__onSuccess.bind(this);
+ this.__callback = method;
+ return this.__service.invokeRemoteObject(url+"/"+method, args);
+ },
+
+ __onSuccess : function(transport, json)
+ {
+ if(this.__handlers[this.__callback])
+ this.__handlers[this.__callback](json, transport.responseText);
+ }
+};
+
+/**
+ * Respond to Prado AJAX request exceptions.
+ */
+Prado.AJAX.Exception =
+{
+ /**
+ * Server returns 505 exception. Just log it.
+ */
+ "on505" : function(request, transport, e)
+ {
+ var msg = 'HTTP '+transport.status+" with response";
+ Logger.error(msg, transport.responseText);
+ Logger.exception(e);
+ },
+
+ onComplete : function(request, transport, e)
+ {
+ if(transport.status != 505)
+ {
+ var msg = 'HTTP '+transport.status+" with response : \n";
+ msg += transport.responseText + "\n";
+ msg += "Data : \n"+inspect(e);
+ Logger.warn(msg);
+ }
+ },
+
+ format : function(e)
+ {
+ var msg = e.type + " with message \""+e.message+"\"";
+ msg += " in "+e.file+"("+e.line+")\n";
+ msg += "Stack trace:\n";
+ var trace = e.trace;
+ for(var i = 0; i<trace.length; i++)
+ {
+ msg += " #"+i+" "+trace[i].file;
+ msg += "("+trace[i].line+"): ";
+ msg += trace[i]["class"]+"->"+trace[i]["function"]+"()"+"\n";
+ }
+ return msg;
+ },
+
+ logException : function(e)
+ {
+ var msg = Prado.AJAX.Exception.format(e);
+ Logger.error("Server Error "+e.code, msg);
+ }
+}
+
+//Add HTTP exception respones when logger is enabled.
+Event.OnLoad(function()
+{
+ if(typeof Logger != "undefined")
+ {
+ Logger.exception = Prado.AJAX.Exception.logException;
+ Ajax.Responders.register(Prado.AJAX.Exception);
+ }
+});
+
+/**
+ * Prado Callback service that provides component intergration,
+ * viewstate (read only), and automatic form data serialization.
+ * Usage: <code>new Prado.AJAX.Callback('MyPage.MyComponentID.raiseCallbackEvent', options)</code>
+ * These classes should be called by the components developers.
+ * For inline callback service, use <t>Prado.Callback(callbackID, params)</t>.
+ */
+Prado.AJAX.Callback = Class.create();
+Prado.AJAX.Callback.prototype = Object.extend(new Prado.AJAX.RemoteObject(),
+{
+
+ /**
+ * Create and request a new Prado callback service.
+ * @param string|element the callback ID, must be of the form, <t>ClassName.ComponentID.MethodName</t>
+ * @param list options with list key onCallbackReturn, and more.
+ *
+ */
+ initialize : function(ID, options)
+ {
+ if(!isString(ID) && typeof(ID.id) != "undefined")
+ ID = ID.id;
+ if(!isString(ID))
+ throw new Error('A Control ID must be specified');
+ this.baseInitialize(this, options);
+ this.options = options || [];
+ this.__service.post.data['__ID'] = ID;
+ this.requestCallback();
+ },
+
+ /**
+ * Get form data for components that implements IPostBackHandler.
+ */
+ collectPostData : function()
+ {
+ var IDs = Prado.AJAX.Callback.IDs;
+ this.__service.post.data['__data'] = {};
+ for(var i = 0; i<IDs.length; i++)
+ {
+ var id = IDs[i];
+ if(id.indexOf("[]") > -1)
+ this.__service.post.data['__data'][id] =
+ this.collectArrayPostData(id);
+ else if(isObject($(id)))
+ this.__service.post.data['__data'][id] = $F(id);
+ }
+ },
+
+ collectArrayPostData : function(name)
+ {
+ var elements = document.getElementsByName(name);
+ var data = [];
+ $A(elements).each(function(el)
+ {
+ if($F(el)) data.push($F(el));
+ });
+ return data;
+ },
+
+ /**
+ * Prepares and calls the AJAX request.
+ * Collects the data from components that implements IPostBackHandler
+ * and the viewstate as part of the request payload.
+ */
+ requestCallback : function()
+ {
+ this.collectPostData();
+ if(Prado.AJAX.Validate(this.options))
+ return this.__call(Prado.AJAX.Callback.Server, 'handleCallback', this.options.params);
+ },
+
+ /**
+ * On callback request return, call the onSuccess function.
+ */
+ handleCallback : function(result, output)
+ {
+ if(typeof(result) != "undefined" && !isNull(result))
+ {
+ this.options.onSuccess(result['data'], output);
+ if(result['actions'])
+ result.actions.each(Prado.AJAX.Callback.Action.__run);
+ }
+ }
+});
+
+/**
+ * Prase and evaluate Callback clien-side actions.
+ */
+Prado.AJAX.Callback.Action =
+{
+ __run : function(command)
+ {
+ for(var name in command)
+ {
+ //first parameter must be a valid element or begins with '@'
+ if(command[name][0] && ($(command[name][0]) || command[name][0].indexOf("[]") > -1))
+ {
+ name.toFunction().apply(this,command[name]);
+ }
+ }
+ }
+};
+
+
+/**
+ * Returns false if validation required and validates to false,
+ * returns true otherwise.
+ * @return boolean true if validation passes.
+ */
+Prado.AJAX.Validate = function(options)
+{
+ if(options.CausesValidation)
+ {
+ if(options.ValidatorGroup)
+ return Prado.Validation.ValidateValidatorGroup(options.ValidatorGroup);
+ else if(options.ValidationGroup)
+ return Prado.Validation.ValidateValidationGroup(options.ValidationGroup);
+ else
+ return Prado.Validation.ValidateNonGroup(options.ValidationForm);
+ }
+ else
+ return true;
+};
+
+
+//Available callback service
+Prado.AJAX.Callback.Server = '';
+
+//List of IDs that implements IPostBackHandler
+Prado.AJAX.Callback.IDs = [];
+
+/**
+ * Simple AJAX callback interface, suitable for inline javascript.
+ * e.g., <code><a href="..." onclick="Prado.Callback('..', 'Hello');">Click me</a></code>
+ * @param string callback ID
+ * @param array parameters to pass to the callback service
+ */
+Prado.Callback = function(ID, params, onSuccess, options)
+{
+ var callback =
+ {
+ 'params' : [params] || [],
+ 'onSuccess' : onSuccess || Prototype.emptyFunction,
+ 'CausesValidation' : true
+ };
+
+ Object.extend(callback, options || {});
+
+ new Prado.AJAX.Callback(ID, callback);
+ return false;
+} \ No newline at end of file
diff --git a/framework/Web/Javascripts/prado/controls.js b/framework/Web/Javascripts/prado/controls.js
new file mode 100644
index 00000000..bc902178
--- /dev/null
+++ b/framework/Web/Javascripts/prado/controls.js
@@ -0,0 +1,167 @@
+Prado.Focus = Class.create();
+
+Prado.Focus.setFocus = function(id)
+{
+ var target = document.getElementById ? document.getElementById(id) : document.all[id];
+ if(target && !Prado.Focus.canFocusOn(target))
+ {
+ target = Prado.Focus.findTarget(target);
+ }
+ if(target)
+ {
+ try
+ {
+ target.focus();
+ target.scrollIntoView(false);
+ if (window.__smartNav)
+ {
+ window.__smartNav.ae = target.id;
+ }
+ }
+ catch (e)
+ {
+ }
+ }
+}
+
+Prado.Focus.canFocusOn = function(element)
+{
+ if(!element || !(element.tagName))
+ return false;
+ var tagName = element.tagName.toLowerCase();
+ return !element.disabled && (!element.type || element.type.toLowerCase() != "hidden") && Prado.Focus.isFocusableTag(tagName) && Prado.Focus.isVisible(element);
+}
+
+Prado.Focus.isFocusableTag = function(tagName)
+{
+ return (tagName == "input" || tagName == "textarea" || tagName == "select" || tagName == "button" || tagName == "a");
+}
+
+
+Prado.Focus.findTarget = function(element)
+{
+ if(!element || !(element.tagName))
+ {
+ return null;
+ }
+ var tagName = element.tagName.toLowerCase();
+ if (tagName == "undefined")
+ {
+ return null;
+ }
+ var children = element.childNodes;
+ if (children)
+ {
+ for(var i=0;i<children.length;i++)
+ {
+ try
+ {
+ if(Prado.Focus.canFocusOn(children[i]))
+ {
+ return children[i];
+ }
+ else
+ {
+ var target = Prado.Focus.findTarget(children[i]);
+ if(target)
+ {
+ return target;
+ }
+ }
+ }
+ catch (e)
+ {
+ }
+ }
+ }
+ return null;
+}
+
+Prado.Focus.isVisible = function(element)
+{
+ var current = element;
+ while((typeof(current) != "undefined") && (current != null))
+ {
+ if(current.disabled || (typeof(current.style) != "undefined" && ((typeof(current.style.display) != "undefined" && current.style.display == "none") || (typeof(current.style.visibility) != "undefined" && current.style.visibility == "hidden") )))
+ {
+ return false;
+ }
+ if(typeof(current.parentNode) != "undefined" && current.parentNode != null && current.parentNode != current && current.parentNode.tagName.toLowerCase() != "body")
+ {
+ current = current.parentNode;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ return true;
+}
+
+
+Prado.doPostBack = function(formID, eventTarget, eventParameter, performValidation, validationGroup, actionUrl, trackFocus, clientSubmit)
+{
+ if (typeof(performValidation) == 'undefined')
+ {
+ var performValidation = false;
+ var validationGroup = '';
+ var actionUrl = null;
+ var trackFocus = false;
+ var clientSubmit = true;
+ }
+ var theForm = document.getElementById ? document.getElementById(formID) : document.forms[formID];
+ var canSubmit = true;
+ if (performValidation)
+ {
+ //canSubmit = Prado.Validation.validate(validationGroup);
+ /* Prado.Validation.ActiveTarget = theForm;
+ Prado.Validation.CurrentTargetGroup = null;
+ Prado.Validation.IsGroupValidation = false;
+ canSubmit = Prado.Validation.IsValid(theForm);
+ Logger.debug(canSubmit);*/
+ canSubmit = Prado.Validation.IsValid(theForm);
+ }
+ if (canSubmit)
+ {
+ if (actionUrl != null && (actionUrl.length > 0))
+ {
+ theForm.action = actionUrl;
+ }
+ if (trackFocus)
+ {
+ var lastFocus = theForm.elements['PRADO_LASTFOCUS'];
+ if ((typeof(lastFocus) != 'undefined') && (lastFocus != null))
+ {
+ var active = document.activeElement;
+ if (typeof(active) == 'undefined')
+ {
+ lastFocus.value = eventTarget;
+ }
+ else
+ {
+ if ((active != null) && (typeof(active.id) != 'undefined'))
+ {
+ if (active.id.length > 0)
+ {
+ lastFocus.value = active.id;
+ }
+ else if (typeof(active.name) != 'undefined')
+ {
+ lastFocus.value = active.name;
+ }
+ }
+ }
+ }
+ }
+ if (!clientSubmit)
+ {
+ canSubmit = false;
+ }
+ }
+ if (canSubmit && (!theForm.onsubmit || theForm.onsubmit()))
+ {
+ theForm.PRADO_POSTBACK_TARGET.value = eventTarget;
+ theForm.PRADO_POSTBACK_PARAMETER.value = eventParameter;
+ theForm.submit();
+ }
+} \ No newline at end of file
diff --git a/framework/Web/Javascripts/prado/datepicker.js b/framework/Web/Javascripts/prado/datepicker.js
new file mode 100644
index 00000000..68e63168
--- /dev/null
+++ b/framework/Web/Javascripts/prado/datepicker.js
@@ -0,0 +1,656 @@
+Prado.Calendar = Class.create();
+
+Prado.Calendar.Util = Class.create();
+
+Object.extend(Prado.Calendar.Util,
+{
+ IsLeapYear : function (year)
+ {
+ return ((year%4 == 0) && ((year%100 != 0) || (year%400 == 0)));
+ },
+
+ yearLength : function(year)
+ {
+ if (this.isLeapYear(year))
+ return 366;
+ else
+ return 365;
+ },
+
+ dayOfYear : function(date)
+ {
+ var a = this.isLeapYear(date.getFullYear()) ?
+ Calendar.LEAP_NUM_DAYS : Calendar.NUM_DAYS;
+ return a[date.getMonth()] + date.getDate();
+ },
+
+ browser : function()
+ {
+ var info = { Version : "1.0" };
+ var is_major = parseInt( navigator.appVersion );
+ info.nver = is_major;
+ info.ver = navigator.appVersion;
+ info.agent = navigator.userAgent;
+ info.dom = document.getElementById ? 1 : 0;
+ info.opera = window.opera ? 1 : 0;
+ info.ie5 = ( info.ver.indexOf( "MSIE 5" ) > -1 && info.dom && !info.opera ) ? 1 : 0;
+ info.ie6 = ( info.ver.indexOf( "MSIE 6" ) > -1 && info.dom && !info.opera ) ? 1 : 0;
+ info.ie4 = ( document.all && !info.dom && !info.opera ) ? 1 : 0;
+ info.ie = info.ie4 || info.ie5 || info.ie6;
+ info.mac = info.agent.indexOf( "Mac" ) > -1;
+ info.ns6 = ( info.dom && parseInt( info.ver ) >= 5 ) ? 1 : 0;
+ info.ie3 = ( info.ver.indexOf( "MSIE" ) && ( is_major < 4 ) );
+ info.hotjava = ( info.agent.toLowerCase().indexOf( 'hotjava' ) != -1 ) ? 1 : 0;
+ info.ns4 = ( document.layers && !info.dom && !info.hotjava ) ? 1 : 0;
+ info.bw = ( info.ie6 || info.ie5 || info.ie4 || info.ns4 || info.ns6 || info.opera );
+ info.ver3 = ( info.hotjava || info.ie3 );
+ info.opera7 = ( ( info.agent.toLowerCase().indexOf( 'opera 7' ) > -1 ) || ( info.agent.toLowerCase().indexOf( 'opera/7' ) > -1 ) );
+ info.operaOld = info.opera && !info.opera7;
+ return info;
+ },
+
+ ImportCss : function(doc, css_file)
+ {
+ if (this.browser().ie)
+ var styleSheet = doc.createStyleSheet(css_file);
+ else
+ {
+ var elm = doc.createElement("link");
+
+ elm.rel = "stylesheet";
+ elm.href = css_file;
+
+ if (headArr = doc.getElementsByTagName("head"))
+ headArr[0].appendChild(elm);
+ }
+ }
+});
+
+Object.extend(Prado.Calendar,
+{
+ // Accumulated days per month, for normal and for leap years.
+ // Used in week number calculations.
+ NUM_DAYS : [0,31,59,90,120,151,181,212,243,273,304,334],
+ LEAP_NUM_DAYS : [0,31,60,91,121,152,182,213,244,274,305,335]
+});
+
+Prado.Calendar.prototype =
+{
+ monthNames : [ "January", "February", "March", "April",
+ "May", "June", "July", "August",
+ "September", "October", "November", "December"
+ ],
+
+ shortWeekDayNames : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
+
+ format : "yyyy-MM-dd",
+
+ css : "calendar_system.css",
+
+ initialize : function(control, attr)
+ {
+ this.attr = attr || [];
+ this.control = $(control);
+ this.dateSlot = new Array(42);
+ this.weekSlot = new Array(6);
+ this.firstDayOfWeek = 1;
+ this.minimalDaysInFirstWeek = 4;
+ this.currentDate = new Date();
+ this.selectedDate = null;
+ this.className = "Prado_Calendar";
+
+ //which element to trigger to show the calendar
+ this.trigger = this.attr.trigger ? $(this.attr.trigger) : this.control;
+ Event.observe(this.trigger, "click", this.show.bind(this));
+
+ Prado.Calendar.Util.ImportCss(document, this.css);
+
+ if(this.attr.format) this.format = this.attr.format;
+
+ //create it
+ this.create();
+ this.hookEvents();
+ },
+
+ create : function()
+ {
+ var div;
+ var table;
+ var tbody;
+ var tr;
+ var td;
+
+ // Create the top-level div element
+ this._calDiv = document.createElement("div");
+ this._calDiv.className = this.className;
+ this._calDiv.style.display = "none";
+
+ // header div
+ div = document.createElement("div");
+ div.className = "calendarHeader";
+ this._calDiv.appendChild(div);
+
+ table = document.createElement("table");
+ table.style.cellSpacing = 0;
+ div.appendChild(table);
+
+ tbody = document.createElement("tbody");
+ table.appendChild(tbody);
+
+ tr = document.createElement("tr");
+ tbody.appendChild(tr);
+
+ // Previous Month Button
+ td = document.createElement("td");
+ td.className = "prevMonthButton";
+ this._previousMonth = document.createElement("button");
+ this._previousMonth.appendChild(document.createTextNode("<<"));
+ td.appendChild(this._previousMonth);
+ tr.appendChild(td);
+
+
+
+ //
+ // Create the month drop down
+ //
+ td = document.createElement("td");
+ td.className = "labelContainer";
+ tr.appendChild(td);
+ this._monthSelect = document.createElement("select");
+ for (var i = 0 ; i < this.monthNames.length ; i++) {
+ var opt = document.createElement("option");
+ opt.innerHTML = this.monthNames[i];
+ opt.value = i;
+ if (i == this.currentDate.getMonth()) {
+ opt.selected = true;
+ }
+ this._monthSelect.appendChild(opt);
+ }
+ td.appendChild(this._monthSelect);
+
+
+ //
+ // Create the year drop down
+ //
+ td = document.createElement("td");
+ td.className = "labelContainer";
+ tr.appendChild(td);
+ this._yearSelect = document.createElement("select");
+ for(var i=1920; i < 2050; ++i) {
+ var opt = document.createElement("option");
+ opt.innerHTML = i;
+ opt.value = i;
+ if (i == this.currentDate.getFullYear()) {
+ opt.selected = false;
+ }
+ this._yearSelect.appendChild(opt);
+ }
+ td.appendChild(this._yearSelect);
+
+
+ td = document.createElement("td");
+ td.className = "nextMonthButton";
+ this._nextMonth = document.createElement("button");
+ this._nextMonth.appendChild(document.createTextNode(">>"));
+ td.appendChild(this._nextMonth);
+ tr.appendChild(td);
+
+ // Calendar body
+ div = document.createElement("div");
+ div.className = "calendarBody";
+ this._calDiv.appendChild(div);
+ this._table = div;
+
+ // Create the inside of calendar body
+
+ var text;
+ table = document.createElement("table");
+ //table.style.width="100%";
+ table.className = "grid";
+
+ div.appendChild(table);
+ var thead = document.createElement("thead");
+ table.appendChild(thead);
+ tr = document.createElement("tr");
+ thead.appendChild(tr);
+
+ for(i=0; i < 7; ++i) {
+ td = document.createElement("th");
+ text = document.createTextNode(this.shortWeekDayNames[(i+this.firstDayOfWeek)%7]);
+ td.appendChild(text);
+ td.className = "weekDayHead";
+ tr.appendChild(td);
+ }
+
+ // Date grid
+ tbody = document.createElement("tbody");
+ table.appendChild(tbody);
+
+ for(week=0; week<6; ++week) {
+ tr = document.createElement("tr");
+ tbody.appendChild(tr);
+
+ for(day=0; day<7; ++day) {
+ td = document.createElement("td");
+ td.className = "calendarDate";
+ text = document.createTextNode(String.fromCharCode(160));
+ td.appendChild(text);
+
+ tr.appendChild(td);
+ var tmp = new Object();
+ tmp.tag = "DATE";
+ tmp.value = -1;
+ tmp.data = text;
+ this.dateSlot[(week*7)+day] = tmp;
+
+ Event.observe(td, "mouseover", this.hover.bind(this));
+ Event.observe(td, "mouseout", this.hover.bind(this));
+
+ }
+ }
+
+ // Calendar Footer
+ div = document.createElement("div");
+ div.className = "calendarFooter";
+ this._calDiv.appendChild(div);
+
+ table = document.createElement("table");
+ //table.style.width="100%";
+ table.className = "footerTable";
+ //table.cellSpacing = 0;
+ div.appendChild(table);
+
+ tbody = document.createElement("tbody");
+ table.appendChild(tbody);
+
+ tr = document.createElement("tr");
+ tbody.appendChild(tr);
+
+ //
+ // The TODAY button
+ //
+ td = document.createElement("td");
+ td.className = "todayButton";
+ this._todayButton = document.createElement("button");
+ var today = new Date();
+ var buttonText = today.getDate() + " " + this.monthNames[today.getMonth()] + ", " + today.getFullYear();
+ this._todayButton.appendChild(document.createTextNode(buttonText));
+ td.appendChild(this._todayButton);
+ tr.appendChild(td);
+
+ //
+ // The CLEAR button
+ //
+ td = document.createElement("td");
+ td.className = "clearButton";
+ this._clearButton = document.createElement("button");
+ var today = new Date();
+ buttonText = "Clear";
+ this._clearButton.appendChild(document.createTextNode(buttonText));
+ td.appendChild(this._clearButton);
+ tr.appendChild(td);
+
+ document.body.appendChild(this._calDiv);
+
+ this.update();
+ this.updateHeader();
+
+ return this._calDiv;
+ },
+
+ hookEvents : function()
+ {
+ // IE55+ extension
+ this._previousMonth.hideFocus = true;
+ this._nextMonth.hideFocus = true;
+ this._todayButton.hideFocus = true;
+ // end IE55+ extension
+
+ // hook up events
+ Event.observe(this._previousMonth, "click", this.prevMonth.bind(this));
+ Event.observe(this._nextMonth, "click", this.nextMonth.bind(this));
+ Event.observe(this._todayButton, "click", this.selectToday.bind(this));
+ Event.observe(this._clearButton, "click", this.clearSelection.bind(this));
+
+ Event.observe(this._monthSelect, "change", this.monthSelect.bind(this));
+ Event.observe(this._yearSelect, "change", this.yearSelect.bind(this));
+
+ // ie6 extension
+ Event.observe(this._calDiv, "mousewheel", this.mouseWheelChange.bind(this));
+
+ Event.observe(this._table, "click", this.selectDate.bind(this));
+
+ Event.observe(this._calDiv,"keydown", this.keyPressed.bind(this));
+
+ /*
+ this._calDiv.onkeydown = function (e) {
+ if (e == null) e = document.parentWindow.event;
+ var kc = e.keyCode != null ? e.keyCode : e.charCode;
+
+ if(kc == 13) {
+ var d = new Date(dp._currentDate).valueOf();
+ dp.setSelectedDate(d);
+
+ if (!dp._alwaysVisible && dp._hideOnSelect) {
+ dp.hide();
+ }
+ return false;
+ }
+
+
+ if (kc < 37 || kc > 40) return true;
+
+ var d = new Date(dp._currentDate).valueOf();
+ if (kc == 37) // left
+ d -= 24 * 60 * 60 * 1000;
+ else if (kc == 39) // right
+ d += 24 * 60 * 60 * 1000;
+ else if (kc == 38) // up
+ d -= 7 * 24 * 60 * 60 * 1000;
+ else if (kc == 40) // down
+ d += 7 * 24 * 60 * 60 * 1000;
+
+ dp.setCurrentDate(new Date(d));
+ return false;
+ }*/
+
+
+ },
+
+ keyPressed : function(ev)
+ {
+ if (!ev) ev = document.parentWindow.event;
+ var kc = ev.keyCode != null ? ev.keyCode : ev.charCode;
+
+ if(kc = Event.KEY_RETURN)
+ {
+ //var d = new Date(this.currentDate);
+ this.setSelectedDate(this.currentDate);
+ this.hide();
+ return false;
+ }
+
+ if(kc < 37 || kc > 40) return true;
+
+ var d = new Date(this.currentDate).valueOf();
+ if(kc == Event.KEY_LEFT)
+ d -= 86400000; //-1 day
+ else if (kc == Event.KEY_RIGHT)
+ d += 86400000; //+1 day
+ else if (kc == Event.KEY_UP)
+ d -= 604800000; // -7 days
+ else if (kc == Event.KEY_DOWN)
+ d += 604800000; // +7 days
+ this.setCurrentDate(new Date(d));
+ return false;
+ },
+
+ selectDate : function(ev)
+ {
+ var el = Event.element(ev);
+ while (el.nodeType != 1)
+ el = el.parentNode;
+
+ while (el != null && el.tagName && el.tagName.toLowerCase() != "td")
+ el = el.parentNode;
+
+ // if no td found, return
+ if (el == null || el.tagName == null || el.tagName.toLowerCase() != "td")
+ return;
+
+ var d = new Date(this.currentDate);
+ var n = Number(el.firstChild.data);
+ if (isNaN(n) || n <= 0 || n == null)
+ return;
+
+ d.setDate(n);
+ this.setSelectedDate(d);
+ this.hide();
+ },
+
+ selectToday : function()
+ {
+ this.setSelectedDate(new Date());
+ this.hide();
+ },
+
+ clearSelection : function()
+ {
+ this.selectedDate = null;
+ if (isFunction(this.onchange))
+ this.onchange();
+ this.hide();
+ },
+
+ monthSelect : function(ev)
+ {
+ this.setMonth(Form.Element.getValue(Event.element(ev)));
+ },
+
+ yearSelect : function(ev)
+ {
+ this.setYear(Form.Element.getValue(Event.element(ev)));
+ },
+
+ // ie6 extension
+ mouseWheelChange : function (e)
+ {
+ if (e == null) e = document.parentWindow.event;
+ var n = - e.wheelDelta / 120;
+ var d = new Date(this.currentDate);
+ var m = this.getMonth() + n;
+ this.setMonth(m);
+ this.setCurrentDate(d);
+
+ return false;
+ },
+
+ onchange : function()
+ {
+ this.control.value = this.formatDate();
+ },
+
+ formatDate : function()
+ {
+ return Prado.Calendar.Util.FormatDate(this.selectedDate, this.format);
+ },
+
+ setCurrentDate : function(date)
+ {
+ if (date == null)
+ return;
+
+ // if string or number create a Date object
+ if (isString(date) || isNumber(date))
+ date = new Date(date);
+
+ // do not update if not really changed
+ if (this.currentDate.getDate() != date.getDate() ||
+ this.currentDate.getMonth() != date.getMonth() ||
+ this.currentDate.getFullYear() != date.getFullYear())
+ {
+
+ this.currentDate = new Date(date);
+
+ this.updateHeader();
+ this.update();
+ }
+
+ },
+
+ setSelectedDate : function(date)
+ {
+ this.selectedDate = new Date(date);
+ this.setCurrentDate(this.selectedDate);
+ if (isFunction(this.onchange))
+ this.onchange();
+ },
+
+ getElement : function()
+ {
+ return this._calDiv;
+ },
+
+ getSelectedDate : function ()
+ {
+ return isNull(this.selectedDate) ? null : new Date(this.selectedDate);
+ },
+
+ setYear : function(year)
+ {
+ var d = new Date(this.currentDate);
+ d.setFullYear(year);
+ this.setCurrentDate(d);
+ },
+
+ setMonth : function (month)
+ {
+ var d = new Date(this.currentDate);
+ d.setMonth(month);
+ this.setCurrentDate(d);
+ },
+
+ nextMonth : function ()
+ {
+ this.setMonth(this.currentDate.getMonth()+1);
+ },
+
+ prevMonth : function ()
+ {
+ this.setMonth(this.currentDate.getMonth()-1);
+ },
+
+ show : function()
+ {
+ if(!this.showing)
+ {
+ var pos = Position.cumulativeOffset(this.control);
+ pos[1] += this.control.offsetHeight;
+ this._calDiv.style.display = "block";
+ this._calDiv.style.top = pos[1] + "px";
+ this._calDiv.style.left = pos[0] + "px";
+ Event.observe(document.body, "click", this.hideOnClick.bind(this));
+ var date = Prado.Calendar.Util.ParseDate(Form.Element.getValue(this.control), this.format);
+ if(!isNull(date))
+ {
+ this.selectedDate = date;
+ this.setCurrentDate(date);
+ }
+ this.showing = true;
+ }
+ },
+
+ //hide the calendar when clicked outside any calendar
+ hideOnClick : function(ev)
+ {
+ if(!this.showing) return;
+ var el = Event.element(ev);
+ var within = false;
+ do
+ {
+ within = within || el.className == this.className;
+ within = within || el == this.trigger;
+ within = within || el == this.control;
+ if(within) break;
+ el = el.parentNode;
+ }
+ while(el);
+ if(!within) this.hide();
+ },
+
+ hide : function()
+ {
+ if(this.showing)
+ {
+ this._calDiv.style.display = "none";
+ this.showing = false;
+ Event.stopObserving(document.body, "click", this.hideOnClick.bind(this));
+ }
+ },
+
+ update : function()
+ {
+ var Util = Prado.Calendar.Util;
+
+ // Calculate the number of days in the month for the selected date
+ var date = this.currentDate;
+ var today = Util.ISODate(new Date());
+
+ var selected = isNull(this.selectedDate) ? "" : Util.ISODate(this.selectedDate);
+ var current = Util.ISODate(date);
+ var d1 = new Date(date.getFullYear(), date.getMonth(), 1);
+ var d2 = new Date(date.getFullYear(), date.getMonth()+1, 1);
+ var monthLength = Math.round((d2 - d1) / (24 * 60 * 60 * 1000));
+
+ // Find out the weekDay index for the first of this month
+ var firstIndex = (d1.getDay() - this.firstDayOfWeek) % 7 ;
+ if (firstIndex < 0)
+ firstIndex += 7;
+
+ var index = 0;
+ while (index < firstIndex) {
+ this.dateSlot[index].value = -1;
+ this.dateSlot[index].data.data = String.fromCharCode(160);
+ this.dateSlot[index].data.parentNode.className = "empty";
+ index++;
+ }
+
+ for (i = 1; i <= monthLength; i++, index++) {
+ var slot = this.dateSlot[index];
+ var slotNode = slot.data.parentNode;
+ slot.value = i;
+ slot.data.data = i;
+ slotNode.className = "date";
+ if (Util.ISODate(d1) == today) {
+ slotNode.className += " today";
+ }
+ if (Util.ISODate(d1) == current) {
+ slotNode.className += " current";
+ }
+ if (Util.ISODate(d1) == selected) {
+ slotNode.className += " selected";
+ }
+ d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()+1);
+ }
+
+ var lastDateIndex = index;
+
+ while(index < 42) {
+ this.dateSlot[index].value = -1;
+ this.dateSlot[index].data.data = String.fromCharCode(160);
+ this.dateSlot[index].data.parentNode.className = "empty";
+ ++index;
+ }
+
+ },
+
+ hover : function(ev)
+ {
+ //conditionally add the hover class to the event target element.
+ Element.condClassName(Event.element(ev), "hover", ev.type=="mouseover");
+ },
+
+ updateHeader : function () {
+
+ var options = this._monthSelect.options;
+ var m = this.currentDate.getMonth();
+ for(var i=0; i < options.length; ++i) {
+ options[i].selected = false;
+ if (options[i].value == m) {
+ options[i].selected = true;
+ }
+ }
+
+ options = this._yearSelect.options;
+ var year = this.currentDate.getFullYear();
+ for(var i=0; i < options.length; ++i) {
+ options[i].selected = false;
+ if (options[i].value == year) {
+ options[i].selected = true;
+ }
+ }
+
+ }
+
+
+}; \ No newline at end of file
diff --git a/framework/Web/Javascripts/prado/prado.js b/framework/Web/Javascripts/prado/prado.js
new file mode 100644
index 00000000..52bf2b89
--- /dev/null
+++ b/framework/Web/Javascripts/prado/prado.js
@@ -0,0 +1,51 @@
+var Prado = { Version: '3.0a' };
+
+Prado.Button = Class.create();
+
+/**
+ * Usage: Event.observe("panelID", "keypress", Prado.fireButton.bindEvent($("panelID"), "targetButtonID"));
+ */
+Object.extend(Prado.Button,
+{
+ buttonFired : false,
+ fireButton : function(e, target)
+ {
+ var eventFired = !this.buttonFired && Event.keyCode(e) == Event.KEY_RETURN;
+ var isTextArea = Event.element(e).targName.toLowerCase() == "textarea";
+ if (eventFired && !isTextArea)
+ {
+ var defaultButton = $(target);
+ if (defaultButton)
+ {
+ Prado.Button.buttonFired = true;
+ Event.fireEvent(defaultButton,"click");
+ Event.stop(e);
+ return false;
+ }
+ }
+ return true;
+ }
+});
+
+Prado.TextBox = Class.create();
+
+/**
+ * Usage: Event.observe("textboxID", "keypress", Prado.fireButton.bindEvent($("textboxID")));
+ */
+Object.extend(Prado.TextBox,
+{
+ handleReturnKey = function(e)
+ {
+ if(Event.keyCode(e) == Event.KEY_RETURN)
+ {
+ var target = Event.element(e);
+ if(target)
+ {
+ Event.fireEvent(target, "change");
+ Event.stop(e);
+ return false;
+ }
+ }
+ return true;
+ }
+});
diff --git a/framework/Web/Javascripts/prado/validation.js b/framework/Web/Javascripts/prado/validation.js
new file mode 100644
index 00000000..33d5411d
--- /dev/null
+++ b/framework/Web/Javascripts/prado/validation.js
@@ -0,0 +1,751 @@
+
+/**
+ * Prado client-side javascript validation class.
+ */
+Prado.Validation = Class.create();
+
+/**
+ * Utilities for validation. Static class.
+ */
+Prado.Validation.Util = Class.create();
+
+/**
+ * Convert a string into integer, returns null if not integer.
+ * @param {string} the string to convert to integer
+ * @type {integer|null} null if string does not represent an integer.
+ */
+Prado.Validation.Util.toInteger = function(value)
+{
+ var exp = /^\s*[-\+]?\d+\s*$/;
+ if (value.match(exp) == null)
+ return null;
+ var num = parseInt(value, 10);
+ return (isNaN(num) ? null : num);
+}
+
+/**
+ * Convert a string into a double/float value. <b>Internationalization
+ * is not supported</b>
+ * @param {string} the string to convert to double/float
+ * @param {string} the decimal character
+ * @return {float|null} null if string does not represent a float value
+ */
+Prado.Validation.Util.toDouble = function(value, decimalchar)
+{
+ decimalchar = undef(decimalchar) ? "." : decimalchar;
+ var exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + decimalchar + "(\\d+))?\\s*$");
+ var m = value.match(exp);
+ if (m == null)
+ return null;
+ var cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4];
+ var num = parseFloat(cleanInput);
+ return (isNaN(num) ? null : num);
+}
+
+/**
+ * Convert strings that represent a currency value (e.g. a float with grouping
+ * characters) to float. E.g. "10,000.50" will become "10000.50". The number
+ * of dicimal digits, grouping and decimal characters can be specified.
+ * <i>The currency input format is <b>very</b> strict, null will be returned if
+ * the pattern does not match</i>.
+ * @param {string} the currency value
+ * @param {string} the grouping character, default is ","
+ * @param {int} number of decimal digits
+ * @param {string} the decimal character, default is "."
+ * @type {float|null} the currency value as float.
+ */
+Prado.Validation.Util.toCurrency = function(value, groupchar, digits, decimalchar)
+{
+ groupchar = undef(groupchar) ? "," : groupchar;
+ decimalchar = undef(decimalchar) ? "." : decimalchar;
+ digits = undef(digits) ? 2 : digits;
+
+ var exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + groupchar + ")*)(\\d+)"
+ + ((digits > 0) ? "(\\" + decimalchar + "(\\d{1," + digits + "}))?" : "")
+ + "\\s*$");
+ var m = value.match(exp);
+ if (m == null)
+ return null;
+ var intermed = m[2] + m[5] ;
+ var cleanInput = m[1] + intermed.replace(
+ new RegExp("(\\" + groupchar + ")", "g"), "")
+ + ((digits > 0) ? "." + m[7] : "");
+ var num = parseFloat(cleanInput);
+ return (isNaN(num) ? null : num);
+}
+
+/**
+ * Get the date from string using the prodivided date format string.
+ * The format notations are
+ * # day -- %d or %e
+ * # month -- %m
+ * # year -- %y or %Y
+ * # hour -- %H, %I, %k, or %l
+ * # minutes -- %M
+ * # P.M. -- %p or %P
+ * @param {string} the formatted date string
+ * @param {string} the date format
+ * @type {Date} the date represented in the string
+ */
+Prado.Validation.Util.toDate = function(value, format)
+{
+ var y = 0;
+ var m = -1;
+ var d = 0;
+ var a = value.split(/\W+/);
+ var b = format.match(/%./g);
+ var i = 0, j = 0;
+ var hr = 0;
+ var min = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (!a[i])
+ continue;
+ switch (b[i]) {
+ case "%d":
+ case "%e":
+ d = parseInt(a[i], 10);
+ break;
+
+ case "%m":
+ m = parseInt(a[i], 10) - 1;
+ break;
+
+ case "%Y":
+ case "%y":
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ break;
+
+ case "%H":
+ case "%I":
+ case "%k":
+ case "%l":
+ hr = parseInt(a[i], 10);
+ break;
+
+ case "%P":
+ case "%p":
+ if (/pm/i.test(a[i]) && hr < 12)
+ hr += 12;
+ break;
+
+ case "%M":
+ min = parseInt(a[i], 10);
+ break;
+ }
+ }
+ if (y != 0 && m != -1 && d != 0)
+ {
+ var date = new Date(y, m, d, hr, min, 0);
+ return (isObject(date)
+ && y == date.getFullYear()
+ && m == date.getMonth()
+ && d == date.getDate()) ? date.valueOf() : null;
+ }
+ return null;
+}
+
+/**
+ * Trim the value, if the value is undefined, empty string is return.
+ * @param {string} string to be trimmed.
+ * @type {string} trimmed string.
+ */
+Prado.Validation.Util.trim = function(value)
+{
+ if(undef(value)) return "";
+ return value.replace(/^\s+|\s+$/g, "");
+}
+
+/**
+ * A delayed focus on a particular element
+ * @param {element} element to apply focus()
+ */
+Prado.Validation.Util.focus = function(element)
+{
+ var obj = $(element);
+ if(isObject(obj) && isdef(obj.focus))
+ setTimeout(function(){ obj.focus(); }, 100);
+ return false;
+}
+
+/**
+ * List of validator instances.
+ */
+Prado.Validation.validators = [];
+
+/**
+ * List of forms.
+ * @type {int}
+ */
+Prado.Validation.forms = [];
+
+/**
+ * List of summary controls.
+ */
+Prado.Validation.summaries = [];
+
+/**
+ * Validation groups.
+ */
+Prado.Validation.groups = [];
+
+
+/**
+ * Second type of grouping.
+ */
+Prado.Validation.TargetGroups = {};
+
+
+/**
+ * Current Target group.
+ */
+Prado.Validation.CurrentTargetGroup = null;
+
+Prado.Validation.HasTargetGroup = false;
+
+/**
+ * Targets that can cause validation.
+ */
+Prado.Validation.ActiveTarget = null;
+
+
+/**
+ * Determine if group validation is active.
+ */
+Prado.Validation.IsGroupValidation = false;
+
+/**
+ * Add a form for validation.
+ * @param {string} form ID
+ */
+Prado.Validation.AddForm = function(id)
+{
+ Prado.Validation.forms.push($(id));
+}
+
+/**
+ * Add a target that causes validation. Only elements that have been added
+ * can cause validation.
+ * @param {string} target id
+ */
+Prado.Validation.AddTarget = function(id, group)
+{
+ var target = $(id);
+ Event.observe(target, "click", function()
+ {
+ Prado.Validation.ActiveTarget = target;
+ Prado.Validation.CurrentTargetGroup = Prado.Validation.TargetGroups[id];
+ });
+ if(group)
+ {
+ Prado.Validation.TargetGroups[id] = group;
+ Prado.Validation.HasTargetGroup = true;
+ }
+}
+
+/**
+ * Associate a list of validators to a particular control element.
+ * This essentially allows a set of validators to be grouped to a particular button.
+ * @param {list} group array show have, {group : "id", target : "target button"}
+ * @param {array} validator ids
+ */
+Prado.Validation.AddGroup = function(group, validators)
+{
+ group.active = false; //default active status is false.
+ group.target = $(group.target);
+ group.validators = validators;
+ Prado.Validation.groups.push(group);
+
+ //update the active group when the button is clicked.
+ Event.observe(group.target, "click", Prado.Validation.UpdateActiveGroup);
+}
+
+/**
+ * Update the active group, if call manually it will deactivate all groups.
+ * @param {string}
+ * @type {int}
+ */
+Prado.Validation.UpdateActiveGroup = function(ev)
+{
+ var groups = Prado.Validation.groups;
+ for (var i = 0; i < groups.length; i++)
+ {
+ groups[i].active = (isdef(ev) && groups[i].target == Event.element(ev));
+ }
+ Prado.Validation.IsGroupValidation = isdef(ev);
+}
+
+/**
+ * Determine if validation is sucessful. Iterate through the list
+ * of validator instances and call validate(). Only validators that
+ * for a particular form are evaluated. Other validators will be disabled.
+ * If performing group validation, only active validators are visible.
+ * @param {element} the form for the controls to validate.
+ * @type {boolean} true is all validators are valid, false otherwise.
+ */
+Prado.Validation.IsValid = function(form)
+{
+ var valid = true;
+ var validators = Prado.Validation.validators;
+
+ for(var i = 0; i < validators.length; i++)
+ {
+ //prevent validating multiple forms
+ validators[i].enabled = !validators[i].control || undef(validators[i].control.form) || validators[i].control.form == form;
+ //when group validation, only validators in the active group are visible.
+ validators[i].visible = Prado.Validation.IsGroupValidation ? validators[i].inActiveGroup() : true;
+
+ if(Prado.Validation.HasTargetGroup)
+ {
+ if(validators[i].group != Prado.Validation.CurrentTargetGroup)
+ validators[i].enabled = false;
+ }
+
+ valid &= validators[i].validate();
+ }
+
+ //show the summary including the alert box
+ Prado.Validation.ShowSummary(form);
+ //reset all the group active status to false
+ Prado.Validation.UpdateActiveGroup();
+ return valid;
+}
+
+/**
+ * Base validator class. Supply a different validation function
+ * to obtain a different validator. E.g. to use the RequiredFieldValidator
+ * <code>new Prado.Validation(Prado.Validation.RequiredFieldValidator, options);</code>
+ * or to use the CustomValidator,
+ * <code>new Prado.Validation(Prado.Validation.CustomValidator, options);</code>
+ */
+Prado.Validation.prototype =
+{
+ /**
+ * Initialize the validator.
+ * @param {function} the function to call to evaluate if
+ * the validator is valid
+ * @param {string|element} the control ID or element
+ * @param {array} the list of attributes for the validator
+ */
+ initialize : function(validator, attr)
+ {
+ this.evaluateIsValid = validator;
+ this.attr = undef(attr) ? [] : attr;
+ this.message = $(attr.id);
+ this.control = $(attr.controltovalidate);
+ this.enabled = isdef(attr.enabled) ? attr.enabled : true;
+ this.visible = isdef(attr.visible) ? attr.visible : true;
+ this.group = isdef(attr.validationgroup) ? attr.validationgroup : null;
+ this.isValid = true;
+ Prado.Validation.validators.push(this);
+ if(this.evaluateIsValid)
+ this.evaluateIsValid.bind(this);
+ },
+
+ /**
+ * Evaluate the validator only when visible and enabled.
+ * @type {boolean} true if valid, false otherwise.
+ */
+ validate : function()
+ {
+ if(this.visible && this.enabled && this.evaluateIsValid)
+ this.isValid = this.evaluateIsValid();
+ else
+ this.isValid = true;
+
+ this.observe(); //watch for changes to the control values
+ this.update(); //update the validation messages
+ return this.isValid;
+ },
+
+ /**
+ * Hide or show the error messages for "Dynamic" displays.
+ */
+ update : function()
+ {
+ if(this.attr.display == "Dynamic")
+ this.isValid ? Element.hide(this.message) : Element.show(this.message);
+
+ if(this.message)
+ this.message.style.visibility = this.isValid ? "hidden" : "visible";
+
+ //update the control css class name
+ var className = this.attr.controlcssclass;
+ if(this.control && isString(className) && className.length>0)
+ Element.condClassName(this.control, className, !this.isValid);
+ Prado.Validation.ShowSummary();
+
+ var focus = this.attr.focusonerror;
+ var hasGroup = Prado.Validation.HasTargetGroup;
+ var inGroup = this.group == Prado.Validation.CurrentTargetGroup;
+
+ if(focus && (!hasGroup || (hasGroup && inGroup)))
+ Prado.Element.focus(this.attr.focuselementid);
+ },
+
+ /**
+ * Change the validity of the validator, calls update().
+ * @param {boolean} change the isValid state of the validator.
+ */
+ setValid : function(valid)
+ {
+ this.isValid = valid;
+ this.update();
+ },
+
+ /**
+ * Observe changes to the control values, add "onchange" event to the control once.
+ */
+ observe : function()
+ {
+ if(undef(this.observing))
+ {
+ if(this.control && this.control.form)
+ Event.observe(this.control, "change", this.validate.bind(this));
+ this.observing = true;
+ }
+ },
+
+ /**
+ * Convert the value of the control to a specific data type.
+ * @param {string} the data type, "Integer", "Double", "Currency" or "Date".
+ * @param {string} the value to convert, null to get the value from the control.
+ * @type {mixed|null} the converted data value.
+ */
+ convert : function(dataType, value)
+ {
+ if(undef(value))
+ value = Form.Element.getValue(this.control);
+ switch(dataType)
+ {
+ case "Integer":
+ return Prado.Validation.Util.toInteger(value);
+ case "Double" :
+ case "Float" :
+ return Prado.Validation.Util.toDouble(value, this.attr.decimalchar);
+ case "Currency" :
+ return Prado.Validation.Util.toCurrency(
+ value, this.attr.groupchar, this.attr.digits, this.attr.decimalchar);
+ case "Date":
+ return Prado.Validation.Util.toDate(value, this.attr.dateformat);
+ }
+ return value.toString();
+ },
+
+ /**
+ * Determine if the current validator is part of a active validation group.
+ * @type {boolean} true if part of active validation group, false otherwise.
+ */
+ inActiveGroup : function()
+ {
+ var groups = Prado.Validation.groups;
+ for (var i = 0; i < groups.length; i++)
+ {
+ if(groups[i].active && groups[i].validators.contains(this.attr.id))
+ return true;
+ }
+ return false;
+ }
+}
+
+/**
+ * Validation summary class.
+ */
+Prado.Validation.Summary = Class.create();
+Prado.Validation.Summary.prototype =
+{
+ /**
+ * Initialize a validation summary.
+ * @param {array} summary options.
+ */
+ initialize : function(attr)
+ {
+ this.attr = attr;
+ this.div = $(attr.id);
+ this.visible = false;
+ this.enabled = false;
+ this.group = isdef(attr.validationgroup) ? attr.validationgroup : null;
+ Prado.Validation.summaries.push(this);
+ },
+
+ /**
+ * Show the validation summary.
+ * @param {boolean} true to allow alert message
+ */
+ show : function(warn)
+ {
+ var refresh = warn || this.attr.refresh == "1";
+ var messages = this.getMessages();
+ if(messages.length <= 0 || !this.visible || !this.enabled)
+ {
+ if(refresh)
+ Element.hide(this.div);
+ return;
+ }
+
+ if(Prado.Validation.HasTargetGroup)
+ {
+ if(Prado.Validation.CurrentTargetGroup != this.group)
+ {
+ if(refresh)
+ Element.hide(this.div);
+ return;
+ }
+ }
+
+ if(this.attr.showsummary != "False" && refresh)
+ {
+ //Element.show(this.div);
+ this.div.style.display = "block";
+ while(this.div.childNodes.length > 0)
+ this.div.removeChild(this.div.lastChild);
+ new Insertion.Bottom(this.div, this.formatSummary(messages));
+ }
+
+ if(warn)
+ window.scrollTo(this.div.offsetLeft-20, this.div.offsetTop-20);
+
+ var summary = this;
+ if(warn && this.attr.showmessagebox == "True" && refresh)
+ setTimeout(function(){alert(summary.formatMessageBox(messages));},20);
+ },
+
+ /**
+ * Get a list of error messages from the validators.
+ * @type {array} list of messages
+ */
+ getMessages : function()
+ {
+ var validators = Prado.Validation.validators;
+ var messages = [];
+ for(var i = 0; i < validators.length; i++)
+ {
+ if(validators[i].isValid == false
+ && isString(validators[i].attr.errormessage)
+ && validators[i].attr.errormessage.length > 0)
+ {
+
+ messages.push(validators[i].attr.errormessage);
+ }
+ }
+ return messages;
+ },
+
+ /**
+ * Return the format parameters for the summary.
+ * @param {string} format type, "List", "SingleParagraph" or "BulletList"
+ * @type {array} formatting parameters
+ */
+ formats : function(type)
+ {
+ switch(type)
+ {
+ case "List":
+ return { header : "<br />", first : "", pre : "", post : "<br />", last : ""};
+ case "SingleParagraph":
+ return { header : " ", first : "", pre : "", post : " ", last : "<br />"};
+ case "BulletList":
+ default:
+ return { header : "", first : "<ul>", pre : "<li>", post : "</li>", last : "</ul>"};
+ }
+ },
+
+ /**
+ * Format the message summary.
+ * @param {array} list of error messages.
+ * @type {string} formatted message
+ */
+ formatSummary : function(messages)
+ {
+ var format = this.formats(this.attr.displaymode);
+ var output = isdef(this.attr.headertext) ? this.attr.headertext + format.header : "";
+ output += format.first;
+ for(var i = 0; i < messages.length; i++)
+ output += (messages[i].length>0) ? format.pre + messages[i] + format.post : "";
+ output += format.last;
+ return output;
+ },
+ /**
+ * Format the message alert box.
+ * @param {array} a list of error messages.
+ * @type {string} format message for alert.
+ */
+ formatMessageBox : function(messages)
+ {
+ var output = isdef(this.attr.headertext) ? this.attr.headertext + "\n" : "";
+ for(var i = 0; i < messages.length; i++)
+ {
+ switch(this.attr.displaymode)
+ {
+ case "List":
+ output += messages[i] + "\n";
+ break;
+ case "BulletList":
+ default:
+ output += " - " + messages[i] + "\n";
+ break;
+ case "SingleParagraph":
+ output += messages[i] + " ";
+ break;
+ }
+ }
+ return output;
+ },
+
+ /**
+ * Determine if this summary belongs to an active group.
+ * @type {boolean} true if belongs to an active group.
+ */
+ inActiveGroup : function()
+ {
+ var groups = Prado.Validation.groups;
+ for (var i = 0; i < groups.length; i++)
+ {
+ if(groups[i].active && groups[i].id == this.attr.group)
+ return true;
+ }
+ return false;
+ }
+}
+
+/**
+ * Show the validation error message summary.
+ * @param {element} the form that activated the summary call.
+ */
+Prado.Validation.ShowSummary = function(form)
+{
+ var summary = Prado.Validation.summaries;
+ for(var i = 0; i < summary.length; i++)
+ {
+ if(isdef(form))
+ {
+ if(Prado.Validation.IsGroupValidation)
+ {
+ summary[i].visible = summary[i].inActiveGroup();
+ }
+ else
+ {
+ summary[i].visible = undef(summary[i].attr.group);
+ }
+
+ summary[i].enabled = $(summary[i].attr.form) == form;
+ }
+ summary[i].show(form);
+ }
+}
+
+
+
+/**
+ * When a form is try to submit, check the validators, submit
+ * the form only when all validators are valid.
+ * @param {event} form submit event.
+ */
+Prado.Validation.OnSubmit = function(ev)
+{
+ //HTML text editor, tigger save first.
+ //alert(tinyMCE);
+ if(typeof tinyMCE != "undefined")
+ tinyMCE.triggerSave();
+
+ //no active target?
+ if(!Prado.Validation.ActiveTarget) return true;
+ var valid = Prado.Validation.IsValid(Event.element(ev) || ev);
+
+ //not valid? do not submit the form
+ if(Event.element(ev) && !valid)
+ Event.stop(ev);
+
+ //reset the target
+ Prado.Validation.ActiveTarget = null;
+ //Prado.Validation.CurrentTargetGroup = null;
+
+ return valid;
+}
+
+/**
+ * During window onload event, attach onsubmit event for each of the
+ * forms in Prado.Validation.forms.
+ */
+Prado.Validation.OnLoad = function()
+{
+ Event.observe(Prado.Validation.forms,"submit", Prado.Validation.OnSubmit);
+}
+
+
+/**
+ * Validate Validator Groups.
+ * @param string ValidatorGroup
+ * @return boolean true if valid, false otherwise
+ */
+Prado.Validation.ValidateValidatorGroup = function(groupId)
+{
+ var groups = Prado.Validation.groups;
+ var group = null;
+ for(var i = 0; i < groups.length; i++)
+ {
+ if(groups[i].id == groupId)
+ {
+ group = groups[i];
+ Prado.Validation.groups[i].active = true;
+ Prado.Validation.CurrentTargetGroup = null;
+ Prado.Validation.IsGroupValidation = true;
+ }
+ else
+ {
+ Prado.Validation.groups[i].active = false;
+ }
+ }
+ if(group)
+ {
+ return Prado.Validation.IsValid(group.target.form);
+ }
+ return true;
+};
+
+/**
+ * Validate ValidationGroup
+ * @param string ValidationGroup
+ * @return boolean true if valid, false otherwise.
+ */
+Prado.Validation.ValidateValidationGroup= function(groupId)
+{
+ var groups = Prado.Validation.TargetGroups;
+ for(var id in groups)
+ {
+ if(groups[id] == groupId)
+ {
+ var target = $(id);
+ Prado.Validation.ActiveTarget = target;
+ Prado.Validation.CurrentTargetGroup = groupId;
+ Prado.Validation.IsGroupValidation = false;
+ return Prado.Validation.IsValid(target.form);
+ }
+ }
+ return true;
+};
+
+/**
+ * Validate the page
+ * @return boolean true if valid, false otherwise.
+ */
+Prado.Validation.ValidateNonGroup= function(formId)
+{
+ if(Prado.Validation)
+ {
+ var form = $(formId);
+ form = form || document.forms[0];
+ Prado.Validation.ActiveTarget = form;
+ Prado.Validation.CurrentTargetGroup = null;
+ Prado.Validation.IsGroupValidation = false;
+ return Prado.Validation.IsValid(form);
+ }
+ return true;
+};
+
+
+
+/**
+ * Register Prado.Validation.Onload() for window.onload event.
+ */
+Event.OnLoad(Prado.Validation.OnLoad); \ No newline at end of file
diff --git a/framework/Web/Javascripts/prado/validators.js b/framework/Web/Javascripts/prado/validators.js
new file mode 100644
index 00000000..427b46fc
--- /dev/null
+++ b/framework/Web/Javascripts/prado/validators.js
@@ -0,0 +1,225 @@
+
+Prado.Validation.TRequiredFieldValidator=function(){
+ var inputType = this.control.getAttribute("type");
+ if(inputType == 'file'){
+ return true;
+ }
+ else{
+ var trim=Prado.Util.trim;
+ var a=trim(Form.Element.getValue(this.control));
+ var b=trim(this.attr.initialvalue);
+ return(a!=b);
+ }
+}
+
+
+Prado.Validation.TRegularExpressionValidator = function()
+{
+ var trim = Prado.Util.trim;
+ var value = trim(Form.Element.getValue(this.control));
+ if (value == "") return true;
+ var rx = new RegExp(this.attr.validationexpression);
+ var matches = rx.exec(value);
+ return (matches != null && value == matches[0]);
+}
+
+Prado.Validation.TEmailAddressValidator = Prado.Validation.TRegularExpressionValidator;
+
+Prado.Validation.TCustomValidator = function()
+{
+ var value = isNull(this.control) ? null : $F(this.control);
+ var func = this.attr.clientvalidationfunction;
+ eval("var validate = "+func);
+ return validate && isFunction(validate) ? validate(this, value) : true;
+}
+
+Prado.Validation.TRangeValidator = function()
+{
+ var trim = Prado.Util.trim;
+ var value = trim(Form.Element.getValue(this.control));
+ if (value == "") return true;
+
+ var minval = this.attr.minimumvalue;
+ var maxval = this.attr.maximumvalue;
+
+ if (undef(minval) && undef(maxval))
+ return true;
+
+ if (minval == "") minval = 0;
+ if (maxval == "") maxval = 0;
+
+ var dataType = this.attr.type;
+
+ if(undef(dataType))
+ return (parseFloat(value) >= parseFloat(minval)) && (parseFloat(value) <= parseFloat(maxval));
+
+ //now do datatype range check.
+ var min = this.convert(dataType, minval);
+ var max = this.convert(dataType, maxval);
+ value = this.convert(dataType, value);
+ return value >= min && value <= max;
+}
+
+Prado.Validation.TCompareValidator = function()
+{
+ var trim = Prado.Util.trim;
+ var value = trim(Form.Element.getValue(this.control));
+ if (value.length == 0) return true;
+
+ var compareTo;
+
+ var comparee = $(this.attr.controlhookup);;
+
+ if(comparee)
+ compareTo = trim(Form.Element.getValue(comparee));
+ else
+ {
+ compareTo = isString(this.attr.valuetocompare) ? this.attr.valuetocompare : "";
+ }
+
+ var compare = Prado.Validation.TCompareValidator.compare;
+
+ var isValid = compare.bind(this)(value, compareTo);
+
+ //update the comparee control css class name and add onchange event once.
+ if(comparee)
+ {
+ var className = this.attr.controlcssclass;
+ if(isString(className) && className.length>0)
+ Element.condClassName(comparee, className, !isValid);
+ if(undef(this.observingComparee))
+ {
+ Event.observe(comparee, "change", this.validate.bind(this));
+ this.observingComparee = true;
+ }
+ }
+ return isValid;
+}
+
+/**
+ * Compare the two values, also performs data type check.
+ * @param {string} value to compare with
+ * @param {string} value to compare
+ * @type {boolean} true if comparison or type check is valid, false otherwise.
+ */
+Prado.Validation.TCompareValidator.compare = function(operand1, operand2)
+{
+ var op1, op2;
+ if ((op1 = this.convert(this.attr.type, operand1)) == null)
+ return false;
+ if (this.attr.operator == "DataTypeCheck")
+ return true;
+ if ((op2 = this.convert(this.attr.type, operand2)) == null)
+ return true;
+ switch (this.attr.operator)
+ {
+ case "NotEqual":
+ return (op1 != op2);
+ case "GreaterThan":
+ return (op1 > op2);
+ case "GreaterThanEqual":
+ return (op1 >= op2);
+ case "LessThan":
+ return (op1 < op2);
+ case "LessThanEqual":
+ return (op1 <= op2);
+ default:
+ return (op1 == op2);
+ }
+}
+
+Prado.Validation.TRequiredListValidator = function()
+{
+ var min = undef(this.attr.min) ? Number.NEGATIVE_INFINITY : parseInt(this.attr.min);
+ var max = undef(this.attr.max) ? Number.POSITIVE_INFINITY : parseInt(this.attr.max);
+
+ var elements = document.getElementsByName(this.attr.selector);
+
+ if(elements.length <= 0)
+ elements = document.getElementsBySelector(this.attr.selector);
+
+ if(elements.length <= 0)
+ return true;
+
+ var required = new Array();
+ if(isString(this.attr.required) && this.attr.required.length > 0)
+ required = this.attr.required.split(/,\s* /);
+
+ var isValid = true;
+
+ var validator = Prado.Validation.TRequiredListValidator;
+
+ switch(elements[0].type)
+ {
+ case 'radio':
+ case 'checkbox':
+ isValid = validator.IsValidRadioList(elements, min, max, required);
+ break;
+ case 'select-multiple':
+ isValid = validator.IsValidSelectMultipleList(elements, min, max, required);
+ break;
+ }
+
+ var className = this.attr.elementcssclass;
+ if(isString(className) && className.length>0)
+ map(elements, function(element){ condClass(element, className, !isValid); });
+ if(undef(this.observingRequiredList))
+ {
+ Event.observe(elements, "change", this.validate.bind(this));
+ this.observingRequiredList = true;
+ }
+ return isValid;
+}
+
+//radio group selection
+Prado.Validation.TRequiredListValidator.IsValidRadioList = function(elements, min, max, required)
+{
+ var checked = 0;
+ var values = new Array();
+ for(var i = 0; i < elements.length; i++)
+ {
+ if(elements[i].checked)
+ {
+ checked++;
+ values.push(elements[i].value);
+ }
+ }
+ return Prado.Validation.TRequiredListValidator.IsValidList(checked, values, min, max, required);
+}
+
+//multiple selection check
+Prado.Validation.TRequiredListValidator.IsValidSelectMultipleList = function(elements, min, max, required)
+{
+ var checked = 0;
+ var values = new Array();
+ for(var i = 0; i < elements.length; i++)
+ {
+ var selection = elements[i];
+ for(var j = 0; j < selection.options.length; j++)
+ {
+ if(selection.options[j].selected)
+ {
+ checked++;
+ values.push(selection.options[j].value);
+ }
+ }
+ }
+ return Prado.Validation.TRequiredListValidator.IsValidList(checked, values, min, max, required);
+}
+
+//check if the list was valid
+Prado.Validation.TRequiredListValidator.IsValidList = function(checkes, values, min, max, required)
+{
+ var exists = true;
+
+ if(required.length > 0)
+ {
+ //required and the values must at least be have same lengths
+ if(values.length < required.length)
+ return false;
+ for(var k = 0; k < required.length; k++)
+ exists = exists && values.contains(required[k]);
+ }
+
+ return exists && checkes >= min && checkes <= max;
+}