Index: branches/salvatoreingala/Gadgets/Gadgets.php |
— | — | @@ -74,7 +74,7 @@ |
75 | 75 | |
76 | 76 | $wgResourceModules['jquery.formBuilder'] = array( |
77 | 77 | 'scripts' => array( 'jquery.formBuilder.js' ), |
78 | | - 'dependencies' => array( 'jquery', 'jquery.validate' ), |
| 78 | + 'dependencies' => array( 'jquery', 'jquery.ui.slider', 'jquery.validate' ), |
79 | 79 | 'messages' => array( |
80 | 80 | 'gadgets-formbuilder-required', 'gadgets-formbuilder-minlength', 'gadgets-formbuilder-maxlength', |
81 | 81 | 'gadgets-formbuilder-min', 'gadgets-formbuilder-max', 'gadgets-formbuilder-integer' |
Index: branches/salvatoreingala/Gadgets/Gadgets_tests.php |
— | — | @@ -313,6 +313,71 @@ |
314 | 314 | $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); |
315 | 315 | } |
316 | 316 | } |
| 317 | + |
| 318 | + //Tests for 'range' type preferences |
| 319 | + function testPrefsDescriptionsRange() { |
| 320 | + $correct = array( |
| 321 | + 'fields' => array( |
| 322 | + 'testRange' => array( |
| 323 | + 'type' => 'range', |
| 324 | + 'label' => 'some label', |
| 325 | + 'default' => 35, |
| 326 | + 'min' => 15, |
| 327 | + 'max' => 45 |
| 328 | + ) |
| 329 | + ) |
| 330 | + ); |
| 331 | + |
| 332 | + //Tests with correct default values |
| 333 | + $correct2 = $correct; |
| 334 | + foreach ( array( 15, 33, 45 ) as $def ) { |
| 335 | + $correct2['fields']['testRange']['default'] = $def; |
| 336 | + $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct2 ) ); |
| 337 | + } |
| 338 | + |
| 339 | + //Tests with wrong default values |
| 340 | + $wrong = $correct; |
| 341 | + foreach ( array( '', true, false, null, array(), '35', 14, 46, 30.2 ) as $def ) { |
| 342 | + $wrong['fields']['testRange']['default'] = $def; |
| 343 | + $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); |
| 344 | + } |
| 345 | + |
| 346 | + //Test with max not in the set min + k*step (step not given, so it's 1) |
| 347 | + $wrong = $correct; |
| 348 | + $wrong['fields']['testRange']['max'] = 45.5; |
| 349 | + $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); |
| 350 | + |
| 351 | + |
| 352 | + //Tests with floating point min, max and step |
| 353 | + $correct = array( |
| 354 | + 'fields' => array( |
| 355 | + 'testRange' => array( |
| 356 | + 'type' => 'range', |
| 357 | + 'label' => 'some label', |
| 358 | + 'default' => 0.20, |
| 359 | + 'min' => -2.8, |
| 360 | + 'max' => 4.2, |
| 361 | + 'step' => 0.25 |
| 362 | + ) |
| 363 | + ) |
| 364 | + ); |
| 365 | + |
| 366 | + $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct ) ); |
| 367 | + |
| 368 | + //Tests with correct default values |
| 369 | + $correct2 = $correct; |
| 370 | + foreach ( array( -2.8, -2.55, 0.20, 4.2 ) as $def ) { |
| 371 | + $correct2['fields']['testRange']['default'] = $def; |
| 372 | + $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct2 ) ); |
| 373 | + } |
| 374 | + |
| 375 | + //Tests with wrong default values |
| 376 | + $wrong = $correct; |
| 377 | + foreach ( array( '', true, false, null, array(), '0.20', -2.7, 0, 4.199999 ) as $def ) { |
| 378 | + $wrong['fields']['testRange']['default'] = $def; |
| 379 | + $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); |
| 380 | + } |
| 381 | + } |
317 | 382 | |
318 | 383 | //Data provider to be able to reuse this preference description for several tests. |
319 | 384 | function prefsDescProvider() { |
Index: branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php |
— | — | @@ -82,6 +82,28 @@ |
83 | 83 | 'isMandatory' => true, |
84 | 84 | 'checker' => 'is_array' |
85 | 85 | ) |
| 86 | + ), |
| 87 | + 'range' => array( |
| 88 | + 'default' => array( |
| 89 | + 'isMandatory' => true, |
| 90 | + 'checker' => 'GadgetPrefs::isFloatOrIntOrNull' |
| 91 | + ), |
| 92 | + 'label' => array( |
| 93 | + 'isMandatory' => true, |
| 94 | + 'checker' => 'is_string' |
| 95 | + ), |
| 96 | + 'min' => array( |
| 97 | + 'isMandatory' => true, |
| 98 | + 'checker' => 'GadgetPrefs::isFloatOrInt' |
| 99 | + ), |
| 100 | + 'max' => array( |
| 101 | + 'isMandatory' => true, |
| 102 | + 'checker' => 'GadgetPrefs::isFloatOrInt' |
| 103 | + ), |
| 104 | + 'step' => array( |
| 105 | + 'isMandatory' => false, |
| 106 | + 'checker' => 'GadgetPrefs::isFloatOrInt' |
| 107 | + ) |
86 | 108 | ) |
87 | 109 | ); |
88 | 110 | |
— | — | @@ -89,7 +111,8 @@ |
90 | 112 | private static $typeCheckers = array( |
91 | 113 | 'string' => 'GadgetPrefs::checkStringOptionDefinition', |
92 | 114 | 'number' => 'GadgetPrefs::checkNumberOptionDefinition', |
93 | | - 'select' => 'GadgetPrefs::checkSelectOptionDefinition' |
| 115 | + 'select' => 'GadgetPrefs::checkSelectOptionDefinition', |
| 116 | + 'range' => 'GadgetPrefs::checkRangeOptionDefinition', |
94 | 117 | ); |
95 | 118 | |
96 | 119 | //Further checks for 'string' options |
— | — | @@ -157,6 +180,28 @@ |
158 | 181 | return true; |
159 | 182 | } |
160 | 183 | |
| 184 | + private static function checkRangeOptionDefinition( $option ) { |
| 185 | + $step = isset( $option['step'] ) ? $option['step'] : 1; |
| 186 | + |
| 187 | + if ( $step <= 0 ) { |
| 188 | + return false; |
| 189 | + } |
| 190 | + |
| 191 | + $min = $option['min']; |
| 192 | + $max = $option['max']; |
| 193 | + |
| 194 | + //Checks if 'max' is a valid value |
| 195 | + //Valid values are min, min + step, min + 2*step, ... |
| 196 | + //Then ( $max - $min ) / $step must be close enough to an integer |
| 197 | + $eps = 1.0e-6; //tolerance |
| 198 | + $tmp = ( $max - $min ) / $step; |
| 199 | + if ( abs( $tmp - floor( $tmp ) ) > $eps ) { |
| 200 | + return false; |
| 201 | + } |
| 202 | + |
| 203 | + return true; |
| 204 | + } |
| 205 | + |
161 | 206 | //Checks if the given description of the preferences is valid |
162 | 207 | public static function isPrefsDescriptionValid( $prefsDescription ) { |
163 | 208 | if ( !is_array( $prefsDescription ) |
— | — | @@ -331,6 +376,33 @@ |
332 | 377 | case 'select': |
333 | 378 | $values = array_values( $prefDescription['options'] ); |
334 | 379 | return in_array( $pref, $values, true ); |
| 380 | + case 'range': |
| 381 | + if ( !is_float( $pref ) && !is_int( $pref ) ) { |
| 382 | + return false; |
| 383 | + } |
| 384 | + |
| 385 | + $min = $prefDescription['min']; |
| 386 | + $max = $prefDescription['max']; |
| 387 | + |
| 388 | + if ( $pref < $min || $pref > $max ) { |
| 389 | + return false; |
| 390 | + } |
| 391 | + |
| 392 | + $step = isset( $prefDescription['step'] ) ? $prefDescription['step'] : 1; |
| 393 | + |
| 394 | + if ( $step <= 0 ) { |
| 395 | + return false; |
| 396 | + } |
| 397 | + |
| 398 | + //Valid values are min, min + step, min + 2*step, ... |
| 399 | + //Then ( $pref - $min ) / $step must be close enough to an integer |
| 400 | + $eps = 1.0e-6; //tolerance |
| 401 | + $tmp = ( $pref - $min ) / $step; |
| 402 | + if ( abs( $tmp - floor( $tmp ) ) > $eps ) { |
| 403 | + return false; |
| 404 | + } |
| 405 | + |
| 406 | + return true; |
335 | 407 | default: |
336 | 408 | return false; //unexisting type |
337 | 409 | } |
Index: branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js |
— | — | @@ -287,13 +287,62 @@ |
288 | 288 | var i = parseInt( this.$select.val(), 10 ); |
289 | 289 | return this.values[i]; |
290 | 290 | }; |
| 291 | + |
| 292 | + |
| 293 | + RangeField.prototype = object( LabelField.prototype ); |
| 294 | + RangeField.prototype.constructor = RangeField; |
| 295 | + function RangeField( $form, name, desc ){ |
| 296 | + LabelField.call( this, $form, name, desc ); |
| 297 | + |
| 298 | + if ( typeof desc.value != 'number' ) { |
| 299 | + $.error( "desc.value is invalid" ); |
| 300 | + } |
| 301 | + |
| 302 | + if ( typeof desc.min != 'number' ) { |
| 303 | + $.error( "desc.min is invalid" ); |
| 304 | + } |
| 305 | + |
| 306 | + if ( typeof desc.max != 'number' ) { |
| 307 | + $.error( "desc.max is invalid" ); |
| 308 | + } |
| 309 | + |
| 310 | + if ( typeof desc.step != 'undefined' && typeof desc.step != 'number' ) { |
| 311 | + $.error( "desc.step is invalid" ); |
| 312 | + } |
| 313 | + |
| 314 | + if ( desc.value < desc.min || desc.value > desc.max ) { |
| 315 | + $.error( "desc.value is out of range" ); |
| 316 | + } |
| 317 | + |
| 318 | + var $slider = this.$slider = $( '<div/>' ) |
| 319 | + .attr( 'id', idPrefix + this.name ); |
| 320 | + |
| 321 | + var options = { |
| 322 | + min: desc.min, |
| 323 | + max: desc.max, |
| 324 | + value: desc.value |
| 325 | + }; |
| 326 | + |
| 327 | + if ( typeof desc.step != 'undefined' ) { |
| 328 | + options['step'] = desc.step; |
| 329 | + } |
| 330 | + |
| 331 | + $slider.slider( options ); |
| 332 | + |
| 333 | + this.$p.append( $slider ); |
| 334 | + } |
291 | 335 | |
| 336 | + RangeField.prototype.getValue = function() { |
| 337 | + return this.$slider.slider( 'value' ); |
| 338 | + }; |
| 339 | + |
292 | 340 | |
293 | 341 | var validFieldTypes = { |
294 | 342 | "boolean": BooleanField, |
295 | 343 | "string" : StringField, |
296 | 344 | "number" : NumberField, |
297 | | - "select" : SelectField |
| 345 | + "select" : SelectField, |
| 346 | + "range" : RangeField |
298 | 347 | }; |
299 | 348 | |
300 | 349 | /* Public methods */ |
Index: branches/salvatoreingala/Gadgets/ui/resources/ext.gadgets.preferences.css |
— | — | @@ -27,3 +27,7 @@ |
28 | 28 | width: 40%; |
29 | 29 | } |
30 | 30 | |
| 31 | +#mw-gadgets-prefsDialog .ui-slider { |
| 32 | + display: inline-block; |
| 33 | + width: 50%; |
| 34 | +} |