├── .gitignore ├── jquery.fs.stepper-arrows.png ├── README.md ├── bower.json ├── stepper.jquery.json ├── package.json ├── src ├── jquery.fs.stepper.css └── jquery.fs.stepper.js ├── jquery.fs.stepper.css ├── demo └── index.html ├── jquery.fs.stepper.min.js ├── Gruntfile.js └── jquery.fs.stepper.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* -------------------------------------------------------------------------------- /jquery.fs.stepper-arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormstoneClassic/Stepper/HEAD/jquery.fs.stepper-arrows.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Development of this plugin has ended. Please upgrade to the new Formstone.


2 | 3 | Built with Grunt 4 | # Stepper 5 | 6 | A jQuery plugin for cross browser number inputs. Part of the Formstone Library. 7 | 8 | - [Demo](http://classic.formstone.it/components/Stepper/demo/index.html) 9 | - [Documentation](http://classic.formstone.it/stepper/) 10 | 11 | #### Bower Support 12 | `bower install Stepper` -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Stepper", 3 | "version": "3.0.8", 4 | "description": "A jQuery plugin for cross browser number inputs. Part of the Formstone Library.", 5 | "keywords": [ 6 | "javascript", 7 | "jquery", 8 | "number", 9 | "input", 10 | "ui", 11 | "formstone", 12 | "benplum" 13 | ], 14 | "license": "MIT", 15 | "homepage": "http://classic.formstone.it/stepper/", 16 | "main": [ 17 | "jquery.fs.stepper.js", 18 | "jquery.fs.stepper.css" 19 | ], 20 | "ignore": [ 21 | "*.jquery.json", 22 | "Gruntfile.js", 23 | "src/" 24 | ], 25 | "dependencies": { 26 | "jquery": ">= 1.7.0" 27 | }, 28 | "devDependencies": {}, 29 | "author": { 30 | "name": "Ben Plum", 31 | "email": "mr@benplum.com", 32 | "url": "http://www.benplum.com" 33 | } 34 | } -------------------------------------------------------------------------------- /stepper.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stepper", 3 | "version": "3.0.8", 4 | "title": "Stepper", 5 | "author": { 6 | "name": "Ben Plum", 7 | "email": "mr@benplum.com", 8 | "url": "http://www.benplum.com" 9 | }, 10 | "licenses": [ 11 | { 12 | "type": "MIT", 13 | "url": "http://opensource.org/licenses/MIT" 14 | } 15 | ], 16 | "dependencies": { 17 | "jquery": ">=1.7" 18 | }, 19 | "description": "A jQuery plugin for cross browser number inputs. Part of the Formstone Library.", 20 | "keywords": [ 21 | "number", 22 | "input", 23 | "form", 24 | "ui", 25 | "javascript", 26 | "jquery", 27 | "formstone" 28 | ], 29 | "docs": "http://classic.formstone.it/stepper/", 30 | "demo": "http://classic.formstone.it/stepper/", 31 | "download": "https://github.com/FormstoneClassic/Stepper", 32 | "bugs": "https://github.com/FormstoneClassic/Stepper/issues", 33 | "homepage": "http://classic.formstone.it/stepper/" 34 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Stepper", 3 | "id": "stepper", 4 | "codename": "jquery.fs.stepper", 5 | "version": "3.0.8", 6 | "description": "A jQuery plugin for cross browser number inputs. Part of the Formstone Library.", 7 | "keywords": [ 8 | "number", 9 | "input", 10 | "form", 11 | "ui", 12 | "javascript", 13 | "jquery", 14 | "formstone" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/FormstoneClassic/Stepper" 19 | }, 20 | "homepage": "http://classic.formstone.it/stepper/", 21 | "demo": "http://classic.formstone.it/components/Stepper/demo/index.html", 22 | "license": "MIT", 23 | "author": { 24 | "name": "Ben Plum", 25 | "email": "mr@benplum.com", 26 | "url": "http://www.benplum.com" 27 | }, 28 | "devDependencies": { 29 | "grunt-contrib-jshint": "~0.7.2", 30 | "grunt-contrib-concat": "~0.3.0", 31 | "grunt-contrib-uglify": "~0.2.7", 32 | "grunt-jquerymanifest": "~0.1.3", 33 | "grunt-npm2bower-sync": "~0.3.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/jquery.fs.stepper.css: -------------------------------------------------------------------------------- 1 | .stepper { border-radius: 3px; margin: 0 0 10px 0; overflow: hidden; position: relative; width: 300px; } 2 | .stepper .stepper-input { background: #F9F9F9; border: 1px solid #ccc; border-radius: 3px; color: #333; font-size: 13px; line-height: 1.2; margin: 0; overflow: hidden; padding: 9px 10px 10px; width: 100%; z-index: 49; 3 | -moz-appearance: textfield; 4 | } 5 | 6 | .stepper .stepper-input::-webkit-inner-spin-button, 7 | .stepper .stepper-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } 8 | 9 | .stepper .stepper-input:focus { background-color: #fff; } 10 | .stepper .stepper-arrow { background: #eee url(jquery.fs.stepper-arrows.png) no-repeat; border: 1px solid #ccc; cursor: pointer; display: block; height: 50%; position: absolute; right: 0; text-indent: -99999px; width: 20px; z-index: 50; } 11 | .stepper .stepper-arrow.up { background-position: center top; border-bottom: none; top: 0; } 12 | .stepper .stepper-arrow.down { background-position: center bottom; bottom: 0; } 13 | 14 | @media screen and (min-width: 740px) { 15 | .stepper:hover .stepper-input { background-color: #fff; } 16 | 17 | .stepper .stepper-step:hover { background-color: #F9F9F9; } 18 | 19 | .stepper.disabled .stepper-arrow { background: #fff; border-color: #eee; cursor: default; } 20 | } 21 | 22 | .stepper.disabled .stepper-input { background: #fff; border-color: #eee; color: #ccc; } 23 | .stepper.disabled .stepper-arrow { background: #fff; border-color: #eee; cursor: default; } -------------------------------------------------------------------------------- /jquery.fs.stepper.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Stepper v3.0.8 - 2015-04-04 3 | * A jQuery plugin for cross browser number inputs. Part of the Formstone Library. 4 | * http://classic.formstone.it/stepper/ 5 | * 6 | * Copyright 2015 Ben Plum; MIT Licensed 7 | */ 8 | 9 | .stepper { border-radius: 3px; margin: 0 0 10px 0; overflow: hidden; position: relative; width: 300px; } 10 | .stepper .stepper-input { background: #F9F9F9; border: 1px solid #ccc; border-radius: 3px; color: #333; font-size: 13px; line-height: 1.2; margin: 0; overflow: hidden; padding: 9px 10px 10px; width: 100%; z-index: 49; 11 | -moz-appearance: textfield; 12 | } 13 | 14 | .stepper .stepper-input::-webkit-inner-spin-button, 15 | .stepper .stepper-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } 16 | 17 | .stepper .stepper-input:focus { background-color: #fff; } 18 | .stepper .stepper-arrow { background: #eee url(jquery.fs.stepper-arrows.png) no-repeat; border: 1px solid #ccc; cursor: pointer; display: block; height: 50%; position: absolute; right: 0; text-indent: -99999px; width: 20px; z-index: 50; } 19 | .stepper .stepper-arrow.up { background-position: center top; border-bottom: none; top: 0; } 20 | .stepper .stepper-arrow.down { background-position: center bottom; bottom: 0; } 21 | 22 | @media screen and (min-width: 740px) { 23 | .stepper:hover .stepper-input { background-color: #fff; } 24 | 25 | .stepper .stepper-step:hover { background-color: #F9F9F9; } 26 | 27 | .stepper.disabled .stepper-arrow { background: #fff; border-color: #eee; cursor: default; } 28 | } 29 | 30 | .stepper.disabled .stepper-input { background: #fff; border-color: #eee; color: #ccc; } 31 | .stepper.disabled .stepper-arrow { background: #fff; border-color: #eee; cursor: default; } -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Stepper Demo 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 34 | 35 | 36 | 37 | 38 | 39 | 46 |
47 |
48 |
49 |

