jquery-keypad-rails 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);