/* * JavaScript Custom Forms Module */ jcf = { // global options modules: {}, plugins: {}, baseOptions: { unselectableClass:'jcf-unselectable', labelActiveClass:'jcf-label-active', labelDisabledClass:'jcf-label-disabled', classPrefix: 'jcf-class-', hiddenClass:'jcf-hidden', focusClass:'jcf-focus', wrapperTag: 'div' }, // replacer function customForms: { setOptions: function(obj) { for(var p in obj) { if(obj.hasOwnProperty(p) && typeof obj[p] === 'object') { jcf.lib.extend(jcf.modules[p].prototype.defaultOptions, obj[p]); } } }, replaceAll: function(context) { for(var k in jcf.modules) { var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector, context); for(var i = 0; i element and rules var head = document.getElementsByTagName('head')[0], style = document.createElement('style'), rules = document.createTextNode('.'+jcf.baseOptions.unselectableClass+'{'+ '-moz-user-select:none;'+ '-webkit-tap-highlight-color:rgba(255,255,255,0);'+ '-webkit-user-select:none;'+ 'user-select:none;'+ '}'); // append style element style.type = 'text/css'; if(style.styleSheet) { style.styleSheet.cssText = rules.nodeValue; } else { style.appendChild(rules); } head.appendChild(style); } }.init(); /* * Custom Form Control prototype */ jcf.setBaseModule({ init: function(){ if(this.options.replaces) { this.realElement = this.options.replaces; this.realElement.jcf = this; this.replaceObject(); } }, defaultOptions: { // default module options (will be merged with base options) }, checkElement: function(el){ return true; // additional check for correct form element }, replaceObject: function(){ this.createWrapper(); this.attachEvents(); this.fixStyles(); this.setupWrapper(); }, createWrapper: function(){ this.fakeElement = jcf.lib.createElement(this.options.wrapperTag); this.labelFor = jcf.lib.getLabelFor(this.realElement); jcf.lib.disableTextSelection(this.fakeElement); jcf.lib.addClass(this.fakeElement, jcf.lib.getAllClasses(this.realElement.className, this.options.classPrefix)); jcf.lib.addClass(this.realElement, jcf.baseOptions.hiddenClass); }, attachEvents: function(){ jcf.lib.event.add(this.realElement, 'focus', this.onFocusHandler, this); jcf.lib.event.add(this.realElement, 'blur', this.onBlurHandler, this); jcf.lib.event.add(this.fakeElement, 'click', this.onFakeClick, this); jcf.lib.event.add(this.fakeElement, jcf.eventPress, this.onFakePressed, this); jcf.lib.event.add(this.fakeElement, jcf.eventRelease, this.onFakeReleased, this); if(this.labelFor) { this.labelFor.jcf = this; jcf.lib.event.add(this.labelFor, 'click', this.onFakeClick, this); jcf.lib.event.add(this.labelFor, jcf.eventPress, this.onFakePressed, this); jcf.lib.event.add(this.labelFor, jcf.eventRelease, this.onFakeReleased, this); } }, fixStyles: function() { // hide mobile webkit tap effect if(jcf.isTouchDevice) { var tapStyle = 'rgba(255,255,255,0)'; this.realElement.style.webkitTapHighlightColor = tapStyle; this.fakeElement.style.webkitTapHighlightColor = tapStyle; if(this.labelFor) { this.labelFor.style.webkitTapHighlightColor = tapStyle; } } }, setupWrapper: function(){ // implement in subclass }, refreshState: function(){ // implement in subclass }, destroy: function() { if(this.fakeElement && this.fakeElement.parentNode) { this.fakeElement.parentNode.removeChild(this.fakeElement); } jcf.lib.removeClass(this.realElement, jcf.baseOptions.hiddenClass); this.realElement.jcf = null; }, onFocus: function(){ // emulated focus event jcf.lib.addClass(this.fakeElement,this.options.focusClass); }, onBlur: function(cb){ // emulated blur event jcf.lib.removeClass(this.fakeElement,this.options.focusClass); }, onFocusHandler: function() { // handle focus loses if(this.focused) return; this.focused = true; // handle touch devices also if(jcf.isTouchDevice) { if(jcf.focusedInstance && jcf.focusedInstance.realElement != this.realElement) { jcf.focusedInstance.onBlur(); jcf.focusedInstance.realElement.blur(); } jcf.focusedInstance = this; } this.onFocus.apply(this, arguments); }, onBlurHandler: function() { // handle focus loses if(!this.pressedFlag) { this.focused = false; this.onBlur.apply(this, arguments); } }, onFakeClick: function(){ if(jcf.isTouchDevice) { this.onFocus(); } else if(!this.realElement.disabled) { this.realElement.focus(); } }, onFakePressed: function(e){ this.pressedFlag = true; }, onFakeReleased: function(){ this.pressedFlag = false; }, onCreateModule: function(){ // implement in subclass }, onModuleAdded: function(module) { // implement in subclass }, onControlReady: function() { // implement in subclass } }); /* * JCF Utility Library */ jcf.lib = { bind: function(func, scope){ return function() { return func.apply(scope, arguments); }; }, browser: (function() { var ua = navigator.userAgent.toLowerCase(), res = {}, match = /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version)?[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+))?/.exec(ua) || []; res[match[1]] = true; res.version = match[2] || "0"; res.safariMac = ua.indexOf('mac') != -1 && ua.indexOf('safari') != -1; return res; })(), getOffset: function (obj) { if (obj.getBoundingClientRect && !jcf.isWinPhoneDevice) { var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; var clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0; var clientTop = document.documentElement.clientTop || document.body.clientTop || 0; return { top:Math.round(obj.getBoundingClientRect().top + scrollTop - clientTop), left:Math.round(obj.getBoundingClientRect().left + scrollLeft - clientLeft) }; } else { var posLeft = 0, posTop = 0; while (obj.offsetParent) {posLeft += obj.offsetLeft; posTop += obj.offsetTop; obj = obj.offsetParent;} return {top:posTop,left:posLeft}; } }, getScrollTop: function() { return window.pageYOffset || document.documentElement.scrollTop; }, getScrollLeft: function() { return window.pageXOffset || document.documentElement.scrollLeft; }, getWindowWidth: function(){ return document.compatMode=='CSS1Compat' ? document.documentElement.clientWidth : document.body.clientWidth; }, getWindowHeight: function(){ return document.compatMode=='CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight; }, getStyle: function(el, prop) { if (document.defaultView && document.defaultView.getComputedStyle) { return document.defaultView.getComputedStyle(el, null)[prop]; } else if (el.currentStyle) { return el.currentStyle[prop]; } else { return el.style[prop]; } }, getParent: function(obj, selector) { while(obj.parentNode && obj.parentNode != document.body) { if(obj.parentNode.tagName.toLowerCase() == selector.toLowerCase()) { return obj.parentNode; } obj = obj.parentNode; } return false; }, isParent: function(parent, child) { while(child.parentNode) { if(child.parentNode === parent) { return true; } child = child.parentNode; } return false; }, getLabelFor: function(object) { var parentLabel = jcf.lib.getParent(object,'label'); if(parentLabel) { return parentLabel; } else if(object.id) { return jcf.lib.queryBySelector('label[for="' + object.id + '"]')[0]; } }, disableTextSelection: function(el){ if (typeof el.onselectstart !== 'undefined') { el.onselectstart = function() {return false;}; } else if(window.opera) { el.setAttribute('unselectable', 'on'); } else { jcf.lib.addClass(el, jcf.baseOptions.unselectableClass); } }, enableTextSelection: function(el) { if (typeof el.onselectstart !== 'undefined') { el.onselectstart = null; } else if(window.opera) { el.removeAttribute('unselectable'); } else { jcf.lib.removeClass(el, jcf.baseOptions.unselectableClass); } }, queryBySelector: function(selector, scope){ if(typeof scope === 'string') { var result = []; var holders = this.getElementsBySelector(scope); for (var i = 0, contextNodes; i < holders.length; i++) { contextNodes = Array.prototype.slice.call(this.getElementsBySelector(selector, holders[i])); result = result.concat(contextNodes); } return result; } else { return this.getElementsBySelector(selector, scope); } }, prevSibling: function(node) { while(node = node.previousSibling) if(node.nodeType == 1) break; return node; }, nextSibling: function(node) { while(node = node.nextSibling) if(node.nodeType == 1) break; return node; }, fireEvent: function(element,event) { if(element.dispatchEvent){ var evt = document.createEvent('HTMLEvents'); evt.initEvent(event, true, true ); return !element.dispatchEvent(evt); }else if(document.createEventObject){ var evt = document.createEventObject(); return element.fireEvent('on'+event,evt); } }, inherit: function(Child, Parent) { var F = function() { } F.prototype = Parent.prototype Child.prototype = new F() Child.prototype.constructor = Child Child.superclass = Parent.prototype }, extend: function(obj) { for(var i = 1; i < arguments.length; i++) { for(var p in arguments[i]) { if(arguments[i].hasOwnProperty(p)) { obj[p] = arguments[i][p]; } } } return obj; }, hasClass: function (obj,cname) { return (obj.className ? obj.className.match(new RegExp('(\\s|^)'+cname+'(\\s|$)')) : false); }, addClass: function (obj,cname) { if (!this.hasClass(obj,cname)) obj.className += (!obj.className.length || obj.className.charAt(obj.className.length - 1) === ' ' ? '' : ' ') + cname; }, removeClass: function (obj,cname) { if (this.hasClass(obj,cname)) obj.className=obj.className.replace(new RegExp('(\\s|^)'+cname+'(\\s|$)'),' ').replace(/\s+$/, ''); }, toggleClass: function(obj, cname, condition) { if(condition) this.addClass(obj, cname); else this.removeClass(obj, cname); }, createElement: function(tagName, options) { var el = document.createElement(tagName); for(var p in options) { if(options.hasOwnProperty(p)) { switch (p) { case 'class': el.className = options[p]; break; case 'html': el.innerHTML = options[p]; break; case 'style': this.setStyles(el, options[p]); break; default: el.setAttribute(p, options[p]); } } } return el; }, setStyles: function(el, styles) { for(var p in styles) { if(styles.hasOwnProperty(p)) { switch (p) { case 'float': el.style.cssFloat = styles[p]; break; case 'opacity': el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+styles[p]*100+')'; el.style.opacity = styles[p]; break; default: el.style[p] = (typeof styles[p] === 'undefined' ? 0 : styles[p]) + (typeof styles[p] === 'number' ? 'px' : ''); } } } return el; }, getInnerWidth: function(el) { return el.offsetWidth - (parseInt(this.getStyle(el,'paddingLeft')) || 0) - (parseInt(this.getStyle(el,'paddingRight')) || 0); }, getInnerHeight: function(el) { return el.offsetHeight - (parseInt(this.getStyle(el,'paddingTop')) || 0) - (parseInt(this.getStyle(el,'paddingBottom')) || 0); }, getAllClasses: function(cname, prefix, skip) { if(!skip) skip = ''; if(!prefix) prefix = ''; return cname ? cname.replace(new RegExp('(\\s|^)'+skip+'(\\s|$)'),' ').replace(/[\s]*([\S]+)+[\s]*/gi,prefix+"$1 ") : ''; }, getElementsBySelector: function(selector, scope) { if(typeof document.querySelectorAll === 'function') { return (scope || document).querySelectorAll(selector); } var selectors = selector.split(','); var resultList = []; for(var s = 0; s < selectors.length; s++) { var currentContext = [scope || document]; var tokens = selectors[s].replace(/^\s+/,'').replace(/\s+$/,'').split(' '); for (var i = 0; i < tokens.length; i++) { token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,''); if (token.indexOf('#') > -1) { var bits = token.split('#'), tagName = bits[0], id = bits[1]; var element = document.getElementById(id); if (tagName && element.nodeName.toLowerCase() != tagName) { return []; } currentContext = [element]; continue; } if (token.indexOf('.') > -1) { var bits = token.split('.'), tagName = bits[0] || '*', className = bits[1], found = [], foundCount = 0; for (var h = 0; h < currentContext.length; h++) { var elements; if (tagName == '*') { elements = currentContext[h].getElementsByTagName('*'); } else { elements = currentContext[h].getElementsByTagName(tagName); } for (var j = 0; j < elements.length; j++) { found[foundCount++] = elements[j]; } } currentContext = []; var currentContextIndex = 0; for (var k = 0; k < found.length; k++) { if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) { currentContext[currentContextIndex++] = found[k]; } } continue; } if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) { var tagName = RegExp.$1 || '*', attrName = RegExp.$2, attrOperator = RegExp.$3, attrValue = RegExp.$4; if(attrName.toLowerCase() == 'for' && this.browser.msie && this.browser.version < 8) { attrName = 'htmlFor'; } var found = [], foundCount = 0; for (var h = 0; h < currentContext.length; h++) { var elements; if (tagName == '*') { elements = currentContext[h].getElementsByTagName('*'); } else { elements = currentContext[h].getElementsByTagName(tagName); } for (var j = 0; elements[j]; j++) { found[foundCount++] = elements[j]; } } currentContext = []; var currentContextIndex = 0, checkFunction; switch (attrOperator) { case '=': checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue) }; break; case '~': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))) }; break; case '|': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))) }; break; case '^': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0) }; break; case '$': checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length) }; break; case '*': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1) }; break; default : checkFunction = function(e) { return e.getAttribute(attrName) }; } currentContext = []; var currentContextIndex = 0; for (var k = 0; k < found.length; k++) { if (checkFunction(found[k])) { currentContext[currentContextIndex++] = found[k]; } } continue; } tagName = token; var found = [], foundCount = 0; for (var h = 0; h < currentContext.length; h++) { var elements = currentContext[h].getElementsByTagName(tagName); for (var j = 0; j < elements.length; j++) { found[foundCount++] = elements[j]; } } currentContext = found; } resultList = [].concat(resultList,currentContext); } return resultList; }, scrollSize: (function(){ var content, hold, sizeBefore, sizeAfter; function buildSizer(){ if(hold) removeSizer(); content = document.createElement('div'); hold = document.createElement('div'); hold.style.cssText = 'position:absolute;overflow:hidden;width:100px;height:100px'; hold.appendChild(content); document.body.appendChild(hold); } function removeSizer(){ document.body.removeChild(hold); hold = null; } function calcSize(vertical) { buildSizer(); content.style.cssText = 'height:'+(vertical ? '100%' : '200px'); sizeBefore = (vertical ? content.offsetHeight : content.offsetWidth); hold.style.overflow = 'scroll'; content.innerHTML = 1; sizeAfter = (vertical ? content.offsetHeight : content.offsetWidth); if(vertical && hold.clientHeight) sizeAfter = hold.clientHeight; removeSizer(); return sizeBefore - sizeAfter; } return { getWidth:function(){ return calcSize(false); }, getHeight:function(){ return calcSize(true) } } }()), domReady: function (handler){ var called = false function ready() { if (called) return; called = true; handler(); } if (document.addEventListener) { document.addEventListener("DOMContentLoaded", ready, false); } else if (document.attachEvent) { if (document.documentElement.doScroll && window == window.top) { function tryScroll(){ if (called) return if (!document.body) return try { document.documentElement.doScroll("left") ready() } catch(e) { setTimeout(tryScroll, 0) } } tryScroll() } document.attachEvent("onreadystatechange", function(){ if (document.readyState === "complete") { ready() } }) } if (window.addEventListener) window.addEventListener('load', ready, false) else if (window.attachEvent) window.attachEvent('onload', ready) }, event: (function(){ var guid = 0; function fixEvent(e) { e = e || window.event; if (e.isFixed) { return e; } e.isFixed = true; e.preventDefault = e.preventDefault || function(){this.returnValue = false} e.stopPropagation = e.stopPropagation || function(){this.cancelBubble = true} if (!e.target) { e.target = e.srcElement } if (!e.relatedTarget && e.fromElement) { e.relatedTarget = e.fromElement == e.target ? e.toElement : e.fromElement; } if (e.pageX == null && e.clientX != null) { var html = document.documentElement, body = document.body; e.pageX = e.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0); e.pageY = e.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0); } if (!e.which && e.button) { e.which = e.button & 1 ? 1 : (e.button & 2 ? 3 : (e.button & 4 ? 2 : 0)); } if(e.type === "DOMMouseScroll" || e.type === 'mousewheel') { e.mWheelDelta = 0; if (e.wheelDelta) { e.mWheelDelta = e.wheelDelta/120; } else if (e.detail) { e.mWheelDelta = -e.detail/3; } } return e; } function commonHandle(event, customScope) { event = fixEvent(event); var handlers = this.events[event.type]; for (var g in handlers) { var handler = handlers[g]; var ret = handler.call(customScope || this, event); if (ret === false) { event.preventDefault() event.stopPropagation() } } } var publicAPI = { add: function(elem, type, handler, forcedScope) { if (elem.setInterval && (elem != window && !elem.frameElement)) { elem = window; } if (!handler.guid) { handler.guid = ++guid; } if (!elem.events) { elem.events = {}; elem.handle = function(event) { return commonHandle.call(elem, event); } } if (!elem.events[type]) { elem.events[type] = {}; if (elem.addEventListener) elem.addEventListener(type, elem.handle, false); else if (elem.attachEvent) elem.attachEvent("on" + type, elem.handle); if(type === 'mousewheel') { publicAPI.add(elem, 'DOMMouseScroll', handler, forcedScope); } } var fakeHandler = jcf.lib.bind(handler, forcedScope); fakeHandler.guid = handler.guid; elem.events[type][handler.guid] = forcedScope ? fakeHandler : handler; }, remove: function(elem, type, handler) { var handlers = elem.events && elem.events[type]; if (!handlers) return; delete handlers[handler.guid]; for(var any in handlers) return; if (elem.removeEventListener) elem.removeEventListener(type, elem.handle, false); else if (elem.detachEvent) elem.detachEvent("on" + type, elem.handle); delete elem.events[type]; for (var any in elem.events) return; try { delete elem.handle; delete elem.events; } catch(e) { if(elem.removeAttribute) { elem.removeAttribute("handle"); elem.removeAttribute("events"); } } if(type === 'mousewheel') { publicAPI.remove(elem, 'DOMMouseScroll', handler); } } } return publicAPI; }()) }