From fd019bf034ef4dbedfc305c77fed0dbd83a732c4 Mon Sep 17 00:00:00 2001 From: wei <> Date: Tue, 25 Apr 2006 00:27:44 +0000 Subject: Add TListControlValidator. Update client-side validators, datepicker.js, colorpicker.js. Merge to 3.0 if necessary. --- .gitattributes | 6 + .gitignore | 11 + .../jsbuilder/JavaScript Documentation Tool.html | 496 +++++++++++++++++++++ buildscripts/jsbuilder/build.php | 25 +- framework/Exceptions/messages.txt | 2 + framework/Util/TSimpleDateFormatter.php | 8 +- .../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 | 12 +- framework/Web/UI/WebControls/TCompareValidator.php | 16 +- .../Web/UI/WebControls/TDataTypeValidator.php | 8 +- framework/Web/UI/WebControls/TDatePicker.php | 45 +- framework/Web/UI/WebControls/THtmlArea.php | 2 +- .../Web/UI/WebControls/TListControlValidator.php | 214 +++++++++ framework/Web/UI/WebControls/TRadioButton.php | 7 +- framework/Web/UI/WebControls/TRangeValidator.php | 41 +- .../Web/UI/WebControls/TRequiredFieldValidator.php | 30 +- .../features/protected/pages/DatePicker.page | 18 +- .../validators/protected/pages/DatePicker.page | 64 +++ .../validators/protected/pages/Layout.tpl | 5 + .../validators/protected/pages/ListControl.page | 58 +++ .../protected/pages/RequiredListValidator.page | 13 +- .../validators/tests/DatePickerTestCase.php | 63 +++ .../validators/tests/ListControlTestCase.php | 48 ++ .../validators/tests/RequiredListTestCase.php | 53 +-- 34 files changed, 1852 insertions(+), 325 deletions(-) create mode 100644 .gitignore create mode 100644 buildscripts/jsbuilder/JavaScript Documentation Tool.html create mode 100644 framework/Web/UI/WebControls/TListControlValidator.php create mode 100644 tests/FunctionalTests/validators/protected/pages/DatePicker.page create mode 100644 tests/FunctionalTests/validators/protected/pages/ListControl.page create mode 100644 tests/FunctionalTests/validators/tests/DatePickerTestCase.php create mode 100644 tests/FunctionalTests/validators/tests/ListControlTestCase.php diff --git a/.gitattributes b/.gitattributes index 09a257fd..de232c2e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,6 +4,7 @@ /UPGRADE -text /build.xml -text buildscripts/.htaccess -text +buildscripts/jsbuilder/JavaScript[!!-~]Documentation[!!-~]Tool.html -text buildscripts/jsbuilder/build.php -text buildscripts/phing/style/coverage-frames.xsl -text buildscripts/phing/style/log.xsl -text @@ -977,6 +978,7 @@ framework/Web/UI/WebControls/TLabel.php -text framework/Web/UI/WebControls/TLinkButton.php -text framework/Web/UI/WebControls/TListBox.php -text framework/Web/UI/WebControls/TListControl.php -text +framework/Web/UI/WebControls/TListControlValidator.php -text framework/Web/UI/WebControls/TLiteral.php -text framework/Web/UI/WebControls/TMultiView.php -text framework/Web/UI/WebControls/TOutputCache.php -text @@ -1160,12 +1162,14 @@ tests/FunctionalTests/validators/protected/pages/CheckBox.php -text tests/FunctionalTests/validators/protected/pages/CompareValidator.page -text tests/FunctionalTests/validators/protected/pages/CustomValidator.page -text tests/FunctionalTests/validators/protected/pages/CustomValidator.php -text +tests/FunctionalTests/validators/protected/pages/DatePicker.page -text tests/FunctionalTests/validators/protected/pages/ImageButton.page -text tests/FunctionalTests/validators/protected/pages/ImageButton.php -text tests/FunctionalTests/validators/protected/pages/Layout.php -text tests/FunctionalTests/validators/protected/pages/Layout.tpl -text tests/FunctionalTests/validators/protected/pages/LinkButton.page -text tests/FunctionalTests/validators/protected/pages/LinkButton.php -text +tests/FunctionalTests/validators/protected/pages/ListControl.page -text tests/FunctionalTests/validators/protected/pages/RangeValidatorDate.page -text tests/FunctionalTests/validators/protected/pages/RangeValidatorFloat.page -text tests/FunctionalTests/validators/protected/pages/RangeValidatorInteger.page -text @@ -1179,8 +1183,10 @@ tests/FunctionalTests/validators/tests/ButtonTestCase.php -text tests/FunctionalTests/validators/tests/CheckBoxTestCase.php -text tests/FunctionalTests/validators/tests/CompareValidatorTestCase.php -text tests/FunctionalTests/validators/tests/CustomValidatorTestCase.php -text +tests/FunctionalTests/validators/tests/DatePickerTestCase.php -text tests/FunctionalTests/validators/tests/ImageButtonTestCase.php -text tests/FunctionalTests/validators/tests/LinkButtonTestCase.php -text +tests/FunctionalTests/validators/tests/ListControlTestCase.php -text tests/FunctionalTests/validators/tests/RangeValidatorTestCase.php -text tests/FunctionalTests/validators/tests/RegExpValidatorTestCase.php -text tests/FunctionalTests/validators/tests/RequiredFieldTestCase.php -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2b2c3957 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +buildscripts/jsbuilder/JSDoc +tests/FunctionalTests/features/assets/589569dd +tests/FunctionalTests/features/assets/8fb9443f +tests/FunctionalTests/features/assets/a4d5cb67 +tests/FunctionalTests/features/assets/d7c2e63b +tests/FunctionalTests/features/assets/fcff0f02 +tests/FunctionalTests/features/protected/runtime/application.xml +tests/FunctionalTests/results.dat +tests/FunctionalTests/validators/assets/8fb9443f +tests/FunctionalTests/validators/assets/a4d5cb67 +tests/FunctionalTests/validators/protected/runtime/global.cache diff --git a/buildscripts/jsbuilder/JavaScript Documentation Tool.html b/buildscripts/jsbuilder/JavaScript Documentation Tool.html new file mode 100644 index 00000000..5a61fd5a --- /dev/null +++ b/buildscripts/jsbuilder/JavaScript Documentation Tool.html @@ -0,0 +1,496 @@ + + + JSDoc Homepage - JavaScript Documentation Tool + + + + + +

