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