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

Revision: 94994
Author:   salvatoreingala
Date:     2011-08-19 10:54:30 +0000 (Fri, 19 Aug 2011)
Log Message:
-----------
JSHint, coding conventions, comments and minor refactoring

Modified Paths:
--------------
    branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.js
    branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js

Modified: 
branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.js
===================================================================
--- branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.js    
2011-08-19 10:21:56 UTC (rev 94993)
+++ branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.js    
2011-08-19 10:54:30 UTC (rev 94994)
@@ -40,8 +40,8 @@
        function removeStylesheet( styleSheet ) {
                var owner =
                        styleSheet.ownerNode ?
-                       styleSheet.ownerNode :   //not-IE or IE >= 9
-                       styleSheet.owningElement //IE < 9
+                       styleSheet.ownerNode :    //not-IE or IE >= 9
+                       styleSheet.owningElement; //IE < 9
                owner.parentNode.removeChild( owner );
        } 
 
@@ -69,7 +69,7 @@
                //just to avoid code duplication
                function error() {
                        //Remove "wait" cursor
-                       removeStylesheet( waitCSS )
+                       removeStylesheet( waitCSS );
                        
                        //Warn the user
                        showMsg( mw.msg( 'gadgets-save-failed' ) );
@@ -92,7 +92,7 @@
                        success: function( response ) {
                                if ( typeof response.error == 'undefined' ) {
                                        //Remove "wait" cursor
-                                       removeStylesheet( waitCSS )
+                                       removeStylesheet( waitCSS );
                        
                                        //Notify success to user
                                        showMsg( mw.msg( 'gadgets-save-success' 
) );
@@ -102,7 +102,7 @@
                                        //update 'savedConfig'
                                        $dialog.data( 'savedValues', config );
                                } else {
-                                       error()
+                                       error();
                                }
                        },
                        error: error
@@ -134,16 +134,16 @@
                                                dataType: "json", // response 
type
                                                success: function( response ) {
                                                        
-                                                       if ( typeof 
response.description != 'object'
-                                                               || typeof 
response.values != 'object')
+                                                       if ( typeof 
response.description != 'object' ||
+                                                               typeof 
response.values != 'object')
                                                        {
-                                                               alert( mw.msg( 
'gadgets-unexpected-error' ) )
+                                                               alert( mw.msg( 
'gadgets-unexpected-error' ) );
                                                                return;
                                                        }
                                                        
                                                        //Create and show dialog
                                                        
-                                                       var prefsDescription = 
response.description,
+                                                       var     
prefsDescription = response.description,
                                                                values = 
response.values,
                                                                $dialogBody;
                                                        
@@ -155,9 +155,9 @@
                                                                        //Hide 
current message, if any
                                                                        
showMsg( null );
                                                                        
-                                                                       var 
savedValues = $dialogBody.data( 'savedValues' ),
+                                                                       var     
savedValues = $dialogBody.data( 'savedValues' ),
                                                                                
currentValues = $form.formBuilder( 'getValues' );
-                                                                       //TODO: 
use a better way of comparing values...
+
                                                                        if ( 
!deepEquals( currentValues, savedValues ) ) {
                                                                                
$( '#mw-gadgets-prefsDialog-save' ).button( 'enable' );
                                                                        } else {

Modified: branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js
===================================================================
--- branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js 
2011-08-19 10:21:56 UTC (rev 94993)
+++ branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js 
2011-08-19 10:54:30 UTC (rev 94994)
@@ -4,244 +4,12 @@
  * Released under the MIT and GPL licenses.
  */
 
-(function($, mw) {
+( function( $, mw ) {
 
        //Field types that can be referred to by preference descriptions
-       var validFieldTypes = {};
+       var     validFieldTypes = {}, //filled when costructors are initialized
+               prefsSpecifications;  //defined later, declaring here to avoid 
references to undeclared variable
 
-       //Describes 'name' and 'label' field members, common to all "simple" 
fields
-       var simpleFields = [
-               {
-                       "name": "name",
-                       "type": "string",
-                       "label": "name",
-                       "required": true,
-                       "maxlength": 40,
-                       "default": ""
-               },
-               {
-                       "name": "label",
-                       "type": "string",
-                       "label": "label",
-                       "required": false,
-                       "default": ""
-               }
-       ];
-
-       //Used by preference editor to build field properties dialogs
-       //TODO: document
-       var prefsDescriptionSpecifications = {
-               "label": {
-                       "simple": false,
-                       "builder": [ {
-                               "name": "label",
-                               "type": "string",
-                               "label": "label",
-                               "required": false,
-                               "default": ""
-                       } ]
-               },
-               "boolean": {
-                       "simple": true,
-                       "builder": simpleFields
-               },
-               "string": {
-                       "simple": true,
-                       "builder": simpleFields.concat( [
-                               {
-                                       "name": "required",
-                                       "type": "boolean",
-                                       "label": "required",
-                                       "default": false
-                               },
-                               {
-                                       "name": "minlength",
-                                       "type": "number",
-                                       "label": "minlength",
-                                       "integer": true,
-                                       "min": 0,
-                                       "required": false,
-                                       "default": null
-                               },
-                               {
-                                       "name": "maxlength",
-                                       "type": "number",
-                                       "label": "maxlength",
-                                       "integer": true,
-                                       "min": 1,
-                                       "required": false,
-                                       "default": null
-                               }
-                       ] )
-               },
-               "number": {
-                       "simple": true,
-                       "builder": simpleFields.concat( [
-                               {
-                                       "name": "required",
-                                       "type": "boolean",
-                                       "label": "required",
-                                       "default": true
-                               },
-                               {
-                                       "name": "integer",
-                                       "type": "boolean",
-                                       "label": "integer",
-                                       "default": false
-                               },
-                               {
-                                       "name": "min",
-                                       "type": "number",
-                                       "label": "min",
-                                       "required": false,
-                                       "default": null
-                               },
-                               {
-                                       "name": "max",
-                                       "type": "number",
-                                       "label": "max",
-                                       "required": false,
-                                       "default": null
-                               }
-                       ] )
-               },
-               "range": {
-                       "simple": true,
-                       "builder": simpleFields.concat( [
-                               {
-                                       "name": "min",
-                                       "type": "number",
-                                       "label": "min",
-                                       "required": true,
-                               },
-                               {
-                                       "name": "step",
-                                       "type": "number",
-                                       "label": "step",
-                                       "required": true,
-                                       "default": 1
-                               },
-                               {
-                                       "name": "max",
-                                       "type": "number",
-                                       "label": "max",
-                                       "required": true,
-                               }
-                       ] )
-               },
-               "date": {
-                       "simple": true,
-                       "builder": simpleFields
-               },
-               "color": {
-                       "simple": true,
-                       "builder": simpleFields
-               },
-               "bundle": {
-                       "simple": false,
-                       "builder": function( options, callback ) {
-                               callback(
-                                       new BundleField( {
-                                               "type": "bundle",
-                                               "sections": [
-                                                       {
-                                                               "title": 
"Section 1",
-                                                               "fields": []
-                                                       },
-                                                       {
-                                                               "title": 
"Section 2",
-                                                               "fields": []
-                                                       }
-                                               ]
-                                       }, options )
-                               );
-                       }
-               },
-               "composite": {
-                       "simple": true,
-                       "builder": [ {
-                               "name": "name",
-                               "type": "string",
-                               "label": "name",
-                               "required": true,
-                               "maxlength": 40,
-                               "default": ""
-                       } ]
-               },
-               "list": {
-                       "simple": true,
-                       "builder": function( options, callback ) {
-
-                               //Create list of "simple" types
-                               var selectOptions = [];
-                               $.each( prefsDescriptionSpecifications, 
function( type, typeInfo ) {
-                                       if ( typeInfo.simple === true ) {
-                                               selectOptions.push( { "name": 
type, "value": type } );
-                                       }
-                               } );
-                               
-                               //Create the dialog to chose the field type
-                               var $form = $( {
-                                       fields: [ {
-                                               "name": "name",
-                                               "type": "string",
-                                               "label": "name",
-                                               "required": true,
-                                               "maxlength": 40,
-                                               "default": ""
-                                       },
-                                       {
-                                               "name": "type",
-                                               "type": "select",
-                                               "label": "type",
-                                               "options": selectOptions
-                                       } ]
-                               } ).formBuilder( { idPrefix: 'list-chose-type-' 
} )
-                                       .submit( function() {
-                                               return false; //prevent form 
submission
-                                       } );
-                               
-                               $form.dialog( {
-                                               width: 450,
-                                               modal: true,
-                                               resizable: false,
-                                               title: mw.msg( 
'gadgets-formbuilder-editor-create-field-title', 'list' ),
-                                               close: function() {
-                                                       $( this ).remove();
-                                               },
-                                               buttons: [
-                                                       {
-                                                               text: mw.msg( 
'gadgets-formbuilder-editor-ok' ),
-                                                               click: 
function() {
-                                                                       var 
values = $( this ).formBuilder( 'getValues' );
-                                                                       $( this 
).dialog( "close" );
-                                                                       
-                                                                       var 
dialog = this;
-                                                                       
createFieldDialog( {
-                                                                               
type: values.type,
-                                                                               
values: {
-                                                                               
        "name": values.name
-                                                                               
},
-                                                                               
callback: function( field ) {
-                                                                               
        $( dialog ).dialog( 'close' );
-                                                                               
        showEditFieldDialog( field.getDesc(), options, callback );
-                                                                               
        return true;
-                                                                               
}
-                                                                       }, { 
editable: true } );
-                                                               }
-                                                       },
-                                                       {
-                                                               text: mw.msg( 
'gadgets-formbuilder-editor-cancel' ),
-                                                               click: 
function() {
-                                                                       $( this 
).dialog( "close" );
-                                                               }
-                                                       }
-                                               ]
-                                       } );
-                       }
-               }
-       };
-
        /* Utility functions */
        
        //Preprocesses strings end possibly replaces them with messages.
@@ -249,7 +17,7 @@
        //a message, and the result of mw.msg is returned.
        //Two "@@" at the beginning escape for a single "@".
        function preproc( msgPrefix, str ) {
-               if ( str.length <= 1 || str[0] !== '@' ) {
+               if ( str.length <= 1 || str.charAt( 0 ) !== '@' ) {
                        return str;
                } else if ( str.substr( 0, 2 ) == '@@' ) {
                        return str.substr( 1 );
@@ -269,6 +37,7 @@
                };
        } )();
 
+       //Pads a number with leading zeroes until length is n characters
        function pad( n, len ) {
                var res = '' + n;
                while ( res.length < len ) {
@@ -299,15 +68,41 @@
                return $.extend( true, {}, obj );
        }
 
+       //EcmaScript 5 standard function, emulate for older browsers
+       //From 
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create
+       if ( !Object.create ) {
+               Object.create = function ( o ) {
+                       if ( arguments.length > 1 ) {
+                               $.error('Object.create implementation only 
accepts the first parameter.');
+                       }
+                       function F() {}
+                       F.prototype = o;
+                       return new F();
+               };
+       }
+
+       //Add a "smart" listener to watch for changes to an <input /> element
+       //This binds to several events, but calls the callback only if the 
value actually changed
+       function addSmartChangeListener( $input, callback ) {
+               var oldValue = $input.val();
+               //bind all events that may change the value of the field (some 
are brower-specific)
+               $input.bind( 'keyup change propertychange input paste', 
function() {
+                       var newValue = $input.val();
+                       if ( oldValue !== newValue ) {
+                               oldValue = newValue;
+                               callback();
+                       }
+               } );
+       }
+
+       /* Validator plugin utility functions and methods */
+
        function deleteFieldRules( field ) {
                //Remove all its validation rules
                var validationSettings = field.getValidationSettings();
                if ( validationSettings.rules ) {
                        $.each( validationSettings.rules, function( name, value 
) {
-                               var $input = $( '#' + name );
-                               if ( $input.length > 0 ) {
-                                       $( '#' + name ).rules( 'remove' );
-                               }
+                               $( '#' + name ).rules( 'remove' );
                        } );
                }
        }
@@ -319,7 +114,7 @@
                                var $input = $( '#' + name );
                                
                                //Find messages associated to this rule, if any
-                               if ( typeof validationSettings.messages != 
'undefined' && 
+                               if ( typeof validationSettings.messages != 
'undefined' &&
                                        typeof 
validationSettings.messages[name] != 'undefined')
                                {
                                        rules.messages = 
validationSettings.messages[name];
@@ -332,7 +127,55 @@
                }
        }
 
-       function createFieldDialog( params, options ) {
+
+       function testOptional( value, element ) {
+               var rules = $( element ).rules();
+               if ( typeof rules.required == 'undefined' || rules.required === 
false ) {
+                       if ( value.length === 0 ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       //validator for "required" fields (without trimming whitespaces)
+       $.validator.addMethod( "requiredStrict", function( value, element ) {
+               return value.length > 0;
+       }, mw.msg( 'gadgets-formbuilder-required' ) );
+
+       //validator for "minlength" fields (without trimming whitespaces)
+       $.validator.addMethod( "minlengthStrict", function( value, element, 
param ) {
+               return testOptional( value, element ) || value.length >= param;
+       } );
+
+       //validator for "maxlength" fields (without trimming whitespaces)
+       $.validator.addMethod( "maxlengthStrict", function( value, element, 
param ) {
+               return testOptional( value, element ) || value.length <= param;
+       } );
+
+       //validator for integer fields
+       $.validator.addMethod( "integer", function( value, element ) {
+               return testOptional( value, element ) || /^-?\d+$/.test(value);
+       }, mw.msg( 'gadgets-formbuilder-integer' ) );
+
+       //validator for datepicker fields
+       $.validator.addMethod( "datePicker", function( value, element ) {
+               var format = $( element ).datepicker( 'option', 'dateFormat' );
+               try {
+                       var date = $.datepicker.parseDate( format, value );
+                       return true;
+               } catch ( e ) {
+                       return false;
+               }
+       }, mw.msg( 'gadgets-formbuilder-date' ) );
+
+       //validator for colorpicker fields
+       $.validator.addMethod( "color", function( value, element ) {
+               return $.colorUtil.getRGB( value ) !== undefined;
+       }, mw.msg( 'gadgets-formbuilder-color' ) );
+
+       /* Functions used by the preferences editor */
+               function createFieldDialog( params, options ) {
                var self = this;
                
                if ( typeof params.callback != 'function' ) {
@@ -399,10 +242,10 @@
                        return;
                } else {
                        type = params.type;
-                       if ( typeof prefsDescriptionSpecifications[type] == 
'undefined' ) {
+                       if ( typeof prefsSpecifications[type] == 'undefined' ) {
                                $.error( 'createFieldDialog: invalid type: ' + 
type );
-                       } else if ( typeof 
prefsDescriptionSpecifications[type].builder == 'function' ) {
-                               prefsDescriptionSpecifications[type].builder( 
options, function( field ) {
+                       } else if ( typeof prefsSpecifications[type].builder == 
'function' ) {
+                               prefsSpecifications[type].builder( options, 
function( field ) {
                                        if ( field !== null ) {
                                                params.callback( field );
                                        }
@@ -410,10 +253,10 @@
                                return;
                        }
                        
-                       //typeof prefsDescriptionSpecifications[type].builder 
== 'object'
+                       //typeof prefsSpecifications[type].builder == 'object'
                        
                        description = {
-                               fields: 
prefsDescriptionSpecifications[type].builder
+                               fields: prefsSpecifications[type].builder
                        };
                }
                
@@ -472,8 +315,8 @@
                                                                } );
                                                        }
                                                        
-                                                       var FieldConstructor = 
validFieldTypes[type];
-                                                       var field;
+                                                       var     
FieldConstructor = validFieldTypes[type],
+                                                               field;
                                                        
                                                        try {
                                                                field = new 
FieldConstructor( fieldDescription, options );
@@ -521,13 +364,14 @@
                                        {
                                                text: mw.msg( 
'gadgets-formbuilder-editor-ok' ),
                                                click: function() {
-                                                       var fieldDesc = $( this 
).formBuilder( 'getDescription' ).fields[0];
+                                                       var     fieldDesc = $( 
this ).formBuilder( 'getDescription' ).fields[0],
                                                                name = 
fieldDesc.name;
                                                        
                                                        delete fieldDesc.name;
                                                        
                                                        $( this ).dialog( 
"close" );
 
+                                                       var ListField = 
validFieldTypes.list;
                                                        callback( new 
ListField( {
                                                                        type: 
'list',
                                                                        name: 
name,
@@ -546,73 +390,6 @@
                        } );
        }
 
-       function testOptional( value, element ) {
-               var rules = $( element ).rules();
-               if ( typeof rules.required == 'undefined' || rules.required === 
false ) {
-                       if ( value.length == 0 ) {
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       //validator for "required" fields (without trimming whitespaces)
-       $.validator.addMethod( "requiredStrict", function( value, element ) {
-               return value.length > 0;
-       }, mw.msg( 'gadgets-formbuilder-required' ) );
-
-       //validator for "minlength" fields (without trimming whitespaces)
-       $.validator.addMethod( "minlengthStrict", function( value, element, 
param ) {
-               return testOptional( value, element ) || value.length >= param;
-       } );
-
-       //validator for "maxlength" fields (without trimming whitespaces)
-       $.validator.addMethod( "maxlengthStrict", function( value, element, 
param ) {
-               return testOptional( value, element ) || value.length <= param;
-       } );
-
-       //validator for integer fields
-       $.validator.addMethod( "integer", function( value, element ) {
-               return testOptional( value, element ) || /^-?\d+$/.test(value);
-       }, mw.msg( 'gadgets-formbuilder-integer' ) );
-
-       //validator for datepicker fields
-       $.validator.addMethod( "datePicker", function( value, element ) {
-               var format = $( element ).datepicker( 'option', 'dateFormat' );
-               try {
-                       var date = $.datepicker.parseDate( format, value );
-                       return true;
-               } catch ( e ) {
-                       return false;
-               }
-       }, mw.msg( 'gadgets-formbuilder-date' ) );
-
-       //validator for colorpicker fields
-       $.validator.addMethod( "color", function( value, element ) {
-               return $.colorUtil.getRGB( value ) !== undefined;
-       }, mw.msg( 'gadgets-formbuilder-color' ) );
-
-       //Helper function for inheritance, see 
http://javascript.crockford.com/prototypal.html
-       function object(o) {
-               function F() {}
-               F.prototype = o;
-               return new F();
-       }
-
-       //Add a "smart" listener to watch for changes to an <input /> element
-       //This binds to several events, but calls the callback only if the 
value actually changed
-       function addSmartChangeListener( $input, callback ) {
-               var oldValue = $input.val();
-               //bind all events that may change the value of the field (some 
are brower-specific)
-               $input.bind( 'keyup change propertychange input paste', 
function() {
-                       var newValue = $input.val();
-                       if ( oldValue !== newValue ) {
-                               oldValue = newValue;
-                               callback();
-                       }
-               } );
-       }
-
        /* Basic interface for fields */
        function Field( desc, options ) {
                if ( typeof options.idPrefix == 'undefined' ) {
@@ -645,9 +422,11 @@
                };
        };
 
-       /* A field with no content, generating an empty container */
-       EmptyField.prototype = object( Field.prototype );
-       EmptyField.prototype.constructor = EmptyField;
+       /*
+        * A field with no content, generating an empty container
+        * and checking existence and type of the 'type' member of description.
+        * 
+        **/
        function EmptyField( desc, options ) {
                Field.call( this, desc, options );
                
@@ -660,14 +439,14 @@
                        .addClass( 'formbuilder-field formbuilder-field-' + 
this.desc.type )
                        .data( 'field', this );
        }
+       EmptyField.prototype = Object.create( Field.prototype );
+       EmptyField.prototype.constructor = EmptyField;
 
        EmptyField.prototype.getElement = function() {
                return this.$div;
        };
 
        /* A field with just a label */
-       LabelField.prototype = object( EmptyField.prototype );
-       LabelField.prototype.constructor = LabelField;
        function LabelField( desc, options ) {
                EmptyField.call( this, desc, options );
 
@@ -681,13 +460,13 @@
 
                this.$div.append( this.$label );
        }
+       LabelField.prototype = Object.create( EmptyField.prototype );
+       LabelField.prototype.constructor = LabelField;
 
-       validFieldTypes["label"] = LabelField;
+       validFieldTypes.label = LabelField;
 
        /* Abstract base class for all "simple" fields. Should not be 
instantiated. */
-       SimpleField.prototype = object( LabelField.prototype );
-       SimpleField.prototype.constructor = SimpleField;
-       function SimpleField( desc, options ){ 
+       function SimpleField( desc, options ) {
                LabelField.call( this, desc, options );
                
                //Validate the 'name' member
@@ -710,6 +489,8 @@
                        this.options = options;
                }
        }
+       SimpleField.prototype = Object.create( LabelField.prototype );
+       SimpleField.prototype.constructor = SimpleField;
        
        SimpleField.prototype.getDesc = function( useValuesAsDefaults ) {
                var desc = clone( LabelField.prototype.getDesc.call( this, 
useValuesAsDefaults ) );
@@ -724,9 +505,7 @@
 
 
        /* A field with a label and a checkbox */
-       BooleanField.prototype = object( SimpleField.prototype );
-       BooleanField.prototype.constructor = BooleanField;
-       function BooleanField( desc, options ){ 
+       function BooleanField( desc, options ) {
                SimpleField.call( this, desc, options );
 
                this.$c = $( '<input/>' ).attr( {
@@ -737,7 +516,7 @@
 
                if ( options.change ) {
                        this.$c.change( function() {
-                               options.change()
+                               options.change();
                        } );
                }
 
@@ -752,21 +531,21 @@
 
                this.$div.append( this.$c );
        }
+       BooleanField.prototype = Object.create( SimpleField.prototype );
+       BooleanField.prototype.constructor = BooleanField;
        
        BooleanField.prototype.getValues = function() {
                return pair( this.desc.name, this.$c.is( ':checked' ) );
        };
 
-       validFieldTypes["boolean"] = BooleanField;
+       validFieldTypes.boolean = BooleanField;
 
        /* A field with a textbox accepting string values */
-       StringField.prototype = object( SimpleField.prototype );
-       StringField.prototype.constructor = StringField;
-       function StringField( desc, options ){ 
+       function StringField( desc, options ) {
                SimpleField.call( this, desc, options );
 
                //Validate minlength and maxlength
-               var minlength = typeof desc.minlength != 'undefined' ? 
desc.minlength : 0,
+               var     minlength = typeof desc.minlength != 'undefined' ? 
desc.minlength : 0,
                        maxlength = typeof desc.maxlength != 'undefined' ? 
desc.maxlength : 1024;
                
                if ( !isInteger( minlength ) || minlength < 0 ) {
@@ -801,6 +580,8 @@
 
                this.$div.append( this.$text );
        }
+       StringField.prototype = Object.create( SimpleField.prototype );
+       StringField.prototype.constructor = StringField;
        
        StringField.prototype.getValues = function() {
                return pair( this.desc.name, this.$text.val() );
@@ -835,21 +616,19 @@
                return settings;
        };
 
-       validFieldTypes["string"] = StringField;
+       validFieldTypes.string = StringField;
 
 
        /* A field with a textbox accepting numeric values */
-       NumberField.prototype = object( SimpleField.prototype );
-       NumberField.prototype.constructor = NumberField;
-       function NumberField( desc, options ){ 
+       function NumberField( desc, options ) {
                SimpleField.call( this, desc, options );
 
                //Validation of description
                if ( desc.integer === true ) {
-                       if ( typeof desc.min != 'undefined' && !isInteger( 
desc.min ) ){
+                       if ( typeof desc.min != 'undefined' && !isInteger( 
desc.min ) ) {
                                $.error( "min is not an integer" );
                        }
-                       if ( typeof desc.max != 'undefined' && !isInteger( 
desc.max ) ){
+                       if ( typeof desc.max != 'undefined' && !isInteger( 
desc.max ) ) {
                                $.error( "max is not an integer" );
                        }
                }
@@ -881,6 +660,8 @@
 
                this.$div.append( this.$text );
        }
+       NumberField.prototype = Object.create( SimpleField.prototype );
+       NumberField.prototype.constructor = NumberField;
        
        NumberField.prototype.getValues = function() {
                var val = parseFloat( this.$text.val() );
@@ -922,12 +703,10 @@
                return settings;
        };
 
-       validFieldTypes["number"] = NumberField;
+       validFieldTypes.number = NumberField;
 
        /* A field with a drop-down list */
-       SelectField.prototype = object( SimpleField.prototype );
-       SelectField.prototype.constructor = SelectField;
-       function SelectField( desc, options ){ 
+       function SelectField( desc, options ) {
                SimpleField.call( this, desc, options );
 
                var $select = this.$select = $( '<select/>' ).attr( {
@@ -935,8 +714,8 @@
                        name: this.options.idPrefix + this.desc.name
                } );
                
-               var validValues = [];
-               var self = this;
+               var     validValues = [],
+                       self = this;
                $.each( this.desc.options, function( idx, option ) {
                        var i = validValues.length;
                        $( '<option/>' )
@@ -967,18 +746,18 @@
 
                this.$div.append( $select );
        }
+       SelectField.prototype = Object.create( SimpleField.prototype );
+       SelectField.prototype.constructor = SelectField;
        
        SelectField.prototype.getValues = function() {
                var i = parseInt( this.$select.val(), 10 );
                return pair( this.desc.name, this.validValues[i] );
        };
 
-       validFieldTypes["select"] = SelectField;
+       validFieldTypes.select = SelectField;
 
        /* A field with a slider, representing ranges of numbers */
-       RangeField.prototype = object( SimpleField.prototype );
-       RangeField.prototype.constructor = RangeField;
-       function RangeField( desc, options ){ 
+       function RangeField( desc, options ) {
                SimpleField.call( this, desc, options );
 
                //Validation
@@ -1065,11 +844,13 @@
                                }, 1 );
                        },
                        stop: function( event, ui ) {
+                               //After a delay, hide tooltip if the handle 
doesn't have focus and pointer isn't over the handle.
                                setTimeout( function() {
-                                       if ( !$handle.is( ':focus' ) && 
!mouseOver) {
+                                       if ( !$slider.find( '.ui-slider-handle' 
).is( ':focus' ) && !mouseOver ) {
                                                refreshTooltip( false, 
$slider.find( '.ui-slider-handle' ), ui.value );
                                        }
                                }, 300 );
+                               
                                sliding = false;
                        },
                        change: function( event, ui ) {
@@ -1103,17 +884,17 @@
                        
                this.$div.append( $slider );
        }
+       RangeField.prototype = Object.create( SimpleField.prototype );
+       RangeField.prototype.constructor = RangeField;
        
        RangeField.prototype.getValues = function() {
                return pair( this.desc.name, this.$slider.slider( 'value' ) );
        };
 
-       validFieldTypes["range"] = RangeField;  
+       validFieldTypes.range = RangeField;     
        
        /* A field with a textbox with a datepicker */
-       DateField.prototype = object( SimpleField.prototype );
-       DateField.prototype.constructor = DateField;
-       function DateField( desc, options ){ 
+       function DateField( desc, options ) {
                SimpleField.call( this, desc, options );
 
                var $text = this.$text = $( '<input/>' )
@@ -1130,8 +911,8 @@
                                }
                        } );
 
-               var value = options.values && options.values[this.desc.name];
-               var date;
+               var     value = options.values && 
options.values[this.desc.name],
+                       date;
                if ( typeof value != 'undefined' && value !== null ) {
                        date = new Date( value );
                        
@@ -1149,9 +930,11 @@
 
                this.$div.append( this.$text );
        }
+       DateField.prototype = Object.create( SimpleField.prototype );
+       DateField.prototype.constructor = DateField;
        
        DateField.prototype.getValues = function() {
-               var d = this.$text.datepicker( 'getDate' ),
+               var     d = this.$text.datepicker( 'getDate' ),
                        res = {};
                
                if ( d === null ) {
@@ -1178,7 +961,7 @@
                return settings;
        };
 
-       validFieldTypes["date"] = DateField;
+       validFieldTypes.date = DateField;
 
        /* A field with color picker */
        
@@ -1191,16 +974,15 @@
        //If a click happens outside the colorpicker while it is showed, remove 
it
        $( document ).mousedown( function( event ) {
                var $target = $( event.target );
-               if ( $target.parents( '#colorpicker' ).length == 0 ) {
+               if ( $target.parents( '#colorpicker' ).length === 0 ) {
                        closeColorPicker();
                }
        } );
        
-       ColorField.prototype = object( SimpleField.prototype );
-       ColorField.prototype.constructor = ColorField;
-       function ColorField( desc, options ){ 
+       function ColorField( desc, options ) {
                SimpleField.call( this, desc, options );
 
+               var value;
                if ( typeof options.values != 'undefined' && typeof 
options.values[this.desc.name] != 'undefined' ) {
                        value = options.values[this.desc.name];
                } else {
@@ -1252,6 +1034,8 @@
 
                this.$div.append( this.$text );
        }
+       ColorField.prototype = Object.create( SimpleField.prototype );
+       ColorField.prototype.constructor = ColorField;
        
        ColorField.prototype.getValidationSettings = function() {
                var     settings = 
SimpleField.prototype.getValidationSettings.call( this ),
@@ -1273,13 +1057,11 @@
                }
        };
 
-       validFieldTypes["color"] = ColorField;
+       validFieldTypes.color = ColorField;
        
        
        /* A field that represent a section (group of fields) */
 
-       SectionField.prototype = object( Field.prototype );
-       SectionField.prototype.constructor = SectionField;
        function SectionField( desc, options, id ) {
                Field.call( this, desc, options );
                
@@ -1295,7 +1077,7 @@
                                this._createSlot( 'yes' ).appendTo( this.$div );
                        }
 
-                       var field = this.desc.fields[i],
+                       var     field = this.desc.fields[i],
                                FieldConstructor = validFieldTypes[field.type];
 
                        if ( typeof FieldConstructor != 'function' ) {
@@ -1309,7 +1091,7 @@
                                editable = 'no';
                        }
 
-                       var f = new FieldConstructor( field, options ),
+                       var     f = new FieldConstructor( field, options ),
                                $slot = this._createSlot( editable, f );
                        
                        $slot.appendTo( this.$div );
@@ -1320,6 +1102,8 @@
                        this._createSlot( 'yes' ).appendTo( this.$div );
                }
        }
+       SectionField.prototype = Object.create( Field.prototype );
+       SectionField.prototype.constructor = SectionField;
        
        SectionField.prototype.getElement = function() {
                return this.$div;
@@ -1379,7 +1163,7 @@
        };
        
        SectionField.prototype._createSlot = function( editable, field ) {
-               var self = this,
+               var     self = this,
                        $slot = $( '<div/>' ).addClass( 'formbuilder-slot 
ui-widget' ),
                        $divButtons;
                
@@ -1413,7 +1197,7 @@
                                //Add the button for changing existing slots
                                var type = field.getDesc().type;
                                //TODO: using the 'builder' info is not optimal
-                               if ( typeof 
prefsDescriptionSpecifications[type].builder != 'function' ) {
+                               if ( typeof prefsSpecifications[type].builder 
!= 'function' ) {
                                        $( '<a href="javascript:;" />' )
                                                .addClass( 'formbuilder-button 
formbuilder-editor-button-edit ui-icon ui-icon-gear' )
                                                .attr( 'title', mw.msg( 
'gadgets-formbuilder-editor-edit-field' ) )
@@ -1425,7 +1209,7 @@
                                                                callback: 
function( newField ) {
                                                                        if ( 
newField !== null ) {
                                                                                
//check that there are no duplicate preference names
-                                                                               
var existingValues = self.$div.closest( '.formbuilder' ).formBuilder( 
'getValues' ),
+                                                                               
var     existingValues = self.$div.closest( '.formbuilder' ).formBuilder( 
'getValues' ),
                                                                                
        removedValues = field.getValues(),
                                                                                
        duplicateName = null;
                                                                                
$.each( field.getValues(), function( name, val ) {
@@ -1463,13 +1247,13 @@
                                                .addClass( 'formbuilder-button 
formbuilder-editor-button-delete ui-icon ui-icon-trash' )
                                                .attr( 'title', mw.msg( 
'gadgets-formbuilder-editor-delete-field' ) )
                                                .click( function( event, ui ) {
-                                                       //Make both slots 
disappear, then delete them 
+                                                       //Make both slots 
disappear, then delete them
                                                        $.each( [$slot, 
$slot.prev()], function( idx, $s ) {
                                                                $s.slideUp( 
function() {
                                                                        
self._deleteSlot( $s );
                                                                } );
                                                        } );
-                                               } ) 
+                                               } )
                                                .appendTo( $divButtons );
 
                                        //Make this slot draggable to allow 
moving it
@@ -1495,7 +1279,8 @@
                                        hoverClass: 'formbuilder-slot-can-drop',
                                        tolerance: 'pointer',
                                        drop: function( event, ui ) {
-                                               var srcSlot = ui.draggable, 
dstSlot = this;
+                                               var srcSlot = ui.draggable,
+                                                       dstSlot = this;
                                                
                                                //Remove one empty slot 
surrounding source
                                                $( srcSlot ).prev().remove();
@@ -1524,7 +1309,7 @@
                                                callback: function( field ) {
                                                        if ( field !== null ) {
                                                                //check that 
there are no duplicate preference names
-                                                               var 
existingValues = $slot.closest( '.formbuilder' ).formBuilder( 'getValues' ),
+                                                               var     
existingValues = $slot.closest( '.formbuilder' ).formBuilder( 'getValues' ),
                                                                        
duplicateName = null;
                                                                $.each( 
field.getValues(), function( name, val ) {
                                                                        if ( 
typeof existingValues[name] != 'undefined' ) {
@@ -1538,7 +1323,7 @@
                                                                        return 
false;
                                                                }
 
-                                                               var $newSlot = 
self._createSlot( 'yes', field ).hide(),
+                                                               var     
$newSlot = self._createSlot( 'yes', field ).hide(),
                                                                        
$newEmptySlot = self._createSlot( 'yes' ).hide();
                                                                
                                                                $slot.after( 
$newSlot, $newEmptySlot );
@@ -1563,9 +1348,7 @@
        };
 
        
-       /* A field for 'bundle's */
-       BundleField.prototype = object( EmptyField.prototype );
-       BundleField.prototype.constructor = BundleField;
+       /* A field for 'bundle' type fields */
        function BundleField( desc, options ) {
                EmptyField.call( this, desc, options );
 
@@ -1583,7 +1366,7 @@
                                                tolerance: 'pointer',
                                                accept:  
'.formbuilder-slot-nonempty',
                                                drop: function( event, ui ) {
-                                                       var $slot = $( 
ui.draggable ),
+                                                       var     $slot = $( 
ui.draggable ),
                                                                $srcSection = 
$slot.parent(),
                                                                $dstSection = 
$( section );
                                                        
@@ -1622,7 +1405,7 @@
                                                        .addClass( 
'formbuilder-button formbuilder-editor-button-edit-section ui-icon 
ui-icon-gear' )
                                                        .attr( 'title', mw.msg( 
'gadgets-formbuilder-editor-edit-section' ) )
                                                        .click( function() {
-                                                               var button = 
this,
+                                                               var     button 
= this,
                                                                        
sectionField = $( ui.panel ).data( 'field' );
                                                                        
                                                                $( {
@@ -1673,15 +1456,15 @@
                        } );
 
                //Save for future reference
-               this.$ui_tabs_nav = $tabs.find( '.ui-tabs-nav' )
+               this.$ui_tabs_nav = $tabs.find( '.ui-tabs-nav' );
 
                var self = this;
                $.each( this.desc.sections, function( index, sectionDescription 
) {
-                       var id = self.options.idPrefix + 'section-' + 
getIncrementalCounter(),
+                       var     id = self.options.idPrefix + 'section-' + 
getIncrementalCounter(),
                                sec = new SectionField( sectionDescription, 
options, id );
                        
                        $tabs.append( sec.getElement() )
-                               .tabs( 'add', '#' + id, preproc( 
options.msgPrefix, sectionDescription.title ) ); 
+                               .tabs( 'add', '#' + id, preproc( 
options.msgPrefix, sectionDescription.title ) );
                } );
 
                if ( options.editable === true ) {
@@ -1707,7 +1490,7 @@
                                                        {
                                                                text: mw.msg( 
'gadgets-formbuilder-editor-ok' ),
                                                                click: 
function() {
-                                                                       var 
title = $( this ).formBuilder( 'getValues' ).title,
+                                                                       var     
title = $( this ).formBuilder( 'getValues' ).title,
                                                                                
id = self.options.idPrefix + 'section-' + getIncrementalCounter(),
                                                                                
newSectionDescription = {
                                                                                
        title: title,
@@ -1742,11 +1525,13 @@
 
                this.$div.append( $tabs );
        }
+       BundleField.prototype = Object.create( EmptyField.prototype );
+       BundleField.prototype.constructor = BundleField;
        
        BundleField.prototype.getValidationSettings = function() {
                var settings = {};
                this.$ui_tabs_nav.find( 'a' ).each( function( idx, anchor ) {
-                       var panel = $( anchor ).data( 'panel' ),
+                       var     panel = $( anchor ).data( 'panel' ),
                                field = $( panel ).data( 'field' );
                                
                        $.extend( true, settings, field.getValidationSettings() 
);
@@ -1758,7 +1543,7 @@
                var desc = clone( this.desc );
                desc.sections = [];
                this.$ui_tabs_nav.find( 'a' ).each( function( idx, anchor ) {
-                       var panel = $( anchor ).data( 'panel' ),
+                       var     panel = $( anchor ).data( 'panel' ),
                                field = $( panel ).data( 'field' );
                                
                        desc.sections.push( field.getDesc( useValuesAsDefaults 
) );
@@ -1769,7 +1554,7 @@
        BundleField.prototype.getValues = function() {
                var values = {};
                this.$ui_tabs_nav.find( 'a' ).each( function( idx, anchor ) {
-                       var panel = $( anchor ).data( 'panel' ),
+                       var     panel = $( anchor ).data( 'panel' ),
                                field = $( panel ).data( 'field' );
                                
                        $.extend( values, field.getValues() );
@@ -1777,13 +1562,11 @@
                return values;
        };
 
-       validFieldTypes["bundle"] = BundleField;
+       validFieldTypes.bundle = BundleField;
 
 
        /* A field for 'composite' fields */
 
-       CompositeField.prototype = object( EmptyField.prototype );
-       CompositeField.prototype.constructor = CompositeField;
        function CompositeField( desc, options ) {
                EmptyField.call( this, desc, options );
                
@@ -1811,6 +1594,8 @@
                this._section = new SectionField( desc, sectionOptions );
                this.$div.append( this._section.getElement() );
        }
+       CompositeField.prototype = Object.create( EmptyField.prototype );
+       CompositeField.prototype.constructor = CompositeField;
 
        CompositeField.prototype.getDesc = function( useValuesAsDefaults ) {
                var desc = clone( this.desc );
@@ -1826,12 +1611,10 @@
                return this._section.getValidationSettings();
        };      
 
-       validFieldTypes["composite"] = CompositeField;
+       validFieldTypes.composite = CompositeField;
 
        /* A field for 'composite' fields */
 
-       ListField.prototype = object( EmptyField.prototype );
-       ListField.prototype.constructor = ListField;
        function ListField( desc, options ) {
                EmptyField.call( this, desc, options );
                
@@ -1844,7 +1627,7 @@
                }
 
                if ( ( typeof desc.field.type != 'string' )
-                       || 
prefsDescriptionSpecifications[desc.field.type].simple !== true )
+                       || prefsSpecifications[desc.field.type].simple !== true 
)
                {
                        $.error( "Missing or invalid field type specified in 
'field' parameter." );
                }
@@ -1855,8 +1638,8 @@
                        options.values = {};
                }
                
-               var value = ( typeof options.values[desc.name] != 'undefined' ) 
? options.values[desc.name] : desc['default'];
-               var self = this;
+               var     value = ( typeof options.values[desc.name] != 
'undefined' ) ? options.values[desc.name] : desc['default'],
+                       self = this;
                if ( typeof value != 'undefined' ) {
                        $.each( value, function( index, itemValue ) {
                                self._createItem( false, itemValue );
@@ -1880,12 +1663,14 @@
                        } )
                        .appendTo( this.$div );
        }
+       ListField.prototype = Object.create( EmptyField.prototype );
+       ListField.prototype.constructor = ListField;
 
        ListField.prototype._createItem = function( animated, itemValue ) {
-               var itemDesc = $.extend( {}, this.desc.field, {
+               var     itemDesc = $.extend( {}, this.desc.field, {
                                "name": this.desc.name
-                       } );
-               var itemOptions = $.extend( {}, this.options, {
+                       } ),
+                       itemOptions = $.extend( {}, this.options, {
                                editable: false,
                                idPrefix: this.options.idPrefix + 
getIncrementalCounter() + "-"
                        } );
@@ -1896,15 +1681,14 @@
                        itemOptions.values = pair( this.desc.name, 
this.desc.field['default'] );
                }
 
-               var FieldConstructor = validFieldTypes[this.desc.field.type];
-               var itemField = new FieldConstructor( itemDesc, itemOptions );
-               var     $itemDiv = $( '<div/>' )
-                       .addClass( 'formbuilder-list-item' )
-                       .data( 'field', itemField );
-
-               var $itemContent = $( '<div/>' )
-                       .addClass( 'formbuilder-list-item-content' )
-                       .append( itemField.getElement() );
+               var FieldConstructor = validFieldTypes[this.desc.field.type],
+                       itemField = new FieldConstructor( itemDesc, itemOptions 
),
+                       $itemDiv = $( '<div/>' )
+                               .addClass( 'formbuilder-list-item' )
+                               .data( 'field', itemField ),
+                       $itemContent = $( '<div/>' )
+                               .addClass( 'formbuilder-list-item-content' )
+                               .append( itemField.getElement() );
                
                $( '<div/>' )
                        .addClass( 'formbuilder-list-item-container' )
@@ -1974,9 +1758,244 @@
                return validationSettings;
        };
 
-       validFieldTypes["list"] = ListField;
+       validFieldTypes.list = ListField;
 
 
+       /* Specifications of preferences descriptions syntax and field types */
+       
+       //Describes 'name' and 'label' field members, common to all "simple" 
fields
+       var simpleFields = [
+               {
+                       "name": "name",
+                       "type": "string",
+                       "label": "name",
+                       "required": true,
+                       "maxlength": 40,
+                       "default": ""
+               },
+               {
+                       "name": "label",
+                       "type": "string",
+                       "label": "label",
+                       "required": false,
+                       "default": ""
+               }
+       ];
+
+       //Used by preference editor to build field properties dialogs
+       //TODO: document
+       prefsSpecifications = {
+               "label": {
+                       "simple": false,
+                       "builder": [ {
+                               "name": "label",
+                               "type": "string",
+                               "label": "label",
+                               "required": false,
+                               "default": ""
+                       } ]
+               },
+               "boolean": {
+                       "simple": true,
+                       "builder": simpleFields
+               },
+               "string": {
+                       "simple": true,
+                       "builder": simpleFields.concat( [
+                               {
+                                       "name": "required",
+                                       "type": "boolean",
+                                       "label": "required",
+                                       "default": false
+                               },
+                               {
+                                       "name": "minlength",
+                                       "type": "number",
+                                       "label": "minlength",
+                                       "integer": true,
+                                       "min": 0,
+                                       "required": false,
+                                       "default": null
+                               },
+                               {
+                                       "name": "maxlength",
+                                       "type": "number",
+                                       "label": "maxlength",
+                                       "integer": true,
+                                       "min": 1,
+                                       "required": false,
+                                       "default": null
+                               }
+                       ] )
+               },
+               "number": {
+                       "simple": true,
+                       "builder": simpleFields.concat( [
+                               {
+                                       "name": "required",
+                                       "type": "boolean",
+                                       "label": "required",
+                                       "default": true
+                               },
+                               {
+                                       "name": "integer",
+                                       "type": "boolean",
+                                       "label": "integer",
+                                       "default": false
+                               },
+                               {
+                                       "name": "min",
+                                       "type": "number",
+                                       "label": "min",
+                                       "required": false,
+                                       "default": null
+                               },
+                               {
+                                       "name": "max",
+                                       "type": "number",
+                                       "label": "max",
+                                       "required": false,
+                                       "default": null
+                               }
+                       ] )
+               },
+               "range": {
+                       "simple": true,
+                       "builder": simpleFields.concat( [
+                               {
+                                       "name": "min",
+                                       "type": "number",
+                                       "label": "min",
+                                       "required": true
+                               },
+                               {
+                                       "name": "step",
+                                       "type": "number",
+                                       "label": "step",
+                                       "required": true,
+                                       "default": 1
+                               },
+                               {
+                                       "name": "max",
+                                       "type": "number",
+                                       "label": "max",
+                                       "required": true
+                               }
+                       ] )
+               },
+               "date": {
+                       "simple": true,
+                       "builder": simpleFields
+               },
+               "color": {
+                       "simple": true,
+                       "builder": simpleFields
+               },
+               "bundle": {
+                       "simple": false,
+                       "builder": function( options, callback ) {
+                               callback(
+                                       new BundleField( {
+                                               "type": "bundle",
+                                               "sections": [
+                                                       {
+                                                               "title": 
"Section 1",
+                                                               "fields": []
+                                                       },
+                                                       {
+                                                               "title": 
"Section 2",
+                                                               "fields": []
+                                                       }
+                                               ]
+                                       }, options )
+                               );
+                       }
+               },
+               "composite": {
+                       "simple": true,
+                       "builder": [ {
+                               "name": "name",
+                               "type": "string",
+                               "label": "name",
+                               "required": true,
+                               "maxlength": 40,
+                               "default": ""
+                       } ]
+               },
+               "list": {
+                       "simple": true,
+                       "builder": function( options, callback ) {
+
+                               //Create list of "simple" types
+                               var selectOptions = [];
+                               $.each( prefsSpecifications, function( type, 
typeInfo ) {
+                                       if ( typeInfo.simple === true ) {
+                                               selectOptions.push( { "name": 
type, "value": type } );
+                                       }
+                               } );
+                               
+                               //Create the dialog to chose the field type
+                               var $form = $( {
+                                       fields: [ {
+                                               "name": "name",
+                                               "type": "string",
+                                               "label": "name",
+                                               "required": true,
+                                               "maxlength": 40,
+                                               "default": ""
+                                       },
+                                       {
+                                               "name": "type",
+                                               "type": "select",
+                                               "label": "type",
+                                               "options": selectOptions
+                                       } ]
+                               } ).formBuilder( { idPrefix: 'list-chose-type-' 
} )
+                                       .submit( function() {
+                                               return false; //prevent form 
submission
+                                       } );
+                               
+                               $form.dialog( {
+                                               width: 450,
+                                               modal: true,
+                                               resizable: false,
+                                               title: mw.msg( 
'gadgets-formbuilder-editor-create-field-title', 'list' ),
+                                               close: function() {
+                                                       $( this ).remove();
+                                               },
+                                               buttons: [
+                                                       {
+                                                               text: mw.msg( 
'gadgets-formbuilder-editor-ok' ),
+                                                               click: 
function() {
+                                                                       var 
values = $( this ).formBuilder( 'getValues' );
+                                                                       $( this 
).dialog( "close" );
+                                                                       
+                                                                       var 
$dialog = $( this );
+                                                                       
createFieldDialog( {
+                                                                               
type: values.type,
+                                                                               
values: {
+                                                                               
        "name": values.name
+                                                                               
},
+                                                                               
callback: function( field ) {
+                                                                               
        $dialog.dialog( 'close' );
+                                                                               
        showEditFieldDialog( field.getDesc(), options, callback );
+                                                                               
        return true;
+                                                                               
}
+                                                                       }, { 
editable: true } );
+                                                               }
+                                                       },
+                                                       {
+                                                               text: mw.msg( 
'gadgets-formbuilder-editor-cancel' ),
+                                                               click: 
function() {
+                                                                       $( this 
).dialog( "close" );
+                                                               }
+                                                       }
+                                               ]
+                                       } );
+                       }
+               }
+       };
+
        /* Public methods */
        
        /**
@@ -2072,5 +2091,5 @@
                        $.error( 'Method ' +  method + ' does not exist on 
jQuery.formBuilder' );
                }
        };
-})( jQuery, mediaWiki );
+} )( jQuery, mediaWiki );
 


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

Reply via email to