placeholder-gem 3.0.0.0
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/MIT-LICENSE +20 -0
- data/README.md +47 -0
- data/Rakefile +126 -0
- data/lib/placeholder-gem.rb +6 -0
- data/lib/placeholder-gem/version.rb +3 -0
- data/vendor/assets/javascripts/placeholder.js +436 -0
- data/vendor/assets/javascripts/v3.0.0/placeholder.js +436 -0
- metadata +134 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2013 UC Berkeley - ETS
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Placeholder Gem
|
|
2
|
+
|
|
3
|
+
[Placeholders.js HTML polyfill][placeholder] as a Ruby gem.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
Add the gem to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem "placeholder-gem"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And run
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
in the terminal to download the resources.
|
|
19
|
+
|
|
20
|
+
### Adding the files to your projects
|
|
21
|
+
|
|
22
|
+
In order for the files to load, you'll need to do add them.
|
|
23
|
+
|
|
24
|
+
`application.js`:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
//= require placeholder
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
and you should be good to go.
|
|
31
|
+
|
|
32
|
+
## Updating this plug-in
|
|
33
|
+
|
|
34
|
+
If you would like to update this gem you should take the following steps:
|
|
35
|
+
|
|
36
|
+
1. `rake download VERSION=vX.X.X`. If you don't specify the version, it will get the latest one.
|
|
37
|
+
1. `rake tag VERSION=vX.X.X` will tag the version you've specified as the standard version.
|
|
38
|
+
1. Make a Pull request
|
|
39
|
+
|
|
40
|
+
Then the maintainer of the gem will need to do the following steps:
|
|
41
|
+
|
|
42
|
+
1. Update the version [lib/placeholder-gem/version.rb](lib/placeholder-gem/version.rb)
|
|
43
|
+
1. Run ``gem build placeholder-gem.gemspec`` to package the gem
|
|
44
|
+
1. Once satisfied, push the gem up to RubyGems.org with ``gem push placeholder-gem-<VERSION>.gem``
|
|
45
|
+
1. Update [the changelog](CHANGELOG.md)
|
|
46
|
+
|
|
47
|
+
[placeholder]: https://github.com/jamesallardice/Placeholders.js
|
data/Rakefile
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
Bundler::GemHelper.install_tasks
|
|
9
|
+
|
|
10
|
+
require 'rake'
|
|
11
|
+
require 'open-uri'
|
|
12
|
+
require 'json'
|
|
13
|
+
|
|
14
|
+
dir_assets = 'vendor/assets/'
|
|
15
|
+
dir_js = dir_assets + 'javascripts'
|
|
16
|
+
|
|
17
|
+
desc 'Downloads the Placeholder.js JavaScript files from GitHub'
|
|
18
|
+
task :download do |t|
|
|
19
|
+
|
|
20
|
+
def create_dir dir, version
|
|
21
|
+
dir_name = File.join(dir, version)
|
|
22
|
+
Dir.mkdir(dir_name) unless Dir.exist?(dir_name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def download_file urls, dir, filename, version
|
|
26
|
+
Dir.chdir(File.join(dir, version)) do
|
|
27
|
+
if File.exists?(filename)
|
|
28
|
+
puts " #{dir + '/' + version + '/' + filename} already exists"
|
|
29
|
+
next
|
|
30
|
+
end
|
|
31
|
+
code = ''
|
|
32
|
+
urls.reverse_each do |url|
|
|
33
|
+
puts " #{url}"
|
|
34
|
+
code += open(url).read
|
|
35
|
+
end
|
|
36
|
+
open(filename, "w") { |file| file.write(code)}
|
|
37
|
+
puts " Concatenating to #{dir + '/' + version + '/' + filename}"
|
|
38
|
+
puts " Done!"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Specify which version you want
|
|
43
|
+
version = ENV['VERSION']
|
|
44
|
+
version ||= 'latest'
|
|
45
|
+
puts "Target version: #{version.chomp('/')}"
|
|
46
|
+
|
|
47
|
+
# Get the different versions
|
|
48
|
+
tags_url = 'https://api.github.com/repos/jamesallardice/Placeholders.js/tags'
|
|
49
|
+
result = JSON.parse(open(tags_url).read)
|
|
50
|
+
versions = result.map { |item| item['name'] }
|
|
51
|
+
|
|
52
|
+
puts "Available versions: #{versions.inspect}"
|
|
53
|
+
|
|
54
|
+
# Figuring out which version to get
|
|
55
|
+
if versions.include? version
|
|
56
|
+
get_version = version
|
|
57
|
+
else
|
|
58
|
+
get_version = versions.first
|
|
59
|
+
|
|
60
|
+
if !(versions.include? version) && version != 'latest'
|
|
61
|
+
puts "Warning: The version you've specified: '#{version}' wasn't found, using the latest version instead: '#{get_version}'"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Get the right commit
|
|
66
|
+
commit = result.select { |item| item['name'] == get_version }.first['commit']['sha']
|
|
67
|
+
puts "We'll use the following commit to get the files: #{commit}"
|
|
68
|
+
|
|
69
|
+
# Creating the necessary directories
|
|
70
|
+
create_dir dir_js, get_version
|
|
71
|
+
|
|
72
|
+
# Download all the right files
|
|
73
|
+
url_root = 'https://raw.github.com/jamesallardice/Placeholders.js/' + commit + '/'
|
|
74
|
+
url_tree = 'https://api.github.com/repos/jamesallardice/Placeholders.js/git/trees/' + commit + '?recursive=1'
|
|
75
|
+
urls = []
|
|
76
|
+
|
|
77
|
+
result_tree = JSON.parse(open(url_tree).read)
|
|
78
|
+
|
|
79
|
+
result_tree['tree'].each do |file|
|
|
80
|
+
if file['path'] =~ /lib\/\w+.js/
|
|
81
|
+
url = url_root + file['path']
|
|
82
|
+
urls.push(url)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
puts "Searching #{url_tree}"
|
|
86
|
+
puts "Downloading..."
|
|
87
|
+
download_file urls, dir_js, 'placeholder.js', get_version
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
desc 'Tag the default file versions for asset helpers'
|
|
92
|
+
task :tag do |t|
|
|
93
|
+
Rake::Task['tag_default'].invoke
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
task :tag_default do |t|
|
|
97
|
+
|
|
98
|
+
def copy_files dir, version
|
|
99
|
+
Dir.entries(File.join(dir, version)).each do |file|
|
|
100
|
+
file_source = File.join(dir, version, file)
|
|
101
|
+
file_destination = File.join(dir, file)
|
|
102
|
+
if File.file?(file_source)
|
|
103
|
+
FileUtils.cp file_source, file_destination, { verbose: true }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
version = ENV['VERSION']
|
|
109
|
+
version ||= 'latest'
|
|
110
|
+
|
|
111
|
+
puts "Target version: #{version.chomp('/')}"
|
|
112
|
+
|
|
113
|
+
Dir.chdir(dir_js) do
|
|
114
|
+
version_directories = Dir.glob("*").select { |fn| File.directory?(fn) }.sort.reverse
|
|
115
|
+
|
|
116
|
+
puts "Available versions: #{version_directories.inspect}"
|
|
117
|
+
if !(version_directories.include? version)
|
|
118
|
+
if version != 'latest'
|
|
119
|
+
puts "WARN: Specified version='#{version}' not found, setting to latest version: '#{version_directories.first}'"
|
|
120
|
+
end
|
|
121
|
+
version = version_directories.first
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
copy_files dir_js, version
|
|
126
|
+
end
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2012 James Allardice
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
|
7
|
+
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
8
|
+
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
*
|
|
10
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
11
|
+
*
|
|
12
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
13
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
14
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
15
|
+
* THE SOFTWARE.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Defines the global Placeholders object along with various utility methods
|
|
19
|
+
(function (global) {
|
|
20
|
+
|
|
21
|
+
"use strict";
|
|
22
|
+
|
|
23
|
+
// Cross-browser DOM event binding
|
|
24
|
+
function addEventListener(elem, event, fn) {
|
|
25
|
+
if (elem.addEventListener) {
|
|
26
|
+
return elem.addEventListener(event, fn, false);
|
|
27
|
+
}
|
|
28
|
+
if (elem.attachEvent) {
|
|
29
|
+
return elem.attachEvent("on" + event, fn);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check whether an item is in an array (we don't use Array.prototype.indexOf so we don't clobber any existing polyfills - this is a really simple alternative)
|
|
34
|
+
function inArray(arr, item) {
|
|
35
|
+
var i, len;
|
|
36
|
+
for (i = 0, len = arr.length; i < len; i++) {
|
|
37
|
+
if (arr[i] === item) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Move the caret to the index position specified. Assumes that the element has focus
|
|
45
|
+
function moveCaret(elem, index) {
|
|
46
|
+
var range;
|
|
47
|
+
if (elem.createTextRange) {
|
|
48
|
+
range = elem.createTextRange();
|
|
49
|
+
range.move("character", index);
|
|
50
|
+
range.select();
|
|
51
|
+
} else if (elem.selectionStart) {
|
|
52
|
+
elem.focus();
|
|
53
|
+
elem.setSelectionRange(index, index);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Attempt to change the type property of an input element
|
|
58
|
+
function changeType(elem, type) {
|
|
59
|
+
try {
|
|
60
|
+
elem.type = type;
|
|
61
|
+
return true;
|
|
62
|
+
} catch (e) {
|
|
63
|
+
// You can't change input type in IE8 and below
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Expose public methods
|
|
69
|
+
global.Placeholders = {
|
|
70
|
+
Utils: {
|
|
71
|
+
addEventListener: addEventListener,
|
|
72
|
+
inArray: inArray,
|
|
73
|
+
moveCaret: moveCaret,
|
|
74
|
+
changeType: changeType
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
}(this));
|
|
79
|
+
(function (global) {
|
|
80
|
+
|
|
81
|
+
"use strict";
|
|
82
|
+
|
|
83
|
+
var validTypes = [
|
|
84
|
+
"text",
|
|
85
|
+
"search",
|
|
86
|
+
"url",
|
|
87
|
+
"tel",
|
|
88
|
+
"email",
|
|
89
|
+
"password",
|
|
90
|
+
"number",
|
|
91
|
+
"textarea"
|
|
92
|
+
],
|
|
93
|
+
|
|
94
|
+
// The list of keycodes that are not allowed when the polyfill is configured to hide-on-input
|
|
95
|
+
badKeys = [
|
|
96
|
+
|
|
97
|
+
// The following keys all cause the caret to jump to the end of the input value
|
|
98
|
+
27, // Escape
|
|
99
|
+
33, // Page up
|
|
100
|
+
34, // Page down
|
|
101
|
+
35, // End
|
|
102
|
+
36, // Home
|
|
103
|
+
|
|
104
|
+
// Arrow keys allow you to move the caret manually, which should be prevented when the placeholder is visible
|
|
105
|
+
37, // Left
|
|
106
|
+
38, // Up
|
|
107
|
+
39, // Right
|
|
108
|
+
40, // Down
|
|
109
|
+
|
|
110
|
+
// The following keys allow you to modify the placeholder text by removing characters, which should be prevented when the placeholder is visible
|
|
111
|
+
8, // Backspace
|
|
112
|
+
46 // Delete
|
|
113
|
+
],
|
|
114
|
+
|
|
115
|
+
// Styling variables
|
|
116
|
+
placeholderStyleColor = "#ccc",
|
|
117
|
+
placeholderClassName = "placeholdersjs",
|
|
118
|
+
classNameRegExp = new RegExp("(?:^|\\s)" + placeholderClassName + "(?!\\S)"),
|
|
119
|
+
|
|
120
|
+
// These will hold references to all elements that can be affected. NodeList objects are live, so we only need to get those references once
|
|
121
|
+
inputs, textareas,
|
|
122
|
+
|
|
123
|
+
// The various data-* attributes used by the polyfill
|
|
124
|
+
ATTR_CURRENT_VAL = "data-placeholder-value",
|
|
125
|
+
ATTR_ACTIVE = "data-placeholder-active",
|
|
126
|
+
ATTR_INPUT_TYPE = "data-placeholder-type",
|
|
127
|
+
ATTR_FORM_HANDLED = "data-placeholder-submit",
|
|
128
|
+
ATTR_EVENTS_BOUND = "data-placeholder-bound",
|
|
129
|
+
ATTR_OPTION_FOCUS = "data-placeholder-focus",
|
|
130
|
+
ATTR_OPTION_LIVE = "data-placeholder-live",
|
|
131
|
+
ATTR_MAXLENGTH = "data-placeholder-maxlength",
|
|
132
|
+
|
|
133
|
+
// Various other variables used throughout the rest of the script
|
|
134
|
+
test = document.createElement("input"),
|
|
135
|
+
head = document.getElementsByTagName("head")[0],
|
|
136
|
+
root = document.documentElement,
|
|
137
|
+
Placeholders = global.Placeholders,
|
|
138
|
+
Utils = Placeholders.Utils,
|
|
139
|
+
hideOnInput, liveUpdates, keydownVal, styleElem, styleRules, placeholder, timer, form, elem, len, i;
|
|
140
|
+
|
|
141
|
+
// No-op (used in place of public methods when native support is detected)
|
|
142
|
+
function noop() {}
|
|
143
|
+
|
|
144
|
+
// Hide the placeholder value on a single element. Returns true if the placeholder was hidden and false if it was not (because it wasn't visible in the first place)
|
|
145
|
+
function hidePlaceholder(elem, keydownValue) {
|
|
146
|
+
var type,
|
|
147
|
+
maxLength,
|
|
148
|
+
valueChanged = (!!keydownValue && elem.value !== keydownValue),
|
|
149
|
+
isPlaceholderValue = (elem.value === elem.getAttribute(ATTR_CURRENT_VAL));
|
|
150
|
+
|
|
151
|
+
if ((valueChanged || isPlaceholderValue) && elem.getAttribute(ATTR_ACTIVE) === "true") {
|
|
152
|
+
elem.removeAttribute(ATTR_ACTIVE);
|
|
153
|
+
elem.value = elem.value.replace(elem.getAttribute(ATTR_CURRENT_VAL), "");
|
|
154
|
+
elem.className = elem.className.replace(classNameRegExp, "");
|
|
155
|
+
|
|
156
|
+
// Restore the maxlength value
|
|
157
|
+
maxLength = elem.getAttribute(ATTR_MAXLENGTH);
|
|
158
|
+
if (maxLength) {
|
|
159
|
+
elem.setAttribute("maxLength", maxLength);
|
|
160
|
+
elem.removeAttribute(ATTR_MAXLENGTH);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// If the polyfill has changed the type of the element we need to change it back
|
|
164
|
+
type = elem.getAttribute(ATTR_INPUT_TYPE);
|
|
165
|
+
if (type) {
|
|
166
|
+
elem.type = type;
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Show the placeholder value on a single element. Returns true if the placeholder was shown and false if it was not (because it was already visible)
|
|
174
|
+
function showPlaceholder(elem) {
|
|
175
|
+
var type,
|
|
176
|
+
maxLength,
|
|
177
|
+
val = elem.getAttribute(ATTR_CURRENT_VAL);
|
|
178
|
+
if (elem.value === "" && val) {
|
|
179
|
+
elem.setAttribute(ATTR_ACTIVE, "true");
|
|
180
|
+
elem.value = val;
|
|
181
|
+
elem.className += " " + placeholderClassName;
|
|
182
|
+
|
|
183
|
+
// Store and remove the maxlength value
|
|
184
|
+
maxLength = elem.getAttribute(ATTR_MAXLENGTH);
|
|
185
|
+
if (!maxLength) {
|
|
186
|
+
elem.setAttribute(ATTR_MAXLENGTH, elem.maxLength);
|
|
187
|
+
elem.removeAttribute("maxLength");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// If the type of element needs to change, change it (e.g. password inputs)
|
|
191
|
+
type = elem.getAttribute(ATTR_INPUT_TYPE);
|
|
192
|
+
if (type) {
|
|
193
|
+
elem.type = "text";
|
|
194
|
+
} else if (elem.type === "password") {
|
|
195
|
+
if (Utils.changeType(elem, "text")) {
|
|
196
|
+
elem.setAttribute(ATTR_INPUT_TYPE, "password");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function handleElem(node, callback) {
|
|
205
|
+
|
|
206
|
+
var handleInputs, handleTextareas, elem, len, i;
|
|
207
|
+
|
|
208
|
+
// Check if the passed in node is an input/textarea (in which case it can't have any affected descendants)
|
|
209
|
+
if (node && node.getAttribute(ATTR_CURRENT_VAL)) {
|
|
210
|
+
callback(node);
|
|
211
|
+
} else {
|
|
212
|
+
|
|
213
|
+
// If an element was passed in, get all affected descendants. Otherwise, get all affected elements in document
|
|
214
|
+
handleInputs = node ? node.getElementsByTagName("input") : inputs;
|
|
215
|
+
handleTextareas = node ? node.getElementsByTagName("textarea") : textareas;
|
|
216
|
+
|
|
217
|
+
// Run the callback for each element
|
|
218
|
+
for (i = 0, len = handleInputs.length + handleTextareas.length; i < len; i++) {
|
|
219
|
+
elem = i < handleInputs.length ? handleInputs[i] : handleTextareas[i - handleInputs.length];
|
|
220
|
+
callback(elem);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Return all affected elements to their normal state (remove placeholder value if present)
|
|
226
|
+
function disablePlaceholders(node) {
|
|
227
|
+
handleElem(node, hidePlaceholder);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Show the placeholder value on all appropriate elements
|
|
231
|
+
function enablePlaceholders(node) {
|
|
232
|
+
handleElem(node, showPlaceholder);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Returns a function that is used as a focus event handler
|
|
236
|
+
function makeFocusHandler(elem) {
|
|
237
|
+
return function () {
|
|
238
|
+
|
|
239
|
+
// Only hide the placeholder value if the (default) hide-on-focus behaviour is enabled
|
|
240
|
+
if (hideOnInput && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") {
|
|
241
|
+
|
|
242
|
+
// Move the caret to the start of the input (this mimics the behaviour of all browsers that do not hide the placeholder on focus)
|
|
243
|
+
Utils.moveCaret(elem, 0);
|
|
244
|
+
|
|
245
|
+
} else {
|
|
246
|
+
|
|
247
|
+
// Remove the placeholder
|
|
248
|
+
hidePlaceholder(elem);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Returns a function that is used as a blur event handler
|
|
254
|
+
function makeBlurHandler(elem) {
|
|
255
|
+
return function () {
|
|
256
|
+
showPlaceholder(elem);
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Functions that are used as a event handlers when the hide-on-input behaviour has been activated - very basic implementation of the "input" event
|
|
261
|
+
function makeKeydownHandler(elem) {
|
|
262
|
+
return function (e) {
|
|
263
|
+
keydownVal = elem.value;
|
|
264
|
+
|
|
265
|
+
//Prevent the use of the arrow keys (try to keep the cursor before the placeholder)
|
|
266
|
+
if (elem.getAttribute(ATTR_ACTIVE) === "true") {
|
|
267
|
+
if (keydownVal === elem.getAttribute(ATTR_CURRENT_VAL) && Utils.inArray(badKeys, e.keyCode)) {
|
|
268
|
+
if (e.preventDefault) {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function makeKeyupHandler(elem) {
|
|
277
|
+
return function () {
|
|
278
|
+
hidePlaceholder(elem, keydownVal);
|
|
279
|
+
|
|
280
|
+
// If the element is now empty we need to show the placeholder
|
|
281
|
+
if (elem.value === "") {
|
|
282
|
+
elem.blur();
|
|
283
|
+
Utils.moveCaret(elem, 0);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function makeClickHandler(elem) {
|
|
288
|
+
return function () {
|
|
289
|
+
if (elem === document.activeElement && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") {
|
|
290
|
+
Utils.moveCaret(elem, 0);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Returns a function that is used as a submit event handler on form elements that have children affected by this polyfill
|
|
296
|
+
function makeSubmitHandler(form) {
|
|
297
|
+
return function () {
|
|
298
|
+
|
|
299
|
+
// Turn off placeholders on all appropriate descendant elements
|
|
300
|
+
disablePlaceholders(form);
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Bind event handlers to an element that we need to affect with the polyfill
|
|
305
|
+
function newElement(elem) {
|
|
306
|
+
|
|
307
|
+
// If the element is part of a form, make sure the placeholder string is not submitted as a value
|
|
308
|
+
if (elem.form) {
|
|
309
|
+
form = elem.form;
|
|
310
|
+
|
|
311
|
+
// Set a flag on the form so we know it's been handled (forms can contain multiple inputs)
|
|
312
|
+
if (!form.getAttribute(ATTR_FORM_HANDLED)) {
|
|
313
|
+
Utils.addEventListener(form, "submit", makeSubmitHandler(form));
|
|
314
|
+
form.setAttribute(ATTR_FORM_HANDLED, "true");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Bind event handlers to the element so we can hide/show the placeholder as appropriate
|
|
319
|
+
Utils.addEventListener(elem, "focus", makeFocusHandler(elem));
|
|
320
|
+
Utils.addEventListener(elem, "blur", makeBlurHandler(elem));
|
|
321
|
+
|
|
322
|
+
// If the placeholder should hide on input rather than on focus we need additional event handlers
|
|
323
|
+
if (hideOnInput) {
|
|
324
|
+
Utils.addEventListener(elem, "keydown", makeKeydownHandler(elem));
|
|
325
|
+
Utils.addEventListener(elem, "keyup", makeKeyupHandler(elem));
|
|
326
|
+
Utils.addEventListener(elem, "click", makeClickHandler(elem));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Remember that we've bound event handlers to this element
|
|
330
|
+
elem.setAttribute(ATTR_EVENTS_BOUND, "true");
|
|
331
|
+
elem.setAttribute(ATTR_CURRENT_VAL, placeholder);
|
|
332
|
+
|
|
333
|
+
// If the element doesn't have a value and is not focussed, set it to the placeholder string
|
|
334
|
+
if (hideOnInput || elem !== document.activeElement) {
|
|
335
|
+
showPlaceholder(elem);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
Placeholders.nativeSupport = test.placeholder !== void 0;
|
|
340
|
+
|
|
341
|
+
if (!Placeholders.nativeSupport) {
|
|
342
|
+
|
|
343
|
+
// Get references to all the input and textarea elements currently in the DOM (live NodeList objects to we only need to do this once)
|
|
344
|
+
inputs = document.getElementsByTagName("input");
|
|
345
|
+
textareas = document.getElementsByTagName("textarea");
|
|
346
|
+
|
|
347
|
+
// Get any settings declared as data-* attributes on the root element (currently the only options are whether to hide the placeholder on focus or input and whether to auto-update)
|
|
348
|
+
hideOnInput = root.getAttribute(ATTR_OPTION_FOCUS) === "false";
|
|
349
|
+
liveUpdates = root.getAttribute(ATTR_OPTION_LIVE) !== "false";
|
|
350
|
+
|
|
351
|
+
// Create style element for placeholder styles (instead of directly setting style properties on elements - allows for better flexibility alongside user-defined styles)
|
|
352
|
+
styleElem = document.createElement("style");
|
|
353
|
+
styleElem.type = "text/css";
|
|
354
|
+
|
|
355
|
+
// Create style rules as text node
|
|
356
|
+
styleRules = document.createTextNode("." + placeholderClassName + " { color:" + placeholderStyleColor + "; }");
|
|
357
|
+
|
|
358
|
+
// Append style rules to newly created stylesheet
|
|
359
|
+
if (styleElem.styleSheet) {
|
|
360
|
+
styleElem.styleSheet.cssText = styleRules.nodeValue;
|
|
361
|
+
} else {
|
|
362
|
+
styleElem.appendChild(styleRules);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Prepend new style element to the head (before any existing stylesheets, so user-defined rules take precedence)
|
|
366
|
+
head.insertBefore(styleElem, head.firstChild);
|
|
367
|
+
|
|
368
|
+
// Set up the placeholders
|
|
369
|
+
for (i = 0, len = inputs.length + textareas.length; i < len; i++) {
|
|
370
|
+
elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length];
|
|
371
|
+
|
|
372
|
+
// Get the value of the placeholder attribute, if any. IE10 emulating IE7 fails with getAttribute, hence the use of the attributes node
|
|
373
|
+
placeholder = elem.attributes.placeholder;
|
|
374
|
+
if (placeholder) {
|
|
375
|
+
|
|
376
|
+
// IE returns an empty object instead of undefined if the attribute is not present
|
|
377
|
+
placeholder = placeholder.nodeValue;
|
|
378
|
+
|
|
379
|
+
// Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value
|
|
380
|
+
if (placeholder && Utils.inArray(validTypes, elem.type)) {
|
|
381
|
+
newElement(elem);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// If enabled, the polyfill will repeatedly check for changed/added elements and apply to those as well
|
|
387
|
+
timer = setInterval(function () {
|
|
388
|
+
for (i = 0, len = inputs.length + textareas.length; i < len; i++) {
|
|
389
|
+
elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length];
|
|
390
|
+
|
|
391
|
+
// Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value
|
|
392
|
+
placeholder = elem.attributes.placeholder;
|
|
393
|
+
if (placeholder) {
|
|
394
|
+
placeholder = placeholder.nodeValue;
|
|
395
|
+
if (placeholder && Utils.inArray(validTypes, elem.type)) {
|
|
396
|
+
|
|
397
|
+
// If the element hasn't had event handlers bound to it then add them
|
|
398
|
+
if (!elem.getAttribute(ATTR_EVENTS_BOUND)) {
|
|
399
|
+
newElement(elem);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// If the placeholder value has changed or not been initialised yet we need to update the display
|
|
403
|
+
if (placeholder !== elem.getAttribute(ATTR_CURRENT_VAL) || (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE))) {
|
|
404
|
+
|
|
405
|
+
// Attempt to change the type of password inputs (fails in IE < 9)
|
|
406
|
+
if (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE) && Utils.changeType(elem, "text")) {
|
|
407
|
+
elem.setAttribute(ATTR_INPUT_TYPE, "password");
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// If the placeholder value has changed and the placeholder is currently on display we need to change it
|
|
411
|
+
if (elem.value === elem.getAttribute(ATTR_CURRENT_VAL)) {
|
|
412
|
+
elem.value = placeholder;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Keep a reference to the current placeholder value in case it changes via another script
|
|
416
|
+
elem.setAttribute(ATTR_CURRENT_VAL, placeholder);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} else if (elem.getAttribute(ATTR_ACTIVE)) {
|
|
420
|
+
hidePlaceholder(elem);
|
|
421
|
+
elem.removeAttribute(ATTR_CURRENT_VAL);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// If live updates are not enabled cancel the timer
|
|
426
|
+
if (!liveUpdates) {
|
|
427
|
+
clearInterval(timer);
|
|
428
|
+
}
|
|
429
|
+
}, 100);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Expose public methods
|
|
433
|
+
Placeholders.disable = Placeholders.nativeSupport ? noop : disablePlaceholders;
|
|
434
|
+
Placeholders.enable = Placeholders.nativeSupport ? noop : enablePlaceholders;
|
|
435
|
+
|
|
436
|
+
}(this));
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2012 James Allardice
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
|
7
|
+
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
8
|
+
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
*
|
|
10
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
11
|
+
*
|
|
12
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
13
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
14
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
15
|
+
* THE SOFTWARE.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Defines the global Placeholders object along with various utility methods
|
|
19
|
+
(function (global) {
|
|
20
|
+
|
|
21
|
+
"use strict";
|
|
22
|
+
|
|
23
|
+
// Cross-browser DOM event binding
|
|
24
|
+
function addEventListener(elem, event, fn) {
|
|
25
|
+
if (elem.addEventListener) {
|
|
26
|
+
return elem.addEventListener(event, fn, false);
|
|
27
|
+
}
|
|
28
|
+
if (elem.attachEvent) {
|
|
29
|
+
return elem.attachEvent("on" + event, fn);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check whether an item is in an array (we don't use Array.prototype.indexOf so we don't clobber any existing polyfills - this is a really simple alternative)
|
|
34
|
+
function inArray(arr, item) {
|
|
35
|
+
var i, len;
|
|
36
|
+
for (i = 0, len = arr.length; i < len; i++) {
|
|
37
|
+
if (arr[i] === item) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Move the caret to the index position specified. Assumes that the element has focus
|
|
45
|
+
function moveCaret(elem, index) {
|
|
46
|
+
var range;
|
|
47
|
+
if (elem.createTextRange) {
|
|
48
|
+
range = elem.createTextRange();
|
|
49
|
+
range.move("character", index);
|
|
50
|
+
range.select();
|
|
51
|
+
} else if (elem.selectionStart) {
|
|
52
|
+
elem.focus();
|
|
53
|
+
elem.setSelectionRange(index, index);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Attempt to change the type property of an input element
|
|
58
|
+
function changeType(elem, type) {
|
|
59
|
+
try {
|
|
60
|
+
elem.type = type;
|
|
61
|
+
return true;
|
|
62
|
+
} catch (e) {
|
|
63
|
+
// You can't change input type in IE8 and below
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Expose public methods
|
|
69
|
+
global.Placeholders = {
|
|
70
|
+
Utils: {
|
|
71
|
+
addEventListener: addEventListener,
|
|
72
|
+
inArray: inArray,
|
|
73
|
+
moveCaret: moveCaret,
|
|
74
|
+
changeType: changeType
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
}(this));
|
|
79
|
+
(function (global) {
|
|
80
|
+
|
|
81
|
+
"use strict";
|
|
82
|
+
|
|
83
|
+
var validTypes = [
|
|
84
|
+
"text",
|
|
85
|
+
"search",
|
|
86
|
+
"url",
|
|
87
|
+
"tel",
|
|
88
|
+
"email",
|
|
89
|
+
"password",
|
|
90
|
+
"number",
|
|
91
|
+
"textarea"
|
|
92
|
+
],
|
|
93
|
+
|
|
94
|
+
// The list of keycodes that are not allowed when the polyfill is configured to hide-on-input
|
|
95
|
+
badKeys = [
|
|
96
|
+
|
|
97
|
+
// The following keys all cause the caret to jump to the end of the input value
|
|
98
|
+
27, // Escape
|
|
99
|
+
33, // Page up
|
|
100
|
+
34, // Page down
|
|
101
|
+
35, // End
|
|
102
|
+
36, // Home
|
|
103
|
+
|
|
104
|
+
// Arrow keys allow you to move the caret manually, which should be prevented when the placeholder is visible
|
|
105
|
+
37, // Left
|
|
106
|
+
38, // Up
|
|
107
|
+
39, // Right
|
|
108
|
+
40, // Down
|
|
109
|
+
|
|
110
|
+
// The following keys allow you to modify the placeholder text by removing characters, which should be prevented when the placeholder is visible
|
|
111
|
+
8, // Backspace
|
|
112
|
+
46 // Delete
|
|
113
|
+
],
|
|
114
|
+
|
|
115
|
+
// Styling variables
|
|
116
|
+
placeholderStyleColor = "#ccc",
|
|
117
|
+
placeholderClassName = "placeholdersjs",
|
|
118
|
+
classNameRegExp = new RegExp("(?:^|\\s)" + placeholderClassName + "(?!\\S)"),
|
|
119
|
+
|
|
120
|
+
// These will hold references to all elements that can be affected. NodeList objects are live, so we only need to get those references once
|
|
121
|
+
inputs, textareas,
|
|
122
|
+
|
|
123
|
+
// The various data-* attributes used by the polyfill
|
|
124
|
+
ATTR_CURRENT_VAL = "data-placeholder-value",
|
|
125
|
+
ATTR_ACTIVE = "data-placeholder-active",
|
|
126
|
+
ATTR_INPUT_TYPE = "data-placeholder-type",
|
|
127
|
+
ATTR_FORM_HANDLED = "data-placeholder-submit",
|
|
128
|
+
ATTR_EVENTS_BOUND = "data-placeholder-bound",
|
|
129
|
+
ATTR_OPTION_FOCUS = "data-placeholder-focus",
|
|
130
|
+
ATTR_OPTION_LIVE = "data-placeholder-live",
|
|
131
|
+
ATTR_MAXLENGTH = "data-placeholder-maxlength",
|
|
132
|
+
|
|
133
|
+
// Various other variables used throughout the rest of the script
|
|
134
|
+
test = document.createElement("input"),
|
|
135
|
+
head = document.getElementsByTagName("head")[0],
|
|
136
|
+
root = document.documentElement,
|
|
137
|
+
Placeholders = global.Placeholders,
|
|
138
|
+
Utils = Placeholders.Utils,
|
|
139
|
+
hideOnInput, liveUpdates, keydownVal, styleElem, styleRules, placeholder, timer, form, elem, len, i;
|
|
140
|
+
|
|
141
|
+
// No-op (used in place of public methods when native support is detected)
|
|
142
|
+
function noop() {}
|
|
143
|
+
|
|
144
|
+
// Hide the placeholder value on a single element. Returns true if the placeholder was hidden and false if it was not (because it wasn't visible in the first place)
|
|
145
|
+
function hidePlaceholder(elem, keydownValue) {
|
|
146
|
+
var type,
|
|
147
|
+
maxLength,
|
|
148
|
+
valueChanged = (!!keydownValue && elem.value !== keydownValue),
|
|
149
|
+
isPlaceholderValue = (elem.value === elem.getAttribute(ATTR_CURRENT_VAL));
|
|
150
|
+
|
|
151
|
+
if ((valueChanged || isPlaceholderValue) && elem.getAttribute(ATTR_ACTIVE) === "true") {
|
|
152
|
+
elem.removeAttribute(ATTR_ACTIVE);
|
|
153
|
+
elem.value = elem.value.replace(elem.getAttribute(ATTR_CURRENT_VAL), "");
|
|
154
|
+
elem.className = elem.className.replace(classNameRegExp, "");
|
|
155
|
+
|
|
156
|
+
// Restore the maxlength value
|
|
157
|
+
maxLength = elem.getAttribute(ATTR_MAXLENGTH);
|
|
158
|
+
if (maxLength) {
|
|
159
|
+
elem.setAttribute("maxLength", maxLength);
|
|
160
|
+
elem.removeAttribute(ATTR_MAXLENGTH);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// If the polyfill has changed the type of the element we need to change it back
|
|
164
|
+
type = elem.getAttribute(ATTR_INPUT_TYPE);
|
|
165
|
+
if (type) {
|
|
166
|
+
elem.type = type;
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Show the placeholder value on a single element. Returns true if the placeholder was shown and false if it was not (because it was already visible)
|
|
174
|
+
function showPlaceholder(elem) {
|
|
175
|
+
var type,
|
|
176
|
+
maxLength,
|
|
177
|
+
val = elem.getAttribute(ATTR_CURRENT_VAL);
|
|
178
|
+
if (elem.value === "" && val) {
|
|
179
|
+
elem.setAttribute(ATTR_ACTIVE, "true");
|
|
180
|
+
elem.value = val;
|
|
181
|
+
elem.className += " " + placeholderClassName;
|
|
182
|
+
|
|
183
|
+
// Store and remove the maxlength value
|
|
184
|
+
maxLength = elem.getAttribute(ATTR_MAXLENGTH);
|
|
185
|
+
if (!maxLength) {
|
|
186
|
+
elem.setAttribute(ATTR_MAXLENGTH, elem.maxLength);
|
|
187
|
+
elem.removeAttribute("maxLength");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// If the type of element needs to change, change it (e.g. password inputs)
|
|
191
|
+
type = elem.getAttribute(ATTR_INPUT_TYPE);
|
|
192
|
+
if (type) {
|
|
193
|
+
elem.type = "text";
|
|
194
|
+
} else if (elem.type === "password") {
|
|
195
|
+
if (Utils.changeType(elem, "text")) {
|
|
196
|
+
elem.setAttribute(ATTR_INPUT_TYPE, "password");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function handleElem(node, callback) {
|
|
205
|
+
|
|
206
|
+
var handleInputs, handleTextareas, elem, len, i;
|
|
207
|
+
|
|
208
|
+
// Check if the passed in node is an input/textarea (in which case it can't have any affected descendants)
|
|
209
|
+
if (node && node.getAttribute(ATTR_CURRENT_VAL)) {
|
|
210
|
+
callback(node);
|
|
211
|
+
} else {
|
|
212
|
+
|
|
213
|
+
// If an element was passed in, get all affected descendants. Otherwise, get all affected elements in document
|
|
214
|
+
handleInputs = node ? node.getElementsByTagName("input") : inputs;
|
|
215
|
+
handleTextareas = node ? node.getElementsByTagName("textarea") : textareas;
|
|
216
|
+
|
|
217
|
+
// Run the callback for each element
|
|
218
|
+
for (i = 0, len = handleInputs.length + handleTextareas.length; i < len; i++) {
|
|
219
|
+
elem = i < handleInputs.length ? handleInputs[i] : handleTextareas[i - handleInputs.length];
|
|
220
|
+
callback(elem);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Return all affected elements to their normal state (remove placeholder value if present)
|
|
226
|
+
function disablePlaceholders(node) {
|
|
227
|
+
handleElem(node, hidePlaceholder);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Show the placeholder value on all appropriate elements
|
|
231
|
+
function enablePlaceholders(node) {
|
|
232
|
+
handleElem(node, showPlaceholder);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Returns a function that is used as a focus event handler
|
|
236
|
+
function makeFocusHandler(elem) {
|
|
237
|
+
return function () {
|
|
238
|
+
|
|
239
|
+
// Only hide the placeholder value if the (default) hide-on-focus behaviour is enabled
|
|
240
|
+
if (hideOnInput && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") {
|
|
241
|
+
|
|
242
|
+
// Move the caret to the start of the input (this mimics the behaviour of all browsers that do not hide the placeholder on focus)
|
|
243
|
+
Utils.moveCaret(elem, 0);
|
|
244
|
+
|
|
245
|
+
} else {
|
|
246
|
+
|
|
247
|
+
// Remove the placeholder
|
|
248
|
+
hidePlaceholder(elem);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Returns a function that is used as a blur event handler
|
|
254
|
+
function makeBlurHandler(elem) {
|
|
255
|
+
return function () {
|
|
256
|
+
showPlaceholder(elem);
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Functions that are used as a event handlers when the hide-on-input behaviour has been activated - very basic implementation of the "input" event
|
|
261
|
+
function makeKeydownHandler(elem) {
|
|
262
|
+
return function (e) {
|
|
263
|
+
keydownVal = elem.value;
|
|
264
|
+
|
|
265
|
+
//Prevent the use of the arrow keys (try to keep the cursor before the placeholder)
|
|
266
|
+
if (elem.getAttribute(ATTR_ACTIVE) === "true") {
|
|
267
|
+
if (keydownVal === elem.getAttribute(ATTR_CURRENT_VAL) && Utils.inArray(badKeys, e.keyCode)) {
|
|
268
|
+
if (e.preventDefault) {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function makeKeyupHandler(elem) {
|
|
277
|
+
return function () {
|
|
278
|
+
hidePlaceholder(elem, keydownVal);
|
|
279
|
+
|
|
280
|
+
// If the element is now empty we need to show the placeholder
|
|
281
|
+
if (elem.value === "") {
|
|
282
|
+
elem.blur();
|
|
283
|
+
Utils.moveCaret(elem, 0);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function makeClickHandler(elem) {
|
|
288
|
+
return function () {
|
|
289
|
+
if (elem === document.activeElement && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") {
|
|
290
|
+
Utils.moveCaret(elem, 0);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Returns a function that is used as a submit event handler on form elements that have children affected by this polyfill
|
|
296
|
+
function makeSubmitHandler(form) {
|
|
297
|
+
return function () {
|
|
298
|
+
|
|
299
|
+
// Turn off placeholders on all appropriate descendant elements
|
|
300
|
+
disablePlaceholders(form);
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Bind event handlers to an element that we need to affect with the polyfill
|
|
305
|
+
function newElement(elem) {
|
|
306
|
+
|
|
307
|
+
// If the element is part of a form, make sure the placeholder string is not submitted as a value
|
|
308
|
+
if (elem.form) {
|
|
309
|
+
form = elem.form;
|
|
310
|
+
|
|
311
|
+
// Set a flag on the form so we know it's been handled (forms can contain multiple inputs)
|
|
312
|
+
if (!form.getAttribute(ATTR_FORM_HANDLED)) {
|
|
313
|
+
Utils.addEventListener(form, "submit", makeSubmitHandler(form));
|
|
314
|
+
form.setAttribute(ATTR_FORM_HANDLED, "true");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Bind event handlers to the element so we can hide/show the placeholder as appropriate
|
|
319
|
+
Utils.addEventListener(elem, "focus", makeFocusHandler(elem));
|
|
320
|
+
Utils.addEventListener(elem, "blur", makeBlurHandler(elem));
|
|
321
|
+
|
|
322
|
+
// If the placeholder should hide on input rather than on focus we need additional event handlers
|
|
323
|
+
if (hideOnInput) {
|
|
324
|
+
Utils.addEventListener(elem, "keydown", makeKeydownHandler(elem));
|
|
325
|
+
Utils.addEventListener(elem, "keyup", makeKeyupHandler(elem));
|
|
326
|
+
Utils.addEventListener(elem, "click", makeClickHandler(elem));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Remember that we've bound event handlers to this element
|
|
330
|
+
elem.setAttribute(ATTR_EVENTS_BOUND, "true");
|
|
331
|
+
elem.setAttribute(ATTR_CURRENT_VAL, placeholder);
|
|
332
|
+
|
|
333
|
+
// If the element doesn't have a value and is not focussed, set it to the placeholder string
|
|
334
|
+
if (hideOnInput || elem !== document.activeElement) {
|
|
335
|
+
showPlaceholder(elem);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
Placeholders.nativeSupport = test.placeholder !== void 0;
|
|
340
|
+
|
|
341
|
+
if (!Placeholders.nativeSupport) {
|
|
342
|
+
|
|
343
|
+
// Get references to all the input and textarea elements currently in the DOM (live NodeList objects to we only need to do this once)
|
|
344
|
+
inputs = document.getElementsByTagName("input");
|
|
345
|
+
textareas = document.getElementsByTagName("textarea");
|
|
346
|
+
|
|
347
|
+
// Get any settings declared as data-* attributes on the root element (currently the only options are whether to hide the placeholder on focus or input and whether to auto-update)
|
|
348
|
+
hideOnInput = root.getAttribute(ATTR_OPTION_FOCUS) === "false";
|
|
349
|
+
liveUpdates = root.getAttribute(ATTR_OPTION_LIVE) !== "false";
|
|
350
|
+
|
|
351
|
+
// Create style element for placeholder styles (instead of directly setting style properties on elements - allows for better flexibility alongside user-defined styles)
|
|
352
|
+
styleElem = document.createElement("style");
|
|
353
|
+
styleElem.type = "text/css";
|
|
354
|
+
|
|
355
|
+
// Create style rules as text node
|
|
356
|
+
styleRules = document.createTextNode("." + placeholderClassName + " { color:" + placeholderStyleColor + "; }");
|
|
357
|
+
|
|
358
|
+
// Append style rules to newly created stylesheet
|
|
359
|
+
if (styleElem.styleSheet) {
|
|
360
|
+
styleElem.styleSheet.cssText = styleRules.nodeValue;
|
|
361
|
+
} else {
|
|
362
|
+
styleElem.appendChild(styleRules);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Prepend new style element to the head (before any existing stylesheets, so user-defined rules take precedence)
|
|
366
|
+
head.insertBefore(styleElem, head.firstChild);
|
|
367
|
+
|
|
368
|
+
// Set up the placeholders
|
|
369
|
+
for (i = 0, len = inputs.length + textareas.length; i < len; i++) {
|
|
370
|
+
elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length];
|
|
371
|
+
|
|
372
|
+
// Get the value of the placeholder attribute, if any. IE10 emulating IE7 fails with getAttribute, hence the use of the attributes node
|
|
373
|
+
placeholder = elem.attributes.placeholder;
|
|
374
|
+
if (placeholder) {
|
|
375
|
+
|
|
376
|
+
// IE returns an empty object instead of undefined if the attribute is not present
|
|
377
|
+
placeholder = placeholder.nodeValue;
|
|
378
|
+
|
|
379
|
+
// Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value
|
|
380
|
+
if (placeholder && Utils.inArray(validTypes, elem.type)) {
|
|
381
|
+
newElement(elem);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// If enabled, the polyfill will repeatedly check for changed/added elements and apply to those as well
|
|
387
|
+
timer = setInterval(function () {
|
|
388
|
+
for (i = 0, len = inputs.length + textareas.length; i < len; i++) {
|
|
389
|
+
elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length];
|
|
390
|
+
|
|
391
|
+
// Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value
|
|
392
|
+
placeholder = elem.attributes.placeholder;
|
|
393
|
+
if (placeholder) {
|
|
394
|
+
placeholder = placeholder.nodeValue;
|
|
395
|
+
if (placeholder && Utils.inArray(validTypes, elem.type)) {
|
|
396
|
+
|
|
397
|
+
// If the element hasn't had event handlers bound to it then add them
|
|
398
|
+
if (!elem.getAttribute(ATTR_EVENTS_BOUND)) {
|
|
399
|
+
newElement(elem);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// If the placeholder value has changed or not been initialised yet we need to update the display
|
|
403
|
+
if (placeholder !== elem.getAttribute(ATTR_CURRENT_VAL) || (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE))) {
|
|
404
|
+
|
|
405
|
+
// Attempt to change the type of password inputs (fails in IE < 9)
|
|
406
|
+
if (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE) && Utils.changeType(elem, "text")) {
|
|
407
|
+
elem.setAttribute(ATTR_INPUT_TYPE, "password");
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// If the placeholder value has changed and the placeholder is currently on display we need to change it
|
|
411
|
+
if (elem.value === elem.getAttribute(ATTR_CURRENT_VAL)) {
|
|
412
|
+
elem.value = placeholder;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Keep a reference to the current placeholder value in case it changes via another script
|
|
416
|
+
elem.setAttribute(ATTR_CURRENT_VAL, placeholder);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} else if (elem.getAttribute(ATTR_ACTIVE)) {
|
|
420
|
+
hidePlaceholder(elem);
|
|
421
|
+
elem.removeAttribute(ATTR_CURRENT_VAL);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// If live updates are not enabled cancel the timer
|
|
426
|
+
if (!liveUpdates) {
|
|
427
|
+
clearInterval(timer);
|
|
428
|
+
}
|
|
429
|
+
}, 100);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Expose public methods
|
|
433
|
+
Placeholders.disable = Placeholders.nativeSupport ? noop : disablePlaceholders;
|
|
434
|
+
Placeholders.enable = Placeholders.nativeSupport ? noop : enablePlaceholders;
|
|
435
|
+
|
|
436
|
+
}(this));
|
metadata
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: placeholder-gem
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 3.0.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Edward Look
|
|
9
|
+
- Christian Vuerings
|
|
10
|
+
autorequire:
|
|
11
|
+
bindir: bin
|
|
12
|
+
cert_chain: []
|
|
13
|
+
date: 2013-10-28 00:00:00.000000000 Z
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: railties
|
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
|
18
|
+
none: false
|
|
19
|
+
requirements:
|
|
20
|
+
- - ! '>='
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '3.1'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
none: false
|
|
27
|
+
requirements:
|
|
28
|
+
- - ! '>='
|
|
29
|
+
- !ruby/object:Gem::Version
|
|
30
|
+
version: '3.1'
|
|
31
|
+
- !ruby/object:Gem::Dependency
|
|
32
|
+
name: bundler
|
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
|
34
|
+
none: false
|
|
35
|
+
requirements:
|
|
36
|
+
- - ! '>='
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: 1.2.2
|
|
39
|
+
type: :development
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
none: false
|
|
43
|
+
requirements:
|
|
44
|
+
- - ! '>='
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 1.2.2
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: tzinfo
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ! '>='
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
type: :development
|
|
56
|
+
prerelease: false
|
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
58
|
+
none: false
|
|
59
|
+
requirements:
|
|
60
|
+
- - ! '>='
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0'
|
|
63
|
+
- !ruby/object:Gem::Dependency
|
|
64
|
+
name: nokogiri
|
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
|
66
|
+
none: false
|
|
67
|
+
requirements:
|
|
68
|
+
- - ! '>='
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
type: :development
|
|
72
|
+
prerelease: false
|
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
74
|
+
none: false
|
|
75
|
+
requirements:
|
|
76
|
+
- - ! '>='
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '0'
|
|
79
|
+
- !ruby/object:Gem::Dependency
|
|
80
|
+
name: coveralls
|
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
|
82
|
+
none: false
|
|
83
|
+
requirements:
|
|
84
|
+
- - ! '>='
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '0'
|
|
87
|
+
type: :development
|
|
88
|
+
prerelease: false
|
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
90
|
+
none: false
|
|
91
|
+
requirements:
|
|
92
|
+
- - ! '>='
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
95
|
+
description: Include Placeholder.js in your Rails projects
|
|
96
|
+
email:
|
|
97
|
+
- edwlook@gmail.com
|
|
98
|
+
- vueringschristian@gmail.com
|
|
99
|
+
executables: []
|
|
100
|
+
extensions: []
|
|
101
|
+
extra_rdoc_files: []
|
|
102
|
+
files:
|
|
103
|
+
- lib/placeholder-gem/version.rb
|
|
104
|
+
- lib/placeholder-gem.rb
|
|
105
|
+
- vendor/assets/javascripts/placeholder.js
|
|
106
|
+
- vendor/assets/javascripts/v3.0.0/placeholder.js
|
|
107
|
+
- MIT-LICENSE
|
|
108
|
+
- Rakefile
|
|
109
|
+
- README.md
|
|
110
|
+
homepage: http://github.com/ets-berkeley-edu/placeholder-gem
|
|
111
|
+
licenses: []
|
|
112
|
+
post_install_message:
|
|
113
|
+
rdoc_options: []
|
|
114
|
+
require_paths:
|
|
115
|
+
- lib
|
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
|
+
none: false
|
|
118
|
+
requirements:
|
|
119
|
+
- - ! '>='
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '0'
|
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
|
+
none: false
|
|
124
|
+
requirements:
|
|
125
|
+
- - ! '>='
|
|
126
|
+
- !ruby/object:Gem::Version
|
|
127
|
+
version: '0'
|
|
128
|
+
requirements: []
|
|
129
|
+
rubyforge_project:
|
|
130
|
+
rubygems_version: 1.8.23
|
|
131
|
+
signing_key:
|
|
132
|
+
specification_version: 3
|
|
133
|
+
summary: Placeholder.js Javascript Polyfill for HTML5 as a gem
|
|
134
|
+
test_files: []
|