diff options
author | jzaefferer <joern.zaefferer@gmail.com> | 2010-12-22 17:36:41 +0100 |
---|---|---|
committer | jzaefferer <joern.zaefferer@gmail.com> | 2010-12-22 17:36:41 +0100 |
commit | 2c89e9587eb0c0cc92ceca690bcbc8b3f0d907ca (patch) | |
tree | e82178995cf30b1002f296fadbd69b50a414dc53 | |
parent | 727d00dec81671f26a15596acfb0df38920071a1 (diff) | |
parent | 26bc70180ac239f198334f9f040719a7ca2f5bb4 (diff) | |
download | jquery-ui-2c89e9587eb0c0cc92ceca690bcbc8b3f0d907ca.tar.gz jquery-ui-2c89e9587eb0c0cc92ceca690bcbc8b3f0d907ca.zip |
Merge remote branch 'origin/spinner'
-rw-r--r-- | demos/index.html | 2 | ||||
-rw-r--r-- | demos/spinner/currency.html | 61 | ||||
-rw-r--r-- | demos/spinner/decimal.html | 59 | ||||
-rw-r--r-- | demos/spinner/default.html | 65 | ||||
-rw-r--r-- | demos/spinner/index.html | 20 | ||||
-rw-r--r-- | demos/spinner/latlong.html | 60 | ||||
-rw-r--r-- | demos/spinner/overflow.html | 47 | ||||
-rw-r--r-- | demos/spinner/time.html | 72 | ||||
-rw-r--r-- | external/glob.de-DE.js | 55 | ||||
-rw-r--r-- | external/glob.ja-JP.js | 74 | ||||
-rw-r--r-- | external/glob.js | 1333 | ||||
-rw-r--r-- | external/jquery.mousewheel-3.0.4.js | 78 | ||||
-rw-r--r-- | tests/unit/spinner/spinner.html | 42 | ||||
-rw-r--r-- | tests/unit/spinner/spinner_core.js | 171 | ||||
-rw-r--r-- | tests/unit/spinner/spinner_defaults.js | 15 | ||||
-rw-r--r-- | tests/unit/spinner/spinner_events.js | 64 | ||||
-rw-r--r-- | tests/unit/spinner/spinner_methods.js | 155 | ||||
-rw-r--r-- | tests/unit/spinner/spinner_options.js | 154 | ||||
-rw-r--r-- | tests/visual/index.html | 1 | ||||
-rw-r--r-- | tests/visual/spinner/spinner.html | 43 | ||||
-rw-r--r-- | themes/base/jquery.ui.base.css | 1 | ||||
-rw-r--r-- | themes/base/jquery.ui.spinner.css | 16 | ||||
-rw-r--r-- | ui/jquery.ui.spinner.js | 360 |
23 files changed, 2948 insertions, 0 deletions
diff --git a/demos/index.html b/demos/index.html index 68418dc8a..07172f064 100644 --- a/demos/index.html +++ b/demos/index.html @@ -23,6 +23,7 @@ <script src="../ui/jquery.ui.selectable.js"></script> <script src="../ui/jquery.ui.slider.js"></script> <script src="../ui/jquery.ui.sortable.js"></script> + <script src="../ui/jquery.ui.spinner.js"></script> <script src="../ui/jquery.ui.tabs.js"></script> <script src="../ui/jquery.effects.core.js"></script> <script src="../ui/jquery.effects.blind.js"></script> @@ -269,6 +270,7 @@ <dd><a href="dialog/index.html">Dialog</a></dd> <dd><a href="progressbar/index.html">Progressbar</a></dd> <dd><a href="slider/index.html">Slider</a></dd> + <dd><a href="spinner/index.html">Spinner</a></dd> <dd><a href="tabs/index.html">Tabs</a></dd> <dt>Effects</dt> <dd><a href="animate/index.html">Color Animation</a></dd> diff --git a/demos/spinner/currency.html b/demos/spinner/currency.html new file mode 100644 index 000000000..6ecd43c1e --- /dev/null +++ b/demos/spinner/currency.html @@ -0,0 +1,61 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>jQuery UI Spinner - Default functionality</title>
+ <link type="text/css" href="../../themes/base/jquery.ui.all.css" rel="stylesheet" />
+ <script type="text/javascript" src="../../jquery-1.4.3.js"></script>
+ <script type="text/javascript" src="../../external/jquery.mousewheel-3.0.4.js"></script>
+ <script type="text/javascript" src="../../external/glob.js"></script>
+ <script type="text/javascript" src="../../external/glob.de-DE.js"></script>
+ <script type="text/javascript" src="../../external/glob.ja-JP.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.button.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.spinner.js"></script>
+ <link type="text/css" href="../demos.css" rel="stylesheet" />
+ <script type="text/javascript">
+ $(function() {
+ $("#currency").change(function() {
+ var current = $("#spinner").spinner("value");
+ Globalization.preferCulture($(this).val());
+ $("#spinner").spinner("value", current);
+ })
+
+ $("#spinner").spinner({
+ min: 5,
+ max: 2500,
+ step: 25,
+ start: 1000,
+ numberformat: "C"
+ });
+
+ });
+ </script>
+</head>
+<body>
+
+<div class="demo">
+
+<p>
+ <label for="currency">Currency to donate</label>
+ <select id="currency" name="currency">
+ <option value="en-US">US $</option>
+ <option value="de-DE">EUR €</option>
+ <option value="ja-JP">YEN ¥</option>
+ </select>
+</p>
+<p>
+ <label for="spinner">Amount to donate:</label>
+ <input id="spinner" name="spinner" value="5" />
+</p>
+</div>
+
+<div class="demo-description">
+<p>
+ Example of a donation form, with currency selection and amout spinner.
+</p>
+</div>
+
+</body>
+</html>
diff --git a/demos/spinner/decimal.html b/demos/spinner/decimal.html new file mode 100644 index 000000000..ae45da1e7 --- /dev/null +++ b/demos/spinner/decimal.html @@ -0,0 +1,59 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>jQuery UI Spinner - decimal</title>
+ <link type="text/css" href="../../themes/base/jquery.ui.all.css" rel="stylesheet" />
+ <script type="text/javascript" src="../../jquery-1.4.3.js"></script>
+ <script type="text/javascript" src="../../external/jquery.mousewheel-3.0.4.js"></script>
+ <script type="text/javascript" src="../../external/glob.js"></script>
+ <script type="text/javascript" src="../../external/glob.de-DE.js"></script>
+ <script type="text/javascript" src="../../external/glob.ja-JP.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.button.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.spinner.js"></script>
+ <link type="text/css" href="../demos.css" rel="stylesheet" />
+ <script type="text/javascript">
+ $(function() {
+ $("#spinner").spinner({
+ step: 0.01,
+ numberformat: "n"
+ });
+
+ $("#culture").change(function() {
+ var current = $("#spinner").spinner("value");
+ Globalization.preferCulture($(this).val());
+ $("#spinner").spinner("value", current);
+ })
+ });
+ </script>
+</head>
+<body>
+
+<div class="demo">
+<p>
+ <label for="spinner">Decimal spinner:</label>
+ <input id="spinner" name="spinner" value="5.06" />
+</p>
+<p>
+ <label for="culture">Select a culture to use for formatting:</label>
+ <select id="culture">
+ <option value="en-EN" selected="selected">English</option>
+ <option value="de-DE">German</option>
+ <option value="ja-JP">Japanese</option>
+ </select>
+</p>
+</div>
+
+<div class="demo-description">
+<p>
+ Example of a decimal spinner. Step is set to 0.01.
+ <br/>The code handling the culture change reads the current spinner value,
+ then changes the culture, then sets the value again, resulting in an updated
+ formatting, based on the new culture.
+</p>
+</div>
+
+</body>
+</html>
diff --git a/demos/spinner/default.html b/demos/spinner/default.html new file mode 100644 index 000000000..b0f62308d --- /dev/null +++ b/demos/spinner/default.html @@ -0,0 +1,65 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>jQuery UI Spinner - Default functionality</title>
+ <link type="text/css" href="../../themes/base/jquery.ui.all.css" rel="stylesheet" />
+ <script type="text/javascript" src="../../jquery-1.4.3.js"></script>
+ <script type="text/javascript" src="../../external/jquery.mousewheel-3.0.4.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.button.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.spinner.js"></script>
+ <link type="text/css" href="../demos.css" rel="stylesheet" />
+ <script type="text/javascript">
+ $(function() {
+ $("#spinner").spinner();
+
+ $("#disable").toggle(function() {
+ $("#spinner").spinner("disable");
+ }, function() {
+ $("#spinner").spinner("enable");
+ });
+ $("#destroy").toggle(function() {
+ $("#spinner").spinner("destroy");
+ }, function() {
+ $("#spinner").spinner();
+ });
+ $("#getvalue").click(function() {
+ alert($("#spinner").spinner("value"));
+ });
+ $("#setvalue").click(function() {
+ $("#spinner").spinner("value", 5);
+ });
+
+ $("button").button();
+ });
+ </script>
+</head>
+<body>
+
+<div class="demo">
+
+<p><label for="spinner">Select a value:</label>
+<input id="spinner" name="value" /></p>
+
+<p>
+<button id="disable">Toggle disable/enable</button>
+<button id="destroy">Toggle widget</button>
+</p>
+
+<p>
+<button id="getvalue">Get value</button>
+<button id="setvalue">Set value to 5</button>
+</p>
+
+</div><!-- End demo -->
+
+<div class="demo-description">
+<p>
+Default spinner.
+</p>
+</div><!-- End demo-description -->
+
+</body>
+</html>
diff --git a/demos/spinner/index.html b/demos/spinner/index.html new file mode 100644 index 000000000..a49fa1368 --- /dev/null +++ b/demos/spinner/index.html @@ -0,0 +1,20 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <title>jQuery UI Spinner Demos</title>
+ <link type="text/css" href="../demos.css" rel="stylesheet" />
+</head>
+<body>
+ <div class="demos-nav">
+ <h4>Examples</h4>
+ <ul>
+ <li class="demo-config-on"><a href="default.html">Default functionality</a></li>
+ <li><a href="decimal.html">Decimal</a></li>
+ <li><a href="currency.html">Currency</a></li>
+ <li><a href="latlong.html">Map</a></li>
+ <li><a href="time.html">Time</a></li>
+ <li><a href="overflow.html">Overflow</a></li>
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/demos/spinner/latlong.html b/demos/spinner/latlong.html new file mode 100644 index 000000000..7c0c9b21a --- /dev/null +++ b/demos/spinner/latlong.html @@ -0,0 +1,60 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>jQuery UI Spinner - Map</title>
+ <link type="text/css" href="../../themes/base/jquery.ui.all.css" rel="stylesheet" />
+ <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
+ <script type="text/javascript" src="../../jquery-1.4.3.js"></script>
+ <script type="text/javascript" src="../../external/jquery.mousewheel-3.0.4.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.button.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.spinner.js"></script>
+ <link type="text/css" href="../demos.css" rel="stylesheet" />
+ <script type="text/javascript">
+ $(function() {
+ function latlong() {
+ return new google.maps.LatLng($("#lat").val(),$("#lng").val());
+ }
+ function position() {
+ map.setCenter(latlong());
+ }
+ $("#lat, #lng").spinner({
+ precision: 6,
+ change: position
+ });
+
+ var map = new google.maps.Map($("#map")[0], {
+ zoom: 8,
+ center: latlong(),
+ mapTypeId: google.maps.MapTypeId.ROADMAP
+ });
+ });
+ </script>
+ <style>
+ #map { width:500px; height:500px; }
+ </style>
+</head>
+<body>
+
+<div class="demo">
+
+<label for="lat">Latitude</label>
+<input id="lat" name="lat" value="44.797916" />
+<br/>
+<label for="lng">Longitude</label>
+<input id="lng" name="lng" value="-93.278046" />
+
+<div id="map"></div>
+
+</div><!-- End demo -->
+
+<div class="demo-description">
+<p>
+Google Maps integration, using spinners to change latidude and longitude.
+</p>
+</div><!-- End demo-description -->
+
+</body>
+</html>
diff --git a/demos/spinner/overflow.html b/demos/spinner/overflow.html new file mode 100644 index 000000000..716489155 --- /dev/null +++ b/demos/spinner/overflow.html @@ -0,0 +1,47 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>jQuery UI Spinner - Default functionality</title>
+ <link type="text/css" href="../../themes/base/jquery.ui.all.css" rel="stylesheet" />
+ <script type="text/javascript" src="../../jquery-1.4.3.js"></script>
+ <script type="text/javascript" src="../../external/jquery.mousewheel-3.0.4.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.button.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.spinner.js"></script>
+ <link type="text/css" href="../demos.css" rel="stylesheet" />
+ <script type="text/javascript">
+ $(function() {
+ $("#spinner").spinner({
+ spin: function(event, ui) {
+ if (ui.value > 10) {
+ $(this).spinner("value", -10);
+ return false;
+ } else if (ui.value < -10) {
+ $(this).spinner("value", 10);
+ return false;
+ }
+ }
+ });
+ });
+ </script>
+</head>
+<body>
+
+<div class="demo">
+ <p>
+ <label for="spinner">Select a value:</label>
+ <input id="spinner" name="value" />
+ </p>
+</div>
+
+<div class="demo-description">
+ <p>
+ Overflowing spinner restricted to a range of -10 to 10.
+ For anything above 10, it'll overflow to -10, and the other way round.
+ </p>
+</div>
+
+</body>
+</html>
diff --git a/demos/spinner/time.html b/demos/spinner/time.html new file mode 100644 index 000000000..72027eb1e --- /dev/null +++ b/demos/spinner/time.html @@ -0,0 +1,72 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>jQuery UI Spinner - decimal</title>
+ <link type="text/css" href="../../themes/base/jquery.ui.all.css" rel="stylesheet" />
+ <script type="text/javascript" src="../../jquery-1.4.3.js"></script>
+ <script type="text/javascript" src="../../external/jquery.mousewheel-3.0.4.js"></script>
+ <script type="text/javascript" src="../../external/glob.js"></script>
+ <script type="text/javascript" src="../../external/glob.de-DE.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.button.js"></script>
+ <script type="text/javascript" src="../../ui/jquery.ui.spinner.js"></script>
+ <link type="text/css" href="../demos.css" rel="stylesheet" />
+ <script type="text/javascript">
+ $.widget("ui.timespinner", $.ui.spinner, {
+ options: {
+ // seconds
+ step: 60 * 1000,
+ // hours
+ page: 60
+ },
+
+ _parse: function(value) {
+ if (typeof value == 'string') {
+ return +Globalization.parseDate(value)
+ }
+ return value;
+ },
+ _format: function() {
+ this.element.val( Globalization.format(new Date(this.options.value), "t") );
+ }
+ })
+ $(function() {
+ $("#spinner").timespinner();
+
+
+ $("#culture").change(function() {
+ var current = $("#spinner").timespinner("value");
+ Globalization.preferCulture($(this).val());
+ $("#spinner").timespinner("value", current);
+ });
+ });
+ </script>
+</head>
+<body>
+
+<div class="demo">
+<p>
+ <label for="spinner">Decimal spinner:</label>
+ <input id="spinner" name="spinner" value="08:30 PM" />
+</p>
+<p>
+ <label for="culture">Select a culture to use for formatting:</label>
+ <select id="culture">
+ <option value="en-EN" selected="selected">English</option>
+ <option value="de-DE">German</option>
+ </select>
+</p>
+</div>
+
+<div class="demo-description">
+<p>
+ A custom widget extending spinner. Use the Globalization plugin to parse and output
+ a timestamp, with custom step and page options. Cursor up/down spins minutes, page up/down
+ spins hours.
+</p>
+</div>
+
+</body>
+</html>
diff --git a/external/glob.de-DE.js b/external/glob.de-DE.js new file mode 100644 index 000000000..d68b84cc8 --- /dev/null +++ b/external/glob.de-DE.js @@ -0,0 +1,55 @@ +(function($) { + var cultures = $.cultures, + en = cultures.en, + standard = en.calendars.standard, + culture = cultures["de-DE"] = $.extend(true, {}, en, { + name: "de-DE", + englishName: "German (Germany)", + nativeName: "Deutsch (Deutschland)", + language: "de", + numberFormat: { + ',': ".", + '.': ",", + percent: { + pattern: ["-n%","n%"], + ',': ".", + '.': "," + }, + currency: { + pattern: ["-n $","n $"], + ',': ".", + '.': ",", + symbol: "€" + } + }, + calendars: { + standard: $.extend(true, {}, standard, { + '/': ".", + firstDay: 1, + days: { + names: ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"], + namesAbbr: ["So","Mo","Di","Mi","Do","Fr","Sa"], + namesShort: ["So","Mo","Di","Mi","Do","Fr","Sa"] + }, + months: { + names: ["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember",""], + namesAbbr: ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez",""] + }, + AM: null, + PM: null, + eras: [{"name":"n. Chr.","start":null,"offset":0}], + patterns: { + d: "dd.MM.yyyy", + D: "dddd, d. MMMM yyyy", + t: "HH:mm", + T: "HH:mm:ss", + f: "dddd, d. MMMM yyyy HH:mm", + F: "dddd, d. MMMM yyyy HH:mm:ss", + M: "dd MMMM", + Y: "MMMM yyyy" + } + }) + } + }, cultures["de-DE"]); + culture.calendar = culture.calendars.standard; +})(Globalization);
\ No newline at end of file diff --git a/external/glob.ja-JP.js b/external/glob.ja-JP.js new file mode 100644 index 000000000..454d478cb --- /dev/null +++ b/external/glob.ja-JP.js @@ -0,0 +1,74 @@ +(function($) { + var cultures = $.cultures, + en = cultures.en, + standard = en.calendars.standard, + culture = cultures["ja-JP"] = $.extend(true, {}, en, { + name: "ja-JP", + englishName: "Japanese (Japan)", + nativeName: "日本語 (日本)", + language: "ja", + numberFormat: { + percent: { + pattern: ["-n%","n%"] + }, + currency: { + pattern: ["-$n","$n"], + decimals: 0, + symbol: "¥" + } + }, + calendars: { + standard: $.extend(true, {}, standard, { + days: { + names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], + namesAbbr: ["日","月","火","水","木","金","土"], + namesShort: ["日","月","火","水","木","金","土"] + }, + months: { + names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], + namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] + }, + AM: ["午前","午前","午前"], + PM: ["午後","午後","午後"], + eras: [{"name":"西暦","start":null,"offset":0}], + patterns: { + d: "yyyy/MM/dd", + D: "yyyy'年'M'月'd'日'", + t: "H:mm", + T: "H:mm:ss", + f: "yyyy'年'M'月'd'日' H:mm", + F: "yyyy'年'M'月'd'日' H:mm:ss", + M: "M'月'd'日'", + Y: "yyyy'年'M'月'" + } + }), + Japanese: $.extend(true, {}, standard, { + name: "Japanese", + days: { + names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], + namesAbbr: ["日","月","火","水","木","金","土"], + namesShort: ["日","月","火","水","木","金","土"] + }, + months: { + names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], + namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] + }, + AM: ["午前","午前","午前"], + PM: ["午後","午後","午後"], + eras: [{"name":"平成","start":null,"offset":1867},{"name":"昭和","start":-1812153600000,"offset":1911},{"name":"大正","start":-1357603200000,"offset":1925},{"name":"明治","start":60022080000,"offset":1988}], + twoDigitYearMax: 99, + patterns: { + d: "gg y/M/d", + D: "gg y'年'M'月'd'日'", + t: "H:mm", + T: "H:mm:ss", + f: "gg y'年'M'月'd'日' H:mm", + F: "gg y'年'M'月'd'日' H:mm:ss", + M: "M'月'd'日'", + Y: "gg y'年'M'月'" + } + }) + } + }, cultures["ja-JP"]); + culture.calendar = culture.calendars.standard; +})(Globalization);
\ No newline at end of file diff --git a/external/glob.js b/external/glob.js new file mode 100644 index 000000000..8075ee2bd --- /dev/null +++ b/external/glob.js @@ -0,0 +1,1333 @@ +/* + * Globalization + * http://github.com/nje/jquery-glob + */ +(function() { + +var Globalization = {}, + localized = { en: {} }; +localized["default"] = localized.en; + +Globalization.extend = function( deep ) { + var target = arguments[ 1 ] || {}; + for ( var i = 2, l = arguments.length; i < l; i++ ) { + var source = arguments[ i ]; + if ( source ) { + for ( var field in source ) { + var sourceVal = source[ field ]; + if ( typeof sourceVal !== "undefined" ) { + if ( deep && (isObject( sourceVal ) || isArray( sourceVal )) ) { + var targetVal = target[ field ]; + // extend onto the existing value, or create a new one + targetVal = targetVal && (isObject( targetVal ) || isArray( targetVal )) + ? targetVal + : (isArray( sourceVal ) ? [] : {}); + target[ field ] = this.extend( true, targetVal, sourceVal ); + } + else { + target[ field ] = sourceVal; + } + } + } + } + } + return target; +} + +Globalization.findClosestCulture = function(name) { + var match; + if ( !name ) { + return this.culture || this.cultures["default"]; + } + if ( isString( name ) ) { + name = name.split( ',' ); + } + if ( isArray( name ) ) { + var lang, + cultures = this.cultures, + list = name, + i, l = list.length, + prioritized = []; + for ( i = 0; i < l; i++ ) { + name = trim( list[ i ] ); + var pri, parts = name.split( ';' ); + lang = trim( parts[ 0 ] ); + if ( parts.length === 1 ) { + pri = 1; + } + else { + name = trim( parts[ 1 ] ); + if ( name.indexOf("q=") === 0 ) { + name = name.substr( 2 ); + pri = parseFloat( name, 10 ); + pri = isNaN( pri ) ? 0 : pri; + } + else { + pri = 1; + } + } + prioritized.push( { lang: lang, pri: pri } ); + } + prioritized.sort(function(a, b) { + return a.pri < b.pri ? 1 : -1; + }); + for ( i = 0; i < l; i++ ) { + lang = prioritized[ i ].lang; + match = cultures[ lang ]; + // exact match? + if ( match ) { + return match; + } + } + for ( i = 0; i < l; i++ ) { + lang = prioritized[ i ].lang; + // for each entry try its neutral language + do { + var index = lang.lastIndexOf( "-" ); + if ( index === -1 ) { + break; + } + // strip off the last part. e.g. en-US => en + lang = lang.substr( 0, index ); + match = cultures[ lang ]; + if ( match ) { + return match; + } + } + while ( 1 ); + } + } + else if ( typeof name === 'object' ) { + return name; + } + return match || null; +} +Globalization.preferCulture = function(name) { + this.culture = this.findClosestCulture( name ) || this.cultures["default"]; +} +Globalization.localize = function(key, culture, value) { + if (typeof culture === 'string') { + culture = culture || "default"; + culture = this.cultures[ culture ] || { name: culture }; + } + var local = localized[ culture.name ]; + if ( arguments.length === 3 ) { + if ( !local) { + local = localized[ culture.name ] = {}; + } + local[ key ] = value; + } + else { + if ( local ) { + value = local[ key ]; + } + if ( typeof value === 'undefined' ) { + var language = localized[ culture.language ]; + if ( language ) { + value = language[ key ]; + } + if ( typeof value === 'undefined' ) { + value = localized["default"][ key ]; + } + } + } + return typeof value === "undefined" ? null : value; +} +Globalization.format = function(value, format, culture) { + culture = this.findClosestCulture( culture ); + if ( typeof value === "number" ) { + value = formatNumber( value, format, culture ); + } + else if ( value instanceof Date ) { + value = formatDate( value, format, culture ); + } + return value; +} +Globalization.parseInt = function(value, radix, culture) { + return Math.floor( this.parseFloat( value, radix, culture ) ); +} +Globalization.parseFloat = function(value, radix, culture) { + culture = this.findClosestCulture( culture ); + var ret = NaN, + nf = culture.numberFormat; + + // trim leading and trailing whitespace + value = trim( value ); + + // allow infinity or hexidecimal + if (regexInfinity.test(value)) { + ret = parseFloat(value, radix); + } + else if (!radix && regexHex.test(value)) { + ret = parseInt(value, 16); + } + else { + var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ), + sign = signInfo[0], + num = signInfo[1]; + // determine sign and number + if ( sign === "" && nf.pattern[0] !== "-n" ) { + signInfo = parseNegativePattern( value, nf, "-n" ); + sign = signInfo[0]; + num = signInfo[1]; + } + sign = sign || "+"; + // determine exponent and number + var exponent, + intAndFraction, + exponentPos = num.indexOf( 'e' ); + if ( exponentPos < 0 ) exponentPos = num.indexOf( 'E' ); + if ( exponentPos < 0 ) { + intAndFraction = num; + exponent = null; + } + else { + intAndFraction = num.substr( 0, exponentPos ); + exponent = num.substr( exponentPos + 1 ); + } + // determine decimal position + var integer, + fraction, + decSep = nf['.'], + decimalPos = intAndFraction.indexOf( decSep ); + if ( decimalPos < 0 ) { + integer = intAndFraction; + fraction = null; + } + else { + integer = intAndFraction.substr( 0, decimalPos ); + fraction = intAndFraction.substr( decimalPos + decSep.length ); + } + // handle groups (e.g. 1,000,000) + var groupSep = nf[","]; + integer = integer.split(groupSep).join(''); + var altGroupSep = groupSep.replace(/\u00A0/g, " "); + if ( groupSep !== altGroupSep ) { + integer = integer.split(altGroupSep).join(''); + } + // build a natively parsable number string + var p = sign + integer; + if ( fraction !== null ) { + p += '.' + fraction; + } + if ( exponent !== null ) { + // exponent itself may have a number patternd + var expSignInfo = parseNegativePattern( exponent, nf, "-n" ); + p += 'e' + (expSignInfo[0] || "+") + expSignInfo[1]; + } + if ( regexParseFloat.test( p ) ) { + ret = parseFloat( p ); + } + } + return ret; +} +Globalization.parseDate = function(value, formats, culture) { + culture = this.findClosestCulture( culture ); + + var date, prop, patterns; + if ( formats ) { + if ( typeof formats === "string" ) { + formats = [ formats ]; + } + if ( formats.length ) { + for ( var i = 0, l = formats.length; i < l; i++ ) { + var format = formats[ i ]; + if ( format ) { + date = parseExact( value, format, culture ); + if ( date ) { + break; + } + } + } + } + } + else { + patterns = culture.calendar.patterns; + for ( prop in patterns ) { + date = parseExact( value, patterns[prop], culture ); + if ( date ) { + break; + } + } + } + return date || null; +} + +// 1. When defining a culture, all fields are required except the ones stated as optional. +// 2. You can use Globalization.extend to copy an existing culture and provide only the differing values, +// a good practice since most cultures do not differ too much from the 'default' culture. +// DO use the 'default' culture if you do this, as it is the only one that definitely +// exists. +// 3. Other plugins may add to the culture information provided by extending it. However, +// that plugin may extend it prior to the culture being defined, or after. Therefore, +// do not overwrite values that already exist when defining the baseline for a culture, +// by extending your culture object with the existing one. +// 4. Each culture should have a ".calendars" object with at least one calendar named "standard" +// which serves as the default calendar in use by that culture. +// 5. Each culture should have a ".calendar" object which is the current calendar being used, +// it may be dynamically changed at any time to one of the calendars in ".calendars". + +// To define a culture, use the following pattern, which handles defining the culture based +// on the 'default culture, extending it with the existing culture if it exists, and defining +// it if it does not exist. +// Globalization.cultures.foo = Globalization.extend(true, Globalization.extend(true, {}, Globalization.cultures['default'], fooCulture), Globalization.cultures.foo) + +var cultures = Globalization.cultures = Globalization.cultures || {}; +var en = cultures["default"] = cultures.en = Globalization.extend(true, { + // A unique name for the culture in the form <language code>-<country/region code> + name: "en", + // the name of the culture in the english language + englishName: "English", + // the name of the culture in its own language + nativeName: "English", + // whether the culture uses right-to-left text + isRTL: false, + // 'language' is used for so-called "specific" cultures. + // For example, the culture "es-CL" means "Spanish, in Chili". + // It represents the Spanish-speaking culture as it is in Chili, + // which might have different formatting rules or even translations + // than Spanish in Spain. A "neutral" culture is one that is not + // specific to a region. For example, the culture "es" is the generic + // Spanish culture, which may be a more generalized version of the language + // that may or may not be what a specific culture expects. + // For a specific culture like "es-CL", the 'language' field refers to the + // neutral, generic culture information for the language it is using. + // This is not always a simple matter of the string before the dash. + // For example, the "zh-Hans" culture is netural (Simplified Chinese). + // And the 'zh-SG' culture is Simplified Chinese in Singapore, whose lanugage + // field is "zh-CHS", not "zh". + // This field should be used to navigate from a specific culture to it's + // more general, neutral culture. If a culture is already as general as it + // can get, the language may refer to itself. + language: "en", + // numberFormat defines general number formatting rules, like the digits in + // each grouping, the group separator, and how negative numbers are displayed. + numberFormat: { + // [negativePattern] + // Note, numberFormat.pattern has no 'positivePattern' unlike percent and currency, + // but is still defined as an array for consistency with them. + // negativePattern: one of "(n)|-n|- n|n-|n -" + pattern: ["-n"], + // number of decimal places normally shown + decimals: 2, + // string that separates number groups, as in 1,000,000 + ',': ",", + // string that separates a number from the fractional portion, as in 1.99 + '.': ".", + // array of numbers indicating the size of each number group. + // TODO: more detailed description and example + groupSizes: [3], + // symbol used for positive numbers + '+': "+", + // symbol used for negative numbers + '-': "-", + percent: { + // [negativePattern, positivePattern] + // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %" + // positivePattern: one of "n %|n%|%n|% n" + pattern: ["-n %","n %"], + // number of decimal places normally shown + decimals: 2, + // array of numbers indicating the size of each number group. + // TODO: more detailed description and example + groupSizes: [3], + // string that separates number groups, as in 1,000,000 + ',': ",", + // string that separates a number from the fractional portion, as in 1.99 + '.': ".", + // symbol used to represent a percentage + symbol: "%" + }, + currency: { + // [negativePattern, positivePattern] + // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)" + // positivePattern: one of "$n|n$|$ n|n $" + pattern: ["($n)","$n"], + // number of decimal places normally shown + decimals: 2, + // array of numbers indicating the size of each number group. + // TODO: more detailed description and example + groupSizes: [3], + // string that separates number groups, as in 1,000,000 + ',': ",", + // string that separates a number from the fractional portion, as in 1.99 + '.': ".", + // symbol used to represent currency + symbol: "$" + } + }, + // calendars defines all the possible calendars used by this culture. + // There should be at least one defined with name 'standard', and is the default + // calendar used by the culture. + // A calendar contains information about how dates are formatted, information about + // the calendar's eras, a standard set of the date formats, + // translations for day and month names, and if the calendar is not based on the Gregorian + // calendar, conversion functions to and from the Gregorian calendar. + calendars: { + standard: { + // name that identifies the type of calendar this is + name: "Gregorian_USEnglish", + // separator of parts of a date (e.g. '/' in 11/05/1955) + '/': "/", + // separator of parts of a time (e.g. ':' in 05:44 PM) + ':': ":", + // the first day of the week (0 = Sunday, 1 = Monday, etc) + firstDay: 0, + days: { + // full day names + names: ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], + // abbreviated day names + namesAbbr: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"], + // shortest day names + namesShort: ["Su","Mo","Tu","We","Th","Fr","Sa"] + }, + months: { + // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar) + names: ["January","February","March","April","May","June","July","August","September","October","November","December",""], + // abbreviated month names + namesAbbr: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""] + }, + // AM and PM designators in one of these forms: + // The usual view, and the upper and lower case versions + // [standard,lowercase,uppercase] + // The culture does not use AM or PM (likely all standard date formats use 24 hour time) + // null + AM: ["AM", "am", "AM"], + PM: ["PM", "pm", "PM"], + eras: [ + // eras in reverse chronological order. + // name: the name of the era in this culture (e.g. A.D., C.E.) + // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era. + // offset: offset in years from gregorian calendar + { "name": "A.D.", "start": null, "offset": 0 } + ], + // when a two digit year is given, it will never be parsed as a four digit + // year greater than this year (in the appropriate era for the culture) + // Set it as a full year (e.g. 2029) or use an offset format starting from + // the current year: "+19" would correspond to 2029 if the current year 2010. + twoDigitYearMax: 2029, + // set of predefined date and time patterns used by the culture + // these represent the format someone in this culture would expect + // to see given the portions of the date that are shown. + patterns: { + // short date pattern + d: "M/d/yyyy", + // long date pattern + D: "dddd, MMMM dd, yyyy", + // short time pattern + t: "h:mm tt", + // long time pattern + T: "h:mm:ss tt", + // long date, short time pattern + f: "dddd, MMMM dd, yyyy h:mm tt", + // long date, long time pattern + F: "dddd, MMMM dd, yyyy h:mm:ss tt", + // month/day pattern + M: "MMMM dd", + // month/year pattern + Y: "yyyy MMMM", + // S is a sortable format that does not vary by culture + S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss" + } + // optional fields for each calendar: + /* + monthsGenitive: + Same as months but used when the day preceeds the month. + Omit if the culture has no genitive distinction in month names. + For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx + convert: + Allows for the support of non-gregorian based calendars. This convert object is used to + to convert a date to and from a gregorian calendar date to handle parsing and formatting. + The two functions: + fromGregorian(date) + Given the date as a parameter, return an array with parts [year, month, day] + corresponding to the non-gregorian based year, month, and day for the calendar. + toGregorian(year, month, day) + Given the non-gregorian year, month, and day, return a new Date() object + set to the corresponding date in the gregorian calendar. + */ + } + } +}, cultures.en); +en.calendar = en.calendar || en.calendars.standard; + +var regexTrim = /^\s+|\s+$/g, + regexInfinity = /^[+-]?infinity$/i, + regexHex = /^0x[a-f0-9]+$/i, + regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/, + toString = Object.prototype.toString; + +function startsWith(value, pattern) { + return value.indexOf( pattern ) === 0; +} + +function endsWith(value, pattern) { + return value.substr( value.length - pattern.length ) === pattern; +} + +function trim(value) { + return (value+"").replace( regexTrim, "" ); +} + +function zeroPad(str, count, left) { + for (var l=str.length; l < count; l++) { + str = (left ? ('0' + str) : (str + '0')); + } + return str; +} + +function isArray(obj) { + return toString.call(obj) === "[object Array]"; +} + +function isString(obj) { + return toString.call(obj) === "[object String]"; +} + +function isObject(obj) { + return toString.call(obj) === "[object Object]"; +} + +function arrayIndexOf( array, item ) { + if ( array.indexOf ) { + return array.indexOf( item ); + } + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === item ) { + return i; + } + } + return -1; +} + +// *************************************** Numbers *************************************** + +function expandNumber(number, precision, formatInfo) { + var groupSizes = formatInfo.groupSizes, + curSize = groupSizes[ 0 ], + curGroupIndex = 1, + factor = Math.pow( 10, precision ), + rounded = Math.round( number * factor ) / factor; + if ( !isFinite(rounded) ) { + rounded = number; + } + number = rounded; + + var numberString = number+"", + right = "", + split = numberString.split(/e/i), + exponent = split.length > 1 ? parseInt( split[ 1 ], 10 ) : 0; + numberString = split[ 0 ]; + split = numberString.split( "." ); + numberString = split[ 0 ]; + right = split.length > 1 ? split[ 1 ] : ""; + + var l; + if ( exponent > 0 ) { + right = zeroPad( right, exponent, false ); + numberString += right.slice( 0, exponent ); + right = right.substr( exponent ); + } + else if ( exponent < 0 ) { + exponent = -exponent; + numberString = zeroPad( numberString, exponent + 1 ); + right = numberString.slice( -exponent, numberString.length ) + right; + numberString = numberString.slice( 0, -exponent ); + } + + if ( precision > 0 ) { + right = formatInfo['.'] + + ((right.length > precision) ? right.slice( 0, precision ) : zeroPad( right, precision )); + } + else { + right = ""; + } + + var stringIndex = numberString.length - 1, + sep = formatInfo[","], + ret = ""; + + while ( stringIndex >= 0 ) { + if ( curSize === 0 || curSize > stringIndex ) { + return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? ( sep + ret + right ) : right ); + } + ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? ( sep + ret ) : "" ); + + stringIndex -= curSize; + + if ( curGroupIndex < groupSizes.length ) { + curSize = groupSizes[ curGroupIndex ]; + curGroupIndex++; + } + } + return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right; +} + + +function parseNegativePattern(value, nf, negativePattern) { + var neg = nf["-"], + pos = nf["+"], + ret; + switch (negativePattern) { + case "n -": + neg = ' ' + neg; + pos = ' ' + pos; + // fall through + case "n-": + if ( endsWith( value, neg ) ) { + ret = [ '-', value.substr( 0, value.length - neg.length ) ]; + } + else if ( endsWith( value, pos ) ) { + ret = [ '+', value.substr( 0, value.length - pos.length ) ]; + } + break; + case "- n": + neg += ' '; + pos += ' '; + // fall through + case "-n": + if ( startsWith( value, neg ) ) { + ret = [ '-', value.substr( neg.length ) ]; + } + else if ( startsWith(value, pos) ) { + ret = [ '+', value.substr( pos.length ) ]; + } + break; + case "(n)": + if ( startsWith( value, '(' ) && endsWith( value, ')' ) ) { + ret = [ '-', value.substr( 1, value.length - 2 ) ]; + } + break; + } + return ret || [ '', value ]; +} + +function formatNumber(value, format, culture) { + if ( !format || format === 'i' ) { + return culture.name.length ? value.toLocaleString() : value.toString(); + } + format = format || "D"; + + var nf = culture.numberFormat, + number = Math.abs(value), + precision = -1, + pattern; + if (format.length > 1) precision = parseInt( format.slice( 1 ), 10 ); + + var current = format.charAt( 0 ).toUpperCase(), + formatInfo; + + switch (current) { + case "D": + pattern = 'n'; + if (precision !== -1) { + number = zeroPad( ""+number, precision, true ); + } + if (value < 0) number = -number; + break; + case "N": + formatInfo = nf; + // fall through + case "C": + formatInfo = formatInfo || nf.currency; + // fall through + case "P": + formatInfo = formatInfo || nf.percent; + pattern = value < 0 ? formatInfo.pattern[0] : (formatInfo.pattern[1] || "n"); + if (precision === -1) precision = formatInfo.decimals; + number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo ); + break; + default: + throw "Bad number format specifier: " + current; + } + + var patternParts = /n|\$|-|%/g, + ret = ""; + for (;;) { + var index = patternParts.lastIndex, + ar = patternParts.exec(pattern); + + ret += pattern.slice( index, ar ? ar.index : pattern.length ); + + if (!ar) { + break; + } + + switch (ar[0]) { + case "n": + ret += number; + break; + case "$": + ret += nf.currency.symbol; + break; + case "-": + // don't make 0 negative + if ( /[1-9]/.test( number ) ) { + ret += nf["-"]; + } + break; + case "%": + ret += nf.percent.symbol; + break; + } + } + + return ret; +} + +// *************************************** Dates *************************************** + +function outOfRange(value, low, high) { + return value < low || value > high; +} + +function expandYear(cal, year) { + // expands 2-digit year into 4 digits. + var now = new Date(), + era = getEra(now); + if ( year < 100 ) { + var twoDigitYearMax = cal.twoDigitYearMax; + twoDigitYearMax = typeof twoDigitYearMax === 'string' ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax; + var curr = getEraYear( now, cal, era ); + year += curr - ( curr % 100 ); + if ( year > twoDigitYearMax ) { + year -= 100; + } + } + return year; +} + +function getEra(date, eras) { + if ( !eras ) return 0; + var start, ticks = date.getTime(); + for ( var i = 0, l = eras.length; i < l; i++ ) { + start = eras[ i ].start; + if ( start === null || ticks >= start ) { + return i; + } + } + return 0; +} + +function toUpper(value) { + // 'he-IL' has non-breaking space in weekday names. + return value.split( "\u00A0" ).join(' ').toUpperCase(); +} + +function toUpperArray(arr) { + var results = []; + for ( var i = 0, l = arr.length; i < l; i++ ) { + results[i] = toUpper(arr[i]); + } + return results; +} + +function getEraYear(date, cal, era, sortable) { + var year = date.getFullYear(); + if ( !sortable && cal.eras ) { + // convert normal gregorian year to era-shifted gregorian + // year by subtracting the era offset + year -= cal.eras[ era ].offset; + } + return year; +} + +function getDayIndex(cal, value, abbr) { + var ret, + days = cal.days, + upperDays = cal._upperDays; + if ( !upperDays ) { + cal._upperDays = upperDays = [ + toUpperArray( days.names ), + toUpperArray( days.namesAbbr ), + toUpperArray( days.namesShort ) + ]; + } + value = toUpper( value ); + if ( abbr ) { + ret = arrayIndexOf( upperDays[ 1 ], value ); + if ( ret === -1 ) { + ret = arrayIndexOf( upperDays[ 2 ], value ); + } + } + else { + ret = arrayIndexOf( upperDays[ 0 ], value ); + } + return ret; +} + +function getMonthIndex(cal, value, abbr) { + var months = cal.months, + monthsGen = cal.monthsGenitive || cal.months, + upperMonths = cal._upperMonths, + upperMonthsGen = cal._upperMonthsGen; + if ( !upperMonths ) { + cal._upperMonths = upperMonths = [ + toUpperArray( months.names ), + toUpperArray( months.namesAbbr ), + ]; + cal._upperMonthsGen = upperMonthsGen = [ + toUpperArray( monthsGen.names ), + toUpperArray( monthsGen.namesAbbr ) + ]; + } + value = toUpper( value ); + var i = arrayIndexOf( abbr ? upperMonths[ 1 ] : upperMonths[ 0 ], value ); + if ( i < 0 ) { + i = arrayIndexOf( abbr ? upperMonthsGen[ 1 ] : upperMonthsGen[ 0 ], value ); + } + return i; +} + +function appendPreOrPostMatch(preMatch, strings) { + // appends pre- and post- token match strings while removing escaped characters. + // Returns a single quote count which is used to determine if the token occurs + // in a string literal. + var quoteCount = 0, + escaped = false; + for ( var i = 0, il = preMatch.length; i < il; i++ ) { + var c = preMatch.charAt( i ); + switch ( c ) { + case '\'': + if ( escaped ) { + strings.push( "'" ); + } + else { + quoteCount++; + } + escaped = false; + break; + case '\\': + if ( escaped ) { + strings.push( "\\" ); + } + escaped = !escaped; + break; + default: + strings.push( c ); + escaped = false; + break; + } + } + return quoteCount; +} + +function expandFormat(cal, format) { + // expands unspecified or single character date formats into the full pattern. + format = format || "F"; + var pattern, + patterns = cal.patterns, + len = format.length; + if ( len === 1 ) { + pattern = patterns[ format ]; + if ( !pattern ) { + throw "Invalid date format string '" + format + "'."; + } + format = pattern; + } + else if ( len === 2 && format.charAt(0) === "%" ) { + // %X escape format -- intended as a custom format string that is only one character, not a built-in format. + format = format.charAt( 1 ); + } + return format; +} + +function getParseRegExp(cal, format) { + // converts a format string into a regular expression with groups that + // can be used to extract date fields from a date string. + // check for a cached parse regex. + var re = cal._parseRegExp; + if ( !re ) { + cal._parseRegExp = re = {}; + } + else { + var reFormat = re[ format ]; + if ( reFormat ) { + return reFormat; + } + } + + // expand single digit formats, then escape regular expression characters. + var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ), + regexp = ["^"], + groups = [], + index = 0, + quoteCount = 0, + tokenRegExp = getTokenRegExp(), + match; + + // iterate through each date token found. + while ( (match = tokenRegExp.exec( expFormat )) !== null ) { + var preMatch = expFormat.slice( index, match.index ); + index = tokenRegExp.lastIndex; + + // don't replace any matches that occur inside a string literal. + quoteCount += appendPreOrPostMatch( preMatch, regexp ); + if ( quoteCount % 2 ) { + regexp.push( match[ 0 ] ); + continue; + } + + // add a regex group for the token. + var m = match[ 0 ], + len = m.length, + add; + switch ( m ) { + case 'dddd': case 'ddd': + case 'MMMM': case 'MMM': + case 'gg': case 'g': + add = "(\\D+)"; + break; + case 'tt': case 't': + add = "(\\D*)"; + break; + case 'yyyy': + case 'fff': + case 'ff': + case 'f': + add = "(\\d{" + len + "})"; + break; + case 'dd': case 'd': + case 'MM': case 'M': + case 'yy': case 'y': + case 'HH': case 'H': + case 'hh': case 'h': + case 'mm': case 'm': + case 'ss': case 's': + add = "(\\d\\d?)"; + break; + case 'zzz': + add = "([+-]?\\d\\d?:\\d{2})"; + break; + case 'zz': case 'z': + add = "([+-]?\\d\\d?)"; + break; + case '/': + add = "(\\" + cal["/"] + ")"; + break; + default: + throw "Invalid date format pattern '" + m + "'."; + break; + } + if ( add ) { + regexp.push( add ); + } + groups.push( match[ 0 ] ); + } + appendPreOrPostMatch( expFormat.slice( index ), regexp ); + regexp.push( "$" ); + + // allow whitespace to differ when matching formats. + var regexpStr = regexp.join( '' ).replace( /\s+/g, "\\s+" ), + parseRegExp = {'regExp': regexpStr, 'groups': groups}; + + // cache the regex for this format. + return re[ format ] = parseRegExp; +} + +function getTokenRegExp() { + // regular expression for matching date and time tokens in format strings. + return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g; +} + +function parseExact(value, format, culture) { + // try to parse the date string by matching against the format string + // while using the specified culture for date field names. + value = trim( value ); + var cal = culture.calendar, + // convert date formats into regular expressions with groupings. + // use the regexp to determine the input format and extract the date fields. + parseInfo = getParseRegExp(cal, format), + match = new RegExp(parseInfo.regExp).exec(value); + if (match === null) { + return null; + } + // found a date format that matches the input. + var groups = parseInfo.groups, + era = null, year = null, month = null, date = null, weekDay = null, + hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null, + pmHour = false; + // iterate the format groups to extract and set the date fields. + for ( var j = 0, jl = groups.length; j < jl; j++ ) { + var matchGroup = match[ j + 1 ]; + if ( matchGroup ) { + var current = groups[ j ], + clength = current.length, + matchInt = parseInt( matchGroup, 10 ); + switch ( current ) { + case 'dd': case 'd': + // Day of month. + date = matchInt; + // check that date is generally in valid range, also checking overflow below. + if ( outOfRange( date, 1, 31 ) ) return null; + break; + case 'MMM': + case 'MMMM': + month = getMonthIndex( cal, matchGroup, clength === 3 ); + if ( outOfRange( month, 0, 11 ) ) return null; + break; + case 'M': case 'MM': + // Month. + month = matchInt - 1; + if ( outOfRange( month, 0, 11 ) ) return null; + break; + case 'y': case 'yy': + case 'yyyy': + year = clength < 4 ? expandYear( cal, matchInt ) : matchInt; + if ( outOfRange( year, 0, 9999 ) ) return null; + break; + case 'h': case 'hh': + // Hours (12-hour clock). + hour = matchInt; + if ( hour === 12 ) hour = 0; + if ( outOfRange( hour, 0, 11 ) ) return null; + break; + case 'H': case 'HH': + // Hours (24-hour clock). + hour = matchInt; + if ( outOfRange( hour, 0, 23 ) ) return null; + break; + case 'm': case 'mm': + // Minutes. + min = matchInt; + if ( outOfRange( min, 0, 59 ) ) return null; + break; + case 's': case 'ss': + // Seconds. + sec = matchInt; + if ( outOfRange( sec, 0, 59 ) ) return null; + break; + case 'tt': case 't': + // AM/PM designator. + // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of + // the AM tokens. If not, fail the parse for this format. + pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] ); + if ( !pmHour && ( !cal.AM || (matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2]) ) ) return null; + break; + case 'f': + // Deciseconds. + case 'ff': + // Centiseconds. + case 'fff': + // Milliseconds. + msec = matchInt * Math.pow( 10, 3-clength ); + if ( outOfRange( msec, 0, 999 ) ) return null; + break; + case 'ddd': + // Day of week. + case 'dddd': + // Day of week. + weekDay = getDayIndex( cal, matchGroup, clength === 3 ); + if ( outOfRange( weekDay, 0, 6 ) ) return null; + break; + case 'zzz': + // Time zone offset in +/- hours:min. + var offsets = matchGroup.split( /:/ ); + if ( offsets.length !== 2 ) return null; + hourOffset = parseInt( offsets[ 0 ], 10 ); + if ( outOfRange( hourOffset, -12, 13 ) ) return null; + var minOffset = parseInt( offsets[ 1 ], 10 ); + if ( outOfRange( minOffset, 0, 59 ) ) return null; + tzMinOffset = (hourOffset * 60) + (startsWith( matchGroup, '-' ) ? -minOffset : minOffset); + break; + case 'z': case 'zz': + // Time zone offset in +/- hours. + hourOffset = matchInt; + if ( outOfRange( hourOffset, -12, 13 ) ) return null; + tzMinOffset = hourOffset * 60; + break; + case 'g': case 'gg': + var eraName = matchGroup; + if ( !eraName || !cal.eras ) return null; + eraName = trim( eraName.toLowerCase() ); + for ( var i = 0, l = cal.eras.length; i < l; i++ ) { + if ( eraName === cal.eras[ i ].name.toLowerCase() ) { + era = i; + break; + } + } + // could not find an era with that name + if ( era === null ) return null; + break; + } + } + } + var result = new Date(), defaultYear, convert = cal.convert; + defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear(); + if ( year === null ) { + year = defaultYear; + } + else if ( cal.eras ) { + // year must be shifted to normal gregorian year + // but not if year was not specified, its already normal gregorian + // per the main if clause above. + year += cal.eras[ (era || 0) ].offset; + } + // set default day and month to 1 and January, so if unspecified, these are the defaults + // instead of the current day/month. + if ( month === null ) { + month = 0; + } + if ( date === null ) { + date = 1; + } + // now have year, month, and date, but in the culture's calendar. + // convert to gregorian if necessary + if ( convert ) { + result = convert.toGregorian( year, month, date ); + // conversion failed, must be an invalid match + if ( result === null ) return null; + } + else { + // have to set year, month and date together to avoid overflow based on current date. + result.setFullYear( year, month, date ); + // check to see if date overflowed for specified month (only checked 1-31 above). + if ( result.getDate() !== date ) return null; + // invalid day of week. + if ( weekDay !== null && result.getDay() !== weekDay ) { + return null; + } + } + // if pm designator token was found make sure the hours fit the 24-hour clock. + if ( pmHour && hour < 12 ) { + hour += 12; + } + result.setHours( hour, min, sec, msec ); + if ( tzMinOffset !== null ) { + // adjust timezone to utc before applying local offset. + var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() ); + // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours + // to ensure both these fields will not exceed this range. adjustedMin will range + // somewhere between -1440 and 1500, so we only need to split this into hours. + result.setHours( result.getHours() + parseInt( adjustedMin / 60, 10 ), adjustedMin % 60 ); + } + return result; +} + +function formatDate(value, format, culture) { + var cal = culture.calendar, + convert = cal.convert; + if ( !format || !format.length || format === 'i' ) { + var ret; + if ( culture && culture.name.length ) { + if ( convert ) { + // non-gregorian calendar, so we cannot use built-in toLocaleString() + ret = formatDate( value, cal.patterns.F, culture ); + } + else { + var eraDate = new Date( value.getTime() ), + era = getEra( value, cal.eras ); + eraDate.setFullYear( getEraYear( value, cal, era ) ); + ret = eraDate.toLocaleString(); + } + } + else { + ret = value.toString(); + } + return ret; + } + + var eras = cal.eras, + sortable = format === "s"; + format = expandFormat( cal, format ); + + // Start with an empty string + ret = []; + var hour, + zeros = ['0','00','000'], + foundDay, + checkedDay, + dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g, + quoteCount = 0, + tokenRegExp = getTokenRegExp(), + converted; + + function padZeros(num, c) { + var r, s = num+''; + if ( c > 1 && s.length < c ) { + r = ( zeros[ c - 2 ] + s); + return r.substr( r.length - c, c ); + } + else { + r = s; + } + return r; + } + + function hasDay() { + if ( foundDay || checkedDay ) { + return foundDay; + } + foundDay = dayPartRegExp.test( format ); + checkedDay = true; + return foundDay; + } + + function getPart( date, part ) { + if ( converted ) { + return converted[ part ]; + } + switch ( part ) { + case 0: return date.getFullYear(); + case 1: return date.getMonth(); + case 2: return date.getDate(); + } + } + + if ( !sortable && convert ) { + converted = convert.fromGregorian( value ); + } + + for (;;) { + // Save the current index + var index = tokenRegExp.lastIndex, + // Look for the next pattern + ar = tokenRegExp.exec( format ); + + // Append the text before the pattern (or the end of the string if not found) + var preMatch = format.slice( index, ar ? ar.index : format.length ); + quoteCount += appendPreOrPostMatch( preMatch, ret ); + + if ( !ar ) { + break; + } + + // do not replace any matches that occur inside a string literal. + if ( quoteCount % 2 ) { + ret.push( ar[ 0 ] ); + continue; + } + + var current = ar[ 0 ], + clength = current.length; + + switch ( current ) { + case "ddd": + //Day of the week, as a three-letter abbreviation + case "dddd": + // Day of the week, using the full name + names = (clength === 3) ? cal.days.namesAbbr : cal.days.names; + ret.push( names[ value.getDay() ] ); + break; + case "d": + // Day of month, without leading zero for single-digit days + case "dd": + // Day of month, with leading zero for single-digit days + foundDay = true; + ret.push( padZeros( getPart( value, 2 ), clength ) ); + break; + case "MMM": + // Month, as a three-letter abbreviation + case "MMMM": + // Month, using the full name + var part = getPart( value, 1 ); + ret.push( (cal.monthsGenitive && hasDay()) + ? cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] + : cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] ); + break; + case "M": + // Month, as digits, with no leading zero for single-digit months + case "MM": + // Month, as digits, with leading zero for single-digit months + ret.push( padZeros( getPart( value, 1 ) + 1, clength ) ); + break; + case "y": + // Year, as two digits, but with no leading zero for years less than 10 + case "yy": + // Year, as two digits, with leading zero for years less than 10 + case "yyyy": + // Year represented by four full digits + part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra( value, eras ), sortable ); + if ( clength < 4 ) { + part = part % 100; + } + ret.push( padZeros( part, clength ) ); + break; + case "h": + // Hours with no leading zero for single-digit hours, using 12-hour clock + case "hh": + // Hours with leading zero for single-digit hours, using 12-hour clock + hour = value.getHours() % 12; + if ( hour === 0 ) hour = 12; + ret.push( padZeros( hour, clength ) ); + break; + case "H": + // Hours with no leading zero for single-digit hours, using 24-hour clock + case "HH": + // Hours with leading zero for single-digit hours, using 24-hour clock + ret.push( padZeros( value.getHours(), clength ) ); + break; + case "m": + // Minutes with no leading zero for single-digit minutes + case "mm": + // Minutes with leading zero for single-digit minutes + ret.push( padZeros( value.getMinutes(), clength ) ); + break; + case "s": + // Seconds with no leading zero for single-digit seconds + case "ss": + // Seconds with leading zero for single-digit seconds + ret.push( padZeros(value .getSeconds(), clength ) ); + break; + case "t": + // One character am/pm indicator ("a" or "p") + case "tt": + // Multicharacter am/pm indicator + part = value.getHours() < 12 ? (cal.AM ? cal.AM[0] : " ") : (cal.PM ? cal.PM[0] : " "); + ret.push( clength === 1 ? part.charAt( 0 ) : part ); + break; + case "f": + // Deciseconds + case "ff": + // Centiseconds + case "fff": + // Milliseconds + ret.push( padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) ); + break; + case "z": + // Time zone offset, no leading zero + case "zz": + // Time zone offset with leading zero + hour = value.getTimezoneOffset() / 60; + ret.push( (hour <= 0 ? '+' : '-') + padZeros( Math.floor( Math.abs( hour ) ), clength ) ); + break; + case "zzz": + // Time zone offset with leading zero + hour = value.getTimezoneOffset() / 60; + ret.push( (hour <= 0 ? '+' : '-') + padZeros( Math.floor( Math.abs( hour ) ), 2 ) + + // Hard coded ":" separator, rather than using cal.TimeSeparator + // Repeated here for consistency, plus ":" was already assumed in date parsing. + ":" + padZeros( Math.abs( value.getTimezoneOffset() % 60 ), 2 ) ); + break; + case "g": + case "gg": + if ( cal.eras ) { + ret.push( cal.eras[ getEra(value, eras) ].name ); + } + break; + case "/": + ret.push( cal["/"] ); + break; + default: + throw "Invalid date format pattern '" + current + "'."; + break; + } + } + return ret.join( '' ); +} + +// EXPORTS + +window.Globalization = Globalization; + +//jQuery.findClosestCulture = Globalization.findClosestCulture; +//jQuery.culture = Globalization.culture; +//jQuery.cultures = Globalization.cultures +//jQuery.preferCulture = Globalization.preferCulture +//jQuery.localize = Globalization.localize +//jQuery.format = Globalization.format +//jQuery.parseInt = Globalization.parseInt +//jQuery.parseFloat = Globalization.parseFloat +//jQuery.parseDate = Globalization.parseDate + +})(); diff --git a/external/jquery.mousewheel-3.0.4.js b/external/jquery.mousewheel-3.0.4.js new file mode 100644 index 000000000..dbf8f4b0f --- /dev/null +++ b/external/jquery.mousewheel-3.0.4.js @@ -0,0 +1,78 @@ +/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
+ * Licensed under the MIT License (LICENSE.txt).
+ *
+ * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
+ * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
+ * Thanks to: Seamus Leahy for adding deltaX and deltaY
+ *
+ * Version: 3.0.4
+ *
+ * Requires: 1.2.2+
+ */
+
+(function($) {
+
+var types = ['DOMMouseScroll', 'mousewheel'];
+
+$.event.special.mousewheel = {
+ setup: function() {
+ if ( this.addEventListener ) {
+ for ( var i=types.length; i; ) {
+ this.addEventListener( types[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = handler;
+ }
+ },
+
+ teardown: function() {
+ if ( this.removeEventListener ) {
+ for ( var i=types.length; i; ) {
+ this.removeEventListener( types[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = null;
+ }
+ }
+};
+
+$.fn.extend({
+ mousewheel: function(fn) {
+ return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
+ },
+
+ unmousewheel: function(fn) {
+ return this.unbind("mousewheel", fn);
+ }
+});
+
+
+function handler(event) {
+ var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
+ event = $.event.fix(orgEvent);
+ event.type = "mousewheel";
+
+ // Old school scrollwheel delta
+ if ( event.wheelDelta ) { delta = event.wheelDelta/120; }
+ if ( event.detail ) { delta = -event.detail/3; }
+
+ // New school multidimensional scroll (touchpads) deltas
+ deltaY = delta;
+
+ // Gecko
+ if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
+ deltaY = 0;
+ deltaX = -1*delta;
+ }
+
+ // Webkit
+ if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
+ if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
+
+ // Add event and delta to the front of the arguments
+ args.unshift(event, delta, deltaX, deltaY);
+
+ return $.event.handle.apply(this, args);
+}
+
+})(jQuery);
\ No newline at end of file diff --git a/tests/unit/spinner/spinner.html b/tests/unit/spinner/spinner.html new file mode 100644 index 000000000..2729fce8d --- /dev/null +++ b/tests/unit/spinner/spinner.html @@ -0,0 +1,42 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <title>jQuery UI Spinner Test Suite</title>
+
+ <link type="text/css" href="../../../themes/base/jquery.ui.spinner.css" rel="stylesheet" />
+
+ <script type="text/javascript" src="../../../jquery-1.4.3.js"></script>
+ <script type="text/javascript" src="../../../external/jquery.mousewheel-3.0.4.js"></script>
+ <script type="text/javascript" src="../../../external/glob.js"></script>
+ <script type="text/javascript" src="../../../ui/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script>
+ <script type="text/javascript" src="../../../ui/jquery.ui.button.js"></script>
+ <script type="text/javascript" src="../../../ui/jquery.ui.spinner.js"></script>
+
+ <link rel="stylesheet" href="../../../external/qunit.css" type="text/css"/>
+ <script type="text/javascript" src="../../../external/qunit.js"></script>
+ <script type="text/javascript" src="../../jquery.simulate.js"></script>
+ <script type="text/javascript" src="../testsuite.js"></script>
+
+ <script type="text/javascript" src="spinner_core.js"></script>
+ <script type="text/javascript" src="spinner_defaults.js"></script>
+ <script type="text/javascript" src="spinner_events.js"></script>
+ <script type="text/javascript" src="spinner_methods.js"></script>
+ <script type="text/javascript" src="spinner_options.js"></script>
+</head>
+<body>
+
+
+<h1 id="qunit-header">jQuery UI Slider Test Suite</h1>
+<h2 id="qunit-banner"></h2>
+<h2 id="qunit-userAgent"></h2>
+<ol id="qunit-tests">
+</ol>
+
+<div id="qunit-fixture">
+ <input id="spin" class="foo">
+ <input id="spin2" value="2">
+</div>
+
+</body>
+</html>
diff --git a/tests/unit/spinner/spinner_core.js b/tests/unit/spinner/spinner_core.js new file mode 100644 index 000000000..88603708f --- /dev/null +++ b/tests/unit/spinner/spinner_core.js @@ -0,0 +1,171 @@ +/*
+ * spinner_core.js
+ */
+
+var simulateKeyDownUp = function(el, kCode, shift) {
+ el.simulate("keydown",{keyCode:kCode, shiftKey: shift || false })
+ .simulate("keyup",{keyCode:kCode, shiftKey: shift || false });
+};
+
+(function($) {
+
+// Spinner Tests
+module("spinner: core");
+
+test("destroy", function() {
+ // cheat a bit to get IE6 to pass
+ $("#spin").val(0);
+ var beforeHtml = $("#spin").parent().html();
+ var afterHtml = $("#spin").spinner().spinner("destroy").parent().html();
+ equal( afterHtml, beforeHtml, "before/after html should be the same" );
+});
+
+test("keydown UP on input, increases value not greater than max", function() {
+ var el = $("#spin").spinner({
+ max:100,
+ value:50,
+ step:10
+ });
+
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+ equals(el.val(), 60);
+
+ for (i = 0; i<11; i++) {
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+ }
+ equals(el.val(), 100);
+
+ el.spinner("value", 50);
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+ equals(el.val(), 60);
+});
+
+test("keydown DOWN on input, decreases value not less than min", function() {
+ var el = $("#spin").spinner({
+ min:-100,
+ value:50,
+ step:10
+ });
+
+ simulateKeyDownUp(el, $.ui.keyCode.DOWN);
+ equals(el.val(), 40);
+
+ for (i = 0; i<21; i++) {
+ simulateKeyDownUp(el, $.ui.keyCode.DOWN);
+ }
+ equals(el.val(), -100);
+
+ el.spinner("value", 50);
+ simulateKeyDownUp(el, $.ui.keyCode.DOWN);
+ equals(el.val(), 40);
+});
+
+test("keydown PGUP on input, increases value not greater than max", function() {
+ var el = $("#spin").spinner({
+ max: 500,
+ value: 0,
+ step: 10
+ });
+
+ simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP);
+ equal(el.val(), 100);
+
+ for (i = 0; i<5; i++) {
+ simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP);
+ }
+ equal(el.val(), 500);
+
+ el.spinner("value", 0);
+ simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP);
+ equals(el.val(), 100);
+});
+
+test("keydown PGDN on input, decreases value not less than min", function() {
+ var el = $("#spin").spinner({
+ min:-500,
+ value:0,
+ step:10
+ });
+
+ simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN);
+ equals(el.val(), -100);
+
+ for (i = 0; i<5; i++) {
+ simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN);
+ }
+ equals(el.val(), -500);
+
+ el.spinner("value", 0);
+ simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN);
+ equals(el.val(), -100);
+});
+
+test("mouse click on buttons", function() {
+ var el = $("#spin").spinner(),
+ val = 0;
+
+ $(".ui-spinner-up").trigger("mousedown").trigger("mouseup");
+ equals(el.val(), ++val, "mouse click to up");
+
+ $(".ui-spinner-down").trigger("mousedown").trigger("mouseup");
+ equals(el.val(), --val, "mouse click to down");
+
+ el.spinner("value", 50);
+ $(".ui-spinner-up").trigger("mousedown").trigger("mouseup");
+ equals(el.val(), 51);
+
+ el.spinner("value", 50);
+ $(".ui-spinner-down").trigger("mousedown").trigger("mouseup");
+ equals(el.val(), 49);
+});
+
+test("mouse wheel on input", function() {
+ expect(3);
+
+ var el = $("#spin").spinner();
+ el.trigger("mousewheel", 1);
+ equal(el.val(), 1);
+
+ // mousewheel handler uses a timeout, need to accomodate that
+ stop();
+ setTimeout(function() {
+ el.trigger("mousewheel", -1);
+ equal(el.val(), 0);
+
+ setTimeout(function() {
+ el.trigger("mousewheel", -1);
+ equal(el.val(), -1);
+ start();
+ }, 100);
+ }, 100);
+
+});
+
+test("reading HTML5 attributes", function() {
+ var el = $('<input id="spinner" type="number" min="-100" max="100" value="5" step="2">').spinner();
+ equals(el.spinner('option', 'value'), 5, 'value');
+ equals(el.spinner('option', 'max'), 100, 'max');
+ equals(el.spinner('option', 'min'), -100, 'min');
+ equals(el.spinner('option', 'step'), 2, 'step');
+});
+
+test("ARIA attributes", function() {
+ var el = $('#spin').spinner({ min: -5, max: 5, value: 2 }),
+ wrapper = el.spinner("widget");
+
+ equals(wrapper.attr('role'), 'spinbutton', 'role');
+ equals(wrapper.attr('aria-valuemin'), -5, 'aria-valuemin');
+ equals(wrapper.attr('aria-valuemax'), 5, 'aria-valuemax');
+ equals(wrapper.attr('aria-valuenow'), 2, 'aria-valuenow');
+
+ el.spinner('stepUp');
+
+ equals(wrapper.attr('aria-valuenow'), 3, 'stepUp 1 step changes aria-valuenow');
+
+ el.spinner('option', { min: -10, max: 10 });
+
+ equals(wrapper.attr('aria-valuemin'), -10, 'min option changed aria-valuemin changes');
+ equals(wrapper.attr('aria-valuemax'), 10, 'max option changed aria-valuemax changes');
+});
+
+})(jQuery);
diff --git a/tests/unit/spinner/spinner_defaults.js b/tests/unit/spinner/spinner_defaults.js new file mode 100644 index 000000000..2f5d77585 --- /dev/null +++ b/tests/unit/spinner/spinner_defaults.js @@ -0,0 +1,15 @@ +/*
+ * spinner_defaults.js
+ */
+
+var spinner_defaults = {
+ disabled: false,
+ incremental: true,
+ max: null,
+ min: null,
+ numberformat: null,
+ step: null,
+ value: null
+};
+
+commonWidgetTests('spinner', { defaults: spinner_defaults });
diff --git a/tests/unit/spinner/spinner_events.js b/tests/unit/spinner/spinner_events.js new file mode 100644 index 000000000..c0771ca2f --- /dev/null +++ b/tests/unit/spinner/spinner_events.js @@ -0,0 +1,64 @@ +/*
+ * spinner_events.js
+ */
+(function($) {
+
+module("spinner: events");
+
+test("start", function() {
+ var start = 0;
+
+ var el = $("#spin").spinner({
+ start: function(){
+ start++;
+ }
+ });
+
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+
+ equals(start, 1, "Start triggered");
+});
+
+test("spin", function() {
+ var spin = 0;
+
+ var el = $("#spin").spinner({
+ spin: function(){
+ spin++;
+ }
+ });
+
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+
+ equals(spin, 1, "Spin triggered");
+});
+
+test("stop", function() {
+ var stop = 0;
+
+ var el = $("#spin").spinner({
+ stop: function(){
+ stop++;
+ }
+ });
+
+ simulateKeyDownUp(el, $.ui.keyCode.DOWN);
+
+ equals(stop, 1, "Stop triggered");
+});
+
+test("change", function() {
+ var change = 0;
+
+ var el = $("#spin").spinner({
+ change: function(){
+ change++;
+ }
+ });
+
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+
+ equals(change, 1, "Change triggered");
+});
+
+})(jQuery);
diff --git a/tests/unit/spinner/spinner_methods.js b/tests/unit/spinner/spinner_methods.js new file mode 100644 index 000000000..fb58edbdf --- /dev/null +++ b/tests/unit/spinner/spinner_methods.js @@ -0,0 +1,155 @@ +/*
+ * spinner_methods.js
+ */
+(function($) {
+
+module("spinner: methods");
+
+test("disable", function() {
+ var el = $("#spin").spinner({ disabled: false }),
+ val = el.val(),
+ wrapper = $("#spin").spinner("widget");
+
+ ok(!wrapper.hasClass("ui-spinner-disabled"), "before: wrapper does not have ui-spinner-disabled class");
+ ok(!el.is(':disabled'), "before: input does not have disabled attribute");
+
+ el.spinner("disable");
+ ok(wrapper.hasClass("ui-spinner-disabled"), "after: wrapper has ui-spinner-disabled class");
+ ok(el.is(':disabled'), "after: input has disabled attribute");
+
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+ equals(val, el.val(), "keyboard - value does not change on key UP");
+
+ simulateKeyDownUp(el, $.ui.keyCode.DOWN);
+ equals(val, el.val(), "keyboard - value does not change on key DOWN");
+
+ simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP);
+ equals(val, el.val(), "keyboard - value does not change on key PGUP");
+
+ simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN);
+ equals(val, el.val(), "keyboard - value does not change on key PGDN");
+
+ wrapper.find(":ui-button").first().trigger('mousedown').trigger('mouseup');
+ equals(val, el.val(), "mouse - value does not change on clicking up button");
+
+ wrapper.find(":ui-button").last().trigger('mousedown').trigger('mouseup');
+ equals(val, el.val(), "mouse - value does not change on clicking down button");
+
+ el.spinner('stepUp', 6);
+ equals(6, el.val(), "script - stepUp 6 steps changes value");
+
+ el.spinner('stepDown');
+ equals(5, el.val(), "script - stepDown 1 step changes value");
+
+ el.spinner('pageUp');
+ equals(15, el.val(), "script - pageUp 1 page changes value");
+
+ el.spinner('pageDown');
+ equals(5, el.val(), "script - pageDown 1 page changes value");
+
+});
+
+test("enable", function() {
+ var el = $("#spin").spinner({ disabled: true })
+ val = el.val(),
+ wrapper = el.spinner("widget");
+
+ ok(wrapper.hasClass("ui-spinner-disabled"), "before: wrapper has ui-spinner-disabled class");
+ ok(el.is(':disabled'), "before: input has disabled attribute");
+
+ el.spinner("enable");
+
+ ok(!wrapper.hasClass(".ui-spinner-disabled"), "after: wrapper does not have ui-spinner-disabled class");
+ ok(!el.is(':disabled'), "after: input does not have disabled attribute");
+});
+
+test("pageDown", function() {
+ var el = $('#spin').spinner({
+ step: 2,
+ value: 0,
+ min: -100
+ });
+
+ el.spinner('pageDown');
+ equals(el.val(), -20, "pageDown 1 page");
+
+ el.spinner('pageDown', 3);
+ equals(el.val(), -80, "pageDown 3 pages");
+
+ el.val(-91).spinner('pageDown');
+ equals(el.val(), -100, "value close to min and pageDown 1 page");
+
+ el.spinner('pageDown', 10);
+ equals(el.val(), -100, "value at min and pageDown 10 pages");
+});
+
+test("pageUp", function() {
+ var el = $('#spin').spinner({
+ step: 2,
+ value: 0,
+ max: 100
+ });
+
+ el.spinner('pageUp');
+ equals(el.val(), 20, "pageUp 1 page");
+
+ el.spinner('pageUp', 3);
+ equals(el.val(), 80, "pageUp 3 pages");
+
+ el.val(91).spinner('pageUp');
+ equals(el.val(), 100, "value close to max and pageUp 1 page");
+
+ el.spinner('pageUp', 10);
+ equals(el.val(), 100, "value at max and pageUp 10 pages");
+
+});
+
+test("stepDown", function() {
+ expect(4);
+
+ el = $('#spin').spinner({ step: 2, page: 5, value: 0, min: -15 });
+
+ el.spinner('stepDown')
+ equals(el.val(), -2, "stepDown 1 step");
+
+ el.spinner('stepDown', 5)
+ equals(el.val(), -12, "stepDown 5 steps");
+
+ el.spinner('stepDown', 3);
+ equals(el.val(), -15, "close to min and stepDown 3 steps");
+
+ el.spinner('stepDown');
+ equals(el.val(), -15, "at min and stepDown 1 step");
+});
+
+test("stepUp", function() {
+ expect(4);
+
+ el = $('#spin').spinner({ step: 2, page: 5, value: 0, max: 15 });
+
+ el.spinner('stepUp')
+ equals(el.val(), 2, "stepUp 1 steps");
+
+ el.spinner('stepUp', 5)
+ equals(el.val(), 12, "stepUp 5 steps");
+
+ el.spinner('stepUp', 3);
+ equals(el.val(), 15, "close to min and stepUp 3 steps");
+
+ el.spinner('stepUp');
+ equals(el.val(), 15, "at min and stepUp 1 step");
+
+});
+
+test("value", function() {
+ expect(2);
+
+ el = $('#spin').spinner({ value: 0 });
+
+ el.spinner('value', 10);
+ equals(el.val(), 10, "change value via value method");
+
+ equals(10, el.spinner('value'), "get value via value method");
+});
+
+})(jQuery);
diff --git a/tests/unit/spinner/spinner_options.js b/tests/unit/spinner/spinner_options.js new file mode 100644 index 000000000..3db7e5c55 --- /dev/null +++ b/tests/unit/spinner/spinner_options.js @@ -0,0 +1,154 @@ +/*
+ * spinner_options.js
+ */
+(function($) {
+
+module("spinner: options");
+
+test("numberformat, number", function() {
+ var el = $("#spin").spinner({
+ value: "1",
+ numberformat: "n"
+ });
+ equal(el.val(), "1.00");
+});
+
+test("numberformat, number, simple", function() {
+ var el = $("#spin").spinner({
+ value: "1",
+ numberformat: "n0"
+ });
+ equal(el.val(), "1");
+});
+
+test("numberformat, currency", function() {
+ var el = $("#spin").spinner({
+ value: "1",
+ numberformat: "C"
+ });
+ equal(el.val(), "$1.00");
+});
+
+/* TODO figure out how to test this properly
+test("incremental - false (default)", function() {
+ var el = $("#spin").spinner({ incremental:false });
+
+ for ( var i = 1 ; i<=120 ; i++ ) {
+ el.simulate("keydown",{keyCode:$.ui.keyCode.UP});
+ }
+ el.simulate("keyup",{keyCode:$.ui.keyCode.UP});
+
+ equals(el.val(), 120, "incremental false - keydown 120 times");
+
+ for ( var i = 1 ; i<=210 ; i++ ) {
+ el.simulate("keydown",{keyCode:$.ui.keyCode.DOWN});
+ }
+ el.simulate("keyup",{keyCode:$.ui.keyCode.DOWN});
+
+ equals(el.val(), -90, "incremental false - keydown 210 times");
+});
+
+test("incremental - true (default)", function() {
+ var el = $("#spin").spinner();
+
+ for ( var i = 1 ; i<=120 ; i++ ) {
+ el.simulate("keydown",{keyCode:$.ui.keyCode.UP});
+ }
+ el.simulate("keyup",{keyCode:$.ui.keyCode.UP});
+
+ equals(el.val(), 300, "incremental true - keydown 120 times (100+20*10)");
+
+ for ( var i = 1 ; i<=210 ; i++ ) {
+ el.simulate("keydown",{keyCode:$.ui.keyCode.DOWN});
+ }
+ el.simulate("keyup",{keyCode:$.ui.keyCode.DOWN});
+
+ equals(el.val(), -1800, "incremental true - keydown 210 times (300-100-100*10-10*100)");
+});
+*/
+
+test("max", function() {
+ var el = $("#spin").spinner({ max: 100, value: 1000 });
+ equals(el.val(), 100, "max constrained if value option is greater");
+
+ el.spinner('value', 1000);
+ equals(el.val(), 100, "max constrained if value method is greater");
+
+ el.val(1000).blur();
+ equals(el.val(), 100, "max constrained if manual entry");
+});
+
+test("min", function() {
+ var el = $("#spin").spinner({ min: -100, value: -1000 });
+ equals(el.val(), -100, "min constrained if value option is greater");
+
+ el.spinner('value', -1000);
+ equals(el.val(), -100, "min constrained if value method is greater");
+
+ el.val(-1000).blur();
+ equals(el.val(), -100, "min constrained if manual entry");
+});
+
+test("step, 2", function() {
+ var el = $("#spin").spinner({ step: 2 });
+ equals(el.val(), "0", "value initialized to");
+
+ for ( var i = 0 ; i < 5 ; i++ ) {
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+ }
+ equals(el.val(), "10", "UP 5 times with step: 2");
+
+ el.spinner('value', '10.5');
+ equals(el.val(), "10.5", "value reset to");
+
+ el.spinner('option', 'step', 2);
+ for ( var i = 0 ; i < 5 ; i++ ) {
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+ }
+ equals(el.val(), "20.5", "UP 5 times with step: 2");
+});
+
+test("step, 0.7", function() {
+ var el = $("#spin").spinner({
+ step: 0.7,
+ numberformat: "n1"
+ });
+ equals(el.val(), "0.0", "value initialized to");
+
+ simulateKeyDownUp(el, $.ui.keyCode.DOWN);
+ equals(el.val(), "-0.7", "DOWN 1 time with step: 0.7");
+
+ for ( var i = 0 ; i < 11 ; i++ ) {
+ simulateKeyDownUp(el, $.ui.keyCode.UP);
+ }
+ equals(el.val(), "7.0", "UP 11 times with step: 0.7");
+});
+
+test("value, default, specified in markup", function() {
+ var el = $('#spin2').spinner();
+ equals(el.val(), 2, "starting value");
+});
+
+test("value, default, nothing specified", function() {
+ var el = $('#spin').spinner();
+ equals(el.val(), 0, "starting value");
+});
+
+test("value, override", function() {
+ var el = $('#spin').spinner({ value: 100 });
+ equals(el.val(), 100, "starting value");
+});
+
+test("value, override markup", function() {
+ var el = $('#spin2').spinner({ value: 100 });
+ equals(el.val(), 100, "starting value");
+});
+
+test("value, override later", function() {
+ var el = $('#spin').spinner();
+ equals(el.val(), 0, "starting value");
+ el.spinner('option', 'value', 1000);
+ equals(el.val(), 1000, "value option changed and set as current value");
+});
+
+})(jQuery);
diff --git a/tests/visual/index.html b/tests/visual/index.html index 05f6fd49a..c7b709279 100644 --- a/tests/visual/index.html +++ b/tests/visual/index.html @@ -43,6 +43,7 @@ <li><a href="dialog/dialog.html">Dialog</a></li> <li><a href="progressbar/progressbar.html">Progressbar</a></li> <li><a href="slider/slider.html">Slider</a></li> + <li><a href="spinner/spinner.html">Spinner</a></li> <li><a href="tabs/tabs.html">Tabs</a></li> </ul> diff --git a/tests/visual/spinner/spinner.html b/tests/visual/spinner/spinner.html new file mode 100644 index 000000000..dc723a514 --- /dev/null +++ b/tests/visual/spinner/spinner.html @@ -0,0 +1,43 @@ +<!doctype html>
+<html lang="en">
+<head>
+ <title>Spinner Visual Test Page</title>
+ <link rel="stylesheet" href="../visual.css" type="text/css" />
+ <link rel="stylesheet" href="../../../themes/base/jquery.ui.all.css" type="text/css" title="ui-theme" />
+ <script type="text/javascript" src="../../../jquery-1.4.3.js"></script>
+ <script type="text/javascript" src="../../../external/jquery.mousewheel-3.0.4.js"></script>
+ <script type="text/javascript" src="../../../ui/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script>
+ <script type="text/javascript" src="../../../ui/jquery.ui.button.js"></script>
+ <script type="text/javascript" src="../../../ui/jquery.ui.spinner.js"></script>
+ <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
+ <script type="text/javascript">
+ $(function() {
+ $.fn.themeswitcher && $('<div/>').css({
+ position: "absolute",
+ right: 10,
+ top: 10
+ }).appendTo(document.body).themeswitcher();
+
+ $('#spinner').spinner({
+ start: function(event, ui) {
+ console.log(event.type, ui.value, $(this).spinner("value"));
+ },
+ spin: function(event, ui) {
+ console.log(event.type, ui.value, $(this).spinner("value"));
+ },
+ stop: function(event) {
+ console.log(event.type, $(this).spinner("value"));
+ },
+ change: function(event) {
+ console.log(event.type, $(this).spinner("value"));
+ }
+ });
+ });
+ </script>
+</head>
+<body>
+
+<p><label for="spinner">Basic with event callbacks:</label> <input id="spinner"></p>
+</body>
+</html>
\ No newline at end of file diff --git a/themes/base/jquery.ui.base.css b/themes/base/jquery.ui.base.css index 7634fb61e..62d17c7e2 100644 --- a/themes/base/jquery.ui.base.css +++ b/themes/base/jquery.ui.base.css @@ -18,4 +18,5 @@ @import url("jquery.ui.resizable.css"); @import url("jquery.ui.selectable.css"); @import url("jquery.ui.slider.css"); +@import url("jquery.ui.spinner.css"); @import url("jquery.ui.tabs.css"); diff --git a/themes/base/jquery.ui.spinner.css b/themes/base/jquery.ui.spinner.css new file mode 100644 index 000000000..cd195ee47 --- /dev/null +++ b/themes/base/jquery.ui.spinner.css @@ -0,0 +1,16 @@ +/* Spinner +----------------------------------*/ +.ui-spinner { position:relative; display: inline-block; overflow: hidden; padding: 0; vertical-align: middle; } +.ui-spinner-input { border: none; background: none; padding: 0; margin: .2em 0; vertical-align: middle; margin-left: .4em; margin-right: 22px; } +.ui-spinner-button { width: 16px; height: 50%; font-size: .5em; padding: 0; margin: 0; z-index: 100; text-align: center; vertical-align: middle; position: absolute; cursor: default; display: block; overflow: hidden; right: 0; } +.ui-spinner a.ui-spinner-button { border-top: none; border-bottom: none; border-right: none; } /* more specificity required here to overide default borders */ +.ui-spinner .ui-icon { position: absolute; margin-top: -8px; top: 50%; left: 0; } /* vertical centre icon */ +.ui-spinner-up { top: 0; } +.ui-spinner-down { bottom: 0; } + +/* TR overrides */ +span.ui-spinner { background: none; } +.ui-spinner .ui-icon-triangle-1-s { + /* need to fix icons sprite */ + background-position:-65px -16px; +} diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js new file mode 100644 index 000000000..39a498359 --- /dev/null +++ b/ui/jquery.ui.spinner.js @@ -0,0 +1,360 @@ +/* + * jQuery UI Spinner @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Spinner + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function($) { + +$.widget('ui.spinner', { + options: { + incremental: true, + max: null, + min: null, + numberformat: null, + page: 10, + step: null, + value: null + }, + + _create: function() { + this._draw(); + this._markupOptions(); + this._mousewheel(); + this._aria(); + }, + + _markupOptions: function() { + var _this = this; + $.each({ + min: -Number.MAX_VALUE, + max: Number.MAX_VALUE, + step: 1 + }, function(attr, defaultValue) { + if (_this.options[attr] === null) { + var value = _this.element.attr(attr); + _this.options[attr] = typeof value == "string" && value.length > 0 ? _this._parse(value) : defaultValue; + } + }); + this.value(this.options.value !== null ? this.options.value : this.element.val() || 0); + }, + + _draw: function() { + var self = this, + options = self.options; + + var uiSpinner = this.uiSpinner = self.element + .addClass('ui-spinner-input') + .attr('autocomplete', 'off') + .wrap(self._uiSpinnerHtml()) + .parent() + // add buttons + .append(self._buttonHtml()) + // add behaviours + .hover(function() { + if (!options.disabled) { + $(this).addClass('ui-state-hover'); + } + self.hovered = true; + }, function() { + $(this).removeClass('ui-state-hover'); + self.hovered = false; + }); + + this.element + .bind('keydown.spinner', function(event) { + if (self.options.disabled) { + return; + } + if (self._start(event)) { + return self._keydown(event); + } + return true; + }) + .bind('keyup.spinner', function(event) { + if (self.options.disabled) { + return; + } + if (self.spinning) { + self._stop(event); + self._change(event); + } + }) + .bind('focus.spinner', function() { + uiSpinner.addClass('ui-state-active'); + self.focused = true; + }) + .bind('blur.spinner', function(event) { + self.value(self.element.val()); + if (!self.hovered) { + uiSpinner.removeClass('ui-state-active'); + } + self.focused = false; + }); + + // button bindings + this.buttons = uiSpinner.find('.ui-spinner-button') + .attr("tabIndex", -1) + .button() + .removeClass("ui-corner-all") + .bind('mousedown', function(event) { + if (self.options.disabled) { + return; + } + if (self._start(event) === false) { + return false; + } + self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); + }) + .bind('mouseup', function(event) { + if (self.options.disabled) { + return; + } + if (self.spinning) { + self._stop(event); + self._change(event); + } + }) + .bind("mouseenter", function() { + if (self.options.disabled) { + return; + } + // button will add ui-state-active if mouse was down while mouseleave and kept down + if ($(this).hasClass("ui-state-active")) { + if (self._start(event) === false) { + return false; + } + self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); + } + }) + .bind("mouseleave", function() { + if (self.spinning) { + self._stop(event); + self._change(event); + } + }); + + // disable spinner if element was already disabled + if (options.disabled) { + this.disable(); + } + }, + + _keydown: function(event) { + var o = this.options, + KEYS = $.ui.keyCode; + + switch (event.keyCode) { + case KEYS.UP: + this._repeat(null, 1, event); + return false; + case KEYS.DOWN: + this._repeat(null, -1, event); + return false; + case KEYS.PAGE_UP: + this._repeat(null, this.options.page, event); + return false; + case KEYS.PAGE_DOWN: + this._repeat(null, -this.options.page, event); + return false; + + case KEYS.ENTER: + this.value(this.element.val()); + } + + return true; + }, + + _mousewheel: function() { + // need the delta normalization that mousewheel plugin provides + if (!$.fn.mousewheel) { + return; + } + var self = this; + this.element.bind("mousewheel.spinner", function(event, delta) { + if (self.options.disabled) { + return; + } + if (!self.spinning && !self._start(event)) { + return false; + } + self._spin((delta > 0 ? 1 : -1) * self.options.step, event); + clearTimeout(self.timeout); + self.timeout = setTimeout(function() { + if (self.spinning) { + self._stop(event); + self._change(event); + } + }, 100); + event.preventDefault(); + }); + }, + + _uiSpinnerHtml: function() { + return '<span role="spinbutton" class="ui-spinner ui-state-default ui-widget ui-widget-content ui-corner-all"></span>'; + }, + + _buttonHtml: function() { + return '<a class="ui-spinner-button ui-spinner-up ui-corner-tr"><span class="ui-icon ui-icon-triangle-1-n">▲</span></a>' + + '<a class="ui-spinner-button ui-spinner-down ui-corner-br"><span class="ui-icon ui-icon-triangle-1-s">▼</span></a>'; + }, + + _start: function(event) { + if (!this.spinning && this._trigger('start', event) !== false) { + if (!this.counter) { + this.counter = 1; + } + this.spinning = true; + return true; + } + return false; + }, + + _repeat: function(i, steps, event) { + var self = this; + i = i || 500; + + clearTimeout(this.timer); + this.timer = setTimeout(function() { + self._repeat(40, steps, event); + }, i); + + self._spin(steps * self.options.step, event); + }, + + _spin: function(step, event) { + if (!this.counter) { + this.counter = 1; + } + + // TODO refactor, maybe figure out some non-linear math + var newVal = this.value() + step * (this.options.incremental && + this.counter > 20 + ? this.counter > 100 + ? this.counter > 200 + ? 100 + : 10 + : 2 + : 1); + + if (this._trigger('spin', event, { value: newVal }) !== false) { + this.value(newVal); + this.counter++; + } + }, + + _stop: function(event) { + this.counter = 0; + if (this.timer) { + window.clearTimeout(this.timer); + } + this.element[0].focus(); + this.spinning = false; + this._trigger('stop', event); + }, + + _change: function(event) { + this._trigger('change', event); + }, + + _setOption: function(key, value) { + if (key == 'value') { + value = this._parse(value); + if (value < this.options.min) { + value = this.options.min; + } + if (value > this.options.max) { + value = this.options.max; + } + } + if (key == 'disabled') { + if (value) { + this.element.attr("disabled", true); + this.buttons.button("disable"); + } else { + this.element.removeAttr("disabled"); + this.buttons.button("enable"); + } + } + $.Widget.prototype._setOption.call( this, key, value ); + }, + + _setOptions: function( options ) { + $.Widget.prototype._setOptions.call( this, options ); + if ( "value" in options ) { + this._format( this.options.value ); + } + this._aria(); + }, + + _aria: function() { + this.element + .attr('aria-valuemin', this.options.min) + .attr('aria-valuemax', this.options.max) + .attr('aria-valuenow', this.options.value); + }, + + _parse: function(val) { + var input = val; + if (typeof val == 'string') { + // special case for currency formatting until Globalization handles currencies + if (this.options.numberformat == "C" && window.Globalization) { + // parseFloat should accept number format, including currency + var culture = Globalization.culture || Globalization.cultures['default']; + val = val.replace(culture.numberFormat.currency.symbol, ""); + } + val = window.Globalization && this.options.numberformat ? Globalization.parseFloat(val) : +val; + } + return isNaN(val) ? null : val; + }, + + _format: function(num) { + var num = this.options.value; + this.element.val( window.Globalization && this.options.numberformat ? Globalization.format(num, this.options.numberformat) : num ); + }, + + destroy: function() { + this.element + .removeClass('ui-spinner-input') + .removeAttr('disabled') + .removeAttr('autocomplete'); + $.Widget.prototype.destroy.call( this ); + this.uiSpinner.replaceWith(this.element); + }, + + stepUp: function(steps) { + this._spin((steps || 1) * this.options.step); + }, + + stepDown: function(steps) { + this._spin((steps || 1) * -this.options.step); + }, + + pageUp: function(pages) { + this.stepUp((pages || 1) * this.options.page); + }, + + pageDown: function(pages) { + this.stepDown((pages || 1) * this.options.page); + }, + + value: function(newVal) { + if (!arguments.length) { + return this._parse(this.element.val()); + } + this.option('value', newVal); + }, + + widget: function() { + return this.uiSpinner; + } +}); + +})(jQuery); |