diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..a7b2017 --- /dev/null +++ b/bower.json @@ -0,0 +1,8 @@ +{ + "name": "html5Forms.js", + "description": "Implements a subset of the HTML5 Forms module, including calendars, colour swatches, sliding widgets, client side validation and more.", + "repository": { + "type": "git", + "url": "git://github.com/zoltan-dulac/html5Forms.js" + } +} diff --git a/shared/js/html5Widgets.js b/shared/js/html5Widgets.js index 0e571d5..c73aedb 100644 --- a/shared/js/html5Widgets.js +++ b/shared/js/html5Widgets.js @@ -1,7 +1,7 @@ /******************************************************************************* * This notice must be untouched at all times. * - * This javascript library contains helper routines to assist with event + * This javascript library contains helper routines to assist with event * handling consinstently among browsers * * html5Widgets.js v.1.1 by Zoltan Hawryluk @@ -12,7 +12,7 @@ * version 1.1: implemented oninput method for form elements for unsupported browsers * fix IE9 to ensure backspace and delete keys fire an oninput event. * version 1.2: Added Number Element Widget - * + * * released under the MIT License: * http://www.opensource.org/licenses/mit-license.php * @@ -20,180 +20,180 @@ var html5Widgets = new function(){ var me = this; - - + + var delayEventTimeout = null; - + me.inputNodes = new Array(); me.outputNodes = new Array(); me.formElements = null; me.placeholderNodes = new Array(); me.dummyLink = document.createElement('input'); var quoteRe = /\"/g; - + var dummyIDCount = 0; var supportsNatively = new Object(); - + var isBadChrome = navigator.userAgent.indexOf('Chrome'); var valueRe = /this\.value/g; var varRe = /([a-zA-Z][a-zA-Z0-9]*\.value)/g; var isDebug; - + var isIE9 = false; - + /*@cc_on @if (@_jscript_version == 9) isIE9 = true; @end @*/ - - - + + + me.init = function(){ - + if (EventHelpers.hasPageLoadHappened(arguments)) { return; } - + supportsNatively['oninput'] = EventHelpers.isSupported('input', 'form'); - + isDebug = CSSHelpers.isMemberOfClass(document.body, 'html5Widgets-debug') - + // dummy link setup me.type = 'text' me.dummyLink.style.position = 'absolute'; me.dummyLink.style.top = '-200px'; document.body.appendChild(me.dummyLink) - + var inputSupport = Modernizr.input - + if (!inputSupport['placeholder']) { setPlaceholders(); } indexOutputNodes(); - insertElements(); - + insertElements(); + me.resolveOutputs(); - - /* document.getElementById('supports').innerHTML = + + /* document.getElementById('supports').innerHTML = DebugHelpers.getProperties(Modernizr.inputtypes, 'inputtypes') + " " + - DebugHelpers.getProperties(Modernizr.input, 'input') + " " + + DebugHelpers.getProperties(Modernizr.input, 'input') + " " + DebugHelpers.getProperties(Modernizr, 'Modernizr'); */ - + } - - - + + + function supports_input_placeholder() { var i = document.createElement('input'); return 'placeholder' in i; } - + function setPlaceholders() { - + var nodes = [document.getElementsByTagName('input'), document.getElementsByTagName('textarea')]; - + for (var i=0; i max) this.value = max; else if (this.value < min) this.value = this.min; */ - + } else if (this.value != '') { var val = parseFloat(this.value); if (isNaN(val)) { this.value = ''; } else { this.value = val; - } + } } - + } - - - - function nearestValid(value, direction) { - + + + + function nearestValid(value, direction) { + var n = (value - min)/step, r; //alert(StringHelpers.sprintf("n: %s, value: %s, min: %s, step: %s", n, value, min, step)) if (n == parseInt(n)) { r = value; } else { - + if (direction < 0) { n = Math.floor(n + 1); } else { n = Math.ceil(n -1); } - + r = min + step * n; } - + if (r > max) { r -= step; } else if (r < min) { r+= step } - + return r; } - + function buttonMouseDownEvent(e) { - + var buttonType = this.className; var stepMult = step; if (buttonType == 'dnbutton') { stepMult = -step; } - - + + var min = this.min; var max = this.max; if ( @@ -836,7 +836,7 @@ var html5Widgets = new function(){ var delayedOnce = false; var date = new Date(); var curDate = null; - + this.interval = setInterval(function() { if (!delayedOnce) { curDate = new Date(); @@ -851,7 +851,7 @@ var html5Widgets = new function(){ }, 50); EventHelpers.preventDefault(e); } - + function setValue(node, value) { if (isNaN(value)) { node.value = ''; @@ -859,7 +859,7 @@ var html5Widgets = new function(){ node.value = value; } } - + function buttonClickEvent(e) { clearInterval(this.interval); EventHelpers.preventDefault(e); @@ -868,142 +868,142 @@ var html5Widgets = new function(){ clearInterval(this.interval); EventHelpers.preventDefault(e); } - + function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); } - + function hasNativeSpinner() { try { return window.getComputedStyle(me.node, '-webkit-inner-spin-button').WebkitAppearance != undefined; } catch (ex) { return false; } - } - + } + function init () { var upbutton = document.createElement('a'); upbutton.className = 'upbutton'; upbutton.appendChild(document.createTextNode("\u25B2")); upbutton.targInput = node; upbutton.max = max; - + var dnbutton = document.createElement('a'); dnbutton.className = 'dnbutton'; dnbutton.appendChild(document.createTextNode("\u25BC")); dnbutton.targInput = node; dnbutton.min = min; dnbutton.max = max; - + EventHelpers.addEvent(upbutton, 'mousedown', buttonMouseDownEvent); EventHelpers.addEvent(dnbutton, 'mousedown', buttonMouseDownEvent); - + EventHelpers.addEvent(upbutton, 'click', buttonClickEvent); EventHelpers.addEvent(upbutton, 'mouseup', buttonMouseUpEvent); EventHelpers.addEvent(dnbutton, 'click', buttonClickEvent); EventHelpers.addEvent(dnbutton, 'mouseup', buttonMouseUpEvent); - - + + if (!hasNativeSpinner()) { var controlsNode = document.createElement('div'); controlsNode.className = 'html5-numberControls'; controlsNode.appendChild(upbutton); controlsNode.appendChild(dnbutton); - + var wrapperNode = document.createElement('div') wrapperNode.className = 'html5-numberWrapper'; wrapperNode.appendChild(controlsNode); var parentNode = node.parentNode; - - - - + + + + parentNode.insertBefore(wrapperNode, node); - - + + var nodeWidth = node.offsetWidth; var nodeStyle = CSSHelpers.getCurrentStyle(node); - + node.style.width = (nodeWidth - upbutton.offsetWidth -9) + 'px'; wrapperNode.style.width = nodeWidth + 'px'; wrapperNode.style.marginTop = nodeStyle.marginTop; wrapperNode.style.height = (node.offsetHeight) + 'px'; } - - - + + + } - /* Finally: if the form field has a value onload that is not a number, remove it + /* Finally: if the form field has a value onload that is not a number, remove it if (!isNumeric(node.value)) { node.value = ''; EventHelpers.fireEvent(node, 'change'); }*/ init(); } - + function PlaceholderInput (node) { var me = this; - + me.node = node; - + var form, defaultText; - + function init () { defaultText = DOMHelpers.getAttributeValue(node, 'placeholder'); form = DOMHelpers.getAncestorByTagName(node, 'form'); - + me.setPlaceholderText(true); EventHelpers.addEvent(me.node, 'blur', blurEvent); EventHelpers.addEvent(me.node, 'focus', focusEvent); - + if (me.node.form) { EventHelpers.addEvent(me.node.form, 'submit', removePlaceholderText); } - + if (window.$wf2) { if ($wf2.callBeforeValidation != undefined) { $wf2.callBeforeValidation.push(removePlaceholderText); } - + if ($wf2.callAfterValidation != undefined) { $wf2.callAfterValidation.push(postValidationEvent); } } } - + me.setPlaceholderText = function (isLoadEvent) { //jslog.debug(StringHelpers.sprintf('initiator: %s', this)); var isAutofocus = DOMHelpers.getAttributeValue(me.node, 'autofocus') != null; - - + + if (me.node.value == "" || (isLoadEvent && me.node.value == defaultText)) { CSSHelpers.addClass(me.node, 'html5-hasPlaceholderText'); me.node.value = defaultText; - + } - + if (isLoadEvent && isAutofocus && me.node.value == defaultText ) { CSSHelpers.removeClass(me.node, 'html5-hasPlaceholderText'); me.node.value = ''; } - - - + + + } - + function focusEvent(e) { - + CSSHelpers.addClass(me.node, 'html5-hasFocus'); removePlaceholderText(); } - + function blurEvent(e) { //jslog.debug('removed focus on ' + me.node.name) CSSHelpers.removeClass(me.node, 'html5-hasFocus'); me.setPlaceholderText(); } - + function removePlaceholderText() { //jslog.debug('removePlaceholderText() for ' + me.node.name) if (CSSHelpers.isMemberOfClass(me.node, 'html5-hasPlaceholderText')) { @@ -1011,99 +1011,99 @@ var html5Widgets = new function(){ CSSHelpers.removeClass(me.node, 'html5-hasPlaceholderText'); } } - + function postValidationEvent(e, didValidate) { ////jslog.debug(StringHelpers.sprintf('post Validation: %s, didValidate = %s, has focus = %s', me.node.name, didValidate, CSSHelpers.isMemberOfClass(me.node, 'html5-hasFocus') ) ) if (!didValidate && !CSSHelpers.isMemberOfClass(me.node, 'html5-hasFocus')) { me.setPlaceholderText(); - } + } } - + init(); } - + var CSSHelpers = new function () { var me = this; - + var blankRe = new RegExp('\\s'); /** * Generates a regular expression string that can be used to detect a class name - * in a tag's class attribute. It is used by a few methods, so I + * in a tag's class attribute. It is used by a few methods, so I * centralized it. - * + * * @param {String} className - a name of a CSS class. */ - + function getClassReString(className) { return '\\s'+className+'\\s|^' + className + '\\s|\\s' + className + '$|' + '^' + className +'$'; } - + function getClassPrefixReString(className) { return '\\s'+className+'-[0-9a-zA-Z_]+\\s|^' + className + '[0-9a-zA-Z_]+\\s|\\s' + className + '[0-9a-zA-Z_]+$|' + '^' + className +'[0-9a-zA-Z_]+$'; } - - + + /** * Make an HTML object be a member of a certain class. - * + * * @param {Object} obj - an HTML object * @param {String} className - a CSS class name. */ me.addClass = function (obj, className) { - + if (blankRe.test(className)) { return; } - + // only add class if the object is not a member of it yet. if (!me.isMemberOfClass(obj, className)) { obj.className += " " + className; } - + } - + /** * Make an HTML object *not* be a member of a certain class. - * + * * @param {Object} obj - an HTML object * @param {Object} className - a CSS class name. */ me.removeClass = function (obj, className) { - + if (blankRe.test(className)) { - return; + return; } - - + + var re = new RegExp(getClassReString(className) , "g"); - + var oldClassName = obj.className; - - + + if (obj.className) { obj.className = oldClassName.replace(re, ' '); } - + } - + /** * Determines if an HTML object is a member of a specific class. * @param {Object} obj - an HTML object. * @param {Object} className - the CSS class name. */ me.isMemberOfClass = function (obj, className) { - + if (blankRe.test(className)) return false; - + var re = new RegExp(getClassReString(className) , "g"); - + return (re.test(obj.className)); - - + + } - + /* from http://blog.stchur.com/2006/06/21/css-computed-style/ */ me.getCurrentStyle = function(obj) { @@ -1112,26 +1112,26 @@ var html5Widgets = new function(){ { computedStyle = obj.currentStyle; } else { computedStyle = document.defaultView.getComputedStyle(obj, null); } - + return computedStyle; } } - - - + + + var DOMHelpers = new function () { var me = this; - + /** * Given an HTML or XML object, find the an attribute by name. - * + * * @param {Object} obj - a DOM object. * @param {String} attrName - the name of an attribute inside the DOM object. * @return {Object} - the attribute object or null if there isn't one. */ me.getAttributeByName = function (obj, attrName) { var i; - + var attributes = obj.attributes; for (i=0; i=0) rs = rs.replace(/^(.*)(e.*)$/,'$1.$2'); - return processFlags(flags,width,rs,arg); + return processFlags(flags,width,rs,arg); } converters['f'] = function(flags,width,precision,arg) { iPrecision = parseInt(precision); @@ -1307,14 +1307,14 @@ var html5Widgets = new function(){ if(rse.indexOf('.')<0 && flags.indexOf('#')>=0) rse = rse.replace(/^(.*)(e.*)$/,'$1.$2'); if(rsf.indexOf('.')<0 && flags.indexOf('#')>=0) rsf = rsf + '.'; rs = rse.length=0) rs='0'+rs; - return processFlags(flags,width,rs,arg); + return processFlags(flags,width,rs,arg); } converters['X'] = function(flags,width,precision,arg) { return (converters['x'](flags,width,precision,arg)).toUpperCase(); @@ -1325,7 +1325,7 @@ var html5Widgets = new function(){ var rs = Math.round(arg).toString(16); if(rs.length=0) rs='0x'+rs; - return processFlags(flags,width,rs,arg); + return processFlags(flags,width,rs,arg); } converters['s'] = function(flags,width,precision,arg) { var iPrecision=parseInt(precision); @@ -1345,16 +1345,16 @@ var html5Widgets = new function(){ return retstr; } } - + var XMLHelpers = new function () { var me = this; - + /** * Given an XML node, return the XML inside as a string and the XML string of the node itself. * Similar to Internet Explorer's outerHTML property, except it is for XML, not HTML. * Created with information from http://www.codingforums.com/showthread.php?t=31489 - * and http://www.mercurytide.co.uk/whitepapers/issues-working-with-ajax/ - * + * and http://www.mercurytide.co.uk/whitepapers/issues-working-with-ajax/ + * * @param {Object} node - a DOM object. * @param {Object} options - a JS object containing options. To date, * the only one supported is "insertClosingTags", when set to @@ -1366,19 +1366,19 @@ var html5Widgets = new function(){ // Internet Explorer if (node.xml) { r = node.xml; - - // Everyone else - } else if (node.outerHTML) { + + // Everyone else + } else if (node.outerHTML) { r = node.outerHTML; } else if (window.XMLSerializer) { - + var serializer = new XMLSerializer(); var text = serializer.serializeToString(node); r = text; } else { return null; } - + /* * If the XML is actually HTML and you are inserting it into an HTML * document, you must use the "insertClosingTags" option, otherwise @@ -1392,21 +1392,21 @@ var html5Widgets = new function(){ return r; } } - - - // default styles + + + // default styles var placeholderCSS = 'color: #999999; font-style: italic'; var placeholderRequiredCSS = 'color: #ffcccc !important;' - + var sb = ""; - + // has to be two seperate rules, or some browsers, like firefox, will not use the rule. if (document.getElementsByTagName('body').length == 0) { sb = ''; - - + + document.write(sb); } } @@ -1414,4 +1414,3 @@ var html5Widgets = new function(){ EventHelpers.addPageLoadEvent('html5Widgets.init'); - diff --git a/shared/js/visibleIf.js b/shared/js/visibleIf.js index f23afcc..146f1d5 100644 --- a/shared/js/visibleIf.js +++ b/shared/js/visibleIf.js @@ -182,7 +182,7 @@ var visibleIf = new function(){ var nodes = nodesToIndex[n]; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; - var parentForm = DOMHelpers.getAncestorByTagName(node, 'form'); + var parentForm = DOMHelpers.getAncestorByTagName(node, 'form') || document.body; //fallback to document.body if element using for attribute outside form @@ -337,7 +337,8 @@ var visibleIf = new function(){ if (rule != null) { - //var parentForm = DOMHelpers.getAncestorByTagName(node, 'form'); + // var parentForm = DOMHelpers.getAncestorByTagName(node, 'form') || document.body; + if (!parentForm) { throw "Error: the rule " + rule + " is not attached to a form." }