jquery-keypad-rails 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ build
19
+ jquery-keypad-rails-test
20
+ jquery*keypad*zip
data/BUILD.txt ADDED
@@ -0,0 +1,10 @@
1
+ I haven't made this process beautiful but it works
2
+
3
+ 1 - obtain distribution package from http://keith-wood.name/keypad.html
4
+ drop zip file in this directory
5
+ 2 - edit build.me and change the VER value to match file version
6
+ 3 - run the ./build.me script to make your new vendor/*/* files
7
+ 4 - run the ./tester script to create a rails app and test that it works
8
+ n.b. the tester script is for my OS X machine, YMMV
9
+
10
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jquery-keypad-rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,24 @@
1
+ jQuery Keypad is available under the MIT License. Please see http://keith-wood.name/keypad.html
2
+
3
+ jquery-keypad-rails, packaging of jQuery Keypad by Keith Wood is Copyright (c) 2013 Jay Lawrence
4
+
5
+ MIT License
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Jquery::Keypad::Rails
2
+
3
+ Simple packaging of the jQuery Keypad assets in a gem. See http://keith-wood.name/keypad.html
4
+
5
+ Thanks Keith.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'jquery-keypad-rails'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install jquery-keypad-rails
20
+
21
+ ## Usage
22
+
23
+ No helpers at this time, maybe in the future or from a contributor?
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/build.me ADDED
@@ -0,0 +1,31 @@
1
+ #!/bin/bash
2
+
3
+ VER=1.4.2
4
+
5
+ rm build/*
6
+ pushd build
7
+ unzip ../jquery.keypad.package-${VER}.zip
8
+ popd
9
+
10
+ rm -rf vendor/assets/stylesheets/*
11
+ rm -rf vendor/assets/javascripts/*
12
+ rm -rf vendor/assets/images/*
13
+
14
+ cp build/*.css vendor/assets/stylesheets
15
+ cp build/*.js vendor/assets/javascripts
16
+ cp build/*.png vendor/assets/images
17
+
18
+ cat >lib/jquery/keypad/rails/version.rb <<EOF
19
+ module Jquery
20
+ module Keypad
21
+ module Rails
22
+ VERSION = "${VER}"
23
+ end
24
+ end
25
+ end
26
+ EOF
27
+
28
+ gem update --system
29
+ gem build jquery-keypad-rails.gemspec
30
+ gem push jquery-keypad-rails-${VER}.gem
31
+
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'jquery/keypad/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "jquery-keypad-rails"
8
+ spec.version = Jquery::Keypad::Rails::VERSION
9
+ spec.authors = ["Jay Lawrence"]
10
+ spec.email = ["jay@patientway.com"]
11
+ spec.description = %q{Simple packaging of assets from http://keith-wood.name/keypad.html}
12
+ spec.summary = %q{Assets for jQuery Keypad by Keith Wood}
13
+ spec.homepage = "http://keith-wood.name/keypad.html"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,10 @@
1
+ require "jquery/keypad/rails/version"
2
+
3
+ module Jquery
4
+ module Keypad
5
+ module Rails
6
+ class Engine < ::Rails::Engine
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Jquery
2
+ module Keypad
3
+ module Rails
4
+ VERSION = "1.4.2"
5
+ end
6
+ end
7
+ end
data/tester ADDED
@@ -0,0 +1,15 @@
1
+ # ./build.me
2
+ rm -rf jquery-keypad-rails-test
3
+ rails new jquery-keypad-rails-test
4
+ cd jquery-keypad-rails-test
5
+ rails generate scaffold tester
6
+ rake db:migrate
7
+ echo 'gem "jquery-keypad-rails", :path => ".."' >> Gemfile
8
+ bundle install
9
+ echo '//= require jquery.keypad.min' >> app/assets/javascripts/application.js
10
+ echo '//= require jquery.keypad' >> app/assets/stylesheets/application.css
11
+ cp ../tester.html app/views/testers/index.html.erb
12
+
13
+ (sleep 5; open http://localhost:3000/testers) &
14
+ rails server
15
+
data/tester.html ADDED
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
5
+ <title>jQuery Keypad</title>
6
+ <script type="text/javascript">
7
+ $(function () {
8
+ $('#defaultKeypad').keypad();
9
+ });
10
+ </script>
11
+ </head>
12
+ <body>
13
+ <h1>jQuery Keypad Basics</h1>
14
+ <p>This page demonstrates the very basics of the
15
+ <a href="http://keith-wood.name/keypad.html">jQuery Keypad plugin</a>.
16
+ It contains the minimum requirements for using the plugin and
17
+ can be used as the basis for your own experimentation.</p>
18
+ <p>For more detail see the <a href="http://keith-wood.name/keypadRef.html">documentation reference</a> page.</p>
19
+ <p><input type="text" id="defaultKeypad"></p>
20
+ </body>
21
+ </html>
Binary file
@@ -0,0 +1,35 @@
1
+ /* http://keith-wood.name/keypad.html
2
+ German localisation for the jQuery keypad extension
3
+ Written by Uwe Jakobs(u.jakobs{at}imageco.de) September 2009. */
4
+ (function($) { // hide the namespace
5
+
6
+ $.keypad.qwertzAlphabetic = ['qwertzuiopüß', 'asdfghjklöä', 'yxcvbnm'];
7
+ $.keypad.qwertzLayout =
8
+ ['!"§$%&/()=?`' + $.keypad.BACK + $.keypad.HALF_SPACE + '$£€/',
9
+ '<>°^@{[]}\\~´;:' + $.keypad.HALF_SPACE + '789*',
10
+ $.keypad.qwertzAlphabetic[0] + '+*' +
11
+ $.keypad.HALF_SPACE + '456-',
12
+ $.keypad.HALF_SPACE + $.keypad.qwertzAlphabetic[1] +
13
+ '#\'' + $.keypad.SPACE + '123+',
14
+ '|' + $.keypad.qwertzAlphabetic[2] + 'µ,.-_' +
15
+ $.keypad.SPACE + $.keypad.HALF_SPACE +'.0,=',
16
+ $.keypad.SHIFT + $.keypad.SPACE + $.keypad.SPACE_BAR +
17
+ $.keypad.SPACE + $.keypad.SPACE + $.keypad.SPACE + $.keypad.CLEAR +
18
+ $.keypad.SPACE + $.keypad.SPACE + $.keypad.HALF_SPACE + $.keypad.CLOSE];
19
+ $.keypad.regional['de'] = {
20
+ buttonText: '...', buttonStatus: 'Öffnen',
21
+ closeText: 'schließen', closeStatus: 'schließen',
22
+ clearText: 'löschen', clearStatus: 'Gesamten Inhalt löschen',
23
+ backText: 'zurück', backStatus: 'Letzte Eingabe löschen',
24
+ shiftText: 'umschalten', shiftStatus: 'Zwischen Groß- und Kleinschreibung wechseln',
25
+ spacebarText: '&nbsp;', spacebarStatus: '',
26
+ enterText: 'Enter', enterStatus: '',
27
+ tabText: '→', tabStatus: '',
28
+ alphabeticLayout: $.keypad.qwertzAlphabetic,
29
+ fullLayout: $.keypad.qwertzLayout,
30
+ isAlphabetic: $.keypad.isAlphabetic,
31
+ isNumeric: $.keypad.isNumeric,
32
+ isRTL: false};
33
+ $.keypad.setDefaults($.keypad.regional['de']);
34
+
35
+ })(jQuery);
@@ -0,0 +1,20 @@
1
+ /* http://keith-wood.name/keypad.html
2
+ Spanish initialisation for the jQuery keypad extension
3
+ Written by Cristhian Benitez (cbenitez@gmail.com). */
4
+ (function($) { // hide the namespace
5
+ $.keypad.regional['es'] = {
6
+ buttonText: '...', buttonStatus: 'Abrir el teclado',
7
+ closeText: 'Cerrar', closeStatus: 'Cerrar el teclado',
8
+ clearText: 'Limpiar', clearStatus: 'Eliminar todo el texto',
9
+ backText: 'Volver', backStatus: 'Borrar el caracter anterior',
10
+ shiftText: 'Shift', shiftStatus: 'Cambiar mayusculas/minusculas',
11
+ spacebarText: '&nbsp;', spacebarStatus: '',
12
+ enterText: 'Enter', enterStatus: '',
13
+ tabText: '→', tabStatus: '',
14
+ alphabeticLayout: $.keypad.qwertyAlphabetic,
15
+ fullLayout: $.keypad.qwertyLayout,
16
+ isAlphabetic: $.keypad.isAlphabetic,
17
+ isNumeric: $.keypad.isNumeric,
18
+ isRTL: false};
19
+ $.keypad.setDefaults($.keypad.regional['es']);
20
+ })(jQuery);
@@ -0,0 +1,36 @@
1
+ /* http://keith-wood.name/keypad.html
2
+ French initialisation for the jQuery keypad extension
3
+ Written by Keith Wood (kbwood{at}iinet.com.au) August 2008. */
4
+ (function($) { // hide the namespace
5
+
6
+ $.keypad.azertyAlphabetic = ['àâçéèêîôùû', 'azertyuiop', 'qsdfghjklm', 'wxcvbn'];
7
+ $.keypad.azertyLayout = ['&~#{([_@])}' + $.keypad.HALF_SPACE + '£$€',
8
+ '<>|`°^!?\'"\\' + $.keypad.HALF_SPACE + '/*=',
9
+ $.keypad.HALF_SPACE + $.keypad.azertyAlphabetic[0] + $.keypad.SPACE + '789',
10
+ $.keypad.azertyAlphabetic[1] + '%' + $.keypad.HALF_SPACE + '456',
11
+ $.keypad.HALF_SPACE + $.keypad.azertyAlphabetic[2] + $.keypad.SPACE + '123',
12
+ '§' + $.keypad.azertyAlphabetic[3] + ',.;:' + $.keypad.HALF_SPACE + '-0+',
13
+ $.keypad.SHIFT + $.keypad.SPACE_BAR + $.keypad.HALF_SPACE +
14
+ $.keypad.BACK + $.keypad.CLEAR + $.keypad.CLOSE];
15
+ $.keypad.regional['fr'] = {
16
+ buttonText: '...', buttonStatus: 'Ouvrir',
17
+ closeText: 'Fermer', closeStatus: 'Fermer le pavé numérique',
18
+ clearText: 'Effacer', clearStatus: 'Effacer la valeur',
19
+ backText: 'Défaire', backStatus: 'Effacer la dernière touche',
20
+ shiftText: 'Maj', shiftStatus: '',
21
+ spacebarText: '&nbsp;', spacebarStatus: '',
22
+ enterText: 'Enter', enterStatus: '',
23
+ tabText: '→', tabStatus: '',
24
+ alphabeticLayout: $.keypad.azertyAlphabetic,
25
+ fullLayout: $.keypad.azertyLayout,
26
+ isAlphabetic: $.keypad.isAlphabetic,
27
+ isNumeric: $.keypad.isNumeric,
28
+ isRTL: false};
29
+ $.keypad.setDefaults($.keypad.regional['fr']);
30
+
31
+ function isAlphabetic(ch) {
32
+ return ($.keypad.isAlphabetic(ch) ||
33
+ 'áàäãâçéèëêíìïîóòöõôúùüû'.indexOf(ch) > -1);
34
+ }
35
+
36
+ })(jQuery);
@@ -0,0 +1,20 @@
1
+ /* http://keith-wood.name/keypad.html
2
+ Italian initialisation for the jQuery keypad extension
3
+ Written by Francesco Strappini (www.strx.it). */
4
+ (function($) { // hide the namespace
5
+ $.keypad.regional['it'] = {
6
+ buttonText: '...', buttonStatus: 'Visualizza Tastiera',
7
+ closeText: 'Chiudi', closeStatus: 'Nascondi Tastiera',
8
+ clearText: 'Pulisci', clearStatus: 'Elimina il Testo',
9
+ backText: 'Del', backStatus: 'Elimina l\'ultimo carattere',
10
+ shiftText: 'Shift', shiftStatus: 'Maiuscole/Minuscole',
11
+ spacebarText: '&nbsp;', spacebarStatus: '',
12
+ enterText: 'Enter', enterStatus: 'Ritorno a Capo',
13
+ tabText: '→', tabStatus: '',
14
+ alphabeticLayout: $.keypad.qwertyAlphabetic,
15
+ fullLayout: $.keypad.qwertyLayout,
16
+ isAlphabetic: $.keypad.isAlphabetic,
17
+ isNumeric: $.keypad.isNumeric,
18
+ isRTL: false};
19
+ $.keypad.setDefaults($.keypad.regional['it']);
20
+ })(jQuery);
@@ -0,0 +1,20 @@
1
+ /* http://keith-wood.name/keypad.html
2
+ dutch initialisation for the jQuery keypad extension
3
+ Written by Michiel Mussies (mail{at}webcrafts.nl) November 2009. */
4
+ (function($) { // hide the namespace
5
+ $.keypad.regional['nl'] = {
6
+ buttonText: '...', buttonStatus: 'Open',
7
+ closeText: 'Sluit', closeStatus: 'Sluit',
8
+ clearText: 'Wissen', clearStatus: 'Wis alle tekens',
9
+ backText: 'Terug', backStatus: 'Wis laatste teken',
10
+ shiftText: 'Shift', shiftStatus: 'Activeer hoofd-/kleine letters',
11
+ spacebarText: '&nbsp;', spacebarStatus: '',
12
+ enterText: 'Enter', enterStatus: '',
13
+ tabText: '→', tabStatus: '',
14
+ alphabeticLayout: $.keypad.qwertyAlphabetic,
15
+ fullLayout: $.keypad.qwertyLayout,
16
+ isAlphabetic: $.keypad.isAlphabetic,
17
+ isNumeric: $.keypad.isNumeric,
18
+ isRTL: false};
19
+ $.keypad.setDefaults($.keypad.regional['nl']);
20
+ })(jQuery);
@@ -0,0 +1,20 @@
1
+ /* http://keith-wood.name/keypad.html
2
+ brazilian portuguese initialisation for the jQuery keypad extension
3
+ Written by Israel Rodriguez (yzraeu{at}gmail.com) July 2009. */
4
+ (function($) { // hide the namespace
5
+ $.keypad.regional['pt-BR'] = {
6
+ buttonText: '...', buttonStatus: 'Abrir o teclado',
7
+ closeText: 'Fechar', closeStatus: 'Fechar o teclado',
8
+ clearText: 'Limpar', clearStatus: 'Limpar todo o texto',
9
+ backText: 'Apagar', backStatus: 'Apagar o caractere anterior',
10
+ shiftText: 'Shift', shiftStatus: 'Ativar maiúsculas/minusculas',
11
+ spacebarText: '&nbsp;', spacebarStatus: '',
12
+ enterText: 'Enter', enterStatus: '',
13
+ tabText: '→', tabStatus: '',
14
+ alphabeticLayout: $.keypad.qwertyAlphabetic,
15
+ fullLayout: $.keypad.qwertyLayout,
16
+ isAlphabetic: $.keypad.isAlphabetic,
17
+ isNumeric: $.keypad.isNumeric,
18
+ isRTL: false};
19
+ $.keypad.setDefaults($.keypad.regional['pt-BR']);
20
+ })(jQuery);
@@ -0,0 +1,20 @@
1
+ /* http://keith-wood.name/keypad.html
2
+ Turkish localisation for the jQuery keypad extension
3
+ Written by Yücel Kandemir(yucel{at}21bilisim.com) September 2010. */
4
+ (function($) { // hide the namespace
5
+ $.keypad.regional['tr'] = {
6
+ buttonText: '...', buttonStatus: 'Aç',
7
+ closeText: 'Kapat', closeStatus: 'Klavyeyi Kapatýr',
8
+ clearText: 'Sil', clearStatus: 'Ýçerisini Temizler',
9
+ backText: 'Geri Al', backStatus: 'Son Karakteri Siler.',
10
+ shiftText: 'Büyüt', shiftStatus: 'Büyük Harfle Yazmak Ýçin Seçiniz.',
11
+ spacebarText: '&nbsp;', spacebarStatus: '',
12
+ enterText: 'Enter', enterStatus: '',
13
+ tabText: '→', tabStatus: '',
14
+ alphabeticLayout: $.keypad.qwertyAlphabetic,
15
+ fullLayout: $.keypad.qwertyLayout,
16
+ isAlphabetic: $.keypad.isAlphabetic,
17
+ isNumeric: $.keypad.isNumeric,
18
+ isRTL: false};
19
+ $.keypad.setDefaults($.keypad.regional['tr']);
20
+ })(jQuery);
@@ -0,0 +1,897 @@
1
+ /* http://keith-wood.name/keypad.html
2
+ Keypad field entry extension for jQuery v1.4.2.
3
+ Written by Keith Wood (kbwood{at}iinet.com.au) August 2008.
4
+ Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
5
+ MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
6
+ Please attribute the author if you use it. */
7
+
8
+ (function($) { // hide the namespace
9
+
10
+ var PROP_NAME = 'keypad';
11
+
12
+ /* Keypad manager.
13
+ Use the singleton instance of this class, $.keypad, to interact with the plugin.
14
+ Settings for keypad fields are maintained in instance objects,
15
+ allowing multiple different settings on the same page. */
16
+ function Keypad() {
17
+ this._curInst = null; // The current instance in use
18
+ this._disabledFields = []; // List of keypad fields that have been disabled
19
+ this._keypadShowing = false; // True if the popup panel is showing , false if not
20
+ this._keyCode = 0;
21
+ this._specialKeys = [];
22
+ this.addKeyDef('CLOSE', 'close', function(inst) {
23
+ $.keypad._curInst = (inst._inline ? inst : $.keypad._curInst);
24
+ $.keypad._hideKeypad();
25
+ });
26
+ this.addKeyDef('CLEAR', 'clear', function(inst) { $.keypad._clearValue(inst); });
27
+ this.addKeyDef('BACK', 'back', function(inst) { $.keypad._backValue(inst); });
28
+ this.addKeyDef('SHIFT', 'shift', function(inst) { $.keypad._shiftKeypad(inst); });
29
+ this.addKeyDef('SPACE_BAR', 'spacebar', function(inst) { $.keypad._selectValue(inst, ' '); }, true);
30
+ this.addKeyDef('SPACE', 'space');
31
+ this.addKeyDef('HALF_SPACE', 'half-space');
32
+ this.addKeyDef('ENTER', 'enter', function(inst) { $.keypad._selectValue(inst, '\x0D'); }, true);
33
+ this.addKeyDef('TAB', 'tab', function(inst) { $.keypad._selectValue(inst, '\x09'); }, true);
34
+ // Standard US keyboard alphabetic layout
35
+ this.qwertyAlphabetic = ['qwertyuiop', 'asdfghjkl', 'zxcvbnm'];
36
+ // Standard US keyboard layout
37
+ this.qwertyLayout = ['!@#$%^&*()_=' + this.HALF_SPACE + this.SPACE + this.CLOSE,
38
+ this.HALF_SPACE + '`~[]{}<>\\|/' + this.SPACE + '789',
39
+ 'qwertyuiop\'"' + this.HALF_SPACE + '456',
40
+ this.HALF_SPACE + 'asdfghjkl;:' + this.SPACE + '123',
41
+ this.SPACE + 'zxcvbnm,.?' + this.SPACE + this.HALF_SPACE + '-0+',
42
+ '' + this.TAB + this.ENTER + this.SPACE_BAR + this.SHIFT +
43
+ this.HALF_SPACE + this.BACK + this.CLEAR];
44
+ this.regional = []; // Available regional settings, indexed by language code
45
+ this.regional[''] = { // Default regional settings
46
+ buttonText: '...', // Display text for trigger button
47
+ buttonStatus: 'Open the keypad', // Status text for trigger button
48
+ closeText: 'Close', // Display text for close link
49
+ closeStatus: 'Close the keypad', // Status text for close link
50
+ clearText: 'Clear', // Display text for clear link
51
+ clearStatus: 'Erase all the text', // Status text for clear link
52
+ backText: 'Back', // Display text for back link
53
+ backStatus: 'Erase the previous character', // Status text for back link
54
+ spacebarText: '&nbsp;', // Display text for space bar
55
+ spacebarStatus: 'Space', // Status text for space bar
56
+ enterText: 'Enter', // Display text for carriage return
57
+ enterStatus: 'Carriage return', // Status text for carriage return
58
+ tabText: '→', // Display text for tab
59
+ tabStatus: 'Horizontal tab', // Status text for tab
60
+ shiftText: 'Shift', // Display text for shift link
61
+ shiftStatus: 'Toggle upper/lower case characters', // Status text for shift link
62
+ alphabeticLayout: this.qwertyAlphabetic, // Default layout for alphabetic characters
63
+ fullLayout: this.qwertyLayout, // Default layout for full keyboard
64
+ isAlphabetic: this.isAlphabetic, // Function to determine if character is alphabetic
65
+ isNumeric: this.isNumeric, // Function to determine if character is numeric
66
+ isRTL: false // True if right-to-left language, false if left-to-right
67
+ };
68
+ this._defaults = { // Global defaults for all the keypad instances
69
+ showOn: 'focus', // 'focus' for popup on focus,
70
+ // 'button' for trigger button, or 'both' for either
71
+ buttonImage: '', // URL for trigger button image
72
+ buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
73
+ showAnim: 'show', // Name of jQuery animation for popup
74
+ showOptions: {}, // Options for enhanced animations
75
+ duration: 'normal', // Duration of display/closure
76
+ appendText: '', // Display text following the text field, e.g. showing the format
77
+ useThemeRoller: false, // True to add ThemeRoller classes
78
+ keypadClass: '', // Additional CSS class for the keypad for an instance
79
+ prompt: '', // Display text at the top of the keypad
80
+ layout: ['123' + this.CLOSE, '456' + this.CLEAR, '789' + this.BACK, this.SPACE + '0'], // Layout of keys
81
+ separator: '', // Separator character between keys
82
+ target: null, // Input target for an inline keypad
83
+ keypadOnly: true, // True for entry only via the keypad, false for real keyboard too
84
+ randomiseAlphabetic: false, // True to randomise the alphabetic key positions, false to keep in order
85
+ randomiseNumeric: false, // True to randomise the numeric key positions, false to keep in order
86
+ randomiseOther: false, // True to randomise the other key positions, false to keep in order
87
+ randomiseAll: false, // True to randomise all key positions, false to keep in order
88
+ beforeShow: null, // Callback before showing the keypad
89
+ onKeypress: null, // Callback when a key is selected
90
+ onClose: null // Callback when the panel is closed
91
+ };
92
+ $.extend(this._defaults, this.regional['']);
93
+ this.mainDiv = $('<div class="' + this._mainDivClass + '" style="display: none;"></div>');
94
+ }
95
+
96
+ $.extend(Keypad.prototype, {
97
+ /* Class name added to elements to indicate already configured with keypad. */
98
+ markerClassName: 'hasKeypad',
99
+
100
+ _mainDivClass: 'keypad-popup', // The name of the main keypad division class
101
+ _inlineClass: 'keypad-inline', // The name of the inline marker class
102
+ _appendClass: 'keypad-append', // The name of the append marker class
103
+ _triggerClass: 'keypad-trigger', // The name of the trigger marker class
104
+ _disableClass: 'keypad-disabled', // The name of the disabled covering marker class
105
+ _inlineEntryClass: 'keypad-keyentry', // The name of the inline entry marker class
106
+ _coverClass: 'keypad-cover', // The name of the IE select cover marker class
107
+
108
+ /* Override the default settings for all instances of keypad.
109
+ @param settings (object) the new settings to use as defaults
110
+ @return (object) the manager object */
111
+ setDefaults: function(settings) {
112
+ extendRemove(this._defaults, settings || {});
113
+ return this;
114
+ },
115
+
116
+ /* Add the definition of a special key.
117
+ @param id (string) the identifier for this key - access via $.keypad.<id>
118
+ @param name (string) the prefix for localisation strings and
119
+ the suffix for a class name
120
+ @param action (function) the action performed for this key -
121
+ receives inst as a parameter
122
+ @param noHighlight (boolean) true to suppress highlight when using ThemeRoller
123
+ @return (object) the manager object */
124
+ addKeyDef: function(id, name, action, noHighlight) {
125
+ if (this._keyCode == 32) {
126
+ throw 'Only 32 special keys allowed';
127
+ }
128
+ this[id] = String.fromCharCode(this._keyCode++);
129
+ this._specialKeys.push({code: this[id], id: id, name: name,
130
+ action: action, noHighlight: noHighlight});
131
+ return this;
132
+ },
133
+
134
+ /* Attach the keypad to a jQuery selection.
135
+ @param target (element) the target control
136
+ @param settings (object) the new settings to use for this instance */
137
+ _attachKeypad: function(target, settings) {
138
+ var inline = (target.nodeName.toLowerCase() != 'input' &&
139
+ target.nodeName.toLowerCase() != 'textarea');
140
+ var inst = {_inline: inline,
141
+ _mainDiv: (inline ? $('<div class="' + this._inlineClass + '"></div>') :
142
+ $.keypad.mainDiv), ucase: false};
143
+ inst.settings = $.extend({}, settings || {});
144
+ this._setInput(target, inst);
145
+ this._connectKeypad(target, inst);
146
+ if (inline) {
147
+ $(target).append(inst._mainDiv).
148
+ bind('click.keypad', function() { inst._input.focus(); });
149
+ this._updateKeypad(inst);
150
+ }
151
+ else if ($(target).is(':disabled')) {
152
+ this._disableKeypad(target);
153
+ }
154
+ },
155
+
156
+ /* Determine the input field for the keypad.
157
+ @param target (jQuery) the target control
158
+ @param inst (object) the instance settings */
159
+ _setInput: function(target, inst) {
160
+ inst._input = $(!inst._inline ? target : this._get(inst, 'target') ||
161
+ '<input type="text" class="' + this._inlineEntryClass + '" disabled="disabled"/>');
162
+ if (inst._inline) {
163
+ target = $(target);
164
+ target.find('input').remove();
165
+ if (!this._get(inst, 'target')) {
166
+ target.append(inst._input);
167
+ }
168
+ }
169
+ },
170
+
171
+ /* Attach the keypad to a text field.
172
+ @param target (element) the target text field
173
+ @param inst (object) the instance settings */
174
+ _connectKeypad: function(target, inst) {
175
+ var field = $(target);
176
+ if (field.hasClass(this.markerClassName)) {
177
+ return;
178
+ }
179
+ var appendText = this._get(inst, 'appendText');
180
+ var isRTL = this._get(inst, 'isRTL');
181
+ if (appendText) {
182
+ field[isRTL ? 'before' : 'after'](
183
+ '<span class="' + this._appendClass + '">' + appendText + '</span>');
184
+ }
185
+ if (!inst._inline) {
186
+ var showOn = this._get(inst, 'showOn');
187
+ if (showOn == 'focus' || showOn == 'both') { // pop-up keypad when in the marked field
188
+ field.focus(this._showKeypad).keydown(this._doKeyDown);
189
+ }
190
+ if (showOn == 'button' || showOn == 'both') { // pop-up keypad when button clicked
191
+ var buttonText = this._get(inst, 'buttonText');
192
+ var buttonStatus = this._get(inst, 'buttonStatus');
193
+ var buttonImage = this._get(inst, 'buttonImage');
194
+ var trigger = $(this._get(inst, 'buttonImageOnly') ?
195
+ $('<img src="' + buttonImage + '" alt="' +
196
+ buttonStatus + '" title="' + buttonStatus + '"/>') :
197
+ $('<button type="button" title="' + buttonStatus + '"></button>').
198
+ html(buttonImage == '' ? buttonText :
199
+ $('<img src="' + buttonImage + '" alt="' +
200
+ buttonStatus + '" title="' + buttonStatus + '"/>')));
201
+ field[isRTL ? 'before' : 'after'](trigger);
202
+ trigger.addClass(this._triggerClass).click(function() {
203
+ if ($.keypad._keypadShowing && $.keypad._lastField == target) {
204
+ $.keypad._hideKeypad();
205
+ }
206
+ else {
207
+ $.keypad._showKeypad(target);
208
+ }
209
+ return false;
210
+ });
211
+ }
212
+ }
213
+ inst.saveReadonly = field.attr('readonly');
214
+ field.addClass(this.markerClassName)
215
+ [this._get(inst, 'keypadOnly') ? 'attr' : 'removeAttr']('readonly', true).
216
+ bind('setData.keypad', function(event, key, value) {
217
+ inst.settings[key] = value;
218
+ }).bind('getData.keypad', function(event, key) {
219
+ return this._get(inst, key);
220
+ });
221
+ $.data(target, PROP_NAME, inst);
222
+ },
223
+
224
+ /* Detach keypad from its control.
225
+ @param target (element) the target text field */
226
+ _destroyKeypad: function(target) {
227
+ var $target = $(target);
228
+ if (!$target.hasClass(this.markerClassName)) {
229
+ return;
230
+ }
231
+ var inst = $.data(target, PROP_NAME);
232
+ if (this._curInst == inst) {
233
+ this._hideKeypad();
234
+ }
235
+ $target.siblings('.' + this._appendClass).remove().end().
236
+ siblings('.' + this._triggerClass).remove().end().
237
+ prev('.' + this._inlineEntryClass).remove();
238
+ $target.empty().unbind('focus', this._showKeypad).
239
+ removeClass(this.markerClassName)
240
+ [inst.saveReadonly ? 'attr' : 'removeAttr']('readonly', true);
241
+ $.removeData(inst._input[0], PROP_NAME);
242
+ $.removeData(target, PROP_NAME);
243
+ },
244
+
245
+ /* Enable the keypad for a jQuery selection.
246
+ @param target (element) the target text field */
247
+ _enableKeypad: function(target) {
248
+ var control = $(target);
249
+ if (!control.hasClass(this.markerClassName)) {
250
+ return;
251
+ }
252
+ var nodeName = target.nodeName.toLowerCase();
253
+ if (nodeName == 'input' || nodeName == 'textarea') {
254
+ target.disabled = false;
255
+ control.siblings('button.' + this._triggerClass).
256
+ each(function() { this.disabled = false; }).end().
257
+ siblings('img.' + this._triggerClass).
258
+ css({opacity: '1.0', cursor: ''});
259
+ }
260
+ else if (nodeName == 'div' || nodeName == 'span') {
261
+ control.children('.' + this._disableClass).remove();
262
+ var inst = $.data(target, PROP_NAME);
263
+ inst._mainDiv.find('button').attr('disabled', '');
264
+ }
265
+ this._disabledFields = $.map(this._disabledFields,
266
+ function(value) { return (value == target ? null : value); }); // delete entry
267
+ },
268
+
269
+ /* Disable the keypad for a jQuery selection.
270
+ @param target (element) the target text field */
271
+ _disableKeypad: function(target) {
272
+ var control = $(target);
273
+ if (!control.hasClass(this.markerClassName)) {
274
+ return;
275
+ }
276
+ var nodeName = target.nodeName.toLowerCase();
277
+ if (nodeName == 'input' || nodeName == 'textarea') {
278
+ target.disabled = true;
279
+ control.siblings('button.' + this._triggerClass).
280
+ each(function() { this.disabled = true; }).end().
281
+ siblings('img.' + this._triggerClass).
282
+ css({opacity: '0.5', cursor: 'default'});
283
+ }
284
+ else if (nodeName == 'div' || nodeName == 'span') {
285
+ var inline = control.children('.' + this._inlineClass);
286
+ var offset = inline.offset();
287
+ var relOffset = {left: 0, top: 0};
288
+ inline.parents().each(function() {
289
+ if ($(this).css('position') == 'relative') {
290
+ relOffset = $(this).offset();
291
+ return false;
292
+ }
293
+ });
294
+ control.prepend('<div class="' + this._disableClass + '" style="width: ' +
295
+ inline.outerWidth() + 'px; height: ' + inline.outerHeight() +
296
+ 'px; left: ' + (offset.left - relOffset.left) +
297
+ 'px; top: ' + (offset.top - relOffset.top) + 'px;"></div>');
298
+ var inst = $.data(target, PROP_NAME);
299
+ inst._mainDiv.find('button').attr('disabled', 'disabled');
300
+ }
301
+ this._disabledFields = $.map(this._disabledFields,
302
+ function(value) { return (value == target ? null : value); }); // delete entry
303
+ this._disabledFields[this._disabledFields.length] = target;
304
+ },
305
+
306
+ /* Is the text field disabled as a keypad?
307
+ @param target (element) the target text field
308
+ @return (boolean) true if disabled, false if enabled */
309
+ _isDisabledKeypad: function(target) {
310
+ return (target && $.inArray(target, this._disabledFields) > -1);
311
+ },
312
+
313
+ /* Update the settings for keypad attached to a text field
314
+ @param target (element) the target text field
315
+ @param name (object) the new settings to update or
316
+ (string) the name of the setting to change
317
+ @param value (any) the new value for the setting (omit if above is an object) */
318
+ _changeKeypad: function(target, name, value) {
319
+ var settings = name || {};
320
+ if (typeof name == 'string') {
321
+ settings = {};
322
+ settings[name] = value;
323
+ }
324
+ var inst = $.data(target, PROP_NAME);
325
+ if (inst) {
326
+ if (this._curInst == inst) {
327
+ this._hideKeypad();
328
+ }
329
+ extendRemove(inst.settings, settings);
330
+ this._setInput($(target), inst);
331
+ this._updateKeypad(inst);
332
+ }
333
+ },
334
+
335
+ /* Pop-up the keypad for a given text field.
336
+ @param field (element) the text field attached to the keypad or
337
+ (event) if triggered by focus */
338
+ _showKeypad: function(field) {
339
+ field = field.target || field;
340
+ if ($.keypad._isDisabledKeypad(field) ||
341
+ $.keypad._lastField == field) { // already here
342
+ return;
343
+ }
344
+ var inst = $.data(field, PROP_NAME);
345
+ $.keypad._hideKeypad(null, '');
346
+ $.keypad._lastField = field;
347
+ $.keypad._pos = $.keypad._findPos(field);
348
+ $.keypad._pos[1] += field.offsetHeight; // add the height
349
+ var isFixed = false;
350
+ $(field).parents().each(function() {
351
+ isFixed |= $(this).css('position') == 'fixed';
352
+ return !isFixed;
353
+ });
354
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
355
+ $.keypad._pos[0] -= document.documentElement.scrollLeft;
356
+ $.keypad._pos[1] -= document.documentElement.scrollTop;
357
+ }
358
+ var offset = {left: $.keypad._pos[0], top: $.keypad._pos[1]};
359
+ $.keypad._pos = null;
360
+ // determine sizing offscreen
361
+ inst._mainDiv.css({position: 'absolute', display: 'block', top: '-1000px',
362
+ width: ($.browser.opera ? '1000px' : 'auto')});
363
+ $.keypad._updateKeypad(inst);
364
+ // and adjust position before showing
365
+ offset = $.keypad._checkOffset(inst, offset, isFixed);
366
+ inst._mainDiv.css({position: (isFixed ? 'fixed' : 'absolute'), display: 'none',
367
+ left: offset.left + 'px', top: offset.top + 'px'});
368
+ var showAnim = $.keypad._get(inst, 'showAnim');
369
+ var duration = $.keypad._get(inst, 'duration');
370
+ duration = (duration == 'normal' && $.ui && $.ui.version >= '1.8' ? '_default' : duration);
371
+ var postProcess = function() {
372
+ $.keypad._keypadShowing = true;
373
+ var borders = $.keypad._getBorders(inst._mainDiv);
374
+ inst._mainDiv.find('iframe.' + $.keypad._coverClass). // IE6- only
375
+ css({left: -borders[0], top: -borders[1],
376
+ width: inst._mainDiv.outerWidth(), height: inst._mainDiv.outerHeight()});
377
+ };
378
+ if ($.effects && $.effects[showAnim]) {
379
+ var data = inst._mainDiv.data(); // Update old effects data
380
+ for (var key in data) {
381
+ if (key.match(/^ec\.storage\./)) {
382
+ data[key] = inst._mainDiv.css(key.replace(/ec\.storage\./, ''));
383
+ }
384
+ }
385
+ inst._mainDiv.data(data).show(showAnim,
386
+ $.keypad._get(inst, 'showOptions'), duration, postProcess);
387
+ }
388
+ else {
389
+ inst._mainDiv[showAnim || 'show']((showAnim ? duration : ''), postProcess);
390
+ }
391
+ if (!showAnim) {
392
+ postProcess();
393
+ }
394
+ if (inst._input[0].type != 'hidden') {
395
+ inst._input[0].focus();
396
+ }
397
+ $.keypad._curInst = inst;
398
+ },
399
+
400
+ /* Generate the keypad content.
401
+ @param inst (object) the instance settings */
402
+ _updateKeypad: function(inst) {
403
+ var borders = this._getBorders(inst._mainDiv);
404
+ inst._mainDiv.empty().append(this._generateHTML(inst)).
405
+ find('iframe.' + this._coverClass). // IE6- only
406
+ css({left: -borders[0], top: -borders[1],
407
+ width: inst._mainDiv.outerWidth(), height: inst._mainDiv.outerHeight()});
408
+ inst._mainDiv.removeClass().addClass(this._get(inst, 'keypadClass') +
409
+ (this._get(inst, 'useThemeRoller') ? ' ui-widget ui-widget-content' : '') +
410
+ (this._get(inst, 'isRTL') ? ' keypad-rtl' : '') + ' ' +
411
+ (inst._inline ? this._inlineClass : this._mainDivClass));
412
+ var beforeShow = this._get(inst, 'beforeShow');
413
+ if (beforeShow) {
414
+ beforeShow.apply((inst._input ? inst._input[0] : null),
415
+ [inst._mainDiv, inst]);
416
+ }
417
+ },
418
+
419
+ /* Retrieve the size of left and top borders for an element.
420
+ @param elem (jQuery object) the element of interest
421
+ @return (number[2]) the left and top borders */
422
+ _getBorders: function(elem) {
423
+ var convert = function(value) {
424
+ var extra = ($.browser.msie ? 1 : 0);
425
+ return {thin: 1 + extra, medium: 3 + extra, thick: 5 + extra}[value] || value;
426
+ };
427
+ return [parseFloat(convert(elem.css('border-left-width'))),
428
+ parseFloat(convert(elem.css('border-top-width')))];
429
+ },
430
+
431
+ /* Check positioning to remain on screen.
432
+ @param inst (object) the instance settings
433
+ @param offset (object) the current offset
434
+ @param isFixed (boolean) true if the text field is fixed in position
435
+ @return (object) the updated offset */
436
+ _checkOffset: function(inst, offset, isFixed) {
437
+ var pos = inst._input ? this._findPos(inst._input[0]) : null;
438
+ var browserWidth = window.innerWidth || document.documentElement.clientWidth;
439
+ var browserHeight = window.innerHeight || document.documentElement.clientHeight;
440
+ var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
441
+ var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
442
+ if (($.browser.msie && parseInt($.browser.version, 10) < 7) || $.browser.opera) {
443
+ // recalculate width as otherwise set to 100%
444
+ var width = 0;
445
+ inst._mainDiv.find(':not(div,iframe)').each(function() {
446
+ width = Math.max(width, this.offsetLeft + $(this).outerWidth() +
447
+ parseInt($(this).css('margin-right'), 10));
448
+ });
449
+ inst._mainDiv.css('width', width);
450
+ }
451
+ // reposition keypad panel horizontally if outside the browser window
452
+ if (this._get(inst, 'isRTL') ||
453
+ (offset.left + inst._mainDiv.outerWidth() - scrollX) > browserWidth) {
454
+ offset.left = Math.max((isFixed ? 0 : scrollX),
455
+ pos[0] + (inst._input ? inst._input.outerWidth() : 0) -
456
+ (isFixed ? scrollX : 0) - inst._mainDiv.outerWidth() -
457
+ (isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0));
458
+ }
459
+ else {
460
+ offset.left -= (isFixed ? scrollX : 0);
461
+ }
462
+ // reposition keypad panel vertically if outside the browser window
463
+ if ((offset.top + inst._mainDiv.outerHeight() - scrollY) > browserHeight) {
464
+ offset.top = Math.max((isFixed ? 0 : scrollY),
465
+ pos[1] - (isFixed ? scrollY : 0) - inst._mainDiv.outerHeight() -
466
+ (isFixed && $.browser.opera ? document.documentElement.scrollTop : 0));
467
+ }
468
+ else {
469
+ offset.top -= (isFixed ? scrollY : 0);
470
+ }
471
+ return offset;
472
+ },
473
+
474
+ /* Find an object's position on the screen.
475
+ @param obj (element) the element to find the position for
476
+ @return (int[2]) the element's position */
477
+ _findPos: function(obj) {
478
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
479
+ obj = obj.nextSibling;
480
+ }
481
+ var position = $(obj).offset();
482
+ return [position.left, position.top];
483
+ },
484
+
485
+ /* Hide the keypad from view.
486
+ @param field (element) the text field attached to the keypad
487
+ @param duration (string) the duration over which to close the keypad */
488
+ _hideKeypad: function(field, duration) {
489
+ var inst = this._curInst;
490
+ if (!inst || (field && inst != $.data(field, PROP_NAME))) {
491
+ return;
492
+ }
493
+ if (this._keypadShowing) {
494
+ duration = (duration != null ? duration : this._get(inst, 'duration'));
495
+ duration = (duration == 'normal' && $.ui && $.ui.version >= '1.8' ? '_default' : duration);
496
+ var showAnim = this._get(inst, 'showAnim');
497
+ if ($.effects && $.effects[showAnim]) {
498
+ inst._mainDiv.hide(showAnim, this._get(inst, 'showOptions'), duration);
499
+ }
500
+ else {
501
+ inst._mainDiv[(showAnim == 'slideDown' ? 'slideUp' :
502
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))](showAnim ? duration : '');
503
+ }
504
+ }
505
+ var onClose = this._get(inst, 'onClose');
506
+ if (onClose) {
507
+ onClose.apply((inst._input ? inst._input[0] : null), // trigger custom callback
508
+ [inst._input.val(), inst]);
509
+ }
510
+ if (this._keypadShowing) {
511
+ this._keypadShowing = false;
512
+ this._lastField = null;
513
+ }
514
+ if (inst._inline) {
515
+ inst._input.val('');
516
+ }
517
+ this._curInst = null;
518
+ },
519
+
520
+ /* Handle keystrokes.
521
+ @param e (event) the key event */
522
+ _doKeyDown: function(e) {
523
+ if (e.keyCode == 9) { // Tab out
524
+ $.keypad.mainDiv.stop(true, true);
525
+ $.keypad._hideKeypad();
526
+ }
527
+ },
528
+
529
+ /* Close keypad if clicked elsewhere.
530
+ @param event (event) the mouseclick details */
531
+ _checkExternalClick: function(event) {
532
+ if (!$.keypad._curInst) {
533
+ return;
534
+ }
535
+ var target = $(event.target);
536
+ if (!target.parents().andSelf().is('.' + $.keypad._mainDivClass) &&
537
+ !target.hasClass($.keypad.markerClassName) &&
538
+ !target.parents().andSelf().hasClass($.keypad._triggerClass) &&
539
+ $.keypad._keypadShowing) {
540
+ $.keypad._hideKeypad();
541
+ }
542
+ },
543
+
544
+ /* Toggle between upper and lower case.
545
+ @param inst (object) the instance settings */
546
+ _shiftKeypad: function(inst) {
547
+ inst.ucase = !inst.ucase;
548
+ this._updateKeypad(inst);
549
+ inst._input.focus(); // for further typing
550
+ },
551
+
552
+ /* Erase the text field.
553
+ @param inst (object) the instance settings */
554
+ _clearValue: function(inst) {
555
+ this._setValue(inst, '', 0);
556
+ this._notifyKeypress(inst, $.keypad.DEL);
557
+ },
558
+
559
+ /* Erase the last character.
560
+ @param inst (object) the instance settings */
561
+ _backValue: function(inst) {
562
+ var field = inst._input[0];
563
+ var value = inst._input.val();
564
+ var range = [value.length, value.length];
565
+ if (field.setSelectionRange) { // Mozilla
566
+ range = (inst._input.attr('readonly') || inst._input.attr('disabled') ?
567
+ range : [field.selectionStart, field.selectionEnd]);
568
+ }
569
+ else if (field.createTextRange) { // IE
570
+ range = (inst._input.attr('readonly') || inst._input.attr('disabled') ?
571
+ range : this._getIERange(field));
572
+ }
573
+ this._setValue(inst, (value.length == 0 ? '' :
574
+ value.substr(0, range[0] - 1) + value.substr(range[1])), range[0] - 1);
575
+ this._notifyKeypress(inst, $.keypad.BS);
576
+ },
577
+
578
+ /* Update the text field with the selected value.
579
+ @param inst (object) the instance settings
580
+ @param value (string) the new character to add */
581
+ _selectValue: function(inst, value) {
582
+ this.insertValue(inst._input[0], value);
583
+ this._setValue(inst, inst._input.val());
584
+ this._notifyKeypress(inst, value);
585
+ },
586
+
587
+ /* Update the text field with the selected value.
588
+ @param input (element) the input field or
589
+ (jQuery) jQuery collection
590
+ @param value (string) the new character to add */
591
+ insertValue: function(input, value) {
592
+ input = (input.jquery ? input : $(input));
593
+ var field = input[0];
594
+ var newValue = input.val();
595
+ var range = [newValue.length, newValue.length];
596
+ if (field.setSelectionRange) { // Mozilla
597
+ range = (input.attr('readonly') || input.attr('disabled') ?
598
+ range : [field.selectionStart, field.selectionEnd]);
599
+ }
600
+ else if (field.createTextRange) { // IE
601
+ range = (input.attr('readonly') || input.attr('disabled') ?
602
+ range : this._getIERange(field));
603
+ }
604
+ input.val(newValue.substr(0, range[0]) + value + newValue.substr(range[1]));
605
+ pos = range[0] + value.length;
606
+ if (input.is(':visible')) {
607
+ input.focus(); // for further typing
608
+ }
609
+ if (field.setSelectionRange) { // Mozilla
610
+ if (input.is(':visible')) {
611
+ field.setSelectionRange(pos, pos);
612
+ }
613
+ }
614
+ else if (field.createTextRange) { // IE
615
+ range = field.createTextRange();
616
+ range.move('character', pos);
617
+ range.select();
618
+ }
619
+ },
620
+
621
+ /* Get the coordinates for the selected area in the text field in IE.
622
+ @param field (element) the target text field
623
+ @return (int[2]) the start and end positions of the selection */
624
+ _getIERange: function(field) {
625
+ field.focus();
626
+ var selectionRange = document.selection.createRange().duplicate();
627
+ // Use two ranges: before and selection
628
+ var beforeRange = this._getIETextRange(field);
629
+ beforeRange.setEndPoint('EndToStart', selectionRange);
630
+ // Check each range for trimmed newlines by shrinking the range by one
631
+ // character and seeing if the text property has changed. If it has not
632
+ // changed then we know that IE has trimmed a \r\n from the end.
633
+ var checkCRLF = function(range) {
634
+ var origText = range.text;
635
+ var text = origText;
636
+ var finished = false;
637
+ while (true) {
638
+ if (range.compareEndPoints('StartToEnd', range) == 0) {
639
+ break;
640
+ }
641
+ else {
642
+ range.moveEnd('character', -1);
643
+ if (range.text == origText) {
644
+ text += '\r\n';
645
+ }
646
+ else {
647
+ break;
648
+ }
649
+ }
650
+ }
651
+ return text;
652
+ };
653
+ var beforeText = checkCRLF(beforeRange);
654
+ var selectionText = checkCRLF(selectionRange);
655
+ return [beforeText.length, beforeText.length + selectionText.length];
656
+ },
657
+
658
+ /* Create an IE text range for the text field.
659
+ @param field (element) the target text field
660
+ @return (object) the corresponding text range */
661
+ _getIETextRange: function(field) {
662
+ var isInput = (field.nodeName.toLowerCase() == 'input');
663
+ var range = (isInput ? field.createTextRange() : document.body.createTextRange());
664
+ if (!isInput) {
665
+ range.moveToElementText(field); // Selects all the text for a textarea
666
+ }
667
+ return range;
668
+ },
669
+
670
+ /* Set the text field to the selected value,
671
+ and trigger any on change event.
672
+ @param inst (object) the instance settings
673
+ @param value (string) the new value for the text field */
674
+ _setValue: function(inst, value) {
675
+ var maxlen = inst._input.attr('maxlength');
676
+ if (maxlen > -1) {
677
+ value = value.substr(0, maxlen);
678
+ }
679
+ inst._input.val(value);
680
+ if (!this._get(inst, 'onKeypress')) {
681
+ inst._input.trigger('change'); // fire the change event
682
+ }
683
+ },
684
+
685
+ _notifyKeypress: function(inst, key) {
686
+ var onKeypress = this._get(inst, 'onKeypress');
687
+ if (onKeypress) { // trigger custom callback
688
+ onKeypress.apply((inst._input ? inst._input[0] : null),
689
+ [key, inst._input.val(), inst]);
690
+ }
691
+ },
692
+
693
+ /* Get a setting value, defaulting if necessary.
694
+ @param inst (object) the instance settings
695
+ @param name (string) the name of the setting
696
+ @return (any) the value of the setting, or its default if not set explicitly */
697
+ _get: function(inst, name) {
698
+ return inst.settings[name] !== undefined ?
699
+ inst.settings[name] : this._defaults[name];
700
+ },
701
+
702
+ /* Generate the HTML for the current state of the keypad.
703
+ @param inst (object) the instance settings
704
+ @return (jQuery) the HTML for this keypad */
705
+ _generateHTML: function(inst) {
706
+ var useTR = this._get(inst, 'useThemeRoller');
707
+ var isRTL = this._get(inst, 'isRTL');
708
+ var prompt = this._get(inst, 'prompt');
709
+ var separator = this._get(inst, 'separator');
710
+ var html = (!prompt ? '' : '<div class="keypad-prompt' +
711
+ (useTR ? ' ui-widget-header ui-corner-all' : '') + '">' + prompt + '</div>');
712
+ var layout = this._randomiseLayout(inst);
713
+ for (var i = 0; i < layout.length; i++) {
714
+ html += '<div class="keypad-row">';
715
+ var keys = layout[i].split(separator);
716
+ for (var j = 0; j < keys.length; j++) {
717
+ if (inst.ucase) {
718
+ keys[j] = keys[j].toUpperCase();
719
+ }
720
+ var keyDef = this._specialKeys[keys[j].charCodeAt(0)];
721
+ if (keyDef) {
722
+ html += (keyDef.action ? '<button type="button" class="keypad-special keypad-' +
723
+ keyDef.name + (useTR ? ' ui-corner-all ui-state-default' +
724
+ (keyDef.noHighlight ? '' : ' ui-state-highlight') : '') +
725
+ '" title="' + this._get(inst, keyDef.name + 'Status') + '">' +
726
+ (this._get(inst, keyDef.name + 'Text') || '&nbsp;') + '</button>' :
727
+ '<div class="keypad-' + keyDef.name + '"></div>');
728
+ }
729
+ else {
730
+ html += '<button type="button" ' +
731
+ 'class="keypad-key' + (useTR ? ' ui-corner-all ui-state-default' : '') + '">' +
732
+ (keys[j] == ' ' ? '&nbsp;' : keys[j]) + '</button>';
733
+ }
734
+ }
735
+ html += '</div>';
736
+ }
737
+ html += '<div style="clear: both;"></div>' +
738
+ (!inst._inline && $.browser.msie && parseInt($.browser.version, 10) < 7 ?
739
+ '<iframe src="javascript:false;" class="' + $.keypad._coverClass + '"></iframe>' : '');
740
+ html = $(html);
741
+ var thisInst = inst;
742
+ var activeClasses = 'keypad-key-down' + (useTR ? ' ui-state-active' : '');
743
+ html.find('button').mousedown(function() { $(this).addClass(activeClasses); }).
744
+ mouseup(function() { $(this).removeClass(activeClasses); }).
745
+ mouseout(function() { $(this).removeClass(activeClasses); }).
746
+ filter('.keypad-key').click(function() { $.keypad._selectValue(thisInst, $(this).text()); });
747
+ $.each(this._specialKeys, function(i, keyDef) {
748
+ html.find('.keypad-' + keyDef.name).click(function() {
749
+ keyDef.action.apply(thisInst._input, [thisInst]);
750
+ });
751
+ });
752
+ return html;
753
+ },
754
+
755
+ /* Check whether characters should be randomised,
756
+ and, if so, produce the randomised layout.
757
+ @param inst (object) the instance settings
758
+ @return (string[]) the layout with any requested randomisations applied */
759
+ _randomiseLayout: function(inst) {
760
+ var randomiseNumeric = this._get(inst, 'randomiseNumeric');
761
+ var randomiseAlpha = this._get(inst, 'randomiseAlphabetic');
762
+ var randomiseOther = this._get(inst, 'randomiseOther');
763
+ var randomiseAll = this._get(inst, 'randomiseAll');
764
+ var layout = this._get(inst, 'layout');
765
+ if (!randomiseNumeric && !randomiseAlpha && !randomiseOther && !randomiseAll) {
766
+ return layout;
767
+ }
768
+ var isNumeric = this._get(inst, 'isNumeric');
769
+ var isAlphabetic = this._get(inst, 'isAlphabetic');
770
+ var separator = this._get(inst, 'separator');
771
+ var numerics = [];
772
+ var alphas = [];
773
+ var others = [];
774
+ var newLayout = [];
775
+ // Find characters of different types
776
+ for (var i = 0; i < layout.length; i++) {
777
+ newLayout[i] = '';
778
+ var keys = layout[i].split(separator);
779
+ for (var j = 0; j < keys.length; j++) {
780
+ if (this._isControl(keys[j])) {
781
+ continue;
782
+ }
783
+ if (randomiseAll) {
784
+ others.push(keys[j]);
785
+ }
786
+ else if (isNumeric(keys[j])) {
787
+ numerics.push(keys[j]);
788
+ }
789
+ else if (isAlphabetic(keys[j])) {
790
+ alphas.push(keys[j]);
791
+ }
792
+ else {
793
+ others.push(keys[j]);
794
+ }
795
+ }
796
+ }
797
+ // Shuffle them
798
+ if (randomiseNumeric) {
799
+ this._shuffle(numerics);
800
+ }
801
+ if (randomiseAlpha) {
802
+ this._shuffle(alphas);
803
+ }
804
+ if (randomiseOther || randomiseAll) {
805
+ this._shuffle(others);
806
+ }
807
+ var n = 0;
808
+ var a = 0;
809
+ var o = 0;
810
+ // And replace them in the layout
811
+ for (var i = 0; i < layout.length; i++) {
812
+ var keys = layout[i].split(separator);
813
+ for (var j = 0; j < keys.length; j++) {
814
+ newLayout[i] += (this._isControl(keys[j]) ? keys[j] :
815
+ (randomiseAll ? others[o++] :
816
+ (isNumeric(keys[j]) ? numerics[n++] :
817
+ (isAlphabetic(keys[j]) ? alphas[a++] :
818
+ others[o++])))) + separator;
819
+ }
820
+ }
821
+ return newLayout;
822
+ },
823
+
824
+ /* Is a given character a control character?
825
+ @param ch (char) the character to test
826
+ @return (boolean) true if a control character, false if not */
827
+ _isControl: function(ch) {
828
+ return ch < ' ';
829
+ },
830
+
831
+ /* Is a given character alphabetic?
832
+ @param ch (char) the character to test
833
+ @return (boolean) true if alphabetic, false if not */
834
+ isAlphabetic: function(ch) {
835
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
836
+ },
837
+
838
+ /* Is a given character numeric?
839
+ @param ch (char) the character to test
840
+ @return (boolean) true if numeric, false if not */
841
+ isNumeric: function(ch) {
842
+ return (ch >= '0' && ch <= '9');
843
+ },
844
+
845
+ /* Randomise the contents of an array.
846
+ @param values (string[]) the array to rearrange */
847
+ _shuffle: function(values) {
848
+ for (var i = values.length - 1; i > 0; i--) {
849
+ var j = Math.floor(Math.random() * values.length);
850
+ var ch = values[i];
851
+ values[i] = values[j];
852
+ values[j] = ch;
853
+ }
854
+ }
855
+ });
856
+
857
+ /* jQuery extend now ignores nulls!
858
+ @param target (object) the object to extend
859
+ @param props (object) the new settings
860
+ @return (object) the updated target */
861
+ function extendRemove(target, props) {
862
+ $.extend(target, props);
863
+ for (var name in props) {
864
+ if (props[name] == null || props[name] == undefined) {
865
+ target[name] = props[name];
866
+ }
867
+ }
868
+ return target;
869
+ };
870
+
871
+ /* Invoke the keypad functionality.
872
+ @param options (string) a command, optionally followed by additional parameters or
873
+ (object) settings for attaching new keypad functionality
874
+ @return (object) the jQuery object */
875
+ $.fn.keypad = function(options) {
876
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
877
+ if (options == 'isDisabled') {
878
+ return $.keypad['_' + options + 'Keypad'].
879
+ apply($.keypad, [this[0]].concat(otherArgs));
880
+ }
881
+ return this.each(function() {
882
+ typeof options == 'string' ?
883
+ $.keypad['_' + options + 'Keypad'].
884
+ apply($.keypad, [this].concat(otherArgs)) :
885
+ $.keypad._attachKeypad(this, options);
886
+ });
887
+ };
888
+
889
+ $.keypad = new Keypad(); // singleton instance
890
+
891
+ // Add the keypad division and external click check
892
+ $(function() {
893
+ $(document.body).append($.keypad.mainDiv).
894
+ mousedown($.keypad._checkExternalClick);
895
+ });
896
+
897
+ })(jQuery);