JSDoc - JavaScript Documentation Tool

+ + + + +
+ +
+ + + + + + + +

Introduction

+

+ JSDoc is a tool that parses inline documentation in JavaScript source files, and produces an documentation of the JavaScript code. This is typically in the form of HTML (example), but XML and XMI (UML) export are also supported. JSDoc is based on the (very successful) javadoc tool that was created for the same purpose for the Java programming language. +

+ +

+ JSDoc is primarily intended for libraries of object-oriented JavaScript files, although it also works with procedural code. There is a basic ability to determine inheritance built into the parser, although some more obscure dynamic constructs will not be understood (for example, defining a method to set one class as the superclass of another). +

+ +

+ Anyone familiar with the javadoc tool will be able to use JSDoc right away; for anyone else it is a very simple matter to get started. JSDoc picks up on comments that are opened with a slash followed by two stars (/**), and closed with the typical star-slash (*/). +

+ +
+ + + + + + + + + +

Installation

+ JSDoc is implemented in Perl, as as such, requires the perl executable. This shouldn't be a problem for Linux/Unix users, but Windows users will probably have to install a perl runtime; ActivePerl is recommended. +

JSDoc is distributed as a gzipped tarball, and can be downloaded from the JSDoc project page. Once you have downloaded JSDoc-x-x.tgz, just unpack it (tar zxf JSDoc-x-x.tgz on Linux/Unix, use WinZip on Windows), and you're ready to go. You can immediately try out JSDoc on the included test.js JavaScript file by going into the JSDoc directory and entering the command +

