http://www.mediawiki.org/wiki/Special:Code/MediaWiki/92633

Revision: 92633
Author:   salvatoreingala
Date:     2011-07-20 14:47:04 +0000 (Wed, 20 Jul 2011)
Log Message:
-----------
- Refactored GadgetPrefs to have a more structured and more flexible language 
for describing preference specifications.
- Decoupled field-description from preference-descriptions, so that it is 
possible to design fields that doesn't encode for preferences or fields that 
possibly encode for more than one preference (e.g.: containers). Refactored 
jquery.formBuilder for the same purpose.
- Changed the return values of the 'getgadgetprefs' api, separating 
"description" and "values" in two separate objects, for consistency.

Modified Paths:
--------------
    branches/salvatoreingala/Gadgets/api/ApiGetGadgetPrefs.php
    branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php
    branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.js
    branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js

Modified: branches/salvatoreingala/Gadgets/api/ApiGetGadgetPrefs.php
===================================================================
--- branches/salvatoreingala/Gadgets/api/ApiGetGadgetPrefs.php  2011-07-20 
12:59:11 UTC (rev 92632)
+++ branches/salvatoreingala/Gadgets/api/ApiGetGadgetPrefs.php  2011-07-20 
14:47:04 UTC (rev 92633)
@@ -51,14 +51,9 @@
                if ( $userPrefs === null ) {
                        throw new MWException( __METHOD__ . ': $userPrefs 
should not be null.' );
                }
-                               
-               //Add user preferences to preference description
-               foreach ( $prefsDescription['fields'] as $prefIdx => 
$prefDescription ) {
-                       $prefName = $prefDescription['name'];
-                       $prefsDescription['fields'][$prefIdx]['value'] = 
$userPrefs[$prefName];
-               }
-
-               $this->getResult()->addValue( null, $this->getModuleName(), 
$prefsDescription );
+               
+               $this->getResult()->addValue( null, 'description', 
$prefsDescription );
+               $this->getResult()->addValue( null, 'values', $userPrefs );
        }
 
        public function getAllowedParams() {

Modified: branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php
===================================================================
--- branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php    2011-07-20 
12:59:11 UTC (rev 92632)
+++ branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php    2011-07-20 
14:47:04 UTC (rev 92633)
@@ -10,145 +10,200 @@
 
 class GadgetPrefs {
        
-       //Syntax specifications of preference description language.
-       //Each element describes a type, and it has a 'description' and may 
have a 'checker'.
-       // - 'description' is an array that describes the fields of that option.
-       // - 'checker' is an optional function that does validation of the 
entire preference description,
-       //   when more complex semantics are needed.
+       /*
+        * Syntax specifications of preference description language.
+        * Each element describes a field; a "simple" field encodes exactly one 
gadget preference, but some fields
+        * may encode for 0 or multiple gadget preferences.
+        * Each field has a 'description' and may have a 'validator', a 
'flattener', and a 'checker'.
+        * - 'description' is an array that describes all the members of that 
fields. Each member description has this shape:
+        *     - 'isMandatory' is a boolean that specifies if that member is 
mandatory for the field;
+        *     - 'validator', if specified, is the name of a function that 
validates that member
+        * - 'validator' is an optional function that does validation of the 
entire field description,
+        *   when member validators does not suffice since more complex 
semantics are needed.
+        * - 'flattener' is an optional function that takes a valid field 
description and returns an array of specification of
+        *   gadget preferences, with preference names as keys and 
corresponding "simple" field descriptions as values.
+        *   If omitted (for "simple fields"), the default flattener is used.
+        * - 'checker', only for "simple" fields, is the name of a function 
that takes a preference description and
+        *   a preference value, and returns true if that value passes 
validation, false otherwise.
+        * - 'getMessages', if specified, is the name of a function that takes 
a valid description of a field and returns
+        *   a list of messages referred to by it. If omitted, only the "label" 
field is returned (if it is a message).
+        */
        private static $prefsDescriptionSpecifications = array(
                'boolean' => array( 
                        'description' => array(
+                               'name' => array(
+                                       'isMandatory' => true,
+                                       'validator' => 
'GadgetPrefs::isValidPreferenceName'
+                               ),
                                'default' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_bool'
+                                       'validator' => 'is_bool'
                                ),
                                'label' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_string'
+                                       'validator' => 'is_string'
                                )
-                       )
+                       ),
+                       'checker' => 'GadgetPrefs::checkBooleanPref'
                ),
                'string' => array(
                        'description' => array(
+                               'name' => array(
+                                       'isMandatory' => true,
+                                       'validator' => 
'GadgetPrefs::isValidPreferenceName'
+                               ),
                                'default' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_string'
+                                       'validator' => 'is_string'
                                ),
                                'label' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_string'
+                                       'validator' => 'is_string'
                                ),
                                'required' => array(
                                        'isMandatory' => false,
-                                       'checker' => 'is_bool'
+                                       'validator' => 'is_bool'
                                ),
                                'minlength' => array(
                                        'isMandatory' => false,
-                                       'checker' => 'is_integer'
+                                       'validator' => 'is_integer'
                                ),
                                'maxlength' => array(
                                        'isMandatory' => false,
-                                       'checker' => 'is_integer'
+                                       'validator' => 'is_integer'
                                )
                        ),
-                       'checker' => 'GadgetPrefs::checkStringOptionDefinition'
+                       'validator' => 
'GadgetPrefs::validateStringOptionDefinition',
+                       'checker' => 'GadgetPrefs::checkStringPref'
                ),
                'number' => array(
                        'description' => array(
+                               'name' => array(
+                                       'isMandatory' => true,
+                                       'validator' => 
'GadgetPrefs::isValidPreferenceName'
+                               ),
                                'default' => array(
                                        'isMandatory' => true,
-                                       'checker' => 
'GadgetPrefs::isFloatOrIntOrNull'
+                                       'validator' => 
'GadgetPrefs::isFloatOrIntOrNull'
                                ),
                                'label' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_string'
+                                       'validator' => 'is_string'
                                ),
                                'required' => array(
                                        'isMandatory' => false,
-                                       'checker' => 'is_bool'
+                                       'validator' => 'is_bool'
                                ),
                                'integer' => array(
                                        'isMandatory' => false,
-                                       'checker' => 'is_bool'
+                                       'validator' => 'is_bool'
                                ),
                                'min' => array(
                                        'isMandatory' => false,
-                                       'checker' => 'GadgetPrefs::isFloatOrInt'
+                                       'validator' => 
'GadgetPrefs::isFloatOrInt'
                                ),
                                'max' => array(
                                        'isMandatory' => false,
-                                       'checker' => 'GadgetPrefs::isFloatOrInt'
+                                       'validator' => 
'GadgetPrefs::isFloatOrInt'
                                )
                        ),
