diff options
Diffstat (limited to 'foundation-default-colours/static/js/vendor/placeholder.js')
-rw-r--r-- | foundation-default-colours/static/js/vendor/placeholder.js | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/foundation-default-colours/static/js/vendor/placeholder.js b/foundation-default-colours/static/js/vendor/placeholder.js new file mode 100644 index 0000000..93bd2ca --- /dev/null +++ b/foundation-default-colours/static/js/vendor/placeholder.js @@ -0,0 +1,426 @@ +/* + * The MIT License + * + * Copyright (c) 2012 James Allardice + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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 + * THE SOFTWARE. + */ + +// Defines the global Placeholders object along with various utility methods +(function (global) { + + "use strict"; + + // Cross-browser DOM event binding + function addEventListener(elem, event, fn) { + if (elem.addEventListener) { + return elem.addEventListener(event, fn, false); + } + if (elem.attachEvent) { + return elem.attachEvent("on" + event, fn); + } + } + + // 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) + function inArray(arr, item) { + var i, len; + for (i = 0, len = arr.length; i < len; i++) { + if (arr[i] === item) { + return true; + } + } + return false; + } + + // Move the caret to the index position specified. Assumes that the element has focus + function moveCaret(elem, index) { + var range; + if (elem.createTextRange) { + range = elem.createTextRange(); + range.move("character", index); + range.select(); + } else if (elem.selectionStart) { + elem.focus(); + elem.setSelectionRange(index, index); + } + } + + // Attempt to change the type property of an input element + function changeType(elem, type) { + try { + elem.type = type; + return true; + } catch (e) { + // You can't change input type in IE8 and below + return false; + } + } + + // Expose public methods + global.Placeholders = { + Utils: { + addEventListener: addEventListener, + inArray: inArray, + moveCaret: moveCaret, + changeType: changeType + } + }; + +}(this)); + +(function (global) { + + "use strict"; + + var validTypes = [ + "text", + "search", + "url", + "tel", + "email", + "password", + "number", + "textarea" + ], + + // The list of keycodes that are not allowed when the polyfill is configured to hide-on-input + badKeys = [ + + // The following keys all cause the caret to jump to the end of the input value + 27, // Escape + 33, // Page up + 34, // Page down + 35, // End + 36, // Home + + // Arrow keys allow you to move the caret manually, which should be prevented when the placeholder is visible + 37, // Left + 38, // Up + 39, // Right + 40, // Down + + // The following keys allow you to modify the placeholder text by removing characters, which should be prevented when the placeholder is visible + 8, // Backspace + 46 // Delete + ], + + // Styling variables + placeholderStyleColor = "#ccc", + placeholderClassName = "placeholdersjs", + classNameRegExp = new RegExp("(?:^|\\s)" + placeholderClassName + "(?!\\S)"), + + // These will hold references to all elements that can be affected. NodeList objects are live, so we only need to get those references once + inputs, textareas, + + // The various data-* attributes used by the polyfill + ATTR_CURRENT_VAL = "data-placeholder-value", + ATTR_ACTIVE = "data-placeholder-active", + ATTR_INPUT_TYPE = "data-placeholder-type", + ATTR_FORM_HANDLED = "data-placeholder-submit", + ATTR_EVENTS_BOUND = "data-placeholder-bound", + ATTR_OPTION_FOCUS = "data-placeholder-focus", + ATTR_OPTION_LIVE = "data-placeholder-live", + + // Various other variables used throughout the rest of the script + test = document.createElement("input"), + head = document.getElementsByTagName("head")[0], + root = document.documentElement, + Placeholders = global.Placeholders, + Utils = Placeholders.Utils, + hideOnInput, liveUpdates, keydownVal, styleElem, styleRules, placeholder, timer, form, elem, len, i; + + // No-op (used in place of public methods when native support is detected) + function noop() {} + + // 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) + function hidePlaceholder(elem) { + var type; + if (elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") { + elem.setAttribute(ATTR_ACTIVE, "false"); + elem.value = ""; + elem.className = elem.className.replace(classNameRegExp, ""); + + // If the polyfill has changed the type of the element we need to change it back + type = elem.getAttribute(ATTR_INPUT_TYPE); + if (type) { + elem.type = type; + } + return true; + } + return false; + } + + // 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) + function showPlaceholder(elem) { + var type, + val = elem.getAttribute(ATTR_CURRENT_VAL); + if (elem.value === "" && val) { + elem.setAttribute(ATTR_ACTIVE, "true"); + elem.value = val; + elem.className += " " + placeholderClassName; + + // If the type of element needs to change, change it (e.g. password inputs) + type = elem.getAttribute(ATTR_INPUT_TYPE); + if (type) { + elem.type = "text"; + } else if (elem.type === "password") { + if (Utils.changeType(elem, "text")) { + elem.setAttribute(ATTR_INPUT_TYPE, "password"); + } + } + return true; + } + return false; + } + + function handleElem(node, callback) { + + var handleInputs, handleTextareas, elem, len, i; + + // Check if the passed in node is an input/textarea (in which case it can't have any affected descendants) + if (node && node.getAttribute(ATTR_CURRENT_VAL)) { + callback(node); + } else { + + // If an element was passed in, get all affected descendants. Otherwise, get all affected elements in document + handleInputs = node ? node.getElementsByTagName("input") : inputs; + handleTextareas = node ? node.getElementsByTagName("textarea") : textareas; + + // Run the callback for each element + for (i = 0, len = handleInputs.length + handleTextareas.length; i < len; i++) { + elem = i < handleInputs.length ? handleInputs[i] : handleTextareas[i - handleInputs.length]; + callback(elem); + } + } + } + + // Return all affected elements to their normal state (remove placeholder value if present) + function disablePlaceholders(node) { + handleElem(node, hidePlaceholder); + } + + // Show the placeholder value on all appropriate elements + function enablePlaceholders(node) { + handleElem(node, showPlaceholder); + } + + // Returns a function that is used as a focus event handler + function makeFocusHandler(elem) { + return function () { + + // Only hide the placeholder value if the (default) hide-on-focus behaviour is enabled + if (hideOnInput && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") { + + // Move the caret to the start of the input (this mimics the behaviour of all browsers that do not hide the placeholder on focus) + Utils.moveCaret(elem, 0); + + } else { + + // Remove the placeholder + hidePlaceholder(elem); + } + }; + } + + // Returns a function that is used as a blur event handler + function makeBlurHandler(elem) { + return function () { + showPlaceholder(elem); + }; + } + + // Functions that are used as a event handlers when the hide-on-input behaviour has been activated - very basic implementation of the "input" event + function makeKeydownHandler(elem) { + return function (e) { + keydownVal = elem.value; + + //Prevent the use of the arrow keys (try to keep the cursor before the placeholder) + if (elem.getAttribute(ATTR_ACTIVE) === "true") { + if (keydownVal === elem.getAttribute(ATTR_CURRENT_VAL) && Utils.inArray(badKeys, e.keyCode)) { + if (e.preventDefault) { + e.preventDefault(); + } + return false; + } + } + }; + } + function makeKeyupHandler(elem) { + return function () { + var type; + + if (elem.getAttribute(ATTR_ACTIVE) === "true" && elem.value !== keydownVal) { + + // Remove the placeholder + elem.className = elem.className.replace(classNameRegExp, ""); + elem.value = elem.value.replace(elem.getAttribute(ATTR_CURRENT_VAL), ""); + elem.setAttribute(ATTR_ACTIVE, false); + + // If the type of element needs to change, change it (e.g. password inputs) + type = elem.getAttribute(ATTR_INPUT_TYPE); + if (type) { + elem.type = type; + } + } + + // If the element is now empty we need to show the placeholder + if (elem.value === "") { + elem.blur(); + Utils.moveCaret(elem, 0); + } + }; + } + function makeClickHandler(elem) { + return function () { + if (elem === document.activeElement && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") { + Utils.moveCaret(elem, 0); + } + }; + } + + // Returns a function that is used as a submit event handler on form elements that have children affected by this polyfill + function makeSubmitHandler(form) { + return function () { + + // Turn off placeholders on all appropriate descendant elements + disablePlaceholders(form); + }; + } + + // Bind event handlers to an element that we need to affect with the polyfill + function newElement(elem) { + + // If the element is part of a form, make sure the placeholder string is not submitted as a value + if (elem.form) { + form = elem.form; + + // Set a flag on the form so we know it's been handled (forms can contain multiple inputs) + if (!form.getAttribute(ATTR_FORM_HANDLED)) { + Utils.addEventListener(form, "submit", makeSubmitHandler(form)); + form.setAttribute(ATTR_FORM_HANDLED, "true"); + } + } + + // Bind event handlers to the element so we can hide/show the placeholder as appropriate + Utils.addEventListener(elem, "focus", makeFocusHandler(elem)); + Utils.addEventListener(elem, "blur", makeBlurHandler(elem)); + + // If the placeholder should hide on input rather than on focus we need additional event handlers + if (hideOnInput) { + Utils.addEventListener(elem, "keydown", makeKeydownHandler(elem)); + Utils.addEventListener(elem, "keyup", makeKeyupHandler(elem)); + Utils.addEventListener(elem, "click", makeClickHandler(elem)); + } + + // Remember that we've bound event handlers to this element + elem.setAttribute(ATTR_EVENTS_BOUND, "true"); + elem.setAttribute(ATTR_CURRENT_VAL, placeholder); + + // If the element doesn't have a value, set it to the placeholder string + showPlaceholder(elem); + } + + Placeholders.nativeSupport = test.placeholder !== void 0; + + if (!Placeholders.nativeSupport) { + + // Get references to all the input and textarea elements currently in the DOM (live NodeList objects to we only need to do this once) + inputs = document.getElementsByTagName("input"); + textareas = document.getElementsByTagName("textarea"); + + // 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) + hideOnInput = root.getAttribute(ATTR_OPTION_FOCUS) === "false"; + liveUpdates = root.getAttribute(ATTR_OPTION_LIVE) !== "false"; + + // Create style element for placeholder styles (instead of directly setting style properties on elements - allows for better flexibility alongside user-defined styles) + styleElem = document.createElement("style"); + styleElem.type = "text/css"; + + // Create style rules as text node + styleRules = document.createTextNode("." + placeholderClassName + " { color:" + placeholderStyleColor + "; }"); + + // Append style rules to newly created stylesheet + if (styleElem.styleSheet) { + styleElem.styleSheet.cssText = styleRules.nodeValue; + } else { + styleElem.appendChild(styleRules); + } + + // Prepend new style element to the head (before any existing stylesheets, so user-defined rules take precedence) + head.insertBefore(styleElem, head.firstChild); + + // Set up the placeholders + for (i = 0, len = inputs.length + textareas.length; i < len; i++) { + elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length]; + + // Get the value of the placeholder attribute, if any. IE10 emulating IE7 fails with getAttribute, hence the use of the attributes node + placeholder = elem.attributes.placeholder; + if (placeholder) { + + // IE returns an empty object instead of undefined if the attribute is not present + placeholder = placeholder.nodeValue; + + // Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value + if (placeholder && Utils.inArray(validTypes, elem.type)) { + newElement(elem); + } + } + } + + // If enabled, the polyfill will repeatedly check for changed/added elements and apply to those as well + timer = setInterval(function () { + for (i = 0, len = inputs.length + textareas.length; i < len; i++) { + elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length]; + + // Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value + placeholder = elem.attributes.placeholder; + if (placeholder) { + placeholder = placeholder.nodeValue; + if (placeholder && Utils.inArray(validTypes, elem.type)) { + + // If the element hasn't had event handlers bound to it then add them + if (!elem.getAttribute(ATTR_EVENTS_BOUND)) { + newElement(elem); + } + + // If the placeholder value has changed or not been initialised yet we need to update the display + if (placeholder !== elem.getAttribute(ATTR_CURRENT_VAL) || (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE))) { + + // Attempt to change the type of password inputs (fails in IE < 9) + if (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE) && Utils.changeType(elem, "text")) { + elem.setAttribute(ATTR_INPUT_TYPE, "password"); + } + + // If the placeholder value has changed and the placeholder is currently on display we need to change it + if (elem.value === elem.getAttribute(ATTR_CURRENT_VAL)) { + elem.value = placeholder; + } + + // Keep a reference to the current placeholder value in case it changes via another script + elem.setAttribute(ATTR_CURRENT_VAL, placeholder); + } + } + } + } + + // If live updates are not enabled cancel the timer + if (!liveUpdates) { + clearInterval(timer); + } + }, 100); + } + + // Expose public methods + Placeholders.disable = Placeholders.nativeSupport ? noop : disablePlaceholders; + Placeholders.enable = Placeholders.nativeSupport ? noop : enablePlaceholders; + +}(this)); |