+                  perl jsdoc.pl test.js
+               
+ This should output a set of HTML files in a directory called js_docs_out. + +

+ If you get an error message right away that looks something like +

+                  Can't locate HTML/Template.pm in @INC ......
+                
+ then you will need to install the HTML::Template Perl module. Luckily, this is a very trivial operation thanks to the CPAN. In a Linux/Unix/POSIX-style operating system, open a terminal and run the following command as root: +
+                  # perl -MCPAN -e 'install HTML::Template'
+               
+

+ If you're running Windows and ActivePerl, open a command prompt window and enter the following command: +

+                  C:\> ppm
+               
+ This will start the Perl Package Manager; at the PPM prompt, enter the following two commands (PPM> is the PPM prompt): +
+                  PPM> install HTML-Template
+                  PPM> quit
+               
+

+ JSDoc should run without problems now. + +

+
+ + + + + + + + + +

Usage

+

+ The general idea of JSDoc is to pick up the form and documentation of code. A major part of JSDoc's functionality is based around documentation strings embedded in JavaScript code. The definitive reference to writing documentation strings for Java (with a large amount of carry-over to JSDoc) can be found at the javadoc reference page. +

+ +

+ JSDoc currently supports a subset of javadoc's '@'-prefixed attributes, but the parser is customizable (as explained later). An example JavaScript file (test.js) is included with the JSDoc distribution. Most functionality of JSDoc can be seen within this script file, however for the impatient a small (and incomplete) example of a common usage of JSDoc is shown below +

+ +

+

+                  
+                  /**
+                   * Shape is an abstract base class. It is defined simply
+                   * to have something to inherit from for geometric 
+                   * subclasses
+                   * @constructor
+                   */
+                   function Shape(color){
+                     this.color = color;
+                   }
+
+                   // Bind the Shape_getColor method to the Shape class
+                   Shape.prototype.getColor = Shape_getColor;
+                    
+                   /**
+                    * Get the name of the color for this shape
+                    * @returns A color string for this shape
+                    */
+                   function Shape_getColor(){
+                     return this.color;
+                   }
+                    
+                   /**
+                    * Circle is a subclass of Shape
+                    */
+                   function Circle(radius){
+                     this.radius = radius;
+                   }
+                    
+                   /**
+                    * A very rough value for pi
+                    */
+                   Circle.PI = 3.14;
+                   
+                   /**
+                    * Get the radius of this circle 
+                    * @returns The radius of this circle
+                    */
+                   function Circle_getRadius(){
+                     return this.radius;
+                   }
+                  
+                   // Circle is a subclass of Shape
+                   Circle.prototype = new Shape(null);
+               
+

+ +

+ One important difference between javadoc and JSDoc is that JSDoc deals with a much more dynamic language that javadoc. For example, in JavaScript there are many different ways to make one class a subclass of another. The JSDoc parser is able to catch some of these methods, but for particularly dynamic scripts the parser will not be able to do it all on its own. For that reason, JSDoc is customizable with a file called .jsdoc_config that resides in the JSDoc install directory. In this configuration file, JSDoc's behaviour can be customized for any kind of '@'-prefixed attribute. Although this configuration file actually contains Perl code, simple customizations can be done by someone with minimal experience with Perl. +

+ + + JSDoc also uses a several non-javadoc '@'-attributes: @constructor and @private are two of the most important ones. As noted above, JavaScript has the capability to be a much more dynamic language than Java. The JSDoc parser can usually find class constructors on it's own, but there are some situations when it needs some additional assistance. For this reason, it is a good practice to always include the @constructor tag in the documentation for a class constructor, as shown below: +
+                  
+                  /**
+                   * Nothing is a class that represents nothing
+                   * @constructor
+                   */
+                  function Nothing(){
+                     // ... initialization ...
+                  }
+               
+ + The @private attribute simply displays the marked method as being private. +