-                       'checker' => 'GadgetPrefs::checkNumberOptionDefinition'
+                       'validator' => 
'GadgetPrefs::validateNumberOptionDefinition',
+                       'checker' => 'GadgetPrefs::checkNumberPref'
                ),
                'select' => array(
                        'description' => array(
+                               'name' => array(
+                                       'isMandatory' => true,
+                                       'validator' => 
'GadgetPrefs::isValidPreferenceName'
+                               ),
                                'default' => array(
                                        'isMandatory' => true
                                ),
                                'label' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_string'
+                                       'validator' => 'is_string'
                                ),
                                'options' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_array'
+                                       'validator' => 'is_array'
                                )
                        ),
-                       'checker' => 'GadgetPrefs::checkSelectOptionDefinition'
+                       'validator' => 
'GadgetPrefs::validateSelectOptionDefinition',
+                       'checker' => 'GadgetPrefs::checkSelectPref',
+                       'getMessages' => 'GadgetPrefs::getSelectMessages'
                ),
                'range' => array(
                        'description' => array(
+                               'name' => array(
+                                       'isMandatory' => true,
+                                       'validator' => 
'GadgetPrefs::isValidPreferenceName'
+                               ),
                                'default' => array(
                                        'isMandatory' => true,
-                                       'checker' => 
'GadgetPrefs::isFloatOrIntOrNull'
+                                       'validator' => 
'GadgetPrefs::isFloatOrIntOrNull'
                                ),
                                'label' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_string'
+                                       'validator' => 'is_string'
                                ),
                                'min' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'GadgetPrefs::isFloatOrInt'
+                                       'validator' => 
'GadgetPrefs::isFloatOrInt'
                                ),
                                'max' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'GadgetPrefs::isFloatOrInt'
+                                       'validator' => 
'GadgetPrefs::isFloatOrInt'
                                ),
                                'step' => array(
                                        'isMandatory' => false,
-                                       'checker' => 'GadgetPrefs::isFloatOrInt'
+                                       'validator' => 
'GadgetPrefs::isFloatOrInt'
                                )
                        ),
-                       'checker' => 'GadgetPrefs::checkRangeOptionDefinition'
+                       'validator' => 
'GadgetPrefs::validateRangeOptionDefinition',
+                       'checker' => 'GadgetPrefs::checkRangePref'
                ),
                'date' => array(
                        'description' => array(
+                               'name' => array(
+                                       'isMandatory' => true,
+                                       'validator' => 
'GadgetPrefs::isValidPreferenceName'
+                               ),
                                'default' => array(
                                        'isMandatory' => true
                                ),
                                'label' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_string'
+                                       'validator' => 'is_string'
                                )
-                       )
+                       ),
+                       'checker' => 'GadgetPrefs::checkDatePref'
                ),
                'color' => array(
                        'description' => array(
+                               'name' => array(
+                                       'isMandatory' => true,
+                                       'validator' => 
'GadgetPrefs::isValidPreferenceName'
+                               ),
                                'default' => array(
                                        'isMandatory' => true
                                ),
                                'label' => array(
                                        'isMandatory' => true,
-                                       'checker' => 'is_string'
+                                       'validator' => 'is_string'
                                )
-                       )
+                       ),
+                       'checker' => 'GadgetPrefs::checkColorPref'
                )
        );
        
+       private static function isValidPreferenceName( $name ) {
+               return strlen( $name ) <= 40
+                               && preg_match( '/^[a-zA-Z_][a-zA-Z0-9_]*$/', 
$name );
+       }
+
+       
        //Further checks for 'string' options
-       private static function checkStringOptionDefinition( $option ) {
+       private static function validateStringOptionDefinition( $option ) {
                if ( isset( $option['minlength'] ) && $option['minlength'] < 0 
) {
                        return false;
                }
@@ -174,8 +229,13 @@
                return is_float( $param ) || is_int( $param ) || $param === 
null;
        }
        
+       //default flattener for simple fields that encode for a single 
preference
+       private static function flattenSimpleField( $fieldDescription ) {
+               return array( $fieldDescription['name'] => $fieldDescription );
+       }
+       
        //Further checks for 'number' options
-       private static function checkNumberOptionDefinition( $option ) {
+       private static function validateNumberOptionDefinition( $option ) {
                if ( isset( $option['integer'] ) && $option['integer'] === true 
) {
                        //Check if 'min', 'max' and 'default' are integers (if 
given)
                        if ( intval( $option['default'] ) != $option['default'] 
) {
@@ -192,7 +252,7 @@
                return true;
        }
 
-       private static function checkSelectOptionDefinition( $option ) {
+       private static function validateSelectOptionDefinition( $option ) {
                $options = $option['options'];
                
                foreach ( $options as $opt => $optVal ) {
@@ -212,7 +272,7 @@
                return true;
        }
        
-       private static function checkRangeOptionDefinition( $option ) {
+       private static function validateRangeOptionDefinition( $option ) {
                $step = isset( $option['step'] ) ? $option['step'] : 1;
                
                if ( $step <= 0 ) {
@@ -233,42 +293,71 @@
                
                return true;
        }
-       
-       //Checks if the given description of the preferences is valid
-       public static function isPrefsDescriptionValid( $prefsDescription ) {
-               if ( !is_array( $prefsDescription )
-                       || !isset( $prefsDescription['fields'] )
-                       || !is_array( $prefsDescription['fields'] ) )
+
+       //Flattens a simple field, by calling its field-specific flattener if 
there is any,
+       //or the default flattener otherwise.
+       private static function flattenFieldDescription( $fieldDescription ) {
+               $typeSpec = 
self::$prefsDescriptionSpecifications[$fieldDescription['type']];
+               $typeDescription = $typeSpec['description'];
+               if ( isset( $typeSpec['flattener'] ) ) {
+                       $flattener = $typeSpec['flattener'];
+               } else {
+                       $flattener = 'GadgetPrefs::flattenSimpleField';
+               }
+               return call_user_func( $flattener, $fieldDescription );
+       }
+
+       //Returns a map keyed at preference names, and with their corresponding
+       //"simple" field descriptions as values.
+       //It is assumed that $prefsDescription is valid.
+       private static function flattenPrefsDescription( $prefsDescription ) {
+               $flattenedPrefsDescription = array();
+               foreach ( $prefsDescription['fields'] as $fieldDescription ) {
+                       $flt = self::flattenFieldDescription( $fieldDescription 
);
+                       $flattenedPrefsDescription = array_merge( 
$flattenedPrefsDescription, $flt );
+               }
+               return $flattenedPrefsDescription;
+       }
+
+       //Validate the description of a 'section' of preferences 
+       private static function validateSectionDefinition( $sectionDescription 
) {
+               static $mandatoryCount = array(), $initialized = false;
+
+               if ( !is_array( $sectionDescription )
+                       || !isset( $sectionDescription['fields'] )
+                       || !is_array( $sectionDescription['fields'] ) )
                {
                        return false;
                }
                
+               if ( !$initialized ) {
+                       //Count of mandatory members for each type
+                       foreach ( self::$prefsDescriptionSpecifications as 
$type => $typeSpec ) {
+                               $mandatoryCount[$type] = 0;
+                               foreach ( $typeSpec['description'] as 
$fieldName => $fieldSpec ) {
+                                       if ( $fieldSpec['isMandatory'] === true 
) {
+                                               ++$mandatoryCount[$type];
+                                       }
+                               }
+                       }
+                       $initialized = true;
+               }
+               
                //Check if 'fields' is a regular (not-associative) array, and 
that it is not empty
-               $count = count( $prefsDescription['fields'] );
-               if ( $count == 0 || array_keys( $prefsDescription['fields'] ) 
!== range( 0, $count - 1 ) ) {
+               $count = count( $sectionDescription['fields'] );
+               if ( $count == 0 || array_keys( $sectionDescription['fields'] ) 
!== range( 0, $count - 1 ) ) {
                        return false;
                }
                
-               //Count of mandatory members for each type
-               $mandatoryCount = array();
-               foreach ( self::$prefsDescriptionSpecifications as $type => 
$typeSpec ) {
-                       $mandatoryCount[$type] = 0;
-                       foreach ( $typeSpec['description'] as $fieldName => 
$fieldSpec ) {
-                               if ( $fieldSpec['isMandatory'] === true ) {
-                                       ++$mandatoryCount[$type];
-                               }
-                       }
-               }
-               
                //TODO: validation of members other than $prefs['fields']
 
-               //Map of encountered names
-               $names = array();
+               //Flattened preferences
+               $flattenedPrefs = array();
                
-               foreach ( $prefsDescription['fields'] as $optionDefinition ) {
+               foreach ( $sectionDescription['fields'] as $optionDefinition ) {
                        
-                       //Check if 'name' and 'type' are set
-                       if ( !isset( $optionDefinition['type'] ) || !isset( 
$optionDefinition['name'] ) )  {
+                       //Check if 'type' is set
+                       if ( !isset( $optionDefinition['type'] ) )  {
                                return false;
                        }
                        
@@ -279,30 +368,14 @@
                                return false;
                        }
                        
-                       $option = $optionDefinition['name'];
-
-                       //check that it's different from previous names
-                       if ( isset( $names[$option] ) ) {
-                               return false;
-                       }
-                       
-                       $names[$option] = true;
-
-                       //check option name compliance
-                       if ( strlen( $option ) > 40
-                               || !preg_match( '/^[a-zA-Z_][a-zA-Z0-9_]*$/', 
$option ) )
-                       {
-                               return false;
-                       }
-                       
                        //Check if all fields satisfy specification
                        $typeSpec = 
self::$prefsDescriptionSpecifications[$type];
                        $typeDescription = $typeSpec['description'];
                        $count = 0; //count of present mandatory members
                        foreach ( $optionDefinition as $fieldName => 
$fieldValue ) {
                                
-                               if ( $fieldName == 'type' || $fieldName == 
'name' ) {
-                                       continue; //'type' and 'name' must not 
be checked
+                               if ( $fieldName == 'type' ) {
+                                       continue; //'type' must not be checked
                                }
                                
                                if ( !isset( $typeDescription[$fieldName] ) ) {
@@ -313,9 +386,9 @@
                                        ++$count;
                                }
                                
-                               if ( isset( 
$typeDescription[$fieldName]['checker'] ) ) {
-                                       $checker = 
$typeDescription[$fieldName]['checker'];
-                                       if ( !call_user_func( $checker, 
$fieldValue ) ) {
+                               if ( isset( 
$typeDescription[$fieldName]['validator'] ) ) {
+                                       $validator = 
$typeDescription[$fieldName]['validator'];
+                                       if ( !call_user_func( $validator, 
$fieldValue ) ) {
                                                return false;
                                        }
                                }
@@ -325,30 +398,48 @@
                                return false; //not all mandatory members are 
given
                        }
                        
-                       if ( isset( $typeSpec['checker'] ) ) {
+                       if ( isset( $typeSpec['validator'] ) ) {
                                //Call type-specific checker for finer 
validation
-                               if ( !call_user_func( $typeSpec['checker'], 
$optionDefinition ) ) {
+                               if ( !call_user_func( $typeSpec['validator'], 
$optionDefinition ) ) {
                                        return false;
                                }
                        }
+
+                       //flatten preferences described by this field
+                       $flt = self::flattenFieldDescription( $optionDefinition 
);
                        
-                       //Finally, check that the 'default' fields exists and 
is valid
-                       if ( !array_key_exists( 'default', $optionDefinition ) 
) {
-                               return false;
+                       foreach ( $flt as $prefName => $prefDescription ) {
+                               //Finally, check that the 'default' fields 
exists and is valid
+                               //for all preferences encoded by this field
+                               if ( !array_key_exists( 'default', 
$prefDescription ) ) {
+                                       return false;
+                               }
+                               
+                               $prefs = array( 'dummy' => 
$optionDefinition['default'] );
+                               if ( !self::checkSinglePref( $optionDefinition, 
$prefs, 'dummy' ) ) {
+                                       return false;
+                               }
                        }
                        
-                       $prefs = array( 'dummy' => $optionDefinition['default'] 
);
-                       if ( !self::checkSinglePref( $optionDefinition, $prefs, 
'dummy' ) ) {
+                       //If there are preferences with the same name of a 
previously encountered preference, fail
+                       if ( array_intersect( array_keys( $flt ), array_keys( 
$flattenedPrefs ) ) ) {
                                return false;
                        }
+                       $flattenedPrefs = array_merge( $flattenedPrefs, $flt );
                }
                
                return true;
        }
        
-       //Check if a preference is valid, according to description
+       //Checks if the given description of the preferences is valid
+       public static function isPrefsDescriptionValid( $prefsDescription ) {
+               return self::validateSectionDefinition( $prefsDescription );
+       }
+       
+       //Check if a preference is valid, according to description.
+       //$prefDescription must be the description of a "simple" field (that 
is, with 'checker')
        //NOTE: we pass both $prefs and $prefName (instead of just 
$prefs[$prefName])
-       //      to allow checking for null.
+       //      to allow checking for undefined values.
        private static function checkSinglePref( $prefDescription, $prefs, 
$prefName ) {
 
                //isset( $prefs[$prefName] ) would return false for null values
@@ -356,129 +447,158 @@
                        return false;
                }
        
-               $pref = $prefs[$prefName];
-       
-               switch ( $prefDescription['type'] ) {
-                       case 'boolean':
-                               return is_bool( $pref );
-                       case 'string':
-                               if ( !is_string( $pref ) ) {
-                                       return false;
-                               }
-                               
-                               $len = strlen( $pref );
-                               
-                               //Checks the "required" option, if present
-                               $required = isset( $prefDescription['required'] 
) ? $prefDescription['required'] : true;
-                               if ( $required === true && $len == 0 ) {
-                                       return false;
-                               } elseif ( $required === false && $len == 0 ) {
-                                       return true; //overriding 'minlength'
-                               }
-                               
-                               //Checks the "minlength" option, if present
-                               $minlength = isset( 
$prefDescription['minlength'] ) ? $prefDescription['minlength'] : 0;
-                               if ( $len < $minlength ){
-                                       return false;
-                               }
+               $value = $prefs[$prefName];
+               $type = $prefDescription['type'];
+               
+               if ( !isset( self::$prefsDescriptionSpecifications[$type] )
+                       || !isset( 
self::$prefsDescriptionSpecifications[$type]['checker'] ) )
+               {
+                       return false;
+               }
+               
+               $checker = 
self::$prefsDescriptionSpecifications[$type]['checker'];
+               return call_user_func( $checker, $prefDescription, $value );
+       }
 
-                               //Checks the "maxlength" option, if present
-                               $maxlength = isset( 
$prefDescription['maxlength'] ) ? $prefDescription['maxlength'] : 1024; //TODO: 
what big integer here?
-                               if ( $len > $maxlength ){
-                                       return false;
-                               }
-                               
-                               return true;
-                       case 'number':
-                               if ( !is_float( $pref ) && !is_int( $pref ) && 
$pref !== null ) {
-                                       return false;
-                               }
+       //Checker for 'boolean' preferences
+       private static function checkBooleanPref( $prefDescription, $value ) {
+               return is_bool( $value );
+       }
 
-                               $required = isset( $prefDescription['required'] 
) ? $prefDescription['required'] : true;
-                               if ( $required === false && $pref === null ) {
-                                       return true;
-                               }
-                               
-                               if ( $pref === null ) {
-                                       return false; //$required === true, so 
null is not acceptable
-                               }
+       //Checker for 'string' preferences
+       private static function checkStringPref( $prefDescription, $value ) {
+               if ( !is_string( $value ) ) {
+                       return false;
+               }
+               
+               $len = strlen( $value );
+               
+               //Checks the "required" option, if present
+               $required = isset( $prefDescription['required'] ) ? 
$prefDescription['required'] : true;
+               if ( $required === true && $len == 0 ) {
+                       return false;
+               } elseif ( $required === false && $len == 0 ) {
+                       return true; //overriding 'minlength'
+               }
+               
+               //Checks the "minlength" option, if present
+               $minlength = isset( $prefDescription['minlength'] ) ? 
$prefDescription['minlength'] : 0;
+               if ( $len < $minlength ){
+                       return false;
+               }
 
-                               $integer = isset( $prefDescription['integer'] ) 
? $prefDescription['integer'] : false;
-                               
-                               if ( $integer === true && intval( $pref ) != 
$pref ) {
-                                       return false; //not integer
-                               }
-                               
-                               if ( isset( $prefDescription['min'] ) ) {
-                                       $min = $prefDescription['min'];
-                                       if ( $pref < $min ) {
-                                               return false; //value below 
minimum
-                                       }
-                               }
+               //Checks the "maxlength" option, if present
+               $maxlength = isset( $prefDescription['maxlength'] ) ? 
$prefDescription['maxlength'] : 1024; //TODO: what big integer here?
+               if ( $len > $maxlength ){
+                       return false;
+               }
+               
+               return true;
+       }
 
-                               if ( isset( $prefDescription['max'] ) ) {
-                                       $max = $prefDescription['max'];
-                                       if ( $pref > $max ) {
-                                               return false; //value above 
maximum
-                                       }
-                               }
+       //Checker for 'number' preferences
+       private static function checkNumberPref( $prefDescription, $value ) {
+               if ( !is_float( $value ) && !is_int( $value ) && $value !== 
null ) {
+                       return false;
+               }
 
-                               return true;
-                       case 'select':
-                               $values = array_values( 
$prefDescription['options'] );
-                               return in_array( $pref, $values, true );
-                       case 'range':
-                               if ( !is_float( $pref ) && !is_int( $pref ) ) {
-                                       return false;
-                               }
-                               
-                               $min = $prefDescription['min'];
-                               $max = $prefDescription['max'];
-                               
-                               if ( $pref < $min || $pref > $max ) {
-                                       return false;
-                               }
-                               
-                               $step = isset( $prefDescription['step'] ) ? 
$prefDescription['step'] : 1;
-                               
-                               if ( $step <= 0 ) {
-                                       return false;
-                               }
-                               
-                               //Valid values are min, min + step, min + 
2*step, ...
-                               //Then ( $pref - $min ) / $step must be close 
enough to an integer
-                               $eps = 1.0e-6; //tolerance
-                               $tmp = ( $pref - $min ) / $step;
-                               if ( abs( $tmp - floor( $tmp ) ) > $eps ) {
-                                       return false;
-                               }
-                               
-                               return true;
-                       case 'date':
-                               if ( $pref === null ) {
-                                       return true;
-                               }
-                               
-                               //Basic syntactic checks
-                               if ( !is_string( $pref ) ||
-                                       !preg_match( 
'/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/', $pref ) )
-                               {
-                                       return false;
-                               }
-                               
-                               //Full parsing
-                               return date_create( $pref ) !== false;
-                       case 'color':
-                               //Check if it's a string representing a color
-                               //(with 6 hexadecimal lowercase characters).
-                               return is_string( $pref ) && preg_match( 
'/^#[0-9a-f]{6}$/', $pref );
-                       default:
-                               return false; //unexisting type
+               $required = isset( $prefDescription['required'] ) ? 
$prefDescription['required'] : true;
+               if ( $required === false && $value === null ) {
+                       return true;
                }
+               
+               if ( $value === null ) {
+                       return false; //$required === true, so null is not 
acceptable
+               }
+
+               $integer = isset( $prefDescription['integer'] ) ? 
$prefDescription['integer'] : false;
+               
+               if ( $integer === true && intval( $value ) != $value ) {
+                       return false; //not integer
+               }
+               
+               if ( isset( $prefDescription['min'] ) ) {
+                       $min = $prefDescription['min'];
+                       if ( $value < $min ) {
+                               return false; //value below minimum
+                       }
+               }
+
+               if ( isset( $prefDescription['max'] ) ) {
+                       $max = $prefDescription['max'];
+                       if ( $value > $max ) {
+                               return false; //value above maximum
+                       }
+               }
+
+               return true;
        }
 
+       //Checker for 'select' preferences
+       private static function checkSelectPref( $prefDescription, $value ) {
+               $values = array_values( $prefDescription['options'] );
+               return in_array( $value, $values, true );
+       }
+
+       //Checker for 'range' preferences
+       private static function checkRangePref( $prefDescription, $value ) {
+               if ( !is_float( $value ) && !is_int( $value ) ) {
+                       return false;
+               }
+               
+               $min = $prefDescription['min'];
+               $max = $prefDescription['max'];
+               
+               if ( $value < $min || $value > $max ) {
+                       return false;
+               }
+               
+               $step = isset( $prefDescription['step'] ) ? 
$prefDescription['step'] : 1;
+               
+               if ( $step <= 0 ) {
+                       return false;
+               }
+               
+               //Valid values are min, min + step, min + 2*step, ...
+               //Then ( $value - $min ) / $step must be close enough to an 
integer
+               $eps = 1.0e-6; //tolerance
+               $tmp = ( $value - $min ) / $step;
+               if ( abs( $tmp - floor( $tmp ) ) > $eps ) {
+                       return false;
+               }
+               
+               return true;
+       }
+
+       //Checker for 'date' preferences
+       private static function checkDatePref( $prefDescription, $value ) {
+               if ( $value === null ) {
+                       return true;
+               }
+               
+               //Basic syntactic checks
+               if ( !is_string( $value ) ||
+                       !preg_match( 
'/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/', $value ) )
+               {
+                       return false;
+               }
+               
+               //Full parsing
+               return date_create( $value ) !== false;
+       }
+
+       //Checker for 'color' preferences
+       private static function checkColorPref( $prefDescription, $value ) {
+               //Check if it's a string representing a color
+               //(with 6 hexadecimal lowercase characters).
+               return is_string( $value ) && preg_match( '/^#[0-9a-f]{6}$/', 
$value );
+       }
+
+
+
        /**
-        * Checks if $prefs is an array of preferences that passes validation
+        * Checks if $prefs is an array of preferences that passes validation.
+        * It is assumed that $prefsDescription is a valid description of 
preferences.
         * 
         * @param $prefsDescription Array: the preferences description to use.
         * @param $prefs Array: reference of the array of preferences to check.
@@ -486,9 +606,10 @@
         * @return boolean true if $prefs passes validation against 
$prefsDescription, false otherwise.
         */
        public static function checkPrefsAgainstDescription( $prefsDescription, 
$prefs ) {
+               $flattenedPrefs = self::flattenPrefsDescription( 
$prefsDescription );
                $validPrefs = array();
                //Check that all the given preferences pass validation
-               foreach ( $prefsDescription['fields'] as $prefDescription ) {
+               foreach ( $flattenedPrefs as $prefDescription ) {
                        $prefName = $prefDescription['name'];
                        if ( !self::checkSinglePref( $prefDescription, $prefs, 
$prefName ) ) {
                                return false;
@@ -509,15 +630,17 @@
        /**
         * Fixes $prefs so that it matches the description given by 
$prefsDescription.
         * All values of $prefs that fail validation are replaced with default 
values.
+        * It is assumed that $prefsDescription is a valid description of 
preferences.
         * 
         * @param $prefsDescription Array: the preferences description to use.
         * @param &$prefs Array: reference of the array of preferences to match.
         */
        public static function matchPrefsWithDescription( $prefsDescription, 
&$prefs ) {
+               $flattenedPrefs = self::flattenPrefsDescription( 
$prefsDescription );
                $validPrefs = array();
                
                //Fix preferences that fail validation, by replacing their 
value with default
-               foreach ( $prefsDescription['fields'] as $prefDescription ) {
+               foreach ( $flattenedPrefs as $prefDescription ) {
                        $prefName = $prefDescription['name'];
                        if ( !self::checkSinglePref( $prefDescription, $prefs, 
$prefName ) ) {
                                $prefs[$prefName] = $prefDescription['default'];
@@ -568,28 +691,36 @@
         * @return Array: the messages needed by $prefsDescription.
         */
        public static function getMessages( $prefsDescription ) {
-               $maybeMsgs = array();
+               $msgs = array();
                
-               if ( isset( $prefsDescription['intro'] ) ) {
-                       $maybeMsgs[] = $prefsDescription['intro'];
+               if ( isset( $prefsDescription['intro'] ) && self::isMessage( 
$prefsDescription['intro'] ) ) {
+                       $msgs[] = substr( $prefsDescription['intro'], 1 );
                }
                
-               foreach ( $prefsDescription['fields'] as $prefName => $prefDesc 
) {
-                       $maybeMsgs[] = $prefDesc['label'];
-                       
-                       if ( $prefDesc['type'] == 'select' ) {
-                               foreach ( $prefDesc['options'] as $optName => 
$value ) {
-                                       $maybeMsgs[] = $optName;
+               foreach ( $prefsDescription['fields'] as $prefDesc ) {
+                       $type = $prefDesc['type'];
+                       $prefSpec = 
self::$prefsDescriptionSpecifications[$type];
+                       if ( isset( $prefSpec['getMessages'] ) ) {
+                               $getMessages = $prefSpec['getMessages'];
+                               $msgs = array_merge( $msgs, call_user_func( 
$getMessages, $prefDesc ) );
+                       } else {
+                               if ( isset( $prefDesc['label'] ) && 
self::isMessage( $prefDesc['label'] ) ) {
+                                       $msgs[] = substr( $prefDesc['label'], 1 
);
                                }
                        }
                }
                
+               return array_unique( $msgs );
+       }
+       
+       //Returns the messages for a 'select' field description
+       private static function getSelectMessages( $prefDescription ) {
                $msgs = array();
-               foreach ( $maybeMsgs as $msg ) {
-                       if ( self::isMessage( $msg ) ) {
-                               $msgs[] = substr( $msg, 1 );
+               foreach ( $prefDescription['options'] as $optName => $value ) {
+                       if ( self::isMessage( $optName ) ) {
+                               $msgs[] = substr( $optName, 1 );
                        }
                }
-               return array_unique( $msgs );
+               return $msgs;
        }
 }

Modified: 
branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.js
===================================================================
--- branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.js    
2011-07-20 12:59:11 UTC (rev 92632)
+++ branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.js    
2011-07-20 14:47:04 UTC (rev 92633)
@@ -57,17 +57,21 @@
                                                dataType: "json", // response 
type
                                                success: function( response ) {
                                                        
-                                                       if ( typeof 
response.getgadgetprefs != 'object' ) {
+                                                       if ( typeof 
response.description != 'object'
+                                                               || typeof 
response.values != 'object')
+                                                       {
                                                                alert( mw.msg( 
'gadgets-unexpected-error' ) )
                                                                return;
                                                        }
                                                        
                                                        //Create and show dialog
                                                        
-                                                       var prefs = 
response.getgadgetprefs;
+                                                       var prefsDescription = 
response.description;
+                                                       var values = 
response.values;
                                                        
-                                                       var dialogBody = $( 
prefs ).formBuilder( {
-                                                               gadget: gadget
+                                                       var dialogBody = $( 
prefsDescription ).formBuilder( {
+                                                               gadget: gadget,
+                                                               values: values
                                                        } );
                                                        
                                                        $( dialogBody ).submit( 
function() {

Modified: branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js
===================================================================
--- branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js 
2011-07-20 12:59:11 UTC (rev 92632)
+++ branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js 
2011-07-20 14:47:04 UTC (rev 92633)
@@ -87,9 +87,9 @@
        }
 
        //A field with no content
-       function EmptyField( $form, name, desc ) {
+       function EmptyField( $form, desc, values ) {
                //Check existence of compulsory fields
-               if ( typeof name == 'undefined' || !desc.type || !desc.label ) {
+               if ( !desc.type || !desc.label ) {
                        $.error( "Missing arguments" );
                }
 
@@ -97,22 +97,16 @@
 
                this.$p = $( '<p/>' );
 
-               this.name = name;
                this.desc = desc;
        }
 
-       EmptyField.prototype.getName = function() {
-               return this.name;
-       };
-
        EmptyField.prototype.getDesc = function() {
                return this.desc;
        };
 
-
        //Override expected
-       EmptyField.prototype.getValue = function() {
-               return null;
+       EmptyField.prototype.getValues = function() {
+               return {};
        };
 
        EmptyField.prototype.getElement = function() {
@@ -129,12 +123,12 @@
        //A field with just a label
        LabelField.prototype = object( EmptyField.prototype );
        LabelField.prototype.constructor = LabelField;
-       function LabelField( $form, name, desc ) {
-               EmptyField.call( this, $form, name, desc );
+       function LabelField( $form, desc, values ) {
+               EmptyField.call( this, $form, desc, values );
 
                var $label = $( '<label/>' )
                        .text( preproc( this.$form, this.desc.label ) )
-                       .attr('for', idPrefix + this.name );
+                       .attr('for', idPrefix + this.desc.name );
 
                this.$p.append( $label );
        }
@@ -142,53 +136,59 @@
        //A field with a label and a checkbox
        BooleanField.prototype = object( LabelField.prototype );
        BooleanField.prototype.constructor = BooleanField;
-       function BooleanField( $form, name, desc ){ 
-               LabelField.call( this, $form, name, desc );
+       function BooleanField( $form, desc, values ){ 
+               LabelField.call( this, $form, desc, values );
 
-               if ( typeof desc.value != 'boolean' ) {
-                       $.error( "desc.value is invalid" );
+               var value = values[this.desc.name];
+               if ( typeof value != 'boolean' ) {
+                       $.error( "value is invalid" );
                }
                
                this.$c = $( '<input/>' )
                        .attr( 'type', 'checkbox' )
-                       .attr( 'id', idPrefix + this.name )
-                       .attr( 'name', idPrefix + this.name )
-                       .attr( 'checked', this.desc.value );
+                       .attr( 'id', idPrefix + this.desc.name )
+                       .attr( 'name', idPrefix + this.desc.name )
+                       .attr( 'checked', value );
 
                this.$p.append( this.$c );
        }
        
-       BooleanField.prototype.getValue = function() {
-               return this.$c.is( ':checked' );
+       BooleanField.prototype.getValues = function() {
+               var res = {};
+               res[this.desc.name] = this.$c.is( ':checked' );
+               return res;
        };
 
        //A field with a textbox accepting string values
 
        StringField.prototype = object( LabelField.prototype );
        StringField.prototype.constructor = StringField;
-       function StringField( $form, name, desc ){ 
-               LabelField.call( this, $form, name, desc );
+       function StringField( $form, desc, values ){ 
+               LabelField.call( this, $form, desc, values );
 
-               if ( typeof desc.value != 'string' ) {
-                       $.error( "desc.value is invalid" );
+               var value = values[this.desc.name];
+               if ( typeof value != 'string' ) {
+                       $.error( "value is invalid" );
                }
 
                this.$text = $( '<input/>' )
                        .attr( 'type', 'text' )
-                       .attr( 'id', idPrefix + this.name )
-                       .attr( 'name', idPrefix + this.name )
-                       .val( desc.value );
+                       .attr( 'id', idPrefix + this.desc.name )
+                       .attr( 'name', idPrefix + this.desc.name )
+                       .val( value );
 
                this.$p.append( this.$text );
        }
        
-       StringField.prototype.getValue = function() {
-               return this.$text.val();
+       StringField.prototype.getValues = function() {
+               var res = {};
+               res[this.desc.name] = this.$text.val();
+               return res;
        };
 
        StringField.prototype.getValidationSettings = function() {
                var     settings = 
LabelField.prototype.getValidationSettings.call( this ),
-                       fieldId = idPrefix + this.name;
+                       fieldId = idPrefix + this.desc.name;
                
                settings.rules[fieldId] = {};
                var     fieldRules = settings.rules[fieldId],
@@ -218,30 +218,33 @@
        //A field with a textbox accepting numeric values
        NumberField.prototype = object( LabelField.prototype );
        NumberField.prototype.constructor = NumberField;
-       function NumberField( $form, name, desc ){ 
-               LabelField.call( this, $form, name, desc );
+       function NumberField( $form, desc, values ){ 
+               LabelField.call( this, $form, desc, values );
 
-               if ( desc.value !== null && typeof desc.value != 'number' ) {
-                       $.error( "desc.value is invalid" );
+               var value = values[this.desc.name];
+               if ( value !== null && typeof value != 'number' ) {
+                       $.error( "value is invalid" );
                }
 
                this.$text = $( '<input/>' )
                        .attr( 'type', 'text' )
-                       .attr( 'id', idPrefix + this.name )
-                       .attr( 'name', idPrefix + this.name )
-                       .val( desc.value );
+                       .attr( 'id', idPrefix + this.desc.name )
+                       .attr( 'name', idPrefix + this.desc.name )
+                       .val( value );
 
                this.$p.append( this.$text );
        }
        
-       NumberField.prototype.getValue = function() {
-               var val = parseFloat( this.$text.val() );
-               return isNaN( val ) ? null : val;
+       NumberField.prototype.getValues = function() {
+               var val = parseFloat( this.$text.val() ),
+                       res = {};
+               res[this.desc.name] = isNaN( val ) ? null : val;
+               return res;
        };
 
        NumberField.prototype.getValidationSettings = function() {
                var     settings = 
LabelField.prototype.getValidationSettings.call( this ),
-                       fieldId = idPrefix + this.name;
+                       fieldId = idPrefix + this.desc.name;
                
                settings.rules[fieldId] = {};
                var     fieldRules = settings.rules[fieldId],
@@ -277,50 +280,54 @@
        //A field with a drop-down list
        SelectField.prototype = object( LabelField.prototype );
        SelectField.prototype.constructor = SelectField;
-       function SelectField( $form, name, desc ){ 
-               LabelField.call( this, $form, name, desc );
+       function SelectField( $form, desc, values ){ 
+               LabelField.call( this, $form, desc, values );
 
                var $select = this.$select = $( '<select/>' )
-                       .attr( 'id', idPrefix + this.name )
-                       .attr( 'name', idPrefix + this.name );
+                       .attr( 'id', idPrefix + this.desc.name )
+                       .attr( 'name', idPrefix + this.desc.name );
                
-               var values = [];
+               var validValues = [];
                var self = this;
                $.each( desc.options, function( optName, optVal ) {
-                       var i = values.length;
+                       var i = validValues.length;
                        $( '<option/>' )
                                .text( preproc( self.$form, optName ) )
                                .val( i )
                                .appendTo( $select );
-                       values.push( optVal );
+                       validValues.push( optVal );
                } );
 
-               this.values = values;
+               this.validValues = validValues;
 
-               if ( $.inArray( desc.value, values ) == -1 ) {
-                       $.error( "desc.value is not in the list of possible 
values" );
+               var value = values[this.desc.name];
+               if ( $.inArray( value, validValues ) == -1 ) {
+                       $.error( "value is not in the list of possible values" 
);
                }
 
-               var i = $.inArray( desc.value, values );
+               var i = $.inArray( value, validValues );
                $select.val( i ).attr( 'selected', 'selected' );
 
                this.$p.append( $select );
        }
        
-       SelectField.prototype.getValue = function() {
-               var i = parseInt( this.$select.val(), 10 );
-               return this.values[i];
+       SelectField.prototype.getValues = function() {
+               var i = parseInt( this.$select.val(), 10 ),
+                       res = {};
+               res[this.desc.name] = this.validValues[i];
+               return res;
        };
 
 
        //A field with a slider, representing ranges of numbers
        RangeField.prototype = object( LabelField.prototype );
        RangeField.prototype.constructor = RangeField;
-       function RangeField( $form, name, desc ){ 
-               LabelField.call( this, $form, name, desc );
+       function RangeField( $form, desc, values ){ 
+               LabelField.call( this, $form, desc, values );
 
-               if ( typeof desc.value != 'number' ) {
-                       $.error( "desc.value is invalid" );
+               var value = values[this.desc.name];
+               if ( typeof value != 'number' ) {
+                       $.error( "value is invalid" );
                }
 
                if ( typeof desc.min != 'number' ) {
@@ -335,17 +342,17 @@
                        $.error( "desc.step is invalid" );
                }
 
-               if ( desc.value < desc.min || desc.value > desc.max ) {
-                       $.error( "desc.value is out of range" );
+               if ( value < desc.min || value > desc.max ) {
+                       $.error( "value is out of range" );
                }
 
                var $slider = this.$slider = $( '<div/>' )
-                       .attr( 'id', idPrefix + this.name );
+                       .attr( 'id', idPrefix + this.desc.name );
 
                var options = {
                        min: desc.min,
                        max: desc.max,
-                       value: desc.value
+                       value: value
                };
 
                if ( typeof desc.step != 'undefined' ) {
@@ -357,34 +364,37 @@
                this.$p.append( $slider );
        }
        
-       RangeField.prototype.getValue = function() {
-               return this.$slider.slider( 'value' );
+       RangeField.prototype.getValues = function() {
+               var res = {};
+               res[this.desc.name] = this.$slider.slider( 'value' );
+               return res;
        };
        
        
        //A field with a textbox with a datepicker
        DateField.prototype = object( LabelField.prototype );
        DateField.prototype.constructor = DateField;
-       function DateField( $form, name, desc ){ 
-               LabelField.call( this, $form, name, desc );
+       function DateField( $form, desc, values ){ 
+               LabelField.call( this, $form, desc, values );
 
-               if ( typeof desc.value == 'undefined' ) {
-                       $.error( "desc.value is invalid" );
+               var value = values[this.desc.name];
+               if ( typeof value == 'undefined' ) {
+                       $.error( "value is invalid" );
                }
 
                var date;
-               if ( desc.value !== null ) {
-                       date = new Date( desc.value );
+               if ( value !== null ) {
+                       date = new Date( value );
                        
                        if ( !isFinite( date ) ) {
-                               $.error( "desc.value is invalid" );
+                               $.error( "value is invalid" );
                        }
                }
 
                this.$text = $( '<input/>' )
                        .attr( 'type', 'text' )
-                       .attr( 'id', idPrefix + this.name )
-                       .attr( 'name', idPrefix + this.name )
+                       .attr( 'id', idPrefix + this.desc.name )
+                       .attr( 'name', idPrefix + this.desc.name )
                        .datepicker( {
                                onSelect: function() {
                                        //Force validation, so that a previous 
'invalid' state is removed
@@ -392,7 +402,7 @@
                                }
                        } );
 
-               if ( desc.value !== null ) {
+               if ( value !== null ) {
                        this.$text.datepicker( 'setDate', date );
                }
 
@@ -400,25 +410,28 @@
                this.$p.append( this.$text );
        }
        
-       DateField.prototype.getValue = function() {
-               var d = this.$text.datepicker( 'getDate' );
+       DateField.prototype.getValues = function() {
+               var d = this.$text.datepicker( 'getDate' ),
+                       res = {};
                
                if ( d === null ) {
                        return null;
                }
 
                //UTC date in ISO 8601 format [YYYY]-[MM]-[DD]T[hh]:[mm]:[ss]Z
-               return '' + pad( d.getUTCFullYear(), 4 ) + '-' +
+               res[this.desc.name] = '' +
+                       pad( d.getUTCFullYear(), 4 ) + '-' +
                        pad( d.getUTCMonth() + 1, 2 ) + '-' +
                        pad( d.getUTCDate(), 2 ) + 'T' +
                        pad( d.getUTCHours(), 2 ) + ':' +
                        pad( d.getUTCMinutes(), 2 ) + ':' +
                        pad( d.getUTCSeconds(), 2 ) + 'Z';
+               return res;             
        };
 
        DateField.prototype.getValidationSettings = function() {
                var     settings = 
LabelField.prototype.getValidationSettings.call( this ),
-                       fieldId = idPrefix + this.name;
+                       fieldId = idPrefix + this.desc.name;
                
                settings.rules[fieldId] = {
                                "datePicker": true
@@ -436,20 +449,21 @@
        
        ColorField.prototype = object( LabelField.prototype );
        ColorField.prototype.constructor = ColorField;
-       function ColorField( $form, name, desc ){ 
-               LabelField.call( this, $form, name, desc );
+       function ColorField( $form, desc, values ){ 
+               LabelField.call( this, $form, desc, values );
 
-               if ( typeof desc.value == 'undefined' ) {
-                       $.error( "desc.value is invalid" );
+               var value = values[this.desc.name];
+               if ( typeof value == 'undefined' ) {
+                       $.error( "value is invalid" );
                }
 
                this.$text = $( '<input/>' )
                        .attr( 'type', 'text' )
-                       .attr( 'id', idPrefix + this.name )
-                       .attr( 'name', idPrefix + this.name )
+                       .attr( 'id', idPrefix + this.desc.name )
+                       .attr( 'name', idPrefix + this.desc.name )
                        .addClass( 'colorpicker-input' )
-                       .val( desc.value )
-                       .css( 'background-color', desc.value )
+                       .val( value )
+                       .css( 'background-color', value )
                        .focus( function() {
                                $( '<div/>' )
                                        .attr( 'id', 'colorpicker' )
@@ -484,7 +498,7 @@
        
        ColorField.prototype.getValidationSettings = function() {
                var     settings = 
LabelField.prototype.getValidationSettings.call( this ),
-                       fieldId = idPrefix + this.name;
+                       fieldId = idPrefix + this.desc.name;
                
                settings.rules[fieldId] = {
                                "color": true
@@ -492,10 +506,12 @@
                return settings;
        }
        
-       ColorField.prototype.getValue = function() {
-               var color = $.colorUtil.getRGB( this.$text.val() );
-               return '#' + pad( color[0].toString( 16 ), 2 ) +
+       ColorField.prototype.getValues = function() {
+               var color = $.colorUtil.getRGB( this.$text.val() ),
+                       res = {}
+               res[this.desc.name] = '#' + pad( color[0].toString( 16 ), 2 ) +
                        pad( color[1].toString( 16 ), 2 ) + pad( 
color[2].toString( 16 ), 2 );
+               return res;
        };
 
        //If a click happens outside the colorpicker while it is showed, remove 
it
@@ -559,7 +575,6 @@
                for ( var i = 0; i < description.fields.length; i++ ) {
                        //TODO: validate fieldName
                        var field = description.fields[i],
-                               fieldName = field.name,
                                FieldConstructor = validFieldTypes[field.type];
 
                        if ( typeof FieldConstructor != 'function' ) {
@@ -569,7 +584,7 @@
 
                        var f;
                        try {
-                               f = new FieldConstructor( $form, fieldName, 
field );
+                               f = new FieldConstructor( $form, field, 
options.values );
                        } catch ( e ) {
                                mw.log( e );
                                return null; //constructor failed, wrong syntax 
in field description
@@ -610,7 +625,7 @@
                        
                        for ( var i = 0; i < data.fields.length; i++ ) {
                                var f = data.fields[i];
-                               result[f.getName()] = f.getValue();
+                               $.extend( result, f.getValues() );
                        }
                         
                        return result;


_______________________________________________
MediaWiki-CVS mailing list
MediaWiki-CVS@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to