Stepper Demo

50 | 51 |
52 | View Documentation 53 |
54 | 55 | 56 | 57 | 58 |

Basic

59 |

The most basic method is simply applying the Stepper plugin:

60 | 61 |
<input type="number" />
62 |
$("input[type='number']").stepper();
63 | 64 |
Demo
65 |
66 | 67 |
68 | 69 |
70 | 71 | 72 |

Ranges

73 |

Stepper will automatically detect the minimum, maximum and increment values based on the min, max and step attributes:

74 | 75 |
<input type="number" min="2" max="20" step="2" />
76 | 77 |
Demo
78 |
79 | 80 |
81 | 82 | 83 | 84 |
85 |
86 | 91 | 92 | -------------------------------------------------------------------------------- /jquery.fs.stepper.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Stepper v3.0.8 - 2015-04-04 3 | * A jQuery plugin for cross browser number inputs. Part of the Formstone Library. 4 | * http://classic.formstone.it/stepper/ 5 | * 6 | * Copyright 2015 Ben Plum; MIT Licensed 7 | */ 8 | 9 | !function(a,b){"use strict";function c(b){b=a.extend({},m,b||{});for(var c=a(this),e=0,f=c.length;f>e;e++)d(c.eq(e),b);return c}function d(b,c){if(!b.hasClass("stepper-input")){c=a.extend({},c,b.data("stepper-options"));var d=parseFloat(b.attr("min")),g=parseFloat(b.attr("max")),h=parseFloat(b.attr("step"))||1;b.addClass("stepper-input").wrap('
').after(''+c.labels.up+''+c.labels.down+"");var i=b.parent(".stepper"),j=a.extend({$stepper:i,$input:b,$arrow:i.find(".stepper-arrow"),min:void 0===typeof d||isNaN(d)?!1:d,max:void 0===typeof g||isNaN(g)?!1:g,step:void 0===typeof h||isNaN(h)?1:h,timer:null},c);j.digits=k(j.step),b.is(":disabled")&&i.addClass("disabled"),i.on("keypress",".stepper-input",j,e),i.on("touchstart.stepper mousedown.stepper",".stepper-arrow",j,f).data("stepper",j)}}function e(a){var b=a.data;(38===a.keyCode||40===a.keyCode)&&(a.preventDefault(),h(b,38===a.keyCode?b.step:-b.step))}function f(b){b.preventDefault(),b.stopPropagation(),g(b);var c=b.data;if(!c.$input.is(":disabled")&&!c.$stepper.hasClass("disabled")){var d=a(b.target).hasClass("up")?c.step:-c.step;c.timer=i(c.timer,125,function(){h(c,d,!1)}),h(c,d),a("body").on("touchend.stepper mouseup.stepper",c,g)}}function g(b){b.preventDefault(),b.stopPropagation();var c=b.data;j(c.timer),a("body").off(".stepper")}function h(a,b){var c=parseFloat(a.$input.val()),d=b;void 0===typeof c||isNaN(c)?d=a.min!==!1?a.min:0:a.min!==!1&&ca.max&&(d-=a.step),d!==c&&(d=l(d,a.digits),a.$input.val(d).trigger("change"))}function i(a,b,c){return j(a),setInterval(c,b)}function j(a){a&&(clearInterval(a),a=null)}function k(a){var b=String(a);return b.indexOf(".")>-1?b.length-b.indexOf(".")-1:0}function l(a,b){var c=Math.pow(10,b);return Math.round(a*c)/c}var m={customClass:"",labels:{up:"Up",down:"Down"}},n={defaults:function(b){return m=a.extend(m,b||{}),"object"==typeof this?a(this):!0},destroy:function(){return a(this).each(function(b){var c=a(this).data("stepper");c&&(c.$stepper.off(".stepper").find(".stepper-arrow").remove(),c.$input.unwrap().removeClass("stepper-input"))})},disable:function(){return a(this).each(function(b){var c=a(this).data("stepper");c&&(c.$input.attr("disabled","disabled"),c.$stepper.addClass("disabled"))})},enable:function(){return a(this).each(function(b){var c=a(this).data("stepper");c&&(c.$input.attr("disabled",null),c.$stepper.removeClass("disabled"))})}};a.fn.stepper=function(a){return n[a]?n[a].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof a&&a?this:c.apply(this,arguments)},a.stepper=function(a){"defaults"===a&&n.defaults.apply(this,Array.prototype.slice.call(arguments,1))}}(jQuery,this); -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | meta: { 7 | banner: '/* \n' + 8 | ' * <%= pkg.name %> v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> \n' + 9 | ' * <%= pkg.description %> \n' + 10 | ' * <%= pkg.homepage %> \n' + 11 | ' * \n' + 12 | ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>; <%= pkg.license %> Licensed \n' + 13 | ' */ \n\n' 14 | }, 15 | // JS Hint 16 | jshint: { 17 | options: { 18 | globals: { 19 | 'jQuery': true, 20 | '$' : true 21 | }, 22 | browser: true, 23 | curly: true, 24 | eqeqeq: true, 25 | forin: true, 26 | freeze: true, 27 | immed: true, 28 | latedef: true, 29 | newcap: true, 30 | noarg: true, 31 | nonew: true, 32 | smarttabs: true, 33 | sub: true, 34 | undef: true, 35 | validthis: true 36 | }, 37 | files: ['src/<%= pkg.codename %>.js'] 38 | }, 39 | // Concat 40 | concat: { 41 | js: { 42 | options: { 43 | banner: '<%= meta.banner %>' 44 | }, 45 | files: { 46 | '<%= pkg.codename %>.js': [ 'src/<%= pkg.codename %>.js' ] 47 | } 48 | }, 49 | css: { 50 | options: { 51 | banner: '<%= meta.banner %>' 52 | }, 53 | files: { 54 | '<%= pkg.codename %>.css': [ 'src/<%= pkg.codename %>.css' ] 55 | } 56 | } 57 | }, 58 | // Uglify 59 | uglify: { 60 | options: { 61 | banner: '<%= meta.banner %>', 62 | report: 'min' 63 | }, 64 | target: { 65 | files: { 66 | '<%= pkg.codename %>.min.js': [ '<%= pkg.codename %>.js' ] 67 | } 68 | } 69 | }, 70 | // jQuery Manifest 71 | jquerymanifest: { 72 | options: { 73 | source: grunt.file.readJSON('package.json'), 74 | overrides: { 75 | name: '<%= pkg.id %>', 76 | keywords: '<%= pkg.keywords %>', 77 | homepage: '<%= pkg.homepage %>', 78 | docs: '<%= pkg.homepage %>', 79 | demo: '<%= pkg.homepage %>', 80 | download: '<%= pkg.repository.url %>', 81 | bugs: '<%= pkg.repository.url %>/issues', 82 | dependencies: { 83 | jquery: '>=1.7' 84 | } 85 | } 86 | } 87 | }, 88 | //Bower sync 89 | sync: { 90 | all: { 91 | options: { 92 | sync: [ 'name', 'version', 'description', 'author', 'license', 'homepage' ], 93 | overrides: { 94 | main: [ 95 | '<%= pkg.codename %>.js', 96 | '<%= pkg.codename %>.css' 97 | ], 98 | ignore: [ "*.jquery.json", "Gruntfile.js", "src/" ] 99 | } 100 | } 101 | } 102 | } 103 | }); 104 | 105 | // Readme 106 | grunt.registerTask('buildReadme', 'Build Formstone README.md file.', function () { 107 | var pkg = grunt.file.readJSON('package.json'), 108 | destination = "README.md", 109 | markdown = '

