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 80(function (global) { 81 82 "use strict"; 83 84 var validTypes = [ 85 "text", 86 "search", 87 "url", 88 "tel", 89 "email", 90 "password", 91 "number", 92 "textarea" 93 ], 94 95 // The list of keycodes that are not allowed when the polyfill is configured to hide-on-input 96 badKeys = [ 97 98 // The following keys all cause the caret to jump to the end of the input value 99 27, // Escape 100 33, // Page up 101 34, // Page down 102 35, // End 103 36, // Home 104 105 // Arrow keys allow you to move the caret manually, which should be prevented when the placeholder is visible 106 37, // Left 107 38, // Up 108 39, // Right 109 40, // Down 110 111 // The following keys allow you to modify the placeholder text by removing characters, which should be prevented when the placeholder is visible 112 8, // Backspace 113 46 // Delete 114 ], 115 116 // Styling variables 117 placeholderStyleColor = "#ccc", 118 placeholderClassName = "placeholdersjs", 119 classNameRegExp = new RegExp("(?:^|\\s)" + placeholderClassName + "(?!\\S)"), 120 121 // These will hold references to all elements that can be affected. NodeList objects are live, so we only need to get those references once 122 inputs, textareas, 123 124 // The various data-* attributes used by the polyfill 125 ATTR_CURRENT_VAL = "data-placeholder-value", 126 ATTR_ACTIVE = "data-placeholder-active", 127 ATTR_INPUT_TYPE = "data-placeholder-type", 128 ATTR_FORM_HANDLED = "data-placeholder-submit", 129 ATTR_EVENTS_BOUND = "data-placeholder-bound", 130 ATTR_OPTION_FOCUS = "data-placeholder-focus", 131 ATTR_OPTION_LIVE = "data-placeholder-live", 132 ATTR_MAXLENGTH = "data-placeholder-maxlength", 133 134 // Various other variables used throughout the rest of the script 135 test = document.createElement("input"), 136 head = document.getElementsByTagName("head")[0], 137 root = document.documentElement, 138 Placeholders = global.Placeholders, 139 Utils = Placeholders.Utils, 140 hideOnInput, liveUpdates, keydownVal, styleElem, styleRules, placeholder, timer, form, elem, len, i; 141 142 // No-op (used in place of public methods when native support is detected) 143 function noop() {} 144 145 // Avoid IE9 activeElement of death when an iframe is used. 146 // More info: 147 // http://bugs.jquery.com/ticket/13393 148 // https://github.com/jquery/jquery/commit/85fc5878b3c6af73f42d61eedf73013e7faae408 149 function safeActiveElement() { 150 try { 151 return document.activeElement; 152 } catch (err) {} 153 } 154 155 // 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) 156 function hidePlaceholder(elem, keydownValue) { 157 var type, 158 maxLength,
159 valueChanged = (!!keydownValue && elem.value !== keydownValue), 160 isPlaceholderValue = (elem.value === elem.getAttribute(ATTR_CURRENT_VAL)); 161 162 if ((valueChanged || isPlaceholderValue) && elem.getAttribute(ATTR_ACTIVE) === "true") { 163 elem.removeAttribute(ATTR_ACTIVE); 164 elem.value = elem.value.replace(elem.getAttribute(ATTR_CURRENT_VAL), ""); 165 elem.className = elem.className.replace(classNameRegExp, ""); 166 167 // Restore the maxlength value 168 maxLength = elem.getAttribute(ATTR_MAXLENGTH); 169 if (parseInt(maxLength, 10) >= 0) { // Old FF returns -1 if attribute not set (see GH-56) 170 elem.setAttribute("maxLength", maxLength); 171 elem.removeAttribute(ATTR_MAXLENGTH); 172 } 173 174 // If the polyfill has changed the type of the element we need to change it back 175 type = elem.getAttribute(ATTR_INPUT_TYPE); 176 if (type) { 177 elem.type = type; 178 } 179 return true; 180 } 181 return false; 182 } 183 184 // 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) 185 function showPlaceholder(elem) { 186 var type, 187 maxLength, 188 val = elem.getAttribute(ATTR_CURRENT_VAL); 189 if (elem.value === "" && val) { 190 elem.setAttribute(ATTR_ACTIVE, "true"); 191 elem.value = val; 192 elem.className += " " + placeholderClassName; 193 194 // Store and remove the maxlength value 195 maxLength = elem.getAttribute(ATTR_MAXLENGTH); 196 if (!maxLength) { 197 elem.setAttribute(ATTR_MAXLENGTH, elem.maxLength); 198 elem.removeAttribute("maxLength"); 199 } 200 201 // If the type of element needs to change, change it (e.g. password inputs) 202 type = elem.getAttribute(ATTR_INPUT_TYPE); 203 if (type) { 204 elem.type = "text"; 205 } else if (elem.type === "password") { 206 if (Utils.changeType(elem, "text")) { 207 elem.setAttribute(ATTR_INPUT_TYPE, "password"); 208 } 209 } 210 return true; 211 } 212 return false; 213 } 214 215 function handleElem(node, callback) { 216 217 var handleInputsLength, handleTextareasLength, handleInputs, handleTextareas, elem, len, i; 218 219 // Check if the passed in node is an input/textarea (in which case it can't have any affected descendants) 220 if (node && node.getAttribute(ATTR_CURRENT_VAL)) { 221 callback(node); 222 } else { 223 224 // If an element was passed in, get all affected descendants. Otherwise, get all affected elements in document 225 handleInputs = node ? node.getElementsByTagName("input") : inputs; 226 handleTextareas = node ? node.getElementsByTagName("textarea") : textareas; 227 228 handleInputsLength = handleInputs ? handleInputs.length : 0; 229 handleTextareasLength = handleTextareas ? handleTextareas.length : 0; 230 231 // Run the callback for each element 232 for (i = 0, len = handleInputsLength + handleTextareasLength; i < len; i++) { 233 elem = i < handleInputsLength ? handleInputs[i] : handleTextareas[i - handleInputsLength]; 234 callback(elem); 235 } 236 } 237 } 238 239 // Return all affected elements to their normal state (remove placeholder value if present) 240 function disablePlaceholders(node) { 241 handleElem(node, hidePlaceholder); 242 } 243 244 // Show the placeholder value on all appropriate elements 245 function enablePlaceholders(node) { 246 handleElem(node, showPlaceholder); 247 } 248 249 // Returns a function that is used as a focus event handler 250 function makeFocusHandler(elem) { 251 return function () { 252 253 // Only hide the placeholder value if the (default) hide-on-focus behaviour is enabled 254 if (hideOnInput && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") { 255 256 // Move the caret to the start of the input (this mimics the behaviour of all browsers that do not hide the placeholder on focus) 257 Utils.moveCaret(elem, 0); 258 259 } else { 260 261 // Remove the placeholder 262 hidePlaceholder(elem); 263 } 264 }; 265 } 266 267 // Returns a function that is used as a blur event handler 268 function makeBlurHandler(elem) { 269 return function () { 270 showPlaceholder(elem); 271 }; 272 } 273 274 // Functions that are used as a event handlers when the hide-on-input behaviour has been activated - very basic implementation of the "input" event 275 function makeKeydownHandler(elem) { 276 return function (e) { 277 keydownVal = elem.value; 278 279 //Prevent the use of the arrow keys (try to keep the cursor before the placeholder) 280 if (elem.getAttribute(ATTR_ACTIVE) === "true") { 281 if (keydownVal === elem.getAttribute(ATTR_CURRENT_VAL) && Utils.inArray(badKeys, e.keyCode)) { 282 if (e.preventDefault) { 283 e.preventDefault(); 284 } 285 return false;
286 } 287 } 288 }; 289 } 290 function makeKeyupHandler(elem) { 291 return function () { 292 hidePlaceholder(elem, keydownVal); 293 294 // If the element is now empty we need to show the placeholder 295 if (elem.value === "") { 296 elem.blur(); 297 Utils.moveCaret(elem, 0); 298 } 299 }; 300 } 301 function makeClickHandler(elem) { 302 return function () { 303 if (elem === safeActiveElement() && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") { 304 Utils.moveCaret(elem, 0); 305 } 306 }; 307 } 308 309 // Returns a function that is used as a submit event handler on form elements that have children affected by this polyfill 310 function makeSubmitHandler(form) { 311 return function () { 312 313 // Turn off placeholders on all appropriate descendant elements 314 disablePlaceholders(form); 315 }; 316 } 317 318 // Bind event handlers to an element that we need to affect with the polyfill 319 function newElement(elem) { 320 321 // If the element is part of a form, make sure the placeholder string is not submitted as a value 322 if (elem.form) { 323 form = elem.form; 324 325 // If the type of the property is a string then we have a "form" attribute and need to get the real form 326 if (typeof form === "string") { 327 form = document.getElementById(form); 328 } 329 330 // Set a flag on the form so we know it's been handled (forms can contain multiple inputs) 331 if (!form.getAttribute(ATTR_FORM_HANDLED)) { 332 Utils.addEventListener(form, "submit", makeSubmitHandler(form)); 333 form.setAttribute(ATTR_FORM_HANDLED, "true"); 334 } 335 } 336 337 // Bind event handlers to the element so we can hide/show the placeholder as appropriate 338 Utils.addEventListener(elem, "focus", makeFocusHandler(elem)); 339 Utils.addEventListener(elem, "blur", makeBlurHandler(elem)); 340 341 // If the placeholder should hide on input rather than on focus we need additional event handlers 342 if (hideOnInput) { 343 Utils.addEventListener(elem, "keydown", makeKeydownHandler(elem)); 344 Utils.addEventListener(elem, "keyup", makeKeyupHandler(elem)); 345 Utils.addEventListener(elem, "click", makeClickHandler(elem)); 346 } 347 348 // Remember that we've bound event handlers to this element 349 elem.setAttribute(ATTR_EVENTS_BOUND, "true"); 350 elem.setAttribute(ATTR_CURRENT_VAL, placeholder); 351 352 // If the element doesn't have a value and is not focussed, set it to the placeholder string 353 if (hideOnInput || elem !== safeActiveElement()) { 354 showPlaceholder(elem); 355 } 356 } 357 358 Placeholders.nativeSupport = test.placeholder !== void 0; 359 360 if (!Placeholders.nativeSupport) { 361 362 // Get references to all the input and textarea elements currently in the DOM (live NodeList objects to we only need to do this once) 363 inputs = document.getElementsByTagName("input"); 364 textareas = document.getElementsByTagName("textarea"); 365 366 // 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) 367 hideOnInput = root.getAttribute(ATTR_OPTION_FOCUS) === "false"; 368 liveUpdates = root.getAttribute(ATTR_OPTION_LIVE) !== "false"; 369 370 // Create style element for placeholder styles (instead of directly setting style properties on elements - allows for better flexibility alongside user-defined styles) 371 styleElem = document.createElement("style"); 372 styleElem.type = "text/css"; 373 374 // Create style rules as text node 375 styleRules = document.createTextNode("." + placeholderClassName + " { color:" + placeholderStyleColor + "; }"); 376 377 // Append style rules to newly created stylesheet 378 if (styleElem.styleSheet) { 379 styleElem.styleSheet.cssText = styleRules.nodeValue; 380 } else { 381 styleElem.appendChild(styleRules); 382 } 383 384 // Prepend new style element to the head (before any existing stylesheets, so user-defined rules take precedence) 385 head.insertBefore(styleElem, head.firstChild); 386 387 // Set up the placeholders
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 // Get the value of the placeholder attribute, if any. IE10 emulating IE7 fails with getAttribute, hence the use of the attributes node 392 placeholder = elem.attributes.placeholder; 393 if (placeholder) { 394 395 // IE returns an empty object instead of undefined if the attribute is not present 396 placeholder = placeholder.nodeValue; 397 398 // Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value 399 if (placeholder && Utils.inArray(validTypes, elem.type)) { 400 newElement(elem); 401 } 402 } 403 } 404 405 // If enabled, the polyfill will repeatedly check for changed/added elements and apply to those as well 406 timer = setInterval(function () { 407 for (i = 0, len = inputs.length + textareas.length; i < len; i++) { 408 elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length]; 409 410 // Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value 411 placeholder = elem.attributes.placeholder; 412 if (placeholder) { 413 placeholder = placeholder.nodeValue; 414 if (placeholder && Utils.inArray(validTypes, elem.type)) { 415 416 // If the element hasn't had event handlers bound to it then add them 417 if (!elem.getAttribute(ATTR_EVENTS_BOUND)) { 418 newElement(elem); 419 } 420 421 // If the placeholder value has changed or not been initialised yet we need to update the display 422 if (placeholder !== elem.getAttribute(ATTR_CURRENT_VAL) || (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE))) { 423 424 // Attempt to change the type of password inputs (fails in IE < 9) 425 if (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE) && Utils.changeType(elem, "text")) { 426 elem.setAttribute(ATTR_INPUT_TYPE, "password"); 427 } 428 429 // If the placeholder value has changed and the placeholder is currently on display we need to change it 430 if (elem.value === elem.getAttribute(ATTR_CURRENT_VAL)) { 431 elem.value = placeholder; 432 } 433 434 // Keep a reference to the current placeholder value in case it changes via another script 435 elem.setAttribute(ATTR_CURRENT_VAL, placeholder); 436 } 437 } 438 } else if (elem.getAttribute(ATTR_ACTIVE)) { 439 hidePlaceholder(elem); 440 elem.removeAttribute(ATTR_CURRENT_VAL); 441 } 442 } 443 444 // If live updates are not enabled cancel the timer 445 if (!liveUpdates) { 446 clearInterval(timer); 447 } 448 }, 100); 449 } 450 451 Utils.addEventListener(global, "beforeunload", function () { 452 Placeholders.disable(); 453 }); 454 455 // Expose public methods 456 Placeholders.disable = Placeholders.nativeSupport ? noop : disablePlaceholders; 457 Placeholders.enable = Placeholders.nativeSupport ? noop : enablePlaceholders; 458 459}(this));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.