+

+ To get more information on the use of the jsdoc.pl executable itself, run it with the --help commandline option. +

+ +
+ + + + + + + + +

Tag Reference

+ The following is a summary of the supported tags ('@'-attributes) that are supported by JSDoc. For actual examples of the usage of these tags, please see the test.js JavaScript file that is included in the JSDoc distribution. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@param + Provide information about a function parameter. A datatype indicator can be added between curly braces with this tag, as follows: +
+        /**
+         * @param {String} paramName This is a string parameter
+         */
+                        
+
@argumentSynonym for @param +
@returnProvide information about the return value of a function. +
@returnsSynonym for @return +
@authorProvide information about the author of a JavaScript file or a function. +
@deprecated + Signify that a function or class is deprecated, and should not be used if possible. +
@see + Link to another class or function that is of importance to the current class or function. This tag can take several forms. +

+ To link to another method within the same class: +

+        @see #methodName
+                        
+

+ To link to another class: +

+        @see ClassName
+                        
+

+ To link to a specific method of another class: +

+        @see ClassName#methodName
+                        
+
@versionShow the version number of the current file or class +
@requiresDefine a dependency upon another class. The syntax for this tag is as follows: +
+        @requires OtherClassName This is text to be shown
+                        
+
@throws + Show that a method can throw a certain type of exception. The syntax for this tag is: +
+        @throws ExceptionType This is the label text
+                        
+
@exceptionSynonym for @throws +
@linkThis is a powerful tag that can be used to link to a large number of other pages. It is also the only tag that can be used in the description text of a documentation string before the '@'-tag section. The usage is very similar to that of the @see tag, but the entire tag is wrapped in curly braces. For example: +
+        /**
+         * This utility method is also a member of the {@link String} class, 
+         * in the form of the {@link String#utility} method. 
+         */
+                        
+
@fileoverviewThis is a special-use tag. If the first block of documentation in a file starts with a @fileoverview tag, the rest of the documentation block will be used to provide a file overview in the documentation. +
@classThis tag is used in a constructor's documentation block to provide information about the actual class. The included documentation will then not be included in the constructor's documentation. +
@constructorSignify that a function is the constructor for a class. +
@typeShow the return type of a function. For example: +
+        /**
+         * This function returns a String.
+         * @return The name of the current user
+         * @type String
+         */
+                        
+
@extendsUsed to show that a class is a subclass of another class. JSDoc is often quite good at picking this up on its own, but in some situations this tag is required. +
@private + Signify that a function or class is private. Private classes and functions will not be available in the documentation unless JSDoc is run with the --private commandline option. +
@final + Flag a value as being a final (constant) value. +
@member + Show that a function is a member of a given class: +
+        /**
+         * @member MyClass
+         */
+ 	function SomeFunc(){
+	}
+                        
+
@ignore + Tell JSDoc to totally ignore this method. +
@base + Force JSDoc to view the current class constructor + as a subclass of the class given as the value to + this tag: +
+       /**
+        * This is a subclass of Shape
+        * @constructor
+        * @base Shape
+        */
+       function Circle(){
+           // ...
+       }
+			
+
@addon + Mark a function as being an "addon" to a core + JavaScript function that isn't defined within + your own sources, as shown below: +
+	/**
+ 	 * This is an addon function to SomeCoreClass which is
+         * not defined within our own sources. 
+	 * @addon
+	 */
+	SomeCoreClass.someFunction = function(){ 
+            // ...
+	}		
+
@execExperimental!
+ Force JSDoc to "execute" this method as part of its + preprocessing step, in the same way that class + contructors are executed. This can allow attributes + to be added to a class from within a function. +
+ +
+ + + + + + + + + +

Frequently Asked Questions

+
    +
  • I get an error message that says Can't locate HTML/Template.pm in @INC ....... What's going on? +

    This means that you don't have the HTML::Template perl module installed. Luckily, it's very easy to remedy this problem; see the Installation section of this page.

  • + +
  • I've found a bug in JSDoc. What should I do? +

    Go to the JSDoc Project Page and register a bug report. You can also send information about the bug to the JSDoc-user mailing list. But just before you do that, please make sure that it's actually a bug that we're talking about, and not just JSDoc acting differently that what you were expecting.

  • + +
  • JSDoc segfaults! What should I do? +

    This is a tough one to answer. JSDoc uses a lot of recursive regular expressions, and sometimes a certain combination of a certain build of perl and a certain JavaScript file will cause perl to segfault while running JSDoc. This problem mostly occurs with binary installations of perl; downloading the most recent source distribution of perl and building it on your own machine seems to solve this problem.

    +

    Another action that can sometimes help in this situation (and doesn't require a new install of perl) is by changing the recursion limit for recursive regexes. You can do this by altering the value given to the constant RECURSION in JSDoc.pm (at line 124 at the time of writing). Changing the value from 10 to a lower value, such as 6, can sometimes take care of this issue. The main risk in taking this action is that some deeply-nested constructs in your JavaScript code might not be processed by JSDoc.

  • + +
  • I want JSDoc to do X, but it doesn't! +

    Well, that's actually not a question, it's more of a statement. However, it is stated quite frequently, so it's not totally out of place here. If you're interested in having a new feature added to JSDoc, you can register a Feature Request at the JSDoc Project Page. You may also want to consider sending information about your request to the JSDoc-user mailing list.

  • + +
  • Is there a mailing list for JSDoc? +

    Okay, I admit that this isn't actually a Frequently Asked Question; it's actually just an attempt to promote the JSDoc-user mailing list.

  • + +
  • I want to help out with JSDoc. Where should I start? +

    First of all, help is very welcome! The most welcome kinds of help are context diffs that fix a bug in JSDoc, but pretty much anything that's an attempt at helping out is appreciated. Use the JSDoc-user mailing list.

  • + + +
+ +
+ + + + + + + + +

More Information

+

+ A very complete and informative article (written by Rainer Eschen) on the use of JSDoc can be found at Webetiser. The article is based on JSDoc 1.5, but is still very relevant. I've been told that the article will be updated in the near future. + +

+ As noted above, the definitive reference for writing Java docstrings can be found at the javadoc reference page. Please send comments, bugs, complaints, requests for additional information, and whatever else to the JSDoc mailing list. +

+ +
+ + SourceForge.net Logo + + diff --git a/buildscripts/jsbuilder/build.php b/buildscripts/jsbuilder/build.php index d5d04713..910f0394 100644 --- a/buildscripts/jsbuilder/build.php +++ b/buildscripts/jsbuilder/build.php @@ -27,6 +27,18 @@ define('SOURCE_DIR',realpath(dirname(__FILE__).'/../../framework/Web/JavaScripts * The directory for storing compressed js files */ define('TARGET_DIR',realpath(dirname(__FILE__).'/../../framework/Web/JavaScripts/js')); +/** + * Location of the perl JS doc generator. + */ +define('JS_DOC', realpath(dirname(__FILE__).'/JSDoc/jsdoc.pl')); +/** + * Javascript documentation output directory. + */ +define('DOC_OUTPUT_DIR', realpath(dirname(__FILE__).'/../../docs/Javascript')); +/** + * Javascript documentation build command + */ +define('BUILD_DOC', sprintf('perl "%s" --no-sources -d "%s" ', JS_DOC, DOC_OUTPUT_DIR).'%s'); if(SOURCE_DIR===false || TARGET_DIR===false) die('Unable to determine the build path.'); @@ -154,7 +166,18 @@ foreach($libraries as $libFile => $sourceFiles) echo "Saving file {$libFile}\n"; $builds++; } -if($builds > 0) +if(preg_match('/doc*/', $argv[1])) +{ + $files = ""; + foreach($libraries as $lib) + { + foreach($lib as $source) + $files .= sprintf(' "%s/%s"', SOURCE_DIR, $source); + } + $command = sprintf(BUILD_DOC, $files); + system($command); +} +else if($builds > 0) echo "\nJavascript build complete, {$builds} file(s) compressed."; else echo "No files to build."; diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index cde4eae1..20bafba2 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -231,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. diff --git a/framework/Util/TSimpleDateFormatter.php b/framework/Util/TSimpleDateFormatter.php index 052e0074..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; 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 2f0cce7c..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; @@ -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/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/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/TRadioButton.php b/framework/Web/UI/WebControls/TRadioButton.php index 9a523b55..fd13e88c 100644 --- a/framework/Web/UI/WebControls/TRadioButton.php +++ b/framework/Web/UI/WebControls/TRadioButton.php @@ -101,14 +101,14 @@ class TRadioButton extends TCheckBox $this->setViewState('GroupName',$value,''); } - protected function getValueAttribute() +/* protected function getValueAttribute() { if(($value=parent::getValueAttribute())==='') return $this->getUniqueID(); else return $value; } - +*/ /** * @return string the name used to fetch radiobutton post data */ @@ -146,7 +146,8 @@ class TRadioButton extends TCheckBox $writer->addAttribute('id',$clientID); $writer->addAttribute('type','radio'); $writer->addAttribute('name',$this->getUniqueGroupName()); - $writer->addAttribute('value',$this->getValueAttribute()); + if(($value = $this->getValueAttribute()) !== '') + $writer->addAttribute('value',$value); if($this->getChecked()) $writer->addAttribute('checked','checked'); if(!$this->getEnabled(true)) 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; } } diff --git a/tests/FunctionalTests/features/protected/pages/DatePicker.page b/tests/FunctionalTests/features/protected/pages/DatePicker.page index 4347f748..261a6994 100644 --- a/tests/FunctionalTests/features/protected/pages/DatePicker.page +++ b/tests/FunctionalTests/features/protected/pages/DatePicker.page @@ -12,45 +12,47 @@ - Button Mode + Button Mode, pre-selected date - + InputMode="DropDownList", custom DateFormat - + InputMode="DropDownList", custom DateFormat, Culture - + - Custom DateFormat, culture, ImageButton mode + Custom DateFormat, culture, ImageButton mode, pre-selected date - + /> Custom DateFormat, DropDownList, pre-selected date set in as Text - + DropDownList, pre-selected date as integer - /> + /> diff --git a/tests/FunctionalTests/validators/protected/pages/DatePicker.page b/tests/FunctionalTests/validators/protected/pages/DatePicker.page new file mode 100644 index 00000000..fdbbbeb1 --- /dev/null +++ b/tests/FunctionalTests/validators/protected/pages/DatePicker.page @@ -0,0 +1,64 @@ + +

Date Picker validation Test

+ + + + +
+ + + + +
+ Date 1: + + +
+ Date 2: + + + +
+ + Date 3: + +
+ Date 4: + + + + +
\ No newline at end of file diff --git a/tests/FunctionalTests/validators/protected/pages/Layout.tpl b/tests/FunctionalTests/validators/protected/pages/Layout.tpl index 224481e1..25dbea09 100644 --- a/tests/FunctionalTests/validators/protected/pages/Layout.tpl +++ b/tests/FunctionalTests/validators/protected/pages/Layout.tpl @@ -21,6 +21,11 @@ margin-top: 2em; display: block; } + .required + { + border: 1px solid red; + background-color: pink; + } /*]]>*/ diff --git a/tests/FunctionalTests/validators/protected/pages/ListControl.page b/tests/FunctionalTests/validators/protected/pages/ListControl.page new file mode 100644 index 00000000..1429e184 --- /dev/null +++ b/tests/FunctionalTests/validators/protected/pages/ListControl.page @@ -0,0 +1,58 @@ + +

List Control Required Field Validation Test

+ + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + +
\ No newline at end of file diff --git a/tests/FunctionalTests/validators/protected/pages/RequiredListValidator.page b/tests/FunctionalTests/validators/protected/pages/RequiredListValidator.page index 9bbd9d5a..665d71b7 100644 --- a/tests/FunctionalTests/validators/protected/pages/RequiredListValidator.page +++ b/tests/FunctionalTests/validators/protected/pages/RequiredListValidator.page @@ -8,7 +8,7 @@ -
- + -
- + - - + open("validators/index.php?page=DatePicker", ""); + $this->verifyTextPresent("Date Picker validation Test", ""); + $this->assertNotVisible("{$base}validator1", ""); + $this->assertNotVisible("{$base}validator2", ""); + $this->assertNotVisible("{$base}validator4", ""); + $this->assertNotVisible("{$base}validator5", ""); + $this->assertNotVisible("{$base}validator6", ""); + $this->assertNotVisible("{$base}validator8", ""); + + $this->click("{$base}submit1"); + $this->assertVisible("{$base}validator1", ""); + $this->assertNotVisible("{$base}validator2", ""); + $this->assertVisible("{$base}validator4", ""); + $this->assertVisible("{$base}validator5", ""); + $this->assertNotVisible("{$base}validator6", ""); + $this->assertVisible("{$base}validator8", ""); + + $this->click("{$base}submit1"); + $this->type("{$base}picker1", "13/4/2006"); + $this->select("{$base}picker2_month", "label=9"); + $this->select("{$base}picker2_day", "label=10"); + $this->type("{$base}picker3", "14/4/2006"); + $this->type("{$base}picker4", "7/4/2006"); + $this->select("{$base}picker5_day", "label=6"); + $this->select("{$base}picker5_month", "label=3"); + $this->select("{$base}picker5_year", "label=2007"); + $this->select("{$base}picker6_month", "label=3"); + $this->select("{$base}picker6_year", "label=2007"); + $this->select("{$base}picker6_day", "label=5"); + + $this->click("{$base}submit1"); + + $this->assertNotVisible("{$base}validator1", ""); + $this->assertVisible("{$base}validator2", ""); + $this->assertNotVisible("{$base}validator4", ""); + $this->assertNotVisible("{$base}validator5", ""); + $this->assertVisible("{$base}validator6", ""); + $this->assertVisible("{$base}validator8", ""); + + $this->type("{$base}picker1", "20/4/2007"); + $this->type("{$base}picker4", "29/4/2006"); + $this->select("{$base}picker6_day", "label=10"); + + $this->clickAndWait("{$base}submit1"); + + $this->assertNotVisible("{$base}validator1", ""); + $this->assertNotVisible("{$base}validator2", ""); + $this->assertNotVisible("{$base}validator4", ""); + $this->assertNotVisible("{$base}validator5", ""); + $this->assertNotVisible("{$base}validator6", ""); + $this->assertNotVisible("{$base}validator8", ""); + } + +} + +?> \ No newline at end of file diff --git a/tests/FunctionalTests/validators/tests/ListControlTestCase.php b/tests/FunctionalTests/validators/tests/ListControlTestCase.php new file mode 100644 index 00000000..6c0c73e7 --- /dev/null +++ b/tests/FunctionalTests/validators/tests/ListControlTestCase.php @@ -0,0 +1,48 @@ +open("validators/index.php?page=ListControl", ""); + $this->verifyTextPresent("List Control Required Field Validation Test", ""); + $this->click("//input[@type='submit' and @value='Submit!']", ""); + + $this->assertVisible("{$base}validator1"); + $this->assertVisible("{$base}validator2"); + $this->assertVisible("{$base}validator3"); + $this->assertVisible("{$base}validator4"); + + $this->click("//input[@id='{$base}list1_1' and @value='Red']", ""); + $this->select("{$base}list2", "label=Red"); + $this->select("{$base}list3", "label=Blue"); + $this->click("{$base}list4_3", ""); + $this->clickAndWait("//input[@type='submit' and @value='Submit!']", ""); + + $this->assertNotVisible("{$base}validator1"); + $this->assertNotVisible("{$base}validator2"); + $this->assertNotVisible("{$base}validator3"); + $this->assertNotVisible("{$base}validator4"); + + $this->select("{$base}list3", "label=Don't select this one"); + $this->click("{$base}list4_0"); + $this->select("{$base}list2", "label=--- Select a color ---"); + $this->click("//input[@type='submit' and @value='Submit!']", ""); + $this->click("//input[@id='{$base}list1_1' and @value='Red']", ""); + $this->click("//input[@id='{$base}list1_0' and @value='Select a color below']", ""); + $this->click("//input[@type='submit' and @value='Submit!']", ""); + + $this->assertVisible("{$base}validator1"); + $this->assertVisible("{$base}validator2"); + $this->assertVisible("{$base}validator3"); + $this->assertVisible("{$base}validator4"); + + } + +} + +?> diff --git a/tests/FunctionalTests/validators/tests/RequiredListTestCase.php b/tests/FunctionalTests/validators/tests/RequiredListTestCase.php index 1eab60d4..ad299dc5 100644 --- a/tests/FunctionalTests/validators/tests/RequiredListTestCase.php +++ b/tests/FunctionalTests/validators/tests/RequiredListTestCase.php @@ -5,35 +5,36 @@ class RequiredListTestCase extends SeleniumTestCase function test() { + $base = "ctl0_Content_"; $this->open("validators/index.php?page=RequiredListValidator"); $this->assertLocation("index.php?page=RequiredListValidator"); - $this->click("submit1"); - $this->assertVisible("validator1"); - $this->assertVisible("validator2"); - $this->assertVisible("validator3"); - $this->click("list1:0"); - $this->select("list2", "label=One"); - $this->select("list2", "label=Two"); - $this->click("list3:3"); - $this->clickAndWait("submit1"); - $this->assertNotVisible("validator1"); - $this->assertNotVisible("validator2"); - $this->assertNotVisible("validator3"); - $this->click("list1:1"); - $this->click("list1:2"); - $this->click("list1:3"); - $this->select("list2", "label=Two"); - $this->click("list1:3"); - $this->click("submit1"); - $this->assertNotVisible("validator1"); - $this->assertNotVisible("validator2"); - $this->assertNotVisible("validator3"); - $this->click("list3:3"); - $this->click("submit1"); + $this->click("{$base}submit1"); + $this->assertVisible("{$base}validator1"); + $this->assertVisible("{$base}validator2"); + $this->assertVisible("{$base}validator3"); + $this->click("{$base}list1_0"); + $this->select("{$base}list2", "label=One"); + $this->select("{$base}list2", "label=Two"); + $this->click("{$base}list3_3"); + $this->clickAndWait("{$base}submit1"); + $this->assertNotVisible("{$base}validator1"); + $this->assertNotVisible("{$base}validator2"); + $this->assertNotVisible("{$base}validator3"); + $this->click("{$base}list1_1"); + $this->click("{$base}list1_2"); + $this->click("{$base}list1_3"); + $this->select("{$base}list2", "label=Two"); + $this->click("{$base}list1_3"); + $this->click("{$base}submit1"); + $this->assertNotVisible("{$base}validator1"); + $this->assertNotVisible("{$base}validator2"); + $this->assertNotVisible("{$base}validator3"); + $this->click("{$base}list3_3"); + $this->click("{$base}submit1"); $this->pause(200); - $this->assertNotVisible("validator1"); - $this->assertNotVisible("validator2"); - $this->assertVisible("validator3"); + $this->assertNotVisible("{$base}validator1"); + $this->assertNotVisible("{$base}validator2"); + $this->assertNotVisible("{$base}validator3"); } } -- cgit v1.2.3