text: /*!
   * 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.
   */
  
  ( function ( global ) {
  
  	'use strict';
  
  	//
  	// Test for support. We do this as early as possible to optimise for browsers
  	// that have native support for the attribute.
  	//
  
  	var test = document.createElement('input');
  	var nativeSupport = test.placeholder !== void 0;
  
  	global.Placeholders = {
  		nativeSupport: nativeSupport,
  		disable: nativeSupport ? noop : disablePlaceholders,
  		enable: nativeSupport ? noop : enablePlaceholders
  	};
  
  	if ( nativeSupport ) {
  		return;
  	}
  
  	//
  	// If we reach this point then the browser does not have native support for
  	// the attribute.
  	//
  
  	// The list of input element types that support the placeholder attribute.
  	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.
  	var 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.
  	var placeholderStyleColor = '#ccc';
  	var placeholderClassName = 'placeholdersjs';
  	var classNameRegExp = new RegExp('(?:^|\\s)' + placeholderClassName + '(?!\\S)');
  
  	// The various data-* attributes used by the polyfill.
  	var ATTR_CURRENT_VAL = 'data-placeholder-value';
  	var ATTR_ACTIVE = 'data-placeholder-active';
  	var ATTR_INPUT_TYPE = 'data-placeholder-type';
  	var ATTR_FORM_HANDLED = 'data-placeholder-submit';
  	var ATTR_EVENTS_BOUND = 'data-placeholder-bound';
  	var ATTR_OPTION_FOCUS = 'data-placeholder-focus';
  	var ATTR_OPTION_LIVE = 'data-placeholder-live';
  	var ATTR_MAXLENGTH = 'data-placeholder-maxlength';
  
  	// Various other variables used throughout the rest of the script.
  	var UPDATE_INTERVAL = 100;
  	var head = document.getElementsByTagName('head')[ 0 ];
  	var root = document.documentElement;
  	var Placeholders = global.Placeholders;
  	var keydownVal;
  
  	// Get references to all the input and textarea elements currently in the DOM
  	// (live NodeList objects to we only need to do this once).
  	var inputs = document.getElementsByTagName('input');
  	var 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.
  	var hideOnInput = root.getAttribute(ATTR_OPTION_FOCUS) === 'false';
  	var 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).
  	var styleElem = document.createElement('style');
  	styleElem.type = 'text/css';
  
  	// Create style rules as text node.
  	var 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.
  	var placeholder;
  	var elem;
  
  	for ( var i = 0, len = inputs.length + textareas.length; i < len; i++ ) {
  
  		// Find the next element. If we've already done all the inputs we move on
  		// to the textareas.
  		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 the element has a placeholder attribute we need to modify it.
  		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 && inArray(validTypes, elem.type) ) {
  				newElement(elem);
  			}
  		}
  	}
  
  	// If enabled, the polyfill will repeatedly check for changed/added elements
  	// and apply to those as well.
  	var timer = setInterval(function () {
  		for ( var 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 && 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) &&
  							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);
  					}
  				}
  			} else if ( elem.getAttribute(ATTR_ACTIVE) ) {
  				hidePlaceholder(elem);
  				elem.removeAttribute(ATTR_CURRENT_VAL);
  			}
  		}
  
  		// If live updates are not enabled cancel the timer.
  		if ( !liveUpdates ) {
  			clearInterval(timer);
  		}
  	}, UPDATE_INTERVAL);
  
  	// Disabling placeholders before unloading the page prevents flash of
  	// unstyled placeholders on load if the page was refreshed.
  	addEventListener(global, 'beforeunload', function () {
  		Placeholders.disable();
  	});
  
  	//
  	// Utility functions
  	//
  
  	// No-op (used in place of public methods when native support is detected).
  	function noop() {}
  
  	// Avoid IE9 activeElement of death when an iframe is used.
  	//
  	// More info:
  	//	- http://bugs.jquery.com/ticket/13393
  	//	- https://github.com/jquery/jquery/commit/85fc5878b3c6af73f42d61eedf73013e7faae408
  	function safeActiveElement() {
  		try {
  			return document.activeElement;
  		} catch ( err ) {}
  	}
  
  	// 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 ) {
  		for ( var i = 0, len = arr.length; i < len; i++ ) {
  			if ( arr[ i ] === item ) {
  				return true;
  			}
  		}
  		return false;
  	}
  
  	// 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);
  		}
  	}
  
  	// 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;
  		}
  	}
  
  	function handleElem( node, callback ) {
  
  		// 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.
  			var handleInputs = node ? node.getElementsByTagName('input') : inputs;
  			var handleTextareas = node ? node.getElementsByTagName('textarea') : textareas;
  
  			var handleInputsLength = handleInputs ? handleInputs.length : 0;
  			var handleTextareasLength = handleTextareas ? handleTextareas.length : 0;
  
  			// Run the callback for each element.
  			var len = handleInputsLength + handleTextareasLength;
  			var elem;
  			for ( var i = 0; i < len; i++ ) {
  
  				elem = i < handleInputsLength ?
  					handleInputs[ i ] :
  					handleTextareas[ i - handleInputsLength ];
  
  				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);
  	}
  
  	// 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, keydownValue ) {
  
  		var valueChanged = !!keydownValue && elem.value !== keydownValue;
  		var isPlaceholderValue = elem.value === elem.getAttribute(ATTR_CURRENT_VAL);
  
  		if (
  			( valueChanged || isPlaceholderValue ) &&
  			elem.getAttribute(ATTR_ACTIVE) === 'true'
  		) {
  
  			elem.removeAttribute(ATTR_ACTIVE);
  			elem.value = elem.value.replace(elem.getAttribute(ATTR_CURRENT_VAL), '');
  			elem.className = elem.className.replace(classNameRegExp, '');
  
  			// Restore the maxlength value. Old FF returns -1 if attribute not set.
  			// See GH-56.
  			var maxLength = elem.getAttribute(ATTR_MAXLENGTH);
  			if ( parseInt(maxLength, 10) >= 0 ) {
  				elem.setAttribute('maxLength', maxLength);
  				elem.removeAttribute(ATTR_MAXLENGTH);
  			}
  
  			// If the polyfill has changed the type of the element we need to change
  			// it back.
  			var 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 val = elem.getAttribute(ATTR_CURRENT_VAL);
  
  		if ( elem.value === '' && val ) {
  
  			elem.setAttribute(ATTR_ACTIVE, 'true');
  			elem.value = val;
  			elem.className += ' ' + placeholderClassName;
  
  			// Store and remove the maxlength value.
  			var maxLength = elem.getAttribute(ATTR_MAXLENGTH);
  			if ( !maxLength ) {
  				elem.setAttribute(ATTR_MAXLENGTH, elem.maxLength);
  				elem.removeAttribute('maxLength');
  			}
  
  			// If the type of element needs to change, change it (e.g. password
  			// inputs).
  			var type = elem.getAttribute(ATTR_INPUT_TYPE);
  			if ( type ) {
  				elem.type = 'text';
  			} else if ( elem.type === 'password' && changeType(elem, 'text') ) {
  				elem.setAttribute(ATTR_INPUT_TYPE, 'password');
  			}
  
  			return true;
  		}
  
  		return false;
  	}
  
  	// 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).
  				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);
  		};
  	}
  
  	// 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);
  		};
  	}
  
  	// 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' &&
  				keydownVal === elem.getAttribute(ATTR_CURRENT_VAL) &&
  				inArray(badKeys, e.keyCode)
  			) {
  				if ( e.preventDefault ) {
  						e.preventDefault();
  				}
  				return false;
  			}
  		};
  	}
  
  	function makeKeyupHandler(elem) {
  		return function () {
  			hidePlaceholder(elem, keydownVal);
  
  			// If the element is now empty we need to show the placeholder
  			if ( elem.value === '' ) {
  				elem.blur();
  				moveCaret(elem, 0);
  			}
  		};
  	}
  
  	function makeClickHandler(elem) {
  		return function () {
  			if (
  				elem === safeActiveElement() &&
  				elem.value === elem.getAttribute(ATTR_CURRENT_VAL) &&
  				elem.getAttribute(ATTR_ACTIVE) === 'true'
  			) {
  				moveCaret(elem, 0);
  			}
  		};
  	}
  
  	// 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.
  		var form = elem.form;
  		if ( form && typeof form === 'string' ) {
  
  			// Get the real form.
  			form = document.getElementById(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) ) {
  				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.
  		addEventListener(elem, 'focus', makeFocusHandler(elem));
  		addEventListener(elem, 'blur', makeBlurHandler(elem));
  
  		// If the placeholder should hide on input rather than on focus we need
  		// additional event handlers
  		if (hideOnInput) {
  			addEventListener(elem, 'keydown', makeKeydownHandler(elem));
  			addEventListener(elem, 'keyup', makeKeyupHandler(elem));
  			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 and is not focussed, set it to the
  		// placeholder string.
  		if ( hideOnInput || elem !== safeActiveElement() ) {
  			showPlaceholder(elem);
  		}
  	}
  
  }(this) );