/** * DefaultHint * @version $Id$; * @author Nazar Lazorko * @uses jQuery * * Example of using DefaultHint in QSF2: * @see DefaultHint::$_defOptions * * $params = array( * 'elements' => array( * array('name', 'Name:', true), * array('emailOrPhone', 'Email:', false), * array('comments', 'Your Message:'), // By default showAsterisk = false * ) * ); * $this->doc->addScript('js/defaultHint.js', array(), 'defaultHint'); * $this->doc->addInitFunction ('DefaultHint.init', array($params)); * * */ var DefaultHint = (function () { /** * @var object Default Options */ var _defOptions = { elements: [], collectFrom: null, // apply defaultHint to children of "collectFrom" requiredClass: 'required', // class of labels marked as required asteriskClass: 'asterisk', // class of asterisk element asteriskText: '*', useWrapper: false, // wrap element with relative hints into div wrapperIdSuffix: '-wrapper', wrapperClass: 'defaultHint-wrapper', containerIdSuffix: '-hint', containerClass: 'defaultHint-element', activeElementClass: '', // class of focused elements activeParentClass: '' // class than applied to active element parent }; var _getType = function (mixed) { return Object.prototype.toString.call(mixed); }; var _isElement = function (hEl) { if (hEl instanceof jQuery) { hEl = hEl.get(0); } var tagName = (hEl.tagName) ? hEl.tagName.toLowerCase() : ''; if ('input' == tagName) { return (hEl.type && ('text' == hEl.type || 'password' == hEl.type)); } return ('textarea' == tagName || 'select' == tagName); }; var _isLabel = function (hEl) { if (hEl instanceof jQuery) { hEl = hEl.get(0); } return ('label' == hEl.tagName.toLowerCase() && hEl.htmlFor); }; function Cls(o) { this._o = {}; this._oType = false; this._tmpL = null; this._tmpE = []; if (!jQuery) { throw new Error('DefaultHint requires jQuery library'); } this.isIE = jQuery.browser.msie || false; jQuery.extend(true, this._o, _defOptions); var options = this._o; this._onElementFocus = function () { if (this.defaultHint) { this.defaultHint.style.display = 'none'; } if (options.activeElementClass.length > 0) { jQuery(this).addClass(options.activeElementClass); } if (options.activeParentClass.length > 0 && this.parentNode) { jQuery(this.parentNode).addClass(options.activeParentClass); } return false; } this._onElementBlur = function () { if ('' === this.value && this.defaultHint) { this.defaultHint.style.display = 'block'; } if (options.activeElementClass.length > 0) { jQuery(this).removeClass(options.activeElementClass); } if (options.activeParentClass.length > 0 && this.parentNode) { jQuery(this.parentNode).removeClass(options.activeParentClass); } return false; } var _oType = _getType(o); switch (_oType) { case '[object String]': this._o.collectFrom = o; break; case '[object Array]': this._o.elements = o; break; case '[object Object]': jQuery.extend(true, this._o, o); break; default: throw new Error('Invalid parameter options type: "' + this._oType + '" given in DefaultHint.init()'); } this._prepare(); } Cls.prototype = { _onHintClick: function () { this.belongTo.focus(); return false; }, _onHintMouseDown: function () { var el = this.belongTo; setTimeout(function () { el.focus(); }, 0); return false; }, _parseElementLabel: function (param) { var el; if (!(el = document.getElementById(param.id)) || !_isElement(el)) { throw new Error('Invalid element: "' + param.id + '" given in DefaultHint._parseElementLabel()'); } if (null === this._tmpL && el.form) { this._collectLabels(el.form); } if (this._tmpL[param.id]) { param.hint = this._tmpL[param.id].innerHTML || param.id; param.asterisk = jQuery(this._tmpL[param.id]).hasClass(this._o.requiredClass); } return param; }, _getParamByArr: function (arr) { var param = {id: false, hint: false, asterisk: false}; param.id = arr[0] || false; param.hint = arr[1] || false; param.asterisk = arr[2] || false; if (false === param.hint) { param = this._parseElementLabel(param); } return param; }, _getParamByStr: function (str) { var param = { id: str || false, hint: false, asterisk: false } if (false === param.hint) { param = this._parseElementLabel(param); } return param; }, _prepareParam: function (prm) { var type = _getType(prm); switch (type) { case '[object Array]': return this._getParamByArr(prm); break; case '[object Number]': if ('undefined' != typeof this._o.elements[prm]) { return this._prepareParam(this._o.elements[prm]); } else { throw new Error('Invalid parameter id: "' + prm + '" given in DefaultHint._prepareParam()'); } break; case '[object String]': return this._getParamByStr(prm); break; } throw new Error('Unknown param type "' + type + '" given in DefaultHint._prepareParam()'); }, _setElementHint: function (oEl, param) { var hint = document.createElement('div'); hint.id = param.id + this._o.containerIdSuffix; hint.className = this._o.containerClass; if (param.asterisk || false) { var span = document.createElement('span'); span.className = this._o.asteriskClass; span.appendChild(document.createTextNode(this._o.asteriskText)); hint.appendChild(span); } hint.appendChild(document.createTextNode(param.hint)); if (oEl.val() !== '') { hint.style.display = 'none'; } jQuery(hint).click(this._onHintClick).mousedown(this._onHintMouseDown); var el = oEl.get(0); hint.belongTo = el; el.defaultHint = hint; return hint; }, _setSelectHint: function (oEl, param) { var first = oEl.children(':first'); var option = document.createElement('option'); option.value = ''; option.label = param.hint; option.text = param.hint; if (first.size()) { if ('' === first.val()) { first.attr('label', param.hint); first.attr('text', param.hint); } else { first.before(option); var second = oEl.children().get(1); if (second && second.value == oEl.val()) { oEl.attr('selectedIndex', 0); } } } else { oEl.append(option); } return false; }, _setHint: function (prm) { var param, oEl, hint; param = this._prepareParam(prm); if (!(oEl = jQuery('#'+param.id))) { throw new Error('Element with id: "' + param.id + '" not found'); } if ('select' == oEl.attr('tagName').toLowerCase()) { hint = this._setSelectHint(oEl, param); } else { hint = this._setElementHint(oEl, param); } oEl.focus(this._onElementFocus).blur(this._onElementBlur); if (this._o.useWrapper) { var oWrap = jQuery('
'); oEl.after(oWrap); oWrap.append(oEl); if ('object' == typeof hint) { oWrap.append(hint); } } else if ('object' == typeof hint) { oEl.after(hint); } return true; }, _collectLabels: function (formId) { var labels; this._tmpL = {}; if ('string' == typeof formId) { labels = jQuery('#' + formId + ' label'); } else { labels = jQuery(formId).find('label'); } for (var i = 0; i < labels.length; i++) { var label = labels[i]; if (_isLabel(label)) { this._tmpL[label.htmlFor] = label; } } }, _collectElements: function (formId) { var form, i, elementHint, showAsterisk; if ('string' == typeof formId) { form = jQuery('#' + formId); } else { form = jQuery(formId); } if (!form.length) { throw new Error('Invalid container: "' + formId + '"'); } this._o.elements = []; this._collectLabels(formId); this._o.elements = []; var elements = form.find(':input'); for (i = 0; i < elements.length; i++) { var el = elements[i]; if (_isElement(el)) { if (this._tmpL[el.id]) { elementHint = this._tmpL[el.id].innerHTML || el.id; showAsterisk = jQuery(el).hasClass(this._o.requiredClass); } else { elementHint = el.id; showAsterisk = false; } this._o.elements.push([el.id, elementHint, showAsterisk]); } } }, _prepare: function () { if (this._o.collectFrom) { this._collectElements(this._o.collectFrom); } this._oType = _getType(this._o.elements); var i; for (i = 0; i < this._o.elements.length; i++) { this._setHint(this._o.elements[i]); } return true; } }; return { /** * Setup hints and return new instance of DefaultHint * @param options mixed * @return DefaultHint */ init: function (options) { return new Cls(options); } } }());