Development of this plugin has ended. Please upgrade to the new Formstone.


\n\n' + 110 | 'Built with Grunt \n' + 111 | '# ' + pkg.name + ' \n\n' + 112 | pkg.description + ' \n\n' + 113 | '- [Demo](' + pkg.demo + ') \n' + 114 | '- [Documentation](' + pkg.homepage + ') \n\n' + 115 | '#### Bower Support \n' + 116 | '`bower install ' + pkg.name + '`'; 117 | 118 | grunt.file.write(destination, markdown); 119 | grunt.log.writeln('File "' + destination + '" created.'); 120 | }); 121 | 122 | // Load tasks 123 | grunt.loadNpmTasks('grunt-contrib-jshint'); 124 | grunt.loadNpmTasks('grunt-contrib-concat'); 125 | grunt.loadNpmTasks('grunt-contrib-uglify'); 126 | grunt.loadNpmTasks('grunt-jquerymanifest'); 127 | grunt.loadNpmTasks('grunt-npm2bower-sync'); 128 | 129 | // Default task. 130 | grunt.registerTask('default', [ 'jshint', 'concat', 'uglify', 'jquerymanifest', 'sync', 'buildReadme' ]); 131 | 132 | }; -------------------------------------------------------------------------------- /src/jquery.fs.stepper.js: -------------------------------------------------------------------------------- 1 | ;(function ($, window) { 2 | "use strict"; 3 | 4 | /** 5 | * @options 6 | * @param customClass [string] <''> "Class applied to instance" 7 | * @param lables.up [string] <'Up'> "Up arrow label" 8 | * @param lables.down [string] <'Down'> "Down arrow label" 9 | */ 10 | var options = { 11 | customClass: "", 12 | labels: { 13 | up: "Up", 14 | down: "Down" 15 | } 16 | }; 17 | 18 | var pub = { 19 | 20 | /** 21 | * @method 22 | * @name defaults 23 | * @description Sets default plugin options 24 | * @param opts [object] <{}> "Options object" 25 | * @example $.stepper("defaults", opts); 26 | */ 27 | defaults: function(opts) { 28 | options = $.extend(options, opts || {}); 29 | return (typeof this === 'object') ? $(this) : true; 30 | }, 31 | 32 | /** 33 | * @method 34 | * @name destroy 35 | * @description Removes instance of plugin 36 | * @example $(".target").stepper("destroy"); 37 | */ 38 | destroy: function() { 39 | return $(this).each(function(i) { 40 | var data = $(this).data("stepper"); 41 | 42 | if (data) { 43 | // Unbind click events 44 | data.$stepper.off(".stepper") 45 | .find(".stepper-arrow") 46 | .remove(); 47 | 48 | // Restore DOM 49 | data.$input.unwrap() 50 | .removeClass("stepper-input"); 51 | } 52 | }); 53 | }, 54 | 55 | /** 56 | * @method 57 | * @name disable 58 | * @description Disables target instance 59 | * @example $(".target").stepper("disable"); 60 | */ 61 | disable: function() { 62 | return $(this).each(function(i) { 63 | var data = $(this).data("stepper"); 64 | 65 | if (data) { 66 | data.$input.attr("disabled", "disabled"); 67 | data.$stepper.addClass("disabled"); 68 | } 69 | }); 70 | }, 71 | 72 | /** 73 | * @method 74 | * @name enable 75 | * @description Enables target instance 76 | * @example $(".target").stepper("enable"); 77 | */ 78 | enable: function() { 79 | return $(this).each(function(i) { 80 | var data = $(this).data("stepper"); 81 | 82 | if (data) { 83 | data.$input.attr("disabled", null); 84 | data.$stepper.removeClass("disabled"); 85 | } 86 | }); 87 | } 88 | }; 89 | 90 | /** 91 | * @method private 92 | * @name _init 93 | * @description Initializes plugin 94 | * @param opts [object] "Initialization options" 95 | */ 96 | function _init(opts) { 97 | // Local options 98 | opts = $.extend({}, options, opts || {}); 99 | 100 | // Apply to each element 101 | var $items = $(this); 102 | for (var i = 0, count = $items.length; i < count; i++) { 103 | _build($items.eq(i), opts); 104 | } 105 | return $items; 106 | } 107 | 108 | /** 109 | * @method private 110 | * @name _build 111 | * @description Builds each instance 112 | * @param $select [jQuery object] "Target jQuery object" 113 | * @param opts [object] <{}> "Options object" 114 | */ 115 | function _build($input, opts) { 116 | if (!$input.hasClass("stepper-input")) { 117 | // EXTEND OPTIONS 118 | opts = $.extend({}, opts, $input.data("stepper-options")); 119 | 120 | // HTML5 attributes 121 | var min = parseFloat($input.attr("min")), 122 | max = parseFloat($input.attr("max")), 123 | step = parseFloat($input.attr("step")) || 1; 124 | 125 | // Modify DOM 126 | $input.addClass("stepper-input") 127 | .wrap('
') 128 | .after('' + opts.labels.up + '' + opts.labels.down + ''); 129 | 130 | // Store data 131 | var $stepper = $input.parent(".stepper"), 132 | data = $.extend({ 133 | $stepper: $stepper, 134 | $input: $input, 135 | $arrow: $stepper.find(".stepper-arrow"), 136 | min: (typeof min !== undefined && !isNaN(min)) ? min : false, 137 | max: (typeof max !== undefined && !isNaN(max)) ? max : false, 138 | step: (typeof step !== undefined && !isNaN(step)) ? step : 1, 139 | timer: null 140 | }, opts); 141 | 142 | data.digits = _digits(data.step); 143 | 144 | // Check disabled 145 | if ($input.is(":disabled")) { 146 | $stepper.addClass("disabled"); 147 | } 148 | 149 | // Bind keyboard events 150 | $stepper.on("keypress", ".stepper-input", data, _onKeyup); 151 | 152 | // Bind click events 153 | $stepper.on("touchstart.stepper mousedown.stepper", ".stepper-arrow", data, _onMouseDown) 154 | .data("stepper", data); 155 | } 156 | } 157 | 158 | /** 159 | * @method private 160 | * @name _onKeyup 161 | * @description Handles keypress event on inputs 162 | * @param e [object] "Event data" 163 | */ 164 | function _onKeyup(e) { 165 | var data = e.data; 166 | 167 | // If arrow keys 168 | if (e.keyCode === 38 || e.keyCode === 40) { 169 | e.preventDefault(); 170 | 171 | _step(data, (e.keyCode === 38) ? data.step : -data.step); 172 | } 173 | } 174 | 175 | /** 176 | * @method private 177 | * @name _onMouseDown 178 | * @description Handles mousedown event on instance arrows 179 | * @param e [object] "Event data" 180 | */ 181 | function _onMouseDown(e) { 182 | e.preventDefault(); 183 | e.stopPropagation(); 184 | 185 | // Make sure we reset the states 186 | _onMouseUp(e); 187 | 188 | var data = e.data; 189 | 190 | if (!data.$input.is(':disabled') && !data.$stepper.hasClass("disabled")) { 191 | var change = $(e.target).hasClass("up") ? data.step : -data.step; 192 | 193 | data.timer = _startTimer(data.timer, 125, function() { 194 | _step(data, change, false); 195 | }); 196 | _step(data, change); 197 | 198 | $("body").on("touchend.stepper mouseup.stepper", data, _onMouseUp); 199 | } 200 | } 201 | 202 | /** 203 | * @method private 204 | * @name _onMouseUp 205 | * @description Handles mouseup event on instance arrows 206 | * @param e [object] "Event data" 207 | */ 208 | function _onMouseUp(e) { 209 | e.preventDefault(); 210 | e.stopPropagation(); 211 | 212 | var data = e.data; 213 | 214 | _clearTimer(data.timer); 215 | 216 | $("body").off(".stepper"); 217 | } 218 | 219 | /** 220 | * @method private 221 | * @name _step 222 | * @description Steps through values 223 | * @param e [object] "Event data" 224 | * @param change [string] "Change value" 225 | */ 226 | function _step(data, change) { 227 | var originalValue = parseFloat(data.$input.val()), 228 | value = change; 229 | 230 | if (typeof originalValue === undefined || isNaN(originalValue)) { 231 | if (data.min !== false) { 232 | value = data.min; 233 | } else { 234 | value = 0; 235 | } 236 | } else if (data.min !== false && originalValue < data.min) { 237 | value = data.min; 238 | } else { 239 | value += originalValue; 240 | } 241 | 242 | var diff = (value - data.min) % data.step; 243 | if (diff !== 0) { 244 | value -= diff; 245 | } 246 | 247 | if (data.min !== false && value < data.min) { 248 | value = data.min; 249 | } 250 | if (data.max !== false && value > data.max) { 251 | value -= data.step; 252 | } 253 | 254 | if (value !== originalValue) { 255 | value = _round(value, data.digits); 256 | 257 | data.$input.val(value) 258 | .trigger("change"); 259 | } 260 | } 261 | 262 | /** 263 | * @method private 264 | * @name _startTimer 265 | * @description Starts an internal timer 266 | * @param timer [int] "Timer ID" 267 | * @param time [int] "Time until execution" 268 | * @param callback [int] "Function to execute" 269 | */ 270 | function _startTimer(timer, time, callback) { 271 | _clearTimer(timer); 272 | return setInterval(callback, time); 273 | } 274 | 275 | /** 276 | * @method private 277 | * @name _clearTimer 278 | * @description Clears an internal timer 279 | * @param timer [int] "Timer ID" 280 | */ 281 | function _clearTimer(timer) { 282 | if (timer) { 283 | clearInterval(timer); 284 | timer = null; 285 | } 286 | } 287 | 288 | /** 289 | * @method private 290 | * @name _digits 291 | * @description Analyzes and returns significant digit count 292 | * @param value [float] "Value to analyze" 293 | * @return [int] "Number of significant digits" 294 | */ 295 | function _digits(value) { 296 | var test = String(value); 297 | if (test.indexOf(".") > -1) { 298 | return test.length - test.indexOf(".") - 1; 299 | } else { 300 | return 0; 301 | } 302 | } 303 | 304 | /** 305 | * @method private 306 | * @name _round 307 | * @description Rounds a number to a sepcific significant digit count 308 | * @param value [float] "Value to round" 309 | * @param digits [float] "Digits to round to" 310 | * @return [number] "Rounded number" 311 | */ 312 | function _round(value, digits) { 313 | var exp = Math.pow(10, digits); 314 | return Math.round(value * exp) / exp; 315 | } 316 | 317 | $.fn.stepper = function(method) { 318 | if (pub[method]) { 319 | return pub[method].apply(this, Array.prototype.slice.call(arguments, 1)); 320 | } else if (typeof method === 'object' || !method) { 321 | return _init.apply(this, arguments); 322 | } 323 | return this; 324 | }; 325 | 326 | $.stepper = function(method) { 327 | if (method === "defaults") { 328 | pub.defaults.apply(this, Array.prototype.slice.call(arguments, 1)); 329 | } 330 | }; 331 | })(jQuery, this); 332 | -------------------------------------------------------------------------------- /jquery.fs.stepper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Stepper v3.0.8 - 2015-04-04 3 | * A jQuery plugin for cross browser number inputs. Part of the Formstone Library. 4 | * http://classic.formstone.it/stepper/ 5 | * 6 | * Copyright 2015 Ben Plum; MIT Licensed 7 | */ 8 | 9 | ;(function ($, window) { 10 | "use strict"; 11 | 12 | /** 13 | * @options 14 | * @param customClass [string] <''> "Class applied to instance" 15 | * @param lables.up [string] <'Up'> "Up arrow label" 16 | * @param lables.down [string] <'Down'> "Down arrow label" 17 | */ 18 | var options = { 19 | customClass: "", 20 | labels: { 21 | up: "Up", 22 | down: "Down" 23 | } 24 | }; 25 | 26 | var pub = { 27 | 28 | /** 29 | * @method 30 | * @name defaults 31 | * @description Sets default plugin options 32 | * @param opts [object] <{}> "Options object" 33 | * @example $.stepper("defaults", opts); 34 | */ 35 | defaults: function(opts) { 36 | options = $.extend(options, opts || {}); 37 | return (typeof this === 'object') ? $(this) : true; 38 | }, 39 | 40 | /** 41 | * @method 42 | * @name destroy 43 | * @description Removes instance of plugin 44 | * @example $(".target").stepper("destroy"); 45 | */ 46 | destroy: function() { 47 | return $(this).each(function(i) { 48 | var data = $(this).data("stepper"); 49 | 50 | if (data) { 51 | // Unbind click events 52 | data.$stepper.off(".stepper") 53 | .find(".stepper-arrow") 54 | .remove(); 55 | 56 | // Restore DOM 57 | data.$input.unwrap() 58 | .removeClass("stepper-input"); 59 | } 60 | }); 61 | }, 62 | 63 | /** 64 | * @method 65 | * @name disable 66 | * @description Disables target instance 67 | * @example $(".target").stepper("disable"); 68 | */ 69 | disable: function() { 70 | return $(this).each(function(i) { 71 | var data = $(this).data("stepper"); 72 | 73 | if (data) { 74 | data.$input.attr("disabled", "disabled"); 75 | data.$stepper.addClass("disabled"); 76 | } 77 | }); 78 | }, 79 | 80 | /** 81 | * @method 82 | * @name enable 83 | * @description Enables target instance 84 | * @example $(".target").stepper("enable"); 85 | */ 86 | enable: function() { 87 | return $(this).each(function(i) { 88 | var data = $(this).data("stepper"); 89 | 90 | if (data) { 91 | data.$input.attr("disabled", null); 92 | data.$stepper.removeClass("disabled"); 93 | } 94 | }); 95 | } 96 | }; 97 | 98 | /** 99 | * @method private 100 | * @name _init 101 | * @description Initializes plugin 102 | * @param opts [object] "Initialization options" 103 | */ 104 | function _init(opts) { 105 | // Local options 106 | opts = $.extend({}, options, opts || {}); 107 | 108 | // Apply to each element 109 | var $items = $(this); 110 | for (var i = 0, count = $items.length; i < count; i++) { 111 | _build($items.eq(i), opts); 112 | } 113 | return $items; 114 | } 115 | 116 | /** 117 | * @method private 118 | * @name _build 119 | * @description Builds each instance 120 | * @param $select [jQuery object] "Target jQuery object" 121 | * @param opts [object] <{}> "Options object" 122 | */ 123 | function _build($input, opts) { 124 | if (!$input.hasClass("stepper-input")) { 125 | // EXTEND OPTIONS 126 | opts = $.extend({}, opts, $input.data("stepper-options")); 127 | 128 | // HTML5 attributes 129 | var min = parseFloat($input.attr("min")), 130 | max = parseFloat($input.attr("max")), 131 | step = parseFloat($input.attr("step")) || 1; 132 | 133 | // Modify DOM 134 | $input.addClass("stepper-input") 135 | .wrap('
') 136 | .after('' + opts.labels.up + '' + opts.labels.down + ''); 137 | 138 | // Store data 139 | var $stepper = $input.parent(".stepper"), 140 | data = $.extend({ 141 | $stepper: $stepper, 142 | $input: $input, 143 | $arrow: $stepper.find(".stepper-arrow"), 144 | min: (typeof min !== undefined && !isNaN(min)) ? min : false, 145 | max: (typeof max !== undefined && !isNaN(max)) ? max : false, 146 | step: (typeof step !== undefined && !isNaN(step)) ? step : 1, 147 | timer: null 148 | }, opts); 149 | 150 | data.digits = _digits(data.step); 151 | 152 | // Check disabled 153 | if ($input.is(":disabled")) { 154 | $stepper.addClass("disabled"); 155 | } 156 | 157 | // Bind keyboard events 158 | $stepper.on("keypress", ".stepper-input", data, _onKeyup); 159 | 160 | // Bind click events 161 | $stepper.on("touchstart.stepper mousedown.stepper", ".stepper-arrow", data, _onMouseDown) 162 | .data("stepper", data); 163 | } 164 | } 165 | 166 | /** 167 | * @method private 168 | * @name _onKeyup 169 | * @description Handles keypress event on inputs 170 | * @param e [object] "Event data" 171 | */ 172 | function _onKeyup(e) { 173 | var data = e.data; 174 | 175 | // If arrow keys 176 | if (e.keyCode === 38 || e.keyCode === 40) { 177 | e.preventDefault(); 178 | 179 | _step(data, (e.keyCode === 38) ? data.step : -data.step); 180 | } 181 | } 182 | 183 | /** 184 | * @method private 185 | * @name _onMouseDown 186 | * @description Handles mousedown event on instance arrows 187 | * @param e [object] "Event data" 188 | */ 189 | function _onMouseDown(e) { 190 | e.preventDefault(); 191 | e.stopPropagation(); 192 | 193 | // Make sure we reset the states 194 | _onMouseUp(e); 195 | 196 | var data = e.data; 197 | 198 | if (!data.$input.is(':disabled') && !data.$stepper.hasClass("disabled")) { 199 | var change = $(e.target).hasClass("up") ? data.step : -data.step; 200 | 201 | data.timer = _startTimer(data.timer, 125, function() { 202 | _step(data, change, false); 203 | }); 204 | _step(data, change); 205 | 206 | $("body").on("touchend.stepper mouseup.stepper", data, _onMouseUp); 207 | } 208 | } 209 | 210 | /** 211 | * @method private 212 | * @name _onMouseUp 213 | * @description Handles mouseup event on instance arrows 214 | * @param e [object] "Event data" 215 | */ 216 | function _onMouseUp(e) { 217 | e.preventDefault(); 218 | e.stopPropagation(); 219 | 220 | var data = e.data; 221 | 222 | _clearTimer(data.timer); 223 | 224 | $("body").off(".stepper"); 225 | } 226 | 227 | /** 228 | * @method private 229 | * @name _step 230 | * @description Steps through values 231 | * @param e [object] "Event data" 232 | * @param change [string] "Change value" 233 | */ 234 | function _step(data, change) { 235 | var originalValue = parseFloat(data.$input.val()), 236 | value = change; 237 | 238 | if (typeof originalValue === undefined || isNaN(originalValue)) { 239 | if (data.min !== false) { 240 | value = data.min; 241 | } else { 242 | value = 0; 243 | } 244 | } else if (data.min !== false && originalValue < data.min) { 245 | value = data.min; 246 | } else { 247 | value += originalValue; 248 | } 249 | 250 | var diff = (value - data.min) % data.step; 251 | if (diff !== 0) { 252 | value -= diff; 253 | } 254 | 255 | if (data.min !== false && value < data.min) { 256 | value = data.min; 257 | } 258 | if (data.max !== false && value > data.max) { 259 | value -= data.step; 260 | } 261 | 262 | if (value !== originalValue) { 263 | value = _round(value, data.digits); 264 | 265 | data.$input.val(value) 266 | .trigger("change"); 267 | } 268 | } 269 | 270 | /** 271 | * @method private 272 | * @name _startTimer 273 | * @description Starts an internal timer 274 | * @param timer [int] "Timer ID" 275 | * @param time [int] "Time until execution" 276 | * @param callback [int] "Function to execute" 277 | */ 278 | function _startTimer(timer, time, callback) { 279 | _clearTimer(timer); 280 | return setInterval(callback, time); 281 | } 282 | 283 | /** 284 | * @method private 285 | * @name _clearTimer 286 | * @description Clears an internal timer 287 | * @param timer [int] "Timer ID" 288 | */ 289 | function _clearTimer(timer) { 290 | if (timer) { 291 | clearInterval(timer); 292 | timer = null; 293 | } 294 | } 295 | 296 | /** 297 | * @method private 298 | * @name _digits 299 | * @description Analyzes and returns significant digit count 300 | * @param value [float] "Value to analyze" 301 | * @return [int] "Number of significant digits" 302 | */ 303 | function _digits(value) { 304 | var test = String(value); 305 | if (test.indexOf(".") > -1) { 306 | return test.length - test.indexOf(".") - 1; 307 | } else { 308 | return 0; 309 | } 310 | } 311 | 312 | /** 313 | * @method private 314 | * @name _round 315 | * @description Rounds a number to a sepcific significant digit count 316 | * @param value [float] "Value to round" 317 | * @param digits [float] "Digits to round to" 318 | * @return [number] "Rounded number" 319 | */ 320 | function _round(value, digits) { 321 | var exp = Math.pow(10, digits); 322 | return Math.round(value * exp) / exp; 323 | } 324 | 325 | $.fn.stepper = function(method) { 326 | if (pub[method]) { 327 | return pub[method].apply(this, Array.prototype.slice.call(arguments, 1)); 328 | } else if (typeof method === 'object' || !method) { 329 | return _init.apply(this, arguments); 330 | } 331 | return this; 332 | }; 333 | 334 | $.stepper = function(method) { 335 | if (method === "defaults") { 336 | pub.defaults.apply(this, Array.prototype.slice.call(arguments, 1)); 337 | } 338 | }; 339 | })(jQuery, this); 340 | --------------------------------------------------------------------------------