http://www.mediawiki.org/wiki/Special:Code/MediaWiki/95144
Revision: 95144
Author: salvatoreingala
Date: 2011-08-21 14:55:26 +0000 (Sun, 21 Aug 2011)
Log Message:
-----------
- Implemented creation and editing of 'select' type fields in the editor. Now
all field types are supported.
- Added a couple of fixes to ListFields, and improved aspect.
- Simplified the inheritance machinery.
- Using "$.isFunction( foo )" instead of "typeof foo == 'function'".
Modified Paths:
--------------
branches/salvatoreingala/Gadgets/Gadgets.i18n.php
branches/salvatoreingala/Gadgets/Gadgets.php
branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.css
branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js
Modified: branches/salvatoreingala/Gadgets/Gadgets.i18n.php
===================================================================
--- branches/salvatoreingala/Gadgets/Gadgets.i18n.php 2011-08-21 14:39:28 UTC
(rev 95143)
+++ branches/salvatoreingala/Gadgets/Gadgets.i18n.php 2011-08-21 14:55:26 UTC
(rev 95144)
@@ -62,6 +62,7 @@
'gadgets-formbuilder-integer' => 'Please enter an integer number.',
'gadgets-formbuilder-date' => 'Please enter a valid date.',
'gadgets-formbuilder-color' => 'Please enter a valid color.',
+ 'gadgets-formbuilder-scalar' => 'Valid values are true, false, null, a
floating point number or a double quoted string.',
'gadgets-formbuilder-editor-ok' => 'OK',
'gadgets-formbuilder-editor-cancel' => 'Cancel',
'gadgets-formbuilder-editor-move-field' => 'Move this field',
Modified: branches/salvatoreingala/Gadgets/Gadgets.php
===================================================================
--- branches/salvatoreingala/Gadgets/Gadgets.php 2011-08-21 14:39:28 UTC
(rev 95143)
+++ branches/salvatoreingala/Gadgets/Gadgets.php 2011-08-21 14:55:26 UTC
(rev 95144)
@@ -92,7 +92,7 @@
'messages' => array(
'gadgets-formbuilder-required',
'gadgets-formbuilder-minlength', 'gadgets-formbuilder-maxlength',
'gadgets-formbuilder-min', 'gadgets-formbuilder-max',
'gadgets-formbuilder-integer', 'gadgets-formbuilder-date',
- 'gadgets-formbuilder-color',
+ 'gadgets-formbuilder-color', 'gadgets-formbuilder-scalar',
'gadgets-formbuilder-editor-ok',
'gadgets-formbuilder-editor-cancel', 'gadgets-formbuilder-editor-move-field',
'gadgets-formbuilder-editor-delete-field',
'gadgets-formbuilder-editor-edit-field',
'gadgets-formbuilder-editor-edit-field-title',
'gadgets-formbuilder-editor-insert-field',
'gadgets-formbuilder-editor-chose-field',
'gadgets-formbuilder-editor-chose-field-title',
'gadgets-formbuilder-editor-create-field-title',
Modified: branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.css
===================================================================
--- branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.css
2011-08-21 14:39:28 UTC (rev 95143)
+++ branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.css
2011-08-21 14:55:26 UTC (rev 95144)
@@ -81,15 +81,21 @@
}
.formbuilder-field-list {
- border: 2px ridge;
+ border: 1px solid #888;
}
.formbuilder-list-item-container {
float: left;
margin-right: -35px;
width: 100%;
+ border-bottom: 1px solid #888;
}
+/* Show all the borders during dragging */
+.ui-sortable-helper .formbuilder-list-item-container {
+ border: 1px solid #888;
+}
+
.formbuilder-list-item-content {
margin-right: 35px;
}
Modified: branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js
===================================================================
--- branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js
2011-08-21 14:39:28 UTC (rev 95143)
+++ branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js
2011-08-21 14:55:26 UTC (rev 95144)
@@ -57,6 +57,13 @@
return typeof val == 'number' && val === Math.floor( val );
}
+ //Returns true if val is either true, false, null, a number or a string
+ function isScalar( val ) {
+ return val === true || val === false || val === null
+ || typeof val == 'number' || typeof val == 'string';
+ }
+
+ //Returns true if name is a valid preference name
function isValidPreferenceName( name ) {
return typeof name == 'string'
&& /^[a-zA-Z_][a-zA-Z0-9_]*$/.test( name )
@@ -68,19 +75,19 @@
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();
- };
+ //Helper function for inheritance, see
http://javascript.crockford.com/prototypal.html
+ function object( o ) {
+ function F() {}
+ F.prototype = o;
+ return new F();
}
+ //Helper function for inheritance
+ function inherit( Derived, Base ) {
+ Derived.prototype = object( Base.prototype );
+ Derived.prototype.constructor = Derived;
+ }
+
//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 ) {
@@ -97,6 +104,8 @@
/* Validator plugin utility functions and methods */
+ //Removes the field rules of "field" to the formbuilder form.
+ //NOTE: this method must be called before physically removing the
element from the form.
function deleteFieldRules( field ) {
//Remove all its validation rules
var validationSettings = field.getValidationSettings();
@@ -107,12 +116,12 @@
}
}
+ //Adds the field rules of "field" to the formbuilder form.
+ //NOTE: the field's element must have been appended to the form, yet.
function addFieldRules( field ) {
var validationSettings = field.getValidationSettings();
if ( validationSettings.rules ) {
$.each( validationSettings.rules, function( name, rules
) {
- var $input = $( '#' + name );
-
//Find messages associated to this rule, if any
if ( typeof validationSettings.messages !=
'undefined' &&
typeof
validationSettings.messages[name] != 'undefined')
@@ -120,9 +129,7 @@
rules.messages =
validationSettings.messages[name];
}
- if ( $input.length > 0 ) {
- $( '#' + name ).rules( 'add', rules );
- }
+ $( field.getElement() ).find( '#' + name
).rules( 'add', rules );
} );
}
}
@@ -174,11 +181,27 @@
return $.colorUtil.getRGB( value ) !== undefined;
}, mw.msg( 'gadgets-formbuilder-color' ) );
+ //validator for scalar fields
+ $.validator.addMethod( "scalar", function( value, element ) {
+ //parseJSON decodes interprets the empty string as 'null', and
we don't want that
+ if ( value === '' ) {
+ return false;
+ }
+
+ try {
+ if ( isScalar( $.parseJSON( value ) ) ) {
+ return true;
+ }
+ } catch( e ) { /* nothing */ }
+
+ return false;
+ }, mw.msg( 'gadgets-formbuilder-scalar' ) );
+
/* Functions used by the preferences editor */
function createFieldDialog( params, options ) {
var self = this;
- if ( typeof params.callback != 'function' ) {
+ if ( !$.isFunction( params.callback ) ) {
$.error( 'createFieldDialog: missing or wrong
"callback" parameter' );
}
@@ -244,7 +267,7 @@
type = params.type;
if ( typeof prefsSpecifications[type] == 'undefined' ) {
$.error( 'createFieldDialog: invalid type: ' +
type );
- } else if ( typeof prefsSpecifications[type].builder ==
'function' ) {
+ } else if ( $.isFunction(
prefsSpecifications[type].builder ) ) {
prefsSpecifications[type].builder( options,
function( field ) {
if ( field !== null ) {
params.callback( field );
@@ -364,6 +387,11 @@
{
text: mw.msg(
'gadgets-formbuilder-editor-ok' ),
click: function() {
+
+ if ( !$( this
).formBuilder( 'validate' ) ) {
+ return;
+ }
+
var fieldDesc = $(
this ).formBuilder( 'getDescription' ).fields[0],
name =
fieldDesc.name;
@@ -431,16 +459,21 @@
Field.call( this, desc, options );
//Check existence and type of the "type" field
- if ( !this.desc.type || typeof this.desc.type != 'string' ) {
+ if ( ( !this.desc.type || typeof this.desc.type != 'string' )
+ && !$.isFunction( this.desc.type ) )
+ {
$.error( "Missing 'type' parameter" );
}
this.$div = $( '<div/>' )
- .addClass( 'formbuilder-field formbuilder-field-' +
this.desc.type )
+ .addClass( 'formbuilder-field' )
.data( 'field', this );
+
+ if ( !$.isFunction( this.desc.type ) ) {
+ this.$div.addClass( 'formbuilder-field-' +
this.desc.type );
+ }
}
- EmptyField.prototype = Object.create( Field.prototype );
- EmptyField.prototype.constructor = EmptyField;
+ inherit( EmptyField, Field );
EmptyField.prototype.getElement = function() {
return this.$div;
@@ -460,8 +493,7 @@
this.$div.append( this.$label );
}
- LabelField.prototype = Object.create( EmptyField.prototype );
- LabelField.prototype.constructor = LabelField;
+ inherit( LabelField, EmptyField );
validFieldTypes.label = LabelField;
@@ -489,8 +521,7 @@
this.options = options;
}
}
- SimpleField.prototype = Object.create( LabelField.prototype );
- SimpleField.prototype.constructor = SimpleField;
+ inherit( SimpleField, LabelField );
SimpleField.prototype.getDesc = function( useValuesAsDefaults ) {
var desc = clone( LabelField.prototype.getDesc.call( this,
useValuesAsDefaults ) );
@@ -531,8 +562,7 @@
this.$div.append( this.$c );
}
- BooleanField.prototype = Object.create( SimpleField.prototype );
- BooleanField.prototype.constructor = BooleanField;
+ inherit( BooleanField, SimpleField );
BooleanField.prototype.getValues = function() {
return pair( this.desc.name, this.$c.is( ':checked' ) );
@@ -580,8 +610,7 @@
this.$div.append( this.$text );
}
- StringField.prototype = Object.create( SimpleField.prototype );
- StringField.prototype.constructor = StringField;
+ inherit( StringField, SimpleField );
StringField.prototype.getValues = function() {
return pair( this.desc.name, this.$text.val() );
@@ -660,8 +689,7 @@
this.$div.append( this.$text );
}
- NumberField.prototype = Object.create( SimpleField.prototype );
- NumberField.prototype.constructor = NumberField;
+ inherit( NumberField, SimpleField );
NumberField.prototype.getValues = function() {
var val = parseFloat( this.$text.val() );
@@ -746,8 +774,7 @@
this.$div.append( $select );
}
- SelectField.prototype = Object.create( SimpleField.prototype );
- SelectField.prototype.constructor = SelectField;
+ inherit( SelectField, SimpleField );
SelectField.prototype.getValues = function() {
var i = parseInt( this.$select.val(), 10 );
@@ -884,8 +911,7 @@
this.$div.append( $slider );
}
- RangeField.prototype = Object.create( SimpleField.prototype );
- RangeField.prototype.constructor = RangeField;
+ inherit( RangeField, SimpleField );
RangeField.prototype.getValues = function() {
return pair( this.desc.name, this.$slider.slider( 'value' ) );
@@ -930,8 +956,7 @@
this.$div.append( this.$text );
}
- DateField.prototype = Object.create( SimpleField.prototype );
- DateField.prototype.constructor = DateField;
+ inherit( DateField, SimpleField );
//Parses a date in the [YYYY]-[MM]-[DD]T[hh]:[mm]:[ss]Z format, returns
a date object
//Used to avoid the "new Date( dateString )" constructor, which is
implementation-specific.
@@ -1067,8 +1092,7 @@
this.$div.append( this.$text );
}
- ColorField.prototype = Object.create( SimpleField.prototype );
- ColorField.prototype.constructor = ColorField;
+ inherit( ColorField, SimpleField );
ColorField.prototype.getValidationSettings = function() {
var settings =
SimpleField.prototype.getValidationSettings.call( this ),
@@ -1111,9 +1135,15 @@
}
var field = this.desc.fields[i],
+ FieldConstructor;
+
+ if ( $.isFunction( field.type ) ) {
+ FieldConstructor = field.type;
+ } else {
FieldConstructor = validFieldTypes[field.type];
+ }
- if ( typeof FieldConstructor != 'function' ) {
+ if ( !$.isFunction( FieldConstructor ) ) {
$.error( "field with invalid type: " +
field.type );
}
@@ -1135,8 +1165,7 @@
this._createSlot( 'yes' ).appendTo( this.$div );
}
}
- SectionField.prototype = Object.create( Field.prototype );
- SectionField.prototype.constructor = SectionField;
+ inherit( SectionField, Field );
SectionField.prototype.getElement = function() {
return this.$div;
@@ -1230,7 +1259,7 @@
//Add the button for changing existing slots
var type = field.getDesc().type;
//TODO: using the 'builder' info is not optimal
- if ( typeof prefsSpecifications[type].builder
!= 'function' ) {
+ if ( !$.isFunction(
prefsSpecifications[type].builder ) ) {
$( '<a href="javascript:;" />' )
.addClass( 'formbuilder-button
formbuilder-editor-button-edit ui-icon ui-icon-gear' )
.attr( 'title', mw.msg(
'gadgets-formbuilder-editor-edit-field' ) )
@@ -1558,8 +1587,7 @@
this.$div.append( $tabs );
}
- BundleField.prototype = Object.create( EmptyField.prototype );
- BundleField.prototype.constructor = BundleField;
+ inherit( BundleField, EmptyField );
BundleField.prototype.getValidationSettings = function() {
var settings = {};
@@ -1627,8 +1655,7 @@
this._section = new SectionField( desc, sectionOptions );
this.$div.append( this._section.getElement() );
}
- CompositeField.prototype = Object.create( EmptyField.prototype );
- CompositeField.prototype.constructor = CompositeField;
+ inherit( CompositeField, EmptyField );
CompositeField.prototype.getDesc = function( useValuesAsDefaults ) {
var desc = clone( this.desc );
@@ -1646,7 +1673,7 @@
validFieldTypes.composite = CompositeField;
- /* A field for 'composite' fields */
+ /* A field for 'list' fields */
function ListField( desc, options ) {
EmptyField.call( this, desc, options );
@@ -1696,10 +1723,9 @@
} )
.appendTo( this.$div );
}
- ListField.prototype = Object.create( EmptyField.prototype );
- ListField.prototype.constructor = ListField;
+ inherit( ListField, EmptyField );
- ListField.prototype._createItem = function( animated, itemValue ) {
+ ListField.prototype._createItem = function( afterInit, itemValue ) {
var itemDesc = $.extend( {}, this.desc.field, {
"name": this.desc.name
} ),
@@ -1714,8 +1740,14 @@
itemOptions.values = pair( this.desc.name,
this.desc.field['default'] );
}
- var FieldConstructor = validFieldTypes[this.desc.field.type],
- itemField = new FieldConstructor( itemDesc, itemOptions
),
+ var FieldConstructor;
+ if ( $.isFunction( this.desc.field.type ) ) {
+ FieldConstructor = this.desc.field.type;
+ } else {
+ FieldConstructor =
validFieldTypes[this.desc.field.type];
+ }
+
+ var itemField = new FieldConstructor( itemDesc, itemOptions ),
$itemDiv = $( '<div/>' )
.addClass( 'formbuilder-list-item' )
.data( 'field', itemField ),
@@ -1753,10 +1785,12 @@
//Add an empty div with clear:both style
$itemDiv.append( $('<div style="clear:both"></div>' ) );
- if ( animated ) {
+ if ( afterInit ) {
$itemDiv.hide()
.appendTo( this._$divItems )
.slideDown();
+
+ addFieldRules( itemField );
} else {
$itemDiv.appendTo( this._$divItems );
}
@@ -1793,7 +1827,67 @@
validFieldTypes.list = ListField;
+ /* Fields for internal use only */
+
+ /*
+ * A text field that allow an arbitrary javascript scalar value, that
is:
+ * true, false, a number, a (double quoted) string or null.
+ *
+ * Used to create editor's select options.
+ *
+ **/
+ function ScalarField( desc, options ) {
+ LabelField.call( this, desc, options );
+
+ this.$div.addClass( 'formbuilder-field-scalar' );
+
+ this.$text = $( '<input/>' )
+ .attr( {
+ type: 'text',
+ id: this.options.idPrefix + this.desc.name,
+ name: this.options.idPrefix + this.desc.name
+ } )
+ .appendTo( this.$div );
+ var value = options.values && options.values[this.desc.name];
+ if ( typeof value != 'undefined' ) {
+ if ( !isScalar( value ) ) {
+ $.error( "value is invalid" );
+ }
+
+ this.$text.val( $.toJSON( value ) );
+ }
+ }
+ inherit( ScalarField, LabelField );
+
+ ScalarField.prototype.getDesc = function( useValuesAsDefault ) {
+ var desc = clone( LabelField.prototype.getDesc.call( this,
useValuesAsDefaults ) );
+ if ( useValuesAsDefaults === true ) {
+ //set 'default' to current value.
+ var values = this.getValues();
+ desc['default'] = values[this.desc.name];
+ }
+ };
+
+ ScalarField.prototype.getValues = function() {
+ var text = this.$text.val();
+
+ try {
+ var value = $.parseJSON( text );
+ return pair( this.desc.name, value );
+ } catch( e ) {
+ return pair( this.desc.name, undefined );
+ }
+ };
+
+ ScalarField.prototype.getValidationSettings = function() {
+ var settings = Field.prototype.getValidationSettings.call(
this ),
+ fieldId = this.options.idPrefix + this.desc.name;
+
+ settings.rules[fieldId] = pair( "scalar", true );
+ return settings;
+ };
+
/* Specifications of preferences descriptions syntax and field types */
//Describes 'name' and 'label' field members, common to all "simple"
fields
@@ -1892,6 +1986,33 @@
}
] )
},
+ "select": {
+ "simple": true,
+ "builder": simpleFields.concat( [
+ {
+ "name": "options",
+ "type": "list",
+ "default": [],
+ "field": {
+ "type": "composite",
+ "fields": [
+ {
+ "name": "name",
+ "type":
"string",
+ "label":
"Option name", //TODO: i18n
+ "default": ""
+ },
+ {
+ "name": "value",
+ "type":
ScalarField,
+ "label":
"Option value", //TODO: i18n
+ "default": ""
+ }
+ ]
+ }
+ }
+ ] )
+ },
"range": {
"simple": true,
"builder": simpleFields.concat( [
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs