From c84f3e19b54cf54f525f4b2d158696ae32d1bf60 Mon Sep 17 00:00:00 2001
From: wei <>
Date: Tue, 25 Apr 2006 01:00:08 +0000
Subject: Add TListControlValidator. Update client-side validators,
datepicker.js, colorpicker.js.
---
framework/Exceptions/messages.txt | 22 +-
framework/Util/TSimpleDateFormatter.php | 20 +-
framework/Web/Javascripts/TJavaScript.php | 13 +-
.../Web/Javascripts/colorpicker/colorpicker.js | 59 ++-
framework/Web/Javascripts/datepicker/datepicker.js | 96 +++--
framework/Web/Javascripts/extended/event.js | 5 +-
framework/Web/Javascripts/js/colorpicker.js | 36 +-
framework/Web/Javascripts/js/datepicker.js | 86 +++--
framework/Web/Javascripts/js/prado.js | 5 +-
framework/Web/Javascripts/js/validator.js | 237 +++++++++---
framework/Web/Javascripts/prado/form.js | 2 +-
framework/Web/Javascripts/prado/validation3.js | 396 +++++++++++++++++----
framework/Web/UI/WebControls/TBaseValidator.php | 6 +-
framework/Web/UI/WebControls/TCheckBox.php | 4 +-
framework/Web/UI/WebControls/TCheckBoxList.php | 14 +-
framework/Web/UI/WebControls/TCompareValidator.php | 16 +-
framework/Web/UI/WebControls/TDataGridColumn.php | 30 +-
.../Web/UI/WebControls/TDataTypeValidator.php | 8 +-
framework/Web/UI/WebControls/TDatePicker.php | 45 ++-
framework/Web/UI/WebControls/THtmlArea.php | 2 +-
framework/Web/UI/WebControls/TListControl.php | 52 ++-
.../Web/UI/WebControls/TListControlValidator.php | 214 +++++++++++
framework/Web/UI/WebControls/TRangeValidator.php | 41 +--
.../Web/UI/WebControls/TRequiredFieldValidator.php | 30 +-
24 files changed, 1132 insertions(+), 307 deletions(-)
create mode 100644 framework/Web/UI/WebControls/TListControlValidator.php
(limited to 'framework')
diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt
index e11d4261..20bafba2 100644
--- a/framework/Exceptions/messages.txt
+++ b/framework/Exceptions/messages.txt
@@ -118,6 +118,7 @@ template_property_duplicated = Property {0} is configured twice or more.
template_eventhandler_invalid = {0}.{1} can only accept a static string.
template_controlid_invalid = {0}.ID can only accept a static text string.
template_controlskinid_invalid = {0}.SkinID can only accept a static text string.
+template_content_unexpected = Unexpected content is encountered when instantiating template: {0}.
xmldocument_file_read_failed = TXmlDocument is unable to read file '{0}'.
xmldocument_file_write_failed = TXmlDocument is unable to write file '{0}'.
@@ -165,7 +166,7 @@ templatecontrol_placeholder_inexistent = TContent '{0}' does not have a matching
page_form_duplicated = A page can contain at most one TForm. Use regular HTML form tags for the rest forms.
page_isvalid_unknown = TPage.IsValid has not been evaluated yet.
page_postbackcontrol_invalid = Unable to determine postback control '{0}'.
-page_control_outofform = Control '{0}' must be enclosed within TForm.
+page_control_outofform = {0} '{1}' must be enclosed within TForm.
page_head_duplicated = A page can contain at most one THead.
page_statepersister_invalid = Page state persister must implement IPageStatePersister interface.
@@ -183,6 +184,7 @@ webcontrol_style_invalid = {0}.Style must take string value only.
listcontrol_selection_invalid = {0} has an invalid selection that is set before performing databinding.
listcontrol_selectedindex_invalid = {0}.SelectedIndex has an invalid value {1}.
listcontrol_selectedvalue_invalid = {0}.SelectedValue has an invalid value '{1}'.
+listcontrol_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2}
label_associatedcontrol_invalid = TLabel.AssociatedControl '{0}' cannot be found.
@@ -196,7 +198,9 @@ tablestyle_cellpadding_invalid = TTableStyle.CellPadding must take an integer
tablestyle_cellspacing_invalid = TTableStyle.CellSpacing must take an integer equal to or greater than -1.
pagestatepersister_pagestate_corrupted = Page state is corrupted.
-pagestatepersister_privatekey_invalid = TPageStatePersister.PrivateKey must take a string no shorter than 8 characters.
+
+sessionpagestatepersister_pagestate_corrupted = Page state is corrupted.
+sessionpagestatepersister_historysize_invalid = TSessionPageStatePersister.History must be an integer greater than 0.
listitemcollection_item_invalid = TListItemCollection can only take strings or TListItem objects.
@@ -227,6 +231,8 @@ basevalidator_forcontrol_unsupported = {0}.ForControl is not supported.
comparevalidator_controltocompare_invalid = TCompareValidator.ControlToCompare contains an invalid control ID path.
+tlistcontrolvalidator_invalid_control = {0}.ControlToValidate contains an invalid TListControl ID path, "{1}" is a {2}.
+
repeater_template_required = TRepeater.{0} requires a template instance implementing ITemplate interface.
datalist_template_required = TDataList.{0} requires a template instance implementing ITemplate interface.
templatecolumn_template_required = TTemplateColumn.{0} requires a template instance implementing ITemplate interface.
@@ -277,4 +283,14 @@ htmlarea_tarfile_invalid = THtmlArea is unable to locate the TinyMCE tar file
parametermodule_parameterfile_unchangeable = TParameterModule.ParameterFile is not changeable because the module is already initialized.
parametermodule_parameterfile_invalid = TParameterModule.ParameterFile '{0}' is invalid. Make sure it is in namespace format and the file extension is '.xml'.
-parametermodule_parameterid_required = Parameter element must have 'id' attribute.
\ No newline at end of file
+parametermodule_parameterid_required = Parameter element must have 'id' attribute.
+
+datagridcolumn_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2}
+
+outputcache_duration_invalid = {0}.Duration must be an integer no less than 0.
+
+stack_data_not_iterable = TStack can only fetch data from an array or a traversable object.
+stack_empty = TStack is empty.
+
+queue_data_not_iterable = TQueue can only fetch data from an array or a traversable object.
+queue_empty = TQueue is empty.
\ No newline at end of file
diff --git a/framework/Util/TSimpleDateFormatter.php b/framework/Util/TSimpleDateFormatter.php
index 7be49bc9..2a3da63a 100644
--- a/framework/Util/TSimpleDateFormatter.php
+++ b/framework/Util/TSimpleDateFormatter.php
@@ -190,18 +190,20 @@ class TSimpleDateFormatter
/**
* Parse the string according to the pattern.
- * @param string date string to parse
+ * @param string|int date string or integer to parse
* @return int date time stamp
* @throws TInvalidDataValueException if date string is malformed.
*/
public function parse($value,$defaultToCurrentTime=true)
{
- if(!is_string($value))
+ if(is_int($value))
+ return $value;
+ else if(!is_string($value))
throw new TInvalidDataValueException('date_to_parse_must_be_string', $value);
if(empty($this->pattern)) return time();
- $date = $this->getDate(time());
+ $date = time();
if($this->length(trim($value)) < 1)
return $defaultToCurrentTime ? $date : null;
@@ -246,7 +248,8 @@ class TSimpleDateFormatter
if ($token=='y') { $x=2;$y=4; }
$year = $this->getInteger($value,$i_val,$x,$y);
if(is_null($year))
- throw new TInvalidDataValueException('Invalid year', $value);
+ return null;
+ //throw new TInvalidDataValueException('Invalid year', $value);
$i_val += strlen($year);
if(strlen($year) == 2)
{
@@ -264,7 +267,8 @@ class TSimpleDateFormatter
$this->length($token),2);
$iMonth = intval($month);
if(is_null($month) || $iMonth < 1 || $iMonth > 12 )
- throw new TInvalidDataValueException('Invalid month', $value);
+ return null;
+ //throw new TInvalidDataValueException('Invalid month', $value);
$i_val += strlen($month);
$month = $iMonth;
}
@@ -274,14 +278,16 @@ class TSimpleDateFormatter
$this->length($token), 2);
$iDay = intval($day);
if(is_null($day) || $iDay < 1 || $iDay >31)
- throw new TInvalidDataValueException('Invalid day', $value);
+ return null;
+ //throw new TInvalidDataValueException('Invalid day', $value);
$i_val += strlen($day);
$day = $iDay;
}
else
{
if($this->substring($value, $i_val, $this->length($token)) != $token)
- throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value);
+ return null;
+ //throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value);
else
$i_val += $this->length($token);
}
diff --git a/framework/Web/Javascripts/TJavaScript.php b/framework/Web/Javascripts/TJavaScript.php
index 4066565b..a3848201 100644
--- a/framework/Web/Javascripts/TJavaScript.php
+++ b/framework/Web/Javascripts/TJavaScript.php
@@ -96,9 +96,16 @@ class TJavaScript
* //expects the following javascript code
* // {'onLoading':'doit','onComplete':'more'}
*
+ *
+ * To pass raw javascript statements start strings with
+ * javascript:. E.g.
+ *
+ * $options['onLoading'] = "javascript:function(){ alert('hello'); }";
+ * //outputs {'onLoading':function(){ alert('hello'); }}
+ *
*
- * For higher complexity data structures use {@link jsonEncode} and {@link jsonDecode}
- * to serialize and unserialize.
+ * For higher complexity data structures use {@link jsonEncode} and {@link
+ * jsonDecode} to serialize and unserialize.
*
* @param mixed PHP variable to be encoded
* @param boolean whether the output is a map or a list.
@@ -115,6 +122,8 @@ class TJavaScript
if(($first==='[' && $last===']') || ($first==='{' && $last==='}'))
return $value;
}
+ else if(strpos($value, 'javascript:')===0)
+ return substr($value,11);
return "'".self::quoteString($value)."'";
}
else if(is_bool($value))
diff --git a/framework/Web/Javascripts/colorpicker/colorpicker.js b/framework/Web/Javascripts/colorpicker/colorpicker.js
index cc4587ff..dc80f0c7 100644
--- a/framework/Web/Javascripts/colorpicker/colorpicker.js
+++ b/framework/Web/Javascripts/colorpicker/colorpicker.js
@@ -83,7 +83,7 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
if(mode == "Full")
this.initializeFullPicker();
}
- this.show();
+ this.show(mode);
},
show : function(type)
@@ -108,6 +108,7 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
if(type == "Full")
{
+ this.observeMouseMovement();
var color = Rico.Color.createFromHex(this.input.value);
this.inputs.oldColor.style.backgroundColor = color.asHex();
this.setColor(color,true);
@@ -124,8 +125,14 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
this.element.style.display = "none";
this.showing = false;
- Event.stopObserving(document.body, "click", this._documentClickEvent);
+ Event.stopObserving(document.body, "click", this._documentClickEvent);
Event.stopObserving(document,"keydown", this._documentKeyDownEvent);
+
+ if(this._observingMouseMove)
+ {
+ Event.stopObserving(document.body, "mousemove", this._onMouseMove);
+ this._observingMouseMove = false;
+ }
}
},
@@ -208,7 +215,7 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
{
this.input.value = color.toString().toUpperCase();
this.button.style.backgroundColor = color.toString();
- if(isFunction(this.onChange))
+ if(typeof(this.onChange) == "function")
this.onChange(color);
},
@@ -246,7 +253,7 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
TR(null,
TD(null,'H:'),
- TD(null,this.inputs['H'], '°')),
+ TD(null,this.inputs['H'], '??')),
TR(null,
TD(null,'S:'),
@@ -333,34 +340,46 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
this._onMouseMove = this.onMouseMove.bind(this);
Event.observe(this.inputs.background, "mousedown", this._onColorMouseDown);
+ Event.observe(this.inputs.selector, "mousedown", this._onColorMouseDown);
Event.observe(this.inputs.hue, "mousedown", this._onHueMouseDown);
+ Event.observe(this.inputs.slider, "mousedown", this._onHueMouseDown);
Event.observe(document.body, "mouseup", this._onMouseUp);
-
- //Because of using the CSS filter, IE can't do colour change quickly
- //if(!Prado.Browser().ie)
- Event.observe(document.body, "mousemove", this._onMouseMove);
+
+ this.observeMouseMovement();
Event.observe(this.buttons.Cancel, "click", this.hide.bindEvent(this,this.options['Mode']));
Event.observe(this.buttons.OK, "click", this.onOKClicked.bind(this));
},
+ observeMouseMovement : function()
+ {
+ if(!this._observingMouseMove)
+ {
+ Event.observe(document.body, "mousemove", this._onMouseMove);
+ this._observingMouseMove = true;
+ }
+ },
+
onColorMouseDown : function(ev)
{
this.isMouseDownOnColor = true;
this.onMouseMove(ev);
+ Event.stop(ev);
},
onHueMouseDown : function(ev)
{
this.isMouseDownOnHue = true;
this.onMouseMove(ev);
+ Event.stop(ev);
},
onMouseUp : function(ev)
{
this.isMouseDownOnColor = false;
this.isMouseDownOnHue = false;
+ Event.stop(ev);
},
onMouseMove : function(ev)
@@ -369,6 +388,7 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
this.changeSV(ev);
if(this.isMouseDownOnHue)
this.changeH(ev);
+ Event.stop(ev);
},
changeSV : function(ev)
@@ -376,18 +396,25 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
var px = Event.pointerX(ev);
var py = Event.pointerY(ev);
var pos = Position.cumulativeOffset(this.inputs.background);
+
var x = this.truncate(px - pos[0],0,255);
var y = this.truncate(py - pos[1],0,255);
- var h = this.truncate(this.inputs.H.value,0,360)/360;
var s = x/255;
var b = (255-y)/255;
+ var current_s = parseInt(this.inputs.S.value);
+ var current_b = parseInt(this.inputs.V.value);
+
+ if(current_s == parseInt(s*100) && current_b == parseInt(b*100)) return;
+
+ var h = this.truncate(this.inputs.H.value,0,360)/360;
var color = new Rico.Color();
color.rgb = Rico.Color.HSBtoRGB(h,s,b);
+
this.inputs.selector.style.left = x+"px";
this.inputs.selector.style.top = y+"px";
@@ -403,6 +430,10 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
var y = this.truncate(py - pos[1],0,255);
var h = (255-y)/255;
+ var current_h = this.truncate(this.inputs.H.value,0,360);
+ current_h = current_h == 0 ? 360 : current_h;
+ if(current_h == parseInt(h*360)) return;
+
var s = parseInt(this.inputs.S.value)/100;
var b = parseInt(this.inputs.V.value)/100;
var color = new Rico.Color();
@@ -472,14 +503,8 @@ Object.extend(Prado.WebUI.TColorPicker.prototype,
var images = Prado.WebUI.TColorPicker.UIImages;
var changeCss = color.isBright() ? 'removeClassName' : 'addClassName';
- Element[changeCss](this.inputs.selector, 'target_white');
-/* if(color.isBright())
- Element.removeCssClass(this.inputs.selector, 'target_white');
- //this.inputs.selector.src = images['target_black.gif'];
- else
- Element.addCssClass(this.inputs.selector, 'target_white');
- //this.inputs.selector.src = images['target_white.gif'];
-*/
+ Element[changeCss](this.inputs.selector, 'target_white');
+
if(update)
this.updateSelectors(color);
},
diff --git a/framework/Web/Javascripts/datepicker/datepicker.js b/framework/Web/Javascripts/datepicker/datepicker.js
index e906120c..79763811 100644
--- a/framework/Web/Javascripts/datepicker/datepicker.js
+++ b/framework/Web/Javascripts/datepicker/datepicker.js
@@ -1,10 +1,51 @@
Prado.WebUI.TDatePicker = Class.create();
+Object.extend(Prado.WebUI.TDatePicker,
+{
+ /**
+ * @return Date the date from drop down list options.
+ */
+ getDropDownDate : function(control)
+ {
+ var now=new Date();
+ var year=now.getFullYear();
+ var month=now.getMonth();
+ var day=1;
+
+ var month_list = this.getMonthListControl(control);
+ var day_list = this.getDayListControl(control);
+ var year_list = this.getYearListControl(control);
+
+ var day = day_list ? $F(day_list) : 1;
+ var month = month_list ? $F(month_list) : now.getMonth();
+ var year = year_list ? $F(year_list) : now.getFullYear();
+
+ return new Date(year,month,day, 0, 0, 0);
+ },
+
+ getYearListControl : function(control)
+ {
+ return $(control.id+"_year");
+ },
+
+ getMonthListControl : function(control)
+ {
+ return $(control.id+"_month");
+ },
+
+ getDayListControl : function(control)
+ {
+ return $(control.id+"_day");
+ }
+});
+
Prado.WebUI.TDatePicker.prototype =
{
MonthNames : [ "January", "February", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December"
],
+ AbbreviatedMonthNames : ["Jan", "Feb", "Mar", "Apr", "May",
+ "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
ShortWeekDayNames : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
@@ -188,7 +229,7 @@ Prado.WebUI.TDatePicker.prototype =
var todayButton = document.createElement("button");
todayButton.className = "todayButton";
var today = this.newDate();
- var buttonText = today.SimpleFormat(this.Format);
+ var buttonText = today.SimpleFormat(this.Format,this);
todayButton.appendChild(document.createTextNode(buttonText));
div.appendChild(todayButton);
@@ -386,20 +427,27 @@ Prado.WebUI.TDatePicker.prototype =
return false;
},
- onchange : function()
+ onChange : function()
{
if(this.options.InputMode == "TextBox")
+ {
this.control.value = this.formatDate();
+ Event.fireEvent(this.control, "change");
+ }
else
{
- var day = $(this.options.ID+"_day");
- var month = $(this.options.ID+"_month");
- var year = $(this.options.ID+"_year");
+ var day = Prado.WebUI.TDatePicker.getDayListControl(this.control);
+ var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control);
+ var year = Prado.WebUI.TDatePicker.getYearListControl(this.control);
var date = this.selectedDate;
if(day)
+ {
day.selectedIndex = date.getDate()-1;
+ }
if(month)
+ {
month.selectedIndex = date.getMonth();
+ }
if(year)
{
var years = year.options;
@@ -407,19 +455,20 @@ Prado.WebUI.TDatePicker.prototype =
for(var i = 0; i < years.length; i++)
years[i].selected = years[i].value.toInteger() == currentYear;
}
+ Event.fireEvent(day || month || year, "change");
}
},
formatDate : function()
{
- return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format) : '';
+ return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format,this) : '';
},
newDate : function(date)
{
if(!date)
date = new Date();
- if(isString(date) || isNumber(date))
+ if(typeof(date) == "string" || typeof(date) == "number")
date = new Date(date);
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0,0,0);
},
@@ -432,8 +481,8 @@ Prado.WebUI.TDatePicker.prototype =
this.updateHeader();
this.update();
- if (isFunction(this.onchange))
- this.onchange();
+ if (typeof(this.onChange) == "function")
+ this.onChange();
},
getElement : function()
@@ -443,7 +492,7 @@ Prado.WebUI.TDatePicker.prototype =
getSelectedDate : function ()
{
- return isNull(this.selectedDate) ? null : this.newDate(this.selectedDate);
+ return this.selectedDate == null ? null : this.newDate(this.selectedDate);
},
setYear : function(year)
@@ -480,8 +529,9 @@ Prado.WebUI.TDatePicker.prototype =
pos[1] += this.control.offsetHeight;
else
{
- if($(this.options.ID+"_day"))
- pos[1] += $(this.options.ID+"_day").offsetHeight-1;
+ var dayList = Prado.WebUI.TDatePicker.getDayListControl(this.control);
+ if(dayList)
+ pos[1] += dayList.offsetHeight-1;
}
this._calDiv.style.display = "block";
@@ -493,7 +543,7 @@ Prado.WebUI.TDatePicker.prototype =
this.documentKeyDownEvent = this.keyPressed.bindEvent(this);
Event.observe(document.body, "click", this.documentClickEvent);
var date = this.getDateFromInput();
- if(!isNull(date))
+ if(date)
{
this.selectedDate = date;
this.setSelectedDate(date);
@@ -508,20 +558,7 @@ Prado.WebUI.TDatePicker.prototype =
if(this.options.InputMode == "TextBox")
return Date.SimpleParse($F(this.control), this.Format);
else
- {
- var now=new Date();
- var year=now.getFullYear();
- var month=now.getMonth();
- var date=1;
- if($(this.options.ID+"_day"))
- day = $F(this.options.ID+"_day");
- if($(this.options.ID+"_month"))
- month = $F(this.options.ID+"_month");
- if($(this.options.ID+"_year"))
- year = $F(this.options.ID+"_year");
- var newdate=new Date(year,month,day, 0, 0, 0);
- return newdate;
- }
+ return Prado.WebUI.TDatePicker.getDropDownDate(this.control);
},
//hide the calendar when clicked outside any calendar
@@ -610,7 +647,10 @@ Prado.WebUI.TDatePicker.prototype =
hover : function(ev)
{
//conditionally add the hover class to the event target element.
- Element.condClassName(Event.element(ev), "hover", ev.type=="mouseover");
+ if(ev.type == "mouseover")
+ Event.element(ev).addClassName("hover");
+ else
+ Event.element(ev).removeClassName("hover");
},
updateHeader : function () {
diff --git a/framework/Web/Javascripts/extended/event.js b/framework/Web/Javascripts/extended/event.js
index 40cf60a1..13e875da 100644
--- a/framework/Web/Javascripts/extended/event.js
+++ b/framework/Web/Javascripts/extended/event.js
@@ -95,9 +95,10 @@ Object.extend(Event,
else if(element.fireEvent)
{
element.fireEvent('on'+type);
- element[type]();
+ if(element[type])
+ element[type]();
}
- else
+ else if(element[type])
element[type]();
}
});
\ No newline at end of file
diff --git a/framework/Web/Javascripts/js/colorpicker.js b/framework/Web/Javascripts/js/colorpicker.js
index 27e180b0..b926dc93 100644
--- a/framework/Web/Javascripts/js/colorpicker.js
+++ b/framework/Web/Javascripts/js/colorpicker.js
@@ -257,7 +257,7 @@ this.input.parentNode.appendChild(this.iePopUp);
if(mode == "Full")
this.initializeFullPicker();
}
-this.show();
+this.show(mode);
},
show : function(type)
{
@@ -276,6 +276,7 @@ Event.observe(document,"keydown", this._documentKeyDownEvent);
this.showing = true;
if(type == "Full")
{
+this.observeMouseMovement();
var color = Rico.Color.createFromHex(this.input.value);
this.inputs.oldColor.style.backgroundColor = color.asHex();
this.setColor(color,true);
@@ -292,6 +293,11 @@ this.element.style.display = "none";
this.showing = false;
Event.stopObserving(document.body, "click", this._documentClickEvent);
Event.stopObserving(document,"keydown", this._documentKeyDownEvent);
+if(this._observingMouseMove)
+{
+Event.stopObserving(document.body, "mousemove", this._onMouseMove);
+this._observingMouseMove = false;
+}
}
},
keyPressed : function(event,type)
@@ -367,7 +373,7 @@ updateColor : function(color)
{
this.input.value = color.toString().toUpperCase();
this.button.style.backgroundColor = color.toString();
-if(isFunction(this.onChange))
+if(typeof(this.onChange) == "function")
this.onChange(color);
},
getFullPickerContainer : function(pickerID)
@@ -394,7 +400,7 @@ TD({className:'currentcolor',colSpan:2},
this.inputs['currentColor'], this.inputs['oldColor'])),
TR(null,
TD(null,'H:'),
-TD(null,this.inputs['H'], '°')),
+TD(null,this.inputs['H'], '??')),
TR(null,
TD(null,'S:'),
TD(null,this.inputs['S'], '%')),
@@ -463,26 +469,39 @@ this._onHueMouseDown = this.onHueMouseDown.bind(this);
this._onMouseUp = this.onMouseUp.bind(this);
this._onMouseMove = this.onMouseMove.bind(this);
Event.observe(this.inputs.background, "mousedown", this._onColorMouseDown);
+Event.observe(this.inputs.selector, "mousedown", this._onColorMouseDown);
Event.observe(this.inputs.hue, "mousedown", this._onHueMouseDown);
+Event.observe(this.inputs.slider, "mousedown", this._onHueMouseDown);
Event.observe(document.body, "mouseup", this._onMouseUp);
-Event.observe(document.body, "mousemove", this._onMouseMove);
+this.observeMouseMovement();
Event.observe(this.buttons.Cancel, "click", this.hide.bindEvent(this,this.options['Mode']));
Event.observe(this.buttons.OK, "click", this.onOKClicked.bind(this));
},
+observeMouseMovement : function()
+{
+if(!this._observingMouseMove)
+{
+Event.observe(document.body, "mousemove", this._onMouseMove);
+this._observingMouseMove = true;
+}
+},
onColorMouseDown : function(ev)
{
this.isMouseDownOnColor = true;
this.onMouseMove(ev);
+Event.stop(ev);
},
onHueMouseDown : function(ev)
{
this.isMouseDownOnHue = true;
this.onMouseMove(ev);
+Event.stop(ev);
},
onMouseUp : function(ev)
{
this.isMouseDownOnColor = false;
this.isMouseDownOnHue = false;
+Event.stop(ev);
},
onMouseMove : function(ev)
{
@@ -490,6 +509,7 @@ if(this.isMouseDownOnColor)
this.changeSV(ev);
if(this.isMouseDownOnHue)
this.changeH(ev);
+Event.stop(ev);
},
changeSV : function(ev)
{
@@ -498,9 +518,12 @@ var py = Event.pointerY(ev);
var pos = Position.cumulativeOffset(this.inputs.background);
var x = this.truncate(px - pos[0],0,255);
var y = this.truncate(py - pos[1],0,255);
-var h = this.truncate(this.inputs.H.value,0,360)/360;
var s = x/255;
var b = (255-y)/255;
+var current_s = parseInt(this.inputs.S.value);
+var current_b = parseInt(this.inputs.V.value);
+if(current_s == parseInt(s*100) && current_b == parseInt(b*100)) return;
+var h = this.truncate(this.inputs.H.value,0,360)/360;
var color = new Rico.Color();
color.rgb = Rico.Color.HSBtoRGB(h,s,b);
this.inputs.selector.style.left = x+"px";
@@ -514,6 +537,9 @@ var py = Event.pointerY(ev);
var pos = Position.cumulativeOffset(this.inputs.background);
var y = this.truncate(py - pos[1],0,255);
var h = (255-y)/255;
+var current_h = this.truncate(this.inputs.H.value,0,360);
+current_h = current_h == 0 ? 360 : current_h;
+if(current_h == parseInt(h*360)) return;
var s = parseInt(this.inputs.S.value)/100;
var b = parseInt(this.inputs.V.value)/100;
var color = new Rico.Color();
diff --git a/framework/Web/Javascripts/js/datepicker.js b/framework/Web/Javascripts/js/datepicker.js
index e82507ea..19d39bbe 100644
--- a/framework/Web/Javascripts/js/datepicker.js
+++ b/framework/Web/Javascripts/js/datepicker.js
@@ -1,10 +1,41 @@
Prado.WebUI.TDatePicker = Class.create();
+Object.extend(Prado.WebUI.TDatePicker,
+{
+getDropDownDate : function(control)
+{
+var now=new Date();
+var year=now.getFullYear();
+var month=now.getMonth();
+var day=1;
+var month_list = this.getMonthListControl(control);
+ var day_list = this.getDayListControl(control);
+ var year_list = this.getYearListControl(control);
+var day = day_list ? $F(day_list) : 1;
+var month = month_list ? $F(month_list) : now.getMonth();
+var year = year_list ? $F(year_list) : now.getFullYear();
+return new Date(year,month,day, 0, 0, 0);
+},
+getYearListControl : function(control)
+{
+return $(control.id+"_year");
+},
+getMonthListControl : function(control)
+{
+return $(control.id+"_month");
+},
+getDayListControl : function(control)
+{
+return $(control.id+"_day");
+}
+});
Prado.WebUI.TDatePicker.prototype =
{
MonthNames : ["January","February","March","April",
"May","June","July","August",
"September","October","November","December"
],
+AbbreviatedMonthNames : ["Jan", "Feb", "Mar", "Apr", "May",
+"Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
ShortWeekDayNames : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
Format : "yyyy-MM-dd",
FirstDayOfWeek : 1,
@@ -138,7 +169,7 @@ this._calDiv.appendChild(div);
var todayButton = document.createElement("button");
todayButton.className = "todayButton";
var today = this.newDate();
-var buttonText = today.SimpleFormat(this.Format);
+var buttonText = today.SimpleFormat(this.Format,this);
todayButton.appendChild(document.createTextNode(buttonText));
div.appendChild(todayButton);
if(Prado.Browser().ie)
@@ -292,20 +323,27 @@ var m = d.getMonth() + n;
this.setMonth(m);
return false;
},
-onchange : function()
+onChange : function()
{
if(this.options.InputMode == "TextBox")
+{
this.control.value = this.formatDate();
+Event.fireEvent(this.control, "change");
+}
else
{
-var day = $(this.options.ID+"_day");
-var month = $(this.options.ID+"_month");
-var year = $(this.options.ID+"_year");
+var day = Prado.WebUI.TDatePicker.getDayListControl(this.control);
+var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control);
+var year = Prado.WebUI.TDatePicker.getYearListControl(this.control);
var date = this.selectedDate;
if(day)
+{
day.selectedIndex = date.getDate()-1;
+}
if(month)
+{
month.selectedIndex = date.getMonth();
+}
if(year)
{
var years = year.options;
@@ -313,17 +351,18 @@ var currentYear = date.getFullYear();
for(var i = 0; i < years.length; i++)
years[i].selected = years[i].value.toInteger() == currentYear;
}
+Event.fireEvent(day || month || year, "change");
}
},
formatDate : function()
{
-return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format) : '';
+return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format,this) : '';
},
newDate : function(date)
{
if(!date)
date = new Date();
-if(isString(date)|| isNumber(date))
+if(typeof(date) == "string" || typeof(date) == "number")
date = new Date(date);
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0,0,0);
},
@@ -334,8 +373,8 @@ return;
this.selectedDate = this.newDate(date);
this.updateHeader();
this.update();
-if (isFunction(this.onchange))
-this.onchange();
+if (typeof(this.onChange) == "function")
+this.onChange();
},
getElement : function()
{
@@ -343,7 +382,7 @@ return this._calDiv;
},
getSelectedDate : function ()
{
-return isNull(this.selectedDate) ? null : this.newDate(this.selectedDate);
+return this.selectedDate == null ? null : this.newDate(this.selectedDate);
},
setYear : function(year)
{
@@ -374,8 +413,9 @@ if(this.options.InputMode == "TextBox")
pos[1] += this.control.offsetHeight;
else
{
-if($(this.options.ID+"_day"))
-pos[1] += $(this.options.ID+"_day").offsetHeight-1;
+var dayList = Prado.WebUI.TDatePicker.getDayListControl(this.control);
+if(dayList)
+pos[1] += dayList.offsetHeight-1;
}
this._calDiv.style.display = "block";
this._calDiv.style.top = (pos[1]-1) + "px";
@@ -385,7 +425,7 @@ this.documentClickEvent = this.hideOnClick.bindEvent(this);
this.documentKeyDownEvent = this.keyPressed.bindEvent(this);
Event.observe(document.body, "click", this.documentClickEvent);
var date = this.getDateFromInput();
-if(!isNull(date))
+if(date)
{
this.selectedDate = date;
this.setSelectedDate(date);
@@ -399,20 +439,7 @@ getDateFromInput : function()
if(this.options.InputMode == "TextBox")
return Date.SimpleParse($F(this.control), this.Format);
else
-{
-var now=new Date();
-var year=now.getFullYear();
-var month=now.getMonth();
-var date=1;
-if($(this.options.ID+"_day"))
-day = $F(this.options.ID+"_day");
-if($(this.options.ID+"_month"))
-month = $F(this.options.ID+"_month");
-if($(this.options.ID+"_year"))
-year = $F(this.options.ID+"_year");
-var newdate=new Date(year,month,day, 0, 0, 0);
-return newdate;
-}
+return Prado.WebUI.TDatePicker.getDropDownDate(this.control);
},
hideOnClick : function(ev)
{
@@ -484,7 +511,10 @@ this.dateSlot[index].data.parentNode.className = "empty";
},
hover : function(ev)
{
-Element.condClassName(Event.element(ev), "hover", ev.type=="mouseover");
+if(ev.type == "mouseover")
+Event.element(ev).addClassName("hover");
+else
+Event.element(ev).removeClassName("hover");
},
updateHeader : function () {
var options = this._monthSelect.options;
diff --git a/framework/Web/Javascripts/js/prado.js b/framework/Web/Javascripts/js/prado.js
index 6737d4ce..c7145188 100644
--- a/framework/Web/Javascripts/js/prado.js
+++ b/framework/Web/Javascripts/js/prado.js
@@ -1299,9 +1299,10 @@ element.dispatchEvent(event);
else if(element.fireEvent)
{
element.fireEvent('on'+type);
+if(element[type])
element[type]();
}
-else
+else if(element[type])
element[type]();
}
});
@@ -1868,9 +1869,9 @@ lastFocus.value = options['EventTarget'];
}
$('PRADO_POSTBACK_TARGET').value = options['EventTarget'];
$('PRADO_POSTBACK_PARAMETER').value = options['EventParameter'];
-Event.fireEvent(form,"submit");
if(options['StopEvent'])
Event.stop(event);
+Event.fireEvent(form,"submit");
}
Prado.Element =
{
diff --git a/framework/Web/Javascripts/js/validator.js b/framework/Web/Javascripts/js/validator.js
index 7d343d87..38d8a2a4 100644
--- a/framework/Web/Javascripts/js/validator.js
+++ b/framework/Web/Javascripts/js/validator.js
@@ -26,6 +26,7 @@ if(this.managers[formID])
this.managers[formID].addValidator(validator);
else
throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
+return this.managers[formID];
},
addSummary : function(formID, validator)
{
@@ -33,9 +34,11 @@ if(this.managers[formID])
this.managers[formID].addSummary(validator);
else
throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
+return this.managers[formID];
}
});
-Prado.Validation.prototype =
+Prado.ValidationManager = Class.create();
+Prado.ValidationManager.prototype =
{
validators : [],
summaries : [],
@@ -56,13 +59,12 @@ return this._validateNonGroup();
_validateGroup: function(groupID)
{
var valid = true;
-var manager = this;
if(this.groups.include(groupID))
{
this.validators.each(function(validator)
{
if(validator.group == groupID)
-valid = valid & validator.validate(manager);
+valid = valid & validator.validate();
else
validator.hide();
});
@@ -73,11 +75,10 @@ return valid;
_validateNonGroup : function()
{
var valid = true;
-var manager = this;
this.validators.each(function(validator)
{
if(!validator.group)
-valid = valid & validator.validate(manager);
+valid = valid & validator.validate();
else
validator.hide();
});
@@ -271,8 +272,9 @@ enabled : true,
visible : false,
isValid : true,
options : {},
-_isObserving : false,
+_isObserving : {},
group : null,
+manager : null,
initialize : function(options)
{
options.OnValidate = options.OnValidate || Prototype.emptyFunction;
@@ -282,7 +284,7 @@ this.options = options;
this.control = $(options.ControlToValidate);
this.message = $(options.ID);
this.group = options.ValidationGroup;
-Prado.Validation.addValidator(options.FormID, this);
+this.manager = Prado.Validation.addValidator(options.FormID, this);
},
getErrorMessage : function()
{
@@ -296,6 +298,7 @@ if(this.options.Display == "Dynamic")
this.isValid ? this.message.hide() : this.message.show();
this.message.style.visibility = this.isValid ? "hidden" : "visible";
}
+if(this.control)
this.updateControlCssClass(this.control, this.isValid);
if(this.options.FocusOnError && !this.isValid)
Prado.Element.focus(this.options.FocusElementID);
@@ -318,33 +321,36 @@ this.isValid = true;
this.updateControl();
this.visible = false;
},
-validate : function(manager)
+validate : function()
{
if(this.enabled)
-this.isValid = this.evaluateIsValid(manager);
-this.options.OnValidate(this, manager);
+this.isValid = this.evaluateIsValid();
+this.options.OnValidate(this);
this.updateControl();
if(this.isValid)
-this.options.OnSuccess(this, manager);
+this.options.OnSuccess(this);
else
-this.options.OnError(this, manager);
-this.observeChanges(manager);
+this.options.OnError(this);
+this.observeChanges(this.control);
return this.isValid;
},
-observeChanges : function(manager)
+observeChanges : function(control)
{
-if(this.options.ObserveChanges != false && !this._isObserving)
+if(!control) return;
+var canObserveChanges = this.options.ObserveChanges != false;
+var currentlyObserving = this._isObserving[control.id+this.options.ID];
+if(canObserveChanges && !currentlyObserving)
{
var validator = this;
-Event.observe(this.control, 'change', function()
+Event.observe(control, 'change', function()
{
if(validator.visible)
{
-validator.validate(manager);
-manager.updateSummary(validator.group);
+validator.validate();
+validator.manager.updateSummary(validator.group);
}
});
-this._isObserving = true;
+this._isObserving[control.id+this.options.ID] = true;
}
},
trim : function(value)
@@ -354,7 +360,7 @@ return typeof(value) == "string" ? value.trim() : "";
convert : function(dataType, value)
{
if(typeof(value) == "undefined")
-value = $F(this.control);
+value = this.getValidationValue();
var string = new String(value);
switch(dataType)
{
@@ -363,18 +369,116 @@ return string.toInteger();
case "Double" :
case "Float" :
return string.toDouble(this.options.DecimalChar);
-case "Currency" :
-return string.toCurrency(this.options.GroupChar, this.options.Digits, this.options.DecimalChar);
case "Date":
+if(typeof(value) != "string")
+return value;
+else
+{
var value = string.toDate(this.options.DateFormat);
if(value && typeof(value.getTime) == "function")
return value.getTime();
else
return null;
+}
case "String":
return string.toString();
}
return value;
+},
+getValidationValue : function(control)
+ {
+ if(!control)
+ control = this.control
+ switch(this.options.ControlType)
+ {
+ case 'TDatePicker':
+ if(control.type == "text")
+ return this.trim($F(control));
+ else
+ {
+ this.observeDatePickerChanges();
+return Prado.WebUI.TDatePicker.getDropDownDate(control).getTime();
+ }
+ default:
+ if(this.isListControlType())
+ return this.getFirstSelectedListValue();
+ else
+ return this.trim($F(control));
+ }
+ },
+observeDatePickerChanges : function()
+ {
+ if(Prado.Browser().ie)
+ {
+ var DatePicker = Prado.WebUI.TDatePicker;
+ this.observeChanges(DatePicker.getDayListControl(this.control));
+this.observeChanges(DatePicker.getMonthListControl(this.control));
+this.observeChanges(DatePicker.getYearListControl(this.control));
+ }
+ },
+getSelectedValuesAndChecks : function(elements, initialValue)
+{
+var checked = 0;
+var values = [];
+var isSelected = this.isCheckBoxType(elements[0]) ? 'checked' : 'selected';
+elements.each(function(element)
+{
+if(element[isSelected] && element.value != initialValue)
+{
+checked++;
+values.push(element.value);
+}
+});
+return {'checks' : checked, 'values' : values};
+},
+getListElements : function()
+{
+switch(this.options.ControlType)
+{
+case 'TCheckBoxList': case 'TRadioButtonList':
+var elements = [];
+for(var i = 0; i < this.options.TotalItems; i++)
+{
+var element = $(this.options.ControlToValidate+"_"+i);
+if(this.isCheckBoxType(element))
+elements.push(element);
+}
+return elements;
+case 'TListBox':
+var elements = [];
+var element = $(this.options.ControlToValidate);
+if(element && (type = element.type.toLowerCase()))
+{
+if(type == "select-one" || type == "select-multiple")
+elements = $A(element.options);
+}
+return elements;
+default:
+return [];
+}
+},
+isCheckBoxType : function(element)
+{
+if(element && element.type)
+{
+var type = element.type.toLowerCase();
+return type == "checkbox" || type == "radio";
+}
+return false;
+},
+isListControlType : function()
+{
+var list = ['TCheckBoxList', 'TRadioButtonList', 'TListBox'];
+return list.include(this.options.ControlType);
+},
+getFirstSelectedListValue : function()
+{
+var initial = "";
+if(typeof(this.options.InitialValue) != "undefined")
+initial = this.options.InitialValue;
+var elements = this.getListElements();
+var selection = this.getSelectedValuesAndChecks(elements, initial);
+return selection.values.length > 0 ? selection.values[0] : initial;
}
}
Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator,
@@ -388,7 +492,7 @@ return true;
}
else
{
-var a = this.trim($F(this.control));
+var a = this.getValidationValue();
var b = this.trim(this.options.InitialValue);
return(a != b);
}
@@ -396,41 +500,24 @@ return(a != b);
});
Prado.WebUI.TCompareValidator = Class.extend(Prado.WebUI.TBaseValidator,
{
-_observingComparee : false,
-evaluateIsValid : function(manager)
+evaluateIsValid : function()
{
-var value = this.trim($F(this.control));
+var value = this.getValidationValue();
if (value.length <= 0)
return true;
var comparee = $(this.options.ControlToCompare);
if(comparee)
-var compareTo = this.trim($F(comparee));
+var compareTo = this.getValidationValue(comparee);
else
var compareTo = this.options.ValueToCompare || "";
var isValid =this.compare(value, compareTo);
if(comparee)
{
this.updateControlCssClass(comparee, isValid);
-this.observeComparee(comparee, manager);
+this.observeChanges(comparee);
}
return isValid;
},
-observeComparee : function(comparee, manager)
-{
-if(this.options.ObserveChanges != false && !this._observingComparee)
-{
-var validator = this;
-Event.observe(comparee, "change", function()
-{
-if(validator.visible)
-{
-validator.validate(manager);
-manager.updateSummary(validator.group);
-}
-});
-this._observingComparee = true;
-}
-},
compare : function(operand1, operand2)
{
var op1, op2;
@@ -457,9 +544,9 @@ return (op1 == op2);
});
Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator,
{
-evaluateIsValid : function(manager)
+evaluateIsValid : function()
{
-var value = $F(this.control);
+var value = this.getValidationValue();
var clientFunction = this.options.ClientValidationFunction;
if(typeof(clientFunction) == "string" && clientFunction.length > 0)
{
@@ -471,9 +558,9 @@ return true;
});
Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator,
{
-evaluateIsValid : function(manager)
+evaluateIsValid : function()
{
-var value = this.trim($F(this.control));
+var value = this.getValidationValue();
if(value.length <= 0)
return true;
if(typeof(this.options.DataType) == "undefined")
@@ -481,7 +568,6 @@ this.options.DataType = "String";
var min = this.convert(this.options.DataType, this.options.MinValue || null);
var max = this.convert(this.options.DataType, this.options.MaxValue || null);
value = this.convert(this.options.DataType, value);
-Logger.warn(min+" <= "+value+" <= "+max);
if(value == null)
return false;
var valid = true;
@@ -494,9 +580,9 @@ return valid;
});
Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidator,
{
-evaluateIsValid : function(master)
+evaluateIsValid : function()
{
-var value = this.trim($F(this.control));
+var value = this.getValidationValue();
if (value.length <= 0)
return true;
var rx = new RegExp(this.options.ValidationExpression);
@@ -505,3 +591,52 @@ return (matches != null && value == matches[0]);
}
});
Prado.WebUI.TEmailAddressValidator = Prado.WebUI.TRegularExpressionValidator;
+Prado.WebUI.TListControlValidator = Class.extend(Prado.WebUI.TBaseValidator,
+{
+evaluateIsValid : function()
+{
+var elements = this.getListElements();
+if(elements && elements.length <= 0)
+return true;
+this.observeListElements(elements);
+var selection = this.getSelectedValuesAndChecks(elements);
+return this.isValidList(selection.checks, selection.values);
+},
+observeListElements : function(elements)
+ {
+if(Prado.Browser().ie && this.isCheckBoxType(elements[0]))
+{
+var validator = this;
+elements.each(function(element)
+{
+validator.observeChanges(element);
+});
+}
+ },
+isValidList : function(checked, values)
+{
+var exists = true;
+var required = this.getRequiredValues();
+if(required.length > 0)
+{
+if(values.length < required.length)
+return false;
+required.each(function(requiredValue)
+{
+exists = exists && values.include(requiredValue);
+});
+}
+var min = typeof(this.options.Min) == "undefined" ?
+Number.NEGATIVE_INFINITY : this.options.Min;
+var max = typeof(this.options.Max) == "undefined" ?
+Number.POSITIVE_INFINITY : this.options.Max;
+return exists && checked >= min && checked <= max;
+},
+getRequiredValues : function()
+{
+var required = [];
+if(this.options.Required && this.options.Required.length > 0)
+required = this.options.Required.split(/,\s*/);
+return required;
+}
+});
diff --git a/framework/Web/Javascripts/prado/form.js b/framework/Web/Javascripts/prado/form.js
index addad893..2beb945b 100644
--- a/framework/Web/Javascripts/prado/form.js
+++ b/framework/Web/Javascripts/prado/form.js
@@ -129,9 +129,9 @@ Prado.PostBack = function(event,options)
$('PRADO_POSTBACK_TARGET').value = options['EventTarget'];
$('PRADO_POSTBACK_PARAMETER').value = options['EventParameter'];
- Event.fireEvent(form,"submit");
if(options['StopEvent'])
Event.stop(event);
+ Event.fireEvent(form,"submit");
}
/*
diff --git a/framework/Web/Javascripts/prado/validation3.js b/framework/Web/Javascripts/prado/validation3.js
index 10169aab..4c189532 100644
--- a/framework/Web/Javascripts/prado/validation3.js
+++ b/framework/Web/Javascripts/prado/validation3.js
@@ -1,6 +1,58 @@
/**
- * Prado client-side javascript validation manager.
+ * Prado client-side javascript validation fascade.
+ *
+ * There are 4 basic classes, Validation, ValidationManager, ValidationSummary
+ * and TBaseValidator, that interact together to perform validation.
+ * The Prado.Validation class co-ordinates together the
+ * validation scheme and is responsible for maintaining references
+ * to ValidationManagers.
+ *
+ * The ValidationManager class is responsible for maintaining refereneces
+ * to individual validators, validation summaries and their associated
+ * groupings.
+ *
+ * The ValidationSummary take cares of display the validator error messages
+ * as html output or an alert output.
+ *
+ * The TBaseValidator is the base class for all validators and contains
+ * methods to interact with the actual inputs, data type conversion.
+ *
+ * An instance of ValidationManager must be instantiated first for a
+ * particular form before instantiating validators and summaries.
+ *
+ * Usage example: adding a required field to a text box input with
+ * ID "input1" in a form with ID "form1".
+ *
+ *
+ *
+ *
+ *
*/
Prado.Validation = Class.create();
@@ -54,6 +106,7 @@ Object.extend(Prado.Validation,
* Add a new validator to a particular form.
* @param string the form that the validator belongs.
* @param object a validator
+ * @return object the manager
*/
addValidator : function(formID, validator)
{
@@ -61,12 +114,14 @@ Object.extend(Prado.Validation,
this.managers[formID].addValidator(validator);
else
throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
+ return this.managers[formID];
},
/**
* Add a new validation summary.
* @param string the form that the validation summary belongs.
* @param object a validation summary
+ * @return object manager
*/
addSummary : function(formID, validator)
{
@@ -74,14 +129,19 @@ Object.extend(Prado.Validation,
this.managers[formID].addSummary(validator);
else
throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
+ return this.managers[formID];
}
});
+Prado.ValidationManager = Class.create();
/**
* Validation manager instances. Manages validators for a particular
- * HTML form.
+ * HTML form. The manager contains references to all the validators
+ * summaries, and their groupings for a particular form.
+ * Generally, Prado.Validation methods should be called rather
+ * than calling directly the ValidationManager.
*/
-Prado.Validation.prototype =
+Prado.ValidationManager.prototype =
{
validators : [], // list of validators
summaries : [], // validation summaries
@@ -120,13 +180,12 @@ Prado.Validation.prototype =
_validateGroup: function(groupID)
{
var valid = true;
- var manager = this;
if(this.groups.include(groupID))
{
this.validators.each(function(validator)
{
if(validator.group == groupID)
- valid = valid & validator.validate(manager);
+ valid = valid & validator.validate();
else
validator.hide();
});
@@ -142,11 +201,10 @@ Prado.Validation.prototype =
_validateNonGroup : function()
{
var valid = true;
- var manager = this;
this.validators.each(function(validator)
{
if(!validator.group)
- valid = valid & validator.validate(manager);
+ valid = valid & validator.validate();
else
validator.hide();
});
@@ -462,8 +520,9 @@ Prado.WebUI.TBaseValidator.prototype =
visible : false,
isValid : true,
options : {},
- _isObserving : false,
+ _isObserving : {},
group : null,
+ manager : null,
/**
*
@@ -493,7 +552,7 @@ Prado.WebUI.TBaseValidator.prototype =
this.message = $(options.ID);
this.group = options.ValidationGroup;
- Prado.Validation.addValidator(options.FormID, this);
+ this.manager = Prado.Validation.addValidator(options.FormID, this);
},
/**
@@ -518,7 +577,8 @@ Prado.WebUI.TBaseValidator.prototype =
this.message.style.visibility = this.isValid ? "hidden" : "visible";
}
- this.updateControlCssClass(this.control, this.isValid);
+ if(this.control)
+ this.updateControlCssClass(this.control, this.isValid);
if(this.options.FocusOnError && !this.isValid)
Prado.Element.focus(this.options.FocusElementID);
@@ -557,24 +617,23 @@ Prado.WebUI.TBaseValidator.prototype =
/**
* Calls evaluateIsValid() function to set the value of isValid property.
* Triggers onValidate event and onSuccess or onError event.
- * @param Validation manager
* @return boolean true if valid.
*/
- validate : function(manager)
+ validate : function()
{
if(this.enabled)
- this.isValid = this.evaluateIsValid(manager);
+ this.isValid = this.evaluateIsValid();
- this.options.OnValidate(this, manager);
+ this.options.OnValidate(this);
this.updateControl();
if(this.isValid)
- this.options.OnSuccess(this, manager);
+ this.options.OnSuccess(this);
else
- this.options.OnError(this, manager);
-
- this.observeChanges(manager);
+ this.options.OnError(this);
+
+ this.observeChanges(this.control);
return this.isValid;
},
@@ -582,21 +641,28 @@ Prado.WebUI.TBaseValidator.prototype =
/**
* Observe changes to the control input, re-validate upon change. If
* the validator is not visible, no updates are propagated.
+ * @param HTMLElement control to observe changes
*/
- observeChanges : function(manager)
+ observeChanges : function(control)
{
- if(this.options.ObserveChanges != false && !this._isObserving)
+ if(!control) return;
+
+ var canObserveChanges = this.options.ObserveChanges != false;
+ var currentlyObserving = this._isObserving[control.id+this.options.ID];
+
+ if(canObserveChanges && !currentlyObserving)
{
var validator = this;
- Event.observe(this.control, 'change', function()
+
+ Event.observe(control, 'change', function()
{
if(validator.visible)
{
- validator.validate(manager);
- manager.updateSummary(validator.group);
+ validator.validate();
+ validator.manager.updateSummary(validator.group);
}
});
- this._isObserving = true;
+ this._isObserving[control.id+this.options.ID] = true;
}
},
@@ -610,14 +676,14 @@ Prado.WebUI.TBaseValidator.prototype =
/**
* Convert the value to a specific data type.
- * @param {string} the data type, "Integer", "Double", "Currency", "Date" or "String"
+ * @param {string} the data type, "Integer", "Double", "Date" or "String"
* @param {string} the value to convert.
* @type {mixed|null} the converted data value.
*/
convert : function(dataType, value)
{
if(typeof(value) == "undefined")
- value = $F(this.control);
+ value = this.getValidationValue();
var string = new String(value);
switch(dataType)
{
@@ -626,18 +692,149 @@ Prado.WebUI.TBaseValidator.prototype =
case "Double" :
case "Float" :
return string.toDouble(this.options.DecimalChar);
- case "Currency" :
- return string.toCurrency(this.options.GroupChar, this.options.Digits, this.options.DecimalChar);
case "Date":
- var value = string.toDate(this.options.DateFormat);
- if(value && typeof(value.getTime) == "function")
- return value.getTime();
+ if(typeof(value) != "string")
+ return value;
else
- return null;
+ {
+ var value = string.toDate(this.options.DateFormat);
+ if(value && typeof(value.getTime) == "function")
+ return value.getTime();
+ else
+ return null;
+ }
case "String":
return string.toString();
}
return value;
+ },
+
+ /**
+ * @return mixed control value to validate
+ */
+ getValidationValue : function(control)
+ {
+ if(!control)
+ control = this.control
+ switch(this.options.ControlType)
+ {
+ case 'TDatePicker':
+ if(control.type == "text")
+ return this.trim($F(control));
+ else
+ {
+ this.observeDatePickerChanges();
+
+ return Prado.WebUI.TDatePicker.getDropDownDate(control).getTime();
+ }
+ default:
+ if(this.isListControlType())
+ return this.getFirstSelectedListValue();
+ else
+ return this.trim($F(control));
+ }
+ },
+
+ /**
+ * Observe changes in the drop down list date picker, IE only.
+ */
+ observeDatePickerChanges : function()
+ {
+ if(Prado.Browser().ie)
+ {
+ var DatePicker = Prado.WebUI.TDatePicker;
+ this.observeChanges(DatePicker.getDayListControl(this.control));
+ this.observeChanges(DatePicker.getMonthListControl(this.control));
+ this.observeChanges(DatePicker.getYearListControl(this.control));
+ }
+ },
+
+ /**
+ * Gets numeber selections and their values.
+ * @return object returns selected values in values property
+ * and number of selections in checks property.
+ */
+ getSelectedValuesAndChecks : function(elements, initialValue)
+ {
+ var checked = 0;
+ var values = [];
+ var isSelected = this.isCheckBoxType(elements[0]) ? 'checked' : 'selected';
+ elements.each(function(element)
+ {
+ if(element[isSelected] && element.value != initialValue)
+ {
+ checked++;
+ values.push(element.value);
+ }
+ });
+ return {'checks' : checked, 'values' : values};
+ },
+
+ /**
+ * Gets an array of the list control item input elements, for TCheckBoxList
+ * checkbox inputs are returned, for TListBox HTML option elements are returned.
+ * @return array list control option elements.
+ */
+ getListElements : function()
+ {
+ switch(this.options.ControlType)
+ {
+ case 'TCheckBoxList': case 'TRadioButtonList':
+ var elements = [];
+ for(var i = 0; i < this.options.TotalItems; i++)
+ {
+ var element = $(this.options.ControlToValidate+"_"+i);
+ if(this.isCheckBoxType(element))
+ elements.push(element);
+ }
+ return elements;
+ case 'TListBox':
+ var elements = [];
+ var element = $(this.options.ControlToValidate);
+ if(element && (type = element.type.toLowerCase()))
+ {
+ if(type == "select-one" || type == "select-multiple")
+ elements = $A(element.options);
+ }
+ return elements;
+ default:
+ return [];
+ }
+ },
+
+ /**
+ * @return boolean true if element is of checkbox or radio type.
+ */
+ isCheckBoxType : function(element)
+ {
+ if(element && element.type)
+ {
+ var type = element.type.toLowerCase();
+ return type == "checkbox" || type == "radio";
+ }
+ return false;
+ },
+
+ /**
+ * @return boolean true if control to validate is of some of the TListControl type.
+ */
+ isListControlType : function()
+ {
+ var list = ['TCheckBoxList', 'TRadioButtonList', 'TListBox'];
+ return list.include(this.options.ControlType);
+ },
+
+ /**
+ * @return string gets the first selected list value, initial value if none found.
+ */
+ getFirstSelectedListValue : function()
+ {
+ var initial = "";
+ if(typeof(this.options.InitialValue) != "undefined")
+ initial = this.options.InitialValue;
+ var elements = this.getListElements();
+ var selection = this.getSelectedValuesAndChecks(elements, initial);
+ return selection.values.length > 0 ? selection.values[0] : initial;
}
}
@@ -664,7 +861,7 @@ Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator,
}
else
{
- var a = this.trim($F(this.control));
+ var a = this.getValidationValue();
var b = this.trim(this.options.InitialValue);
return(a != b);
}
@@ -686,7 +883,6 @@ Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator,
* type before the comparison operation is performed. The following value types are supported:
* - Integer A 32-bit signed integer data type.
* - Float A double-precision floating point number data type.
- * - Currency A decimal data type that can contain currency symbols.
* - Date A date data type. The format can be by the DateFormat option.
* - String A string data type.
*
@@ -703,56 +899,34 @@ Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator,
*/
Prado.WebUI.TCompareValidator = Class.extend(Prado.WebUI.TBaseValidator,
{
- _observingComparee : false,
+ //_observingComparee : false,
/**
* Compares the input to another input or a given value.
*/
- evaluateIsValid : function(manager)
+ evaluateIsValid : function()
{
- var value = this.trim($F(this.control));
+ var value = this.getValidationValue();
if (value.length <= 0)
return true;
var comparee = $(this.options.ControlToCompare);
if(comparee)
- var compareTo = this.trim($F(comparee));
+ var compareTo = this.getValidationValue(comparee);
else
- var compareTo = this.options.ValueToCompare || "";
+ var compareTo = this.options.ValueToCompare || "";
var isValid = this.compare(value, compareTo);
if(comparee)
{
this.updateControlCssClass(comparee, isValid);
- this.observeComparee(comparee, manager);
+ this.observeChanges(comparee);
}
return isValid;
},
- /**
- * Observe the comparee input element for changes.
- * @param object HTML input element to observe
- * @param object Validation manager.
- */
- observeComparee : function(comparee, manager)
- {
- if(this.options.ObserveChanges != false && !this._observingComparee)
- {
- var validator = this;
- Event.observe(comparee, "change", function()
- {
- if(validator.visible)
- {
- validator.validate(manager);
- manager.updateSummary(validator.group);
- }
- });
- this._observingComparee = true;
- }
- },
-
/**
* Compares two values, their values are casted to type defined
* by DataType option. False is returned if the first
@@ -816,9 +990,9 @@ Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator,
/**
* Calls custom validation function.
*/
- evaluateIsValid : function(manager)
+ evaluateIsValid : function()
{
- var value = $F(this.control);
+ var value = this.getValidationValue();
var clientFunction = this.options.ClientValidationFunction;
if(typeof(clientFunction) == "string" && clientFunction.length > 0)
{
@@ -840,7 +1014,6 @@ Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator,
* operation is performed. The following value types are supported:
* - Integer A 32-bit signed integer data type.
* - Float A double-precision floating point number data type.
- * - Currency A decimal data type that can contain currency symbols.
* - Date A date data type. The date format can be specified by
* setting DateFormat option, which must be recognizable
* by Date.SimpleParse javascript function.
@@ -856,11 +1029,11 @@ Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator,
{
/**
* Compares the input value with a minimum and/or maximum value.
- * Returns true if the value is empty, returns false if conversion fails.
+ * @return boolean true if the value is empty, returns false if conversion fails.
*/
- evaluateIsValid : function(manager)
+ evaluateIsValid : function()
{
- var value = this.trim($F(this.control));
+ var value = this.getValidationValue();
if(value.length <= 0)
return true;
if(typeof(this.options.DataType) == "undefined")
@@ -870,8 +1043,6 @@ Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator,
var max = this.convert(this.options.DataType, this.options.MaxValue || null);
value = this.convert(this.options.DataType, value);
- Logger.warn(min+" <= "+value+" <= "+max);
-
if(value == null)
return false;
@@ -897,9 +1068,9 @@ Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidato
/**
* Compare the control input against a regular expression.
*/
- evaluateIsValid : function(master)
+ evaluateIsValid : function()
{
- var value = this.trim($F(this.control));
+ var value = this.getValidationValue();
if (value.length <= 0)
return true;
@@ -916,3 +1087,84 @@ Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidato
Prado.WebUI.TEmailAddressValidator = Prado.WebUI.TRegularExpressionValidator;
+/**
+ * TListControlValidator checks the number of selection and their values
+ * for a TListControl that allows multiple selections.
+ */
+Prado.WebUI.TListControlValidator = Class.extend(Prado.WebUI.TBaseValidator,
+{
+ /**
+ * @return true if the number of selections and/or their values
+ * match the requirements.
+ */
+ evaluateIsValid : function()
+ {
+ var elements = this.getListElements();
+ if(elements && elements.length <= 0)
+ return true;
+
+ this.observeListElements(elements);
+
+ var selection = this.getSelectedValuesAndChecks(elements);
+ return this.isValidList(selection.checks, selection.values);
+ },
+
+ /**
+ * Observe list elements for IE browsers of changes
+ */
+ observeListElements : function(elements)
+ {
+ if(Prado.Browser().ie && this.isCheckBoxType(elements[0]))
+ {
+ var validator = this;
+ elements.each(function(element)
+ {
+ validator.observeChanges(element);
+ });
+ }
+ },
+
+ /**
+ * Determine if the number of checked and the checked values
+ * satisfy the required number of checks and/or the checked values
+ * equal to the required values.
+ * @return boolean true if checked values and number of checks are satisfied.
+ */
+ isValidList : function(checked, values)
+ {
+ var exists = true;
+
+ //check the required values
+ var required = this.getRequiredValues();
+ if(required.length > 0)
+ {
+ if(values.length < required.length)
+ return false;
+ required.each(function(requiredValue)
+ {
+ exists = exists && values.include(requiredValue);
+ });
+ }
+
+ var min = typeof(this.options.Min) == "undefined" ?
+ Number.NEGATIVE_INFINITY : this.options.Min;
+ var max = typeof(this.options.Max) == "undefined" ?
+ Number.POSITIVE_INFINITY : this.options.Max;
+ return exists && checked >= min && checked <= max;
+ },
+
+ /**
+ * @return array list of required options that must be selected.
+ */
+ getRequiredValues : function()
+ {
+ var required = [];
+ if(this.options.Required && this.options.Required.length > 0)
+ required = this.options.Required.split(/,\s*/);
+ return required;
+ }
+});
+
+
+
+
diff --git a/framework/Web/UI/WebControls/TBaseValidator.php b/framework/Web/UI/WebControls/TBaseValidator.php
index c5b5e7a5..adbc85ae 100644
--- a/framework/Web/UI/WebControls/TBaseValidator.php
+++ b/framework/Web/UI/WebControls/TBaseValidator.php
@@ -131,6 +131,7 @@ abstract class TBaseValidator extends TLabel implements IValidator
*/
protected function getClientScriptOptions()
{
+ $control = $this->getValidationTarget();
$options['ID'] = $this->getClientID();
$options['FormID'] = $this->getPage()->getForm()->getClientID();
$options['Display'] = $this->getDisplay();
@@ -141,8 +142,9 @@ abstract class TBaseValidator extends TLabel implements IValidator
$options['FocusElementID'] = $this->getFocusElementID();
}
$options['ValidationGroup'] = $this->getValidationGroup();
- $options['ControlToValidate'] = $this->getValidationTarget()->getClientID();
+ $options['ControlToValidate'] = $control->getClientID();
$options['ControlCssClass'] = $this->getControlCssClass();
+ $options['ControlType'] = get_class($control);
return $options;
}
@@ -163,7 +165,7 @@ abstract class TBaseValidator extends TLabel implements IValidator
$manager['FormID'] = $formID;
$options = TJavaScript::encode($manager);
$scripts->registerPradoScript('validator');
- $scripts->registerEndScript($scriptKey, "new Prado.Validation({$options});");
+ $scripts->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});");
}
if($this->getEnableClientScript())
$this->registerClientScriptValidator();
diff --git a/framework/Web/UI/WebControls/TCheckBox.php b/framework/Web/UI/WebControls/TCheckBox.php
index ff7f57f7..681c8748 100644
--- a/framework/Web/UI/WebControls/TCheckBox.php
+++ b/framework/Web/UI/WebControls/TCheckBox.php
@@ -340,7 +340,7 @@ class TCheckBox extends TWebControl implements IPostBackDataHandler, IValidatabl
if($clientID!=='')
$writer->addAttribute('id',$clientID);
$writer->addAttribute('type','checkbox');
- if(($value=$this->getValueAttribute())!=='')
+ if(($value = $this->getValueAttribute()) !== '')
$writer->addAttribute('value',$value);
if(($uniqueID=$this->getUniqueID())!=='')
$writer->addAttribute('name',$uniqueID);
@@ -378,4 +378,4 @@ class TCheckBox extends TWebControl implements IPostBackDataHandler, IValidatabl
}
-?>
\ No newline at end of file
+?>
diff --git a/framework/Web/UI/WebControls/TCheckBoxList.php b/framework/Web/UI/WebControls/TCheckBoxList.php
index 8a106b46..de332897 100644
--- a/framework/Web/UI/WebControls/TCheckBoxList.php
+++ b/framework/Web/UI/WebControls/TCheckBoxList.php
@@ -47,7 +47,7 @@ Prado::using('System.Web.UI.WebControls.TCheckBox');
* @package System.Web.UI.WebControls
* @since 3.0
*/
-class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingContainer, IPostBackDataHandler
+class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingContainer, IPostBackDataHandler, IValidatable
{
private $_repeatedControl;
private $_isEnabled;
@@ -309,7 +309,7 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
if($item->getEnabled())
{
$checked=isset($values[$key]);
- if($item->getSelected()!=$checked)
+ if($item->getSelected()!==$checked)
{
$item->setSelected($checked);
if(!$this->_changedEventRaised)
@@ -381,6 +381,16 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
$this->setTabIndex($tabIndex);
}
}
+
+ /**
+ * Returns the value to be validated.
+ * This methid is required by IValidatable interface.
+ * @return mixed the value of the property to be validated.
+ */
+ public function getValidationPropertyValue()
+ {
+ return $this->getSelectedValue();
+ }
}
?>
\ No newline at end of file
diff --git a/framework/Web/UI/WebControls/TCompareValidator.php b/framework/Web/UI/WebControls/TCompareValidator.php
index 172e472f..b5ebd3ab 100644
--- a/framework/Web/UI/WebControls/TCompareValidator.php
+++ b/framework/Web/UI/WebControls/TCompareValidator.php
@@ -31,7 +31,6 @@ Prado::using('System.Web.UI.WebControls.TBaseValidator');
* type before the comparison operation is performed. The following value types are supported:
* - Integer A 32-bit signed integer data type.
* - Float A double-precision floating point number data type.
- * - Currency A decimal data type that can contain currency symbols.
* - Date A date data type. The format can be specified by the
* {@link setDateFormat DateFormat} property
* - String A string data type.
@@ -56,12 +55,13 @@ class TCompareValidator extends TBaseValidator
}
/**
- * Sets the data type (Integer, Float, Currency, Date, String) that the values being compared are converted to before the comparison is made.
+ * Sets the data type (Integer, Float, Date, String) that the values being
+ * compared are converted to before the comparison is made.
* @param string the data type
*/
public function setDataType($value)
{
- $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'Integer','Float','Date','Currency','String'),'String');
+ $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'Integer','Float','Date','String'),'String');
}
/**
@@ -188,16 +188,6 @@ class TCompareValidator extends TBaseValidator
return array(intval($value1), intval($value2));
case 'Float':
return array(floatval($value1), floatval($value2));
- case 'Currency':
- if(preg_match('/[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?/',$value1,$matches))
- $value1=floatval($matches[0]);
- else
- $value1=0;
- if(preg_match('/[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?/',$value2,$matches))
- $value2=floatval($matches[0]);
- else
- $value2=0;
- return array($value1, $value2);
case 'Date':
$dateFormat = $this->getDateFormat();
if($dateFormat!=='')
diff --git a/framework/Web/UI/WebControls/TDataGridColumn.php b/framework/Web/UI/WebControls/TDataGridColumn.php
index de9d9d5c..e43b4895 100644
--- a/framework/Web/UI/WebControls/TDataGridColumn.php
+++ b/framework/Web/UI/WebControls/TDataGridColumn.php
@@ -11,7 +11,7 @@
*/
/**
- * Includes TDataFieldAccessor class
+ * Includes TDataFieldAccessor and TDataValueFormatter classes
*/
Prado::using('System.Util.TDataFieldAccessor');
@@ -331,17 +331,37 @@ abstract class TDataGridColumn extends TApplicationComponent
}
/**
- * Formats the text value according to format string.
- * This method invokes the {@link sprintf} to do string formatting.
+ * Formats the text value according to a format string.
* If the format string is empty, the original value is converted into
* a string and returned.
+ * If the format string starts with '#', the string is treated as a PHP expression
+ * within which the token '{0}' is translated with the data value to be formated.
+ * Otherwise, the format string and the data value are passed
+ * as the first and second parameters in {@link sprintf}.
* @param string format string
- * @param mixed the data associated with the cell
+ * @param mixed the data to be formatted
* @return string the formatted result
*/
protected function formatDataValue($formatString,$value)
{
- return $formatString===''?TPropertyValue::ensureString($value):sprintf($formatString,$value);
+ if($formatString==='')
+ return TPropertyValue::ensureString($value);
+ else if($formatString[0]==='#')
+ {
+ $expression=strtr(substr($formatString,1),array('{0}'=>'$value'));
+ try
+ {
+ if(eval("\$result=$expression;")===false)
+ throw new Exception('');
+ return $result;
+ }
+ catch(Exception $e)
+ {
+ throw new TInvalidDataValueException('datagridcolumn_expression_invalid',get_class($this),$expression,$e->getMessage());
+ }
+ }
+ else
+ return sprintf($formatString,$value);
}
}
diff --git a/framework/Web/UI/WebControls/TDataTypeValidator.php b/framework/Web/UI/WebControls/TDataTypeValidator.php
index 81c23ce4..d78be7bf 100644
--- a/framework/Web/UI/WebControls/TDataTypeValidator.php
+++ b/framework/Web/UI/WebControls/TDataTypeValidator.php
@@ -23,7 +23,6 @@ Prado::using('System.Web.UI.WebControls.TBaseValidator');
* The following data types are supported:
* - Integer A 32-bit signed integer data type.
* - Float A double-precision floating point number data type.
- * - Currency A decimal data type that can contain currency symbols.
* - Date A date data type.
* - String A string data type.
* For Date type, the property {@link setDateFormat DateFormat}
@@ -46,12 +45,13 @@ class TDataTypeValidator extends TBaseValidator
}
/**
- * Sets the data type (Integer, Float, Currency, Date, String) that the values being compared are converted to before the comparison is made.
+ * Sets the data type (Integer, Float, Date, String) that the values being
+ * compared are converted to before the comparison is made.
* @param string the data type
*/
public function setDataType($value)
{
- $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'Integer','Float','Date','Currency','String'),'String');
+ $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'Integer','Float','Date','String'),'String');
}
/**
@@ -85,8 +85,6 @@ class TDataTypeValidator extends TBaseValidator
return preg_match('/^[-+]?[0-9]+$/',trim($value));
case 'Float':
return preg_match('/^[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value));
- case 'Currency':
- return preg_match('/[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value));
case 'Date':
$dateFormat = $this->getDateFormat();
if(strlen($dateFormat))
diff --git a/framework/Web/UI/WebControls/TDatePicker.php b/framework/Web/UI/WebControls/TDatePicker.php
index c6a2345b..02386515 100644
--- a/framework/Web/UI/WebControls/TDatePicker.php
+++ b/framework/Web/UI/WebControls/TDatePicker.php
@@ -245,17 +245,16 @@ class TDatePicker extends TTextBox
/**
* @return integer current selected date from the date picker as timestamp.
*/
- public function getDate()
+ public function getTimeStamp()
{
- $date = $this->getDateFromText();
- return $date[0];
+ return $this->getTimeStampFromText();
}
/**
* Sets the date for the date picker using timestamp.
* @param integer time stamp for the date picker
*/
- public function setDate($value)
+ public function setTimeStamp($value)
{
$date = TPropertyValue::ensureInteger($value);
$formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',
@@ -263,6 +262,34 @@ class TDatePicker extends TTextBox
$this->setText($formatter->format($date));
}
+ /**
+ * @return string the date string.
+ */
+ public function getDate()
+ {
+ return $this->getText();
+ }
+
+ /**
+ * @param string date string
+ */
+ public function setDate($value)
+ {
+ $this->setText($value);
+ }
+
+ /**
+ * Returns the value to be validated.
+ * This methid is required by IValidatable interface.
+ * @return integer the value of the property to be validated.
+ */
+ public function getValidationPropertyValue()
+ {
+ if($this->getText() === '')
+ return '';
+ return $this->getTimeStamp();
+ }
+
/**
* Publish the date picker Css asset files.
*/
@@ -396,6 +423,7 @@ class TDatePicker extends TTextBox
$date = $this->getLocalizedCalendarInfo();
$options['MonthNames'] = TJavaScript::encode($date->getMonthNames(),false);
+ $options['AbbreviatedMonthNames'] = TJavaScript::encode($date->getAbbreviatedMonthNames(),false);
$options['ShortWeekDayNames'] = TJavaScript::encode($date->getAbbreviatedDayNames(),false);
return $options;
@@ -439,7 +467,8 @@ class TDatePicker extends TTextBox
$writer->addAttribute('class', $class);
$writer->renderBeginTag('span');
- $date = $this->getDateFromText();
+ $date = @getdate($this->getTimeStampFromText());
+
$this->renderCalendarSelections($writer, $date);
//render a hidden input field
@@ -481,9 +510,9 @@ class TDatePicker extends TTextBox
/**
* Gets the date from the text input using TSimpleDateFormatter
- * @return array current selected date
+ * @return integer current selected date timestamp
*/
- protected function getDateFromText()
+ protected function getTimeStampFromText()
{
$pattern = $this->getDateFormat();
$pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern);
@@ -559,7 +588,7 @@ class TDatePicker extends TTextBox
case 'MMM':
case 'MM': return $info->getAbbreviatedMonthNames();
case 'M':
- $array = array(); for($i=1;$i<=12;$i++) $array[$i] = $i;
+ $array = array(); for($i=1;$i<=12;$i++) $array[$i-1] = $i;
return $array;
default : return $info->getMonthNames();
}
diff --git a/framework/Web/UI/WebControls/THtmlArea.php b/framework/Web/UI/WebControls/THtmlArea.php
index 7e47d638..8e5fcd16 100644
--- a/framework/Web/UI/WebControls/THtmlArea.php
+++ b/framework/Web/UI/WebControls/THtmlArea.php
@@ -315,7 +315,7 @@ class THtmlArea extends TTextBox
//default the variant to "en"
if(count($variants) == 0)
{
- if($empty($culture))
+ if(empty($culture))
return 'en';
$variants[] = strtolower($culture);
}
diff --git a/framework/Web/UI/WebControls/TListControl.php b/framework/Web/UI/WebControls/TListControl.php
index 37c232e6..1c615edc 100644
--- a/framework/Web/UI/WebControls/TListControl.php
+++ b/framework/Web/UI/WebControls/TListControl.php
@@ -11,18 +11,13 @@
*/
/**
- * Includes TDataBoundControl class
+ * Includes the supporting classes
*/
Prado::using('System.Web.UI.WebControls.TDataBoundControl');
-/**
- * Includes TAttributeCollection class
- */
Prado::using('System.Collections.TAttributeCollection');
-/**
- * Includes TDataFieldAccessor class
- */
Prado::using('System.Util.TDataFieldAccessor');
+
/**
* TListControl class
*
@@ -74,8 +69,8 @@ Prado::using('System.Util.TDataFieldAccessor');
* to 'name' and 'age' will make the first item's text be 'John', value be 31,
* the second item's text be 'Cary', value be 28, and so on.
* The {@link setDataTextFormatString DataTextFormatString} property may be further
- * used to format how the item should be displayed. The formatting function is
- * the sprintf() PHP function.
+ * used to format how the item should be displayed. See {@link formatDataValue()}
+ * for an explanation of the format string.
*
* @author Qiang Xue
* @version $Revision: $ $Date: $
@@ -192,7 +187,7 @@ abstract class TListControl extends TDataBoundControl
$text=$object;
$item->setValue("$key");
}
- $item->setText($textFormat===''?$text:sprintf($textFormat,$text));
+ $item->setText($this->formatDataValue($textFormat,$text));
$items->add($item);
}
// SelectedValue or SelectedIndex may be set before databinding
@@ -329,9 +324,10 @@ abstract class TListControl extends TDataBoundControl
/**
* Sets data text format string.
- * The format string is used in sprintf() to format the Text property value
+ * The format string is used in {@link TDataValueFormatter::format()} to format the Text property value
* of each item in the list control.
* @param string the formatting string used to control how data bound to the list control is displayed.
+ * @see TDataValueFormatter::format()
*/
public function setDataTextFormatString($value)
{
@@ -585,6 +581,40 @@ abstract class TListControl extends TDataBoundControl
}
}
}
+
+ /**
+ * Formats the text value according to a format string.
+ * If the format string is empty, the original value is converted into
+ * a string and returned.
+ * If the format string starts with '#', the string is treated as a PHP expression
+ * within which the token '{0}' is translated with the data value to be formated.
+ * Otherwise, the format string and the data value are passed
+ * as the first and second parameters in {@link sprintf}.
+ * @param string format string
+ * @param mixed the data to be formatted
+ * @return string the formatted result
+ */
+ protected function formatDataValue($formatString,$value)
+ {
+ if($formatString==='')
+ return TPropertyValue::ensureString($value);
+ else if($formatString[0]==='#')
+ {
+ $expression=strtr(substr($formatString,1),array('{0}'=>'$value'));
+ try
+ {
+ if(eval("\$result=$expression;")===false)
+ throw new Exception('');
+ return $result;
+ }
+ catch(Exception $e)
+ {
+ throw new TInvalidDataValueException('listcontrol_expression_invalid',get_class($this),$expression,$e->getMessage());
+ }
+ }
+ else
+ return sprintf($formatString,$value);
+ }
}
/**
diff --git a/framework/Web/UI/WebControls/TListControlValidator.php b/framework/Web/UI/WebControls/TListControlValidator.php
new file mode 100644
index 00000000..9264e891
--- /dev/null
+++ b/framework/Web/UI/WebControls/TListControlValidator.php
@@ -0,0 +1,214 @@
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Using TBaseValidator class
+ */
+Prado::using('System.Web.UI.WebControls.TBaseValidator');
+
+/**
+ * TListControlValidator class.
+ *
+ * TListControlValidator checks the number of selection and their values
+ * for a TListControl that allows multiple selection.
+ *
+ * You can specify the minimum or maximum (or both) number of selections
+ * required using the {@link setMinSelection MinSelection} and
+ * {@link setMaxSelection MaxSelection} properties, respectively. In addition,
+ * you can specify a comma separated list of required selected values via the
+ * {@link setRequiredSelections RequiredSelections} property.
+ *
+ * Examples
+ * - At least two selections
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * - "value1" must be selected and at least 1 other
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @author Xiang Wei Zhuo
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TListControlValidator extends TBaseValidator
+{
+ /**
+ * @return int min number of selections
+ */
+ function getMinSelection()
+ {
+ return $this->getViewState('MinSelection','');
+ }
+
+ /**
+ * @param int minimum number of selections.
+ */
+ function setMinSelection($value)
+ {
+ $this->setViewState('MinSelection',$value,'');
+ }
+
+ /**
+ * @return int max number of selections
+ */
+ function getMaxSelection()
+ {
+ return $this->getViewState('MaxSelection','');
+ }
+
+ /**
+ * @param int max number of selections.
+ */
+ function setMaxSelection($value)
+ {
+ $this->setViewState('MaxSelection',$value,'');
+ }
+
+ /**
+ * Get a comma separated list of required selected values.
+ * @return string comma separated list of required values.
+ */
+ function getRequiredSelections()
+ {
+ return $this->getViewState('RequiredSelections','');
+ }
+
+ /**
+ * Set the list of required values, using aa comma separated list.
+ * @param string comma separated list of required values.
+ */
+ function setRequiredSelections($value)
+ {
+ $this->setViewState('RequiredSelections',$value,'');
+ }
+
+ /**
+ * This method overrides the parent's implementation.
+ * The validation succeeds if the input component changes its data
+ * from the InitialValue or the input component is not given.
+ * @return boolean whether the validation succeeds
+ */
+ public function evaluateIsValid()
+ {
+ $control=$this->getValidationTarget();
+
+ $exists = true;
+ list($count, $values) = $this->getSelection($control);
+ $required = $this->getRequiredValues();
+
+ //if required, check the values
+ if(!empty($required))
+ {
+ if(count($values) < count($required) )
+ return false;
+ foreach($required as $require)
+ $exists = $exists && in_array($require, $values);
+ }
+
+ $min = $this->getMinSelection();
+ $max = $this->getMaxSelection();
+
+ if($min !== '' && $max !=- '')
+ return $exists && $count >= intval($min) && $count <= intval($max);
+ else if($min === '' && $max !== '')
+ return $exists && $count <= intval($max);
+ else if($min !== '' && $max === '')
+ return $exists && $count >= intval($min);
+ }
+
+ /**
+ * @param TListControl control to validate
+ * @return array number of selected values and its values.
+ */
+ protected function getSelection($control)
+ {
+ $count = 0;
+ $values = array();
+
+ //get the data
+ foreach($control->getItems() as $item)
+ {
+ if($item->getSelected())
+ {
+ $count++;
+ $values[] = $item->getValue();
+ }
+ }
+ return array($count, $values);
+ }
+
+ /**
+ * @return array list of required values.
+ */
+ protected function getRequiredValues()
+ {
+ $required = array();
+ $string = $this->getRequiredSelections();
+ if(!empty($string))
+ $required = preg_split('/,\s*/', $string);
+ return $required;
+ }
+
+ /**
+ * Returns an array of javascript validator options.
+ * @return array javascript validator options.
+ */
+ protected function getClientScriptOptions()
+ {
+ $options = parent::getClientScriptOptions();
+ $control = $this->getValidationTarget();
+
+ if(!$control instanceof TListControl)
+ {
+ throw new TConfigurationException(
+ 'tlistcontrolvalidator_invalid_control',
+ $this->getID(),$this->getControlToValidate(), get_class($control));
+ }
+
+ $min = $this->getMinSelection();
+ $max = $this->getMaxSelection();
+ if($min !== '')
+ $options['Min']= intval($min);
+ if($max !== '')
+ $options['Max']= intval($max);
+ $required = $this->getRequiredSelections();
+ if(strlen($required) > 0)
+ $options['Required']= $required;
+ $options['TotalItems'] = $control->getItemCount();
+
+ return $options;
+ }
+}
+?>
\ No newline at end of file
diff --git a/framework/Web/UI/WebControls/TRangeValidator.php b/framework/Web/UI/WebControls/TRangeValidator.php
index 56cc16bc..b7387522 100644
--- a/framework/Web/UI/WebControls/TRangeValidator.php
+++ b/framework/Web/UI/WebControls/TRangeValidator.php
@@ -29,7 +29,6 @@ Prado::using('System.Web.UI.WebControls.TBaseValidator');
* operation is performed. The following value types are supported:
* - Integer A 32-bit signed integer data type.
* - Float A double-precision floating point number data type.
- * - Currency A decimal data type that can contain currency symbols.
* - Date A date data type. The date format can be specified by
* setting {@link setDateFormat DateFormat} property, which must be recognizable
* by {@link TSimpleDateFormatter}. If the property is not set,
@@ -87,13 +86,13 @@ class TRangeValidator extends TBaseValidator
}
/**
- * Sets the data type (Integer, Float, Currency, Date, String) that the values
- * being compared are converted to before the comparison is made.
+ * Sets the data type (Integer, Float, Date, String) that the values being
+ * compared are converted to before the comparison is made.
* @param string the data type
*/
public function setDataType($value)
{
- $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'Integer','Float','Date','Currency','String'),'String');
+ $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'Integer','Float','Date','String'),'String');
}
/**
@@ -131,8 +130,6 @@ class TRangeValidator extends TBaseValidator
return $this->isValidInteger($value);
case 'Float':
return $this->isValidFloat($value);
- case 'Currency':
- return $this->isValidCurrency($value);
case 'Date':
return $this->isValidDate($value);
default:
@@ -178,38 +175,6 @@ class TRangeValidator extends TBaseValidator
return $valid;
}
- /**
- * Determine if the value is a valid currency range,
- * @param string currency value
- * @return boolean true if within range.
- */
- protected function isValidCurrency($value)
- {
- $minValue=$this->getMinValue();
- $maxValue=$this->getMaxValue();
-
- $valid=true;
- $value = $this->getCurrencyValue($value);
- if($minValue!=='')
- $valid=$valid && ($value>= $this->getCurrencyValue($minValue));
- if($maxValue!=='')
- $valid=$valid && ($value<= $this->getCurrencyValue($minValue));
- return $valid;
- }
-
- /**
- * Parse the string into a currency value, return the float value of the currency.
- * @param string currency as string
- * @return float currency value.
- */
- protected function getCurrencyValue($value)
- {
- if(preg_match('/[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?/',$value,$matches))
- return floatval($matches[0]);
- else
- return 0.0;
- }
-
/**
* Determine if the date is within the specified range.
* Uses pradoParseDate and strtotime to get the date from string.
diff --git a/framework/Web/UI/WebControls/TRequiredFieldValidator.php b/framework/Web/UI/WebControls/TRequiredFieldValidator.php
index ddbb12c8..04e333eb 100644
--- a/framework/Web/UI/WebControls/TRequiredFieldValidator.php
+++ b/framework/Web/UI/WebControls/TRequiredFieldValidator.php
@@ -21,6 +21,9 @@ Prado::using('System.Web.UI.WebControls.TBaseValidator');
* TRequiredFieldValidator makes the associated input control a required field.
* The input control fails validation if its value does not change from
* the {@link setInitialValue InitialValue} property upon losing focus.
+ *
+ * Validation will also succeed if input is of TListControl type and the number
+ * of selected values different from the initial value is greater than zero.
*
* @author Qiang Xue
* @version $Revision: $ $Date: $
@@ -53,12 +56,32 @@ class TRequiredFieldValidator extends TBaseValidator
* This method overrides the parent's implementation.
* The validation succeeds if the input component changes its data
* from the {@link getInitialValue InitialValue} or the input control is not given.
+ *
+ * Validation will also succeed if input is of TListControl type and the
+ * number of selected values different from the initial value is greater
+ * than zero.
+ *
* @return boolean whether the validation succeeds
*/
protected function evaluateIsValid()
{
- $value=$this->getValidationValue($this->getValidationTarget());
- return trim($value)!==trim($this->getInitialValue()) || (is_bool($value) && $value);
+ $control = $this->getValidationTarget();
+ $initial = trim($this->getInitialValue());
+ if($control instanceof TListControl)
+ {
+ $count = 0;
+ foreach($control->getItems() as $item)
+ {
+ if($item->getSelected() && $item->getValue() != $initial)
+ $count++;
+ }
+ return $count > 0;
+ }
+ else
+ {
+ $value=$this->getValidationValue($control);
+ return trim($value)!==$initial || (is_bool($value) && $value);
+ }
}
/**
@@ -69,6 +92,9 @@ class TRequiredFieldValidator extends TBaseValidator
{
$options = parent::getClientScriptOptions();
$options['InitialValue']=$this->getInitialValue();
+ $control = $this->getValidationTarget();
+ if($control instanceof TListControl)
+ $options['TotalItems'] = $control->getItemCount();
return $options;
}
}
--
cgit v1.2.3