// ---------------------------------------------------------------- // EwCore // ---------------------------------------------------------------- var EwCore = new function () { var self = this; this.lang = ""; this.applicationPath = "/"; this.getEwUrl = function (ewAppUrl) { return this.applicationPath + ewAppUrl; }; this.ajaxContentAdded = function (element) { $(element).ajaxFormParse(); }; var ajaxUrlInternal = function (ajaxItemPath, command, addUniqueId) { var url = self.getEwUrl("ew/ajax/" + command + "?path=" + EwUtils.urlEncode(ajaxItemPath.replace("\\", "/"))); if (addUniqueId) { var uniqueId = EwCore.newUniqueId("ajax"); url += "&uniqueId=" + EwUtils.urlEncode(uniqueId); } return url; }; this.ajaxUrlGet = function (ajaxItemPath, model) { var requestData = this.createDataRequestBasic(); if (model != undefined) { $.extend(requestData, { model: model }); } var requestDataParam = JSON.stringify(requestData); return ajaxUrlInternal(ajaxItemPath, "get", true) + "&data=" + EwUtils.urlEncode(requestDataParam); }; this.createDataRequestBasic = function () { var ret = { lang: self.lang, masterTemplateName: self.masterTemplateName }; if (self.templateContextGuid != undefined) { ret.templateContextGuid = self.templateContextGuid; } return ret; }; var ajaxBaseCall = function (url, optsInternal, opts) { var requestData = self.createDataRequestBasic(); if (opts != undefined && opts.model != undefined) { $.extend(requestData, { model: opts.model }); } if (optsInternal.requestExt != undefined) { $.extend(requestData, optsInternal.requestExt); } var ajaxParams = { type: 'POST', cache: false, url: url, dataType: optsInternal.dataType, contentType: 'application/json', data: JSON.stringify(requestData), success: optsInternal.success, complete: function (xhr) { var location = xhr.getResponseHeader("Location"); if (location) { window.location = location; } } }; if (opts != undefined) { $.extend(ajaxParams, { error: opts.error, async: opts.async }); } $.ajax(ajaxParams); }; this.ajaxLoad = function (ajaxItemPath, opts) { var ajaxCallSuccess = function (htmlContent) { if (opts != undefined && opts.success != undefined) { opts.success(htmlContent); } if (opts != undefined && opts.targetId != undefined) { var $target = $("#" + opts.targetId); $target.html(htmlContent); self.ajaxContentAdded($target[0]); } }; var url = ajaxUrlInternal(ajaxItemPath, "post", true); ajaxBaseCall(url, { success: ajaxCallSuccess, dataType: "html" }, opts); }; this.ajaxAction = function (ajaxItemPath, actionName, actionData, opts) { var ajaxCallSuccess = function (responseData) { var htmlContent = responseData.htmlContent; if (opts != undefined && opts.success != undefined) { opts.success(htmlContent, responseData); } if (opts != undefined && opts.targetId != undefined) { var $target = $("#" + opts.targetId); $target.html(htmlContent); self.ajaxContentAdded($target[0]); } self.processAjaxControlActionResponse(responseData); }; var url = ajaxUrlInternal(ajaxItemPath, "action", false); ajaxBaseCall(url, { requestExt: { actionName: actionName, actionData: actionData }, success: ajaxCallSuccess, dataType: "json" }, opts); }; this.ajaxData = function (ajaxItemPath, methodName, methodData, opts) { var ajaxCallSuccess = function (data) { if (opts != undefined && opts.success != undefined) { opts.success(data); } }; var url = ajaxUrlInternal(ajaxItemPath, "data"); ajaxBaseCall(url, { requestExt: { methodName: methodName, methodData: methodData }, success: ajaxCallSuccess, dataType: "json" }, opts); }; this.ajaxLoadError = undefined; this.processAjaxControlActionResponse = function (responseData) { if (responseData.resultErrorMessages != undefined) { var message = ""; for (var i = 0; i < responseData.resultErrorMessages.length; i++) { var mes = responseData.resultErrorMessages[i]; if (mes != "") { if (message != "") { message += "
"; } message += "" + mes + ""; } } EwDialogs.userMessage(message); } if (responseData.resultJavaScriptCode != undefined) { var caller = function () { eval(responseData.resultJavaScriptCode); }; caller.call(responseData); } if (responseData.actionResultRedirectToUrl != undefined) { window.location = responseData.actionResultRedirectToUrl; } else if (responseData.actionResultReload) { window.location.reload(true); } }; this.isAjaxUserAborted = function(xhr) { return !xhr.getAllResponseHeaders(); } var uniqueIdIndex = 0; this.newUniqueId = function (prefix) { if (prefix == null) { prefix = "default"; }; return prefix + "-ewUniqueId-" + (++uniqueIdIndex); }; }; $(function () { $(document).ajaxError(function (event, jqxhr, settings, thrownError) { if (settings.error == undefined) { if (!EwCore.isAjaxUserAborted(jqxhr)) { if (EwCore.ajaxLoadError == undefined) { EwDialogs.errorMessage("Ajax request error '" + thrownError + "' on url '" + settings.url + "'!"); } else { if (EwCore.ajaxLoadError != null) { EwCore.ajaxLoadError(event, jqxhr, settings, thrownError); } } } } }); }); // ---------------------------------------------------------------- // EwUtils // ---------------------------------------------------------------- var EwUtils = new function () { this.updateQueryStringParameter = function (url, key, value, setItEmpty) { var re = new RegExp("([?|&])" + key + "=.*?(&|$)", "i"); var clearEmpty = (setItEmpty != true && (value == undefined || value == '')); if (url.match(re)) { if (clearEmpty) { return url.replace(re, '$1').replace(/[&|?]+$/, ""); } return url.replace(re, '$1' + key + "=" + value + '$2'); } else { if (clearEmpty) { return url; } else { var separator = url.indexOf('?') !== -1 ? "&" : "?"; return url + separator + key + "=" + value; } } }; this.notUndefined = function (value) { if (value != undefined) { return value; } return ""; }; this.utf8Encode = function (string) { string = string.replace(/\r\n/g, "\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; }; this.utf8Decode = function (utftext) { var string = ""; var i = 0; var c, c2, c3; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; }; this.urlEncode = function (string) { return escape(this.utf8Encode(string)); }; this.urlDecode = function (urlParam) { return this.utf8Decode(unescape(urlParam)); }; this.htmlEncode = function (value) { return $('
').text(value).html(); }; this.htmlDecode = function (value) { return $('
').html(value).text(); }; var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; this.base64Encode = function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = this.utf8Encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); } return output; }; this.base64Decode = function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = this.utf8Decode(output); return output; }; this.setCookie = function (name, value, opts) { var expires = ""; var seconds = undefined; if (opts != undefined) { if (opts.seconds != undefined) { seconds = opts.seconds; } if (opts.minutes != undefined) { if (!seconds) seconds = 0; seconds += opts.minutes * 60; } if (opts.hours != undefined) { if (!seconds) seconds = 0; seconds += opts.hours * 60 * 60; } if (opts.days != undefined) { if (!seconds) seconds = 0; seconds += opts.days * 24 * 60 * 60; } } if (typeof (seconds) != 'undefined') { var date = new Date(); date.setTime(date.getTime() + (seconds * 1000)); expires = "; expires=" + date.toGMTString(); } document.cookie = name + "=" + value + expires + "; path=/"; }; this.getCookie = function (name) { name = name + "="; var carray = document.cookie.split(';'); for (var i = 0; i < carray.length; i++) { var c = carray[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(name) == 0) return c.substring(name.length, c.length); } return null; }; this.deleteCookie = function (name) { this.setCookie(name, "", -1); }; this.getUrlParamsAsJson = function () { var urlParams = {}; var d = function (s) { return decodeURIComponent(s.replace(/\+/g, " ")); }; var q = window.location.search.substring(1); var r = /([^&=]+)=?([^&]*)/g; var e; while (e = r.exec(q)) { urlParams[d(e[1])] = d(e[2]); } return urlParams; //var search = location.search.substring(1); //return JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}'); }; this.getUrlParameter = function (name) { return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ""])[1].replace(/\+/g, '%20')) || null; }; this.parseNumber = function (originalValue, defaultValue) { return this.parseValue(originalValue, "number", defaultValue); }; this.parseBoolean = function (originalValue, defaultValue) { return this.parseValue(originalValue, "boolean", defaultValue); }; this.parseValue = function (originalValue, type, defaultValue) { var val = originalValue; switch (typeof val) { case "function": val = val(); break; case "object": if (val instanceof HTMLElement) { val = $(val).val(); } break; } if (val == undefined) { return defaultValue; } switch (type) { case "string": { return originalValue + ""; } case "number": { if (typeof val === "boolean") { return val ? 1 : 0; } if (typeof val === "number") { return val; } val = parseFloat((val + "").replace(',', '.').replace(/\s/g, '')); return isNaN(val) ? defaultValue : val; } case "boolean": { if (typeof val === "number") { return val != 0; } if (typeof val === "boolean") { return val; } val += ""; if (val.match(/\.(true|yes|1|t|y)$/i)) { return true; } if (val.match(/\.(false|no|0|f|n)$/i)) { return true; } return defaultValue; } default: { throw "EwUtils.parseValue(): unsupported type '" + type + "'!"; } } }; }; // ---------------------------------------------------------------- // DataViewManager // ---------------------------------------------------------------- var DataViewManager = new function () { this.firstPageUrl = []; this.isAjaxView = []; this.setFirstPageUrl = function (dataViewManagerInstance, url) { this.isAjaxView[dataViewManagerInstance] = false; this.firstPageUrl[dataViewManagerInstance] = url; }; this.setAjaxView = function (dataViewManagerInstance) { this.isAjaxView[dataViewManagerInstance] = true; }; this.navigateViewWithParameterUpdate = function (dataViewManagerInstance, key, value, setIfEmpty, firstPath) { var isAjaxView = this.isAjaxView[dataViewManagerInstance]; if (!isAjaxView) { if (firstPath == undefined || firstPath) { var url = "http://humlickovi-com.cs4.cstech.cz/this.firstPageUrl[dataViewManagerInstance];" window.location.href = EwUtils.updateQueryStringParameter(url, key, value, setIfEmpty); } else { window.location.href = EwUtils.updateQueryStringParameter(window.location.href, key, value, setIfEmpty); } } }; this.navigateViewWithParameterUpdateFromCombo = function (dataViewManagerInstance, key, obj, setIfEmpty, firstPath) { this.navigateViewWithParameterUpdate(dataViewManagerInstance, key, obj.options[obj.selectedIndex].value, setIfEmpty, firstPath); }; }; // ---------------------------------------------------------------- // Partial render controls support // ---------------------------------------------------------------- var PartialRenderControls = new function () { var self = this; var partialControls = []; var actionCallInternal = function (controlDivId, controls, actionName, actionData, actionInitiator) { var dataBasic = EwCore.createDataRequestBasic(); var actionControl = self.getInstanceByControlDivId(controlDivId); if (actionControl == undefined) { throw { toString: function () { return "Partial render control #" + controlDivId + " is not registered!"; } }; } var controlsArray = []; controlsArray.push(actionControl); if (controls != undefined) { for (var i = 0; i < controls.length; i++) { var partialRenderInfo = controls[i]; if (partialRenderInfo.controlDivId != controlDivId) { controlsArray.push(partialRenderInfo); } } } var requestData = $.extend({ controls: controlsArray, actionName: actionName, actionData: actionData }, dataBasic); if (actionInitiator != undefined) { requestData.actionInitiator = actionInitiator; } var reloadUrl = self.getPartialRenderHandlerUrl("action"); self.jsonRequest(reloadUrl, requestData); }; var reloadInternal = function (requestData) { var reloadUrl = self.getPartialRenderHandlerUrl("reload"); self.jsonRequest(reloadUrl, requestData); }; var removeByControlDivId = function (controlDivId) { for (var i = partialControls.length - 1; i >= 0; i--) { if (partialControls[i].controlDivId == controlDivId) { partialControls.splice(i, 1); } } }; var checkControlDivIdIsValid = function (controlDivId) { return $('#' + controlDivId).length > 0; }; this.reloadByTags = function (tags) { reloadInternal(this.createDataRequestReloadByTags(tags)); }; this.reloadByControlDivIds = function (controlDivIds) { reloadInternal(this.createDataRequestReloadByControlDivIds(controlDivIds)); }; this.reloadAll = function () { this.reloadByTags(); }; this.actionCallSimple = function (controlDivId, actionName, actionData, actionInitiator) { actionCallInternal(controlDivId, undefined, actionName, actionData, actionInitiator); }; this.actionCallAndRefreshByTags = function (controlDivId, refreshTags, actionName, actionData, actionInitiator) { var controls = this.getInstancesByTags(refreshTags); actionCallInternal(controlDivId, controls, actionName, actionData, actionInitiator); }; this.actionCallAndRefreshByIds = function (controlDivId, refreshControlDivIds, actionName, actionData, actionInitiator) { var controls = this.getInstancesByControlDivIds(refreshControlDivIds); actionCallInternal(controlDivId, controls, actionName, actionData, actionInitiator); }; this.requestCallAndRefreshByTags = function (url, refreshTags, data) { var dataRequest = PartialRenderControls.createDataRequestReloadByTags(refreshTags); this.jsonRequest(url, $.extend(dataRequest, data)); }; this.requestCallAndRefreshByIds = function (url, refreshControlDivIds, data) { var dataRequest = PartialRenderControls.createDataRequestReloadByIds(refreshControlDivIds); this.jsonRequest(url, $.extend(dataRequest, data)); }; this.getPartialRenderHandlerUrl = function (command) { return EwCore.getEwUrl("ew/partial_render/" + command); }; this.registerPartialRenderControlInstance = function (controlDivId, controlType, renderParams, tags) { removeByControlDivId(controlDivId); partialControls.push({ controlDivId: controlDivId, controlType: controlType, renderParams: renderParams, tags: tags }); }; this.getInstancesByTags = function (tags, checkValitity) { var ret = []; var tagsArray; if (tags != undefined && tags.length > 0) { tagsArray = tags.split(","); } for (var i = 0; i < partialControls.length; i++) { var partialRenderInfo = partialControls[i]; var tagsMatch = false; if (tagsArray == undefined) { tagsMatch = true; } else { if (partialRenderInfo.tags != undefined) { var n = partialRenderInfo.tags.split(","); for (var j = 0; j < tagsArray.length; j++) { var tagCheck = tagsArray[j]; if ($.inArray(tagCheck, n) != -1) { tagsMatch = true; break; } } } } if (tagsMatch) { if (!checkValitity || checkControlDivIdIsValid(partialRenderInfo.controlDivId)) { ret.push(partialRenderInfo); } } } return ret; }; this.getInstancesByControlDivIds = function (controlDivIds, checkValitity) { var ret = []; var controlDivIdArray; if (controlDivIds != undefined && controlDivIds.length > 0) { controlDivIdArray = controlDivIds.split(","); for (var i = 0; i < partialControls.length; i++) { var partialRenderInfo = partialControls[i]; if ($.inArray(partialRenderInfo.controlDivId, controlDivIdArray) != -1) { if (!checkValitity || checkControlDivIdIsValid(partialRenderInfo.controlDivId)) { ret.push(partialRenderInfo); } } } } return ret; }; this.getInstanceByControlDivId = function (controlDivId) { for (var i = 0; i < partialControls.length; i++) { var partialRenderInfo = partialControls[i]; if (partialRenderInfo.controlDivId == controlDivId) { return partialRenderInfo; } } return undefined; }; this.createDataRequestReloadByTags = function (tags) { var dataBasic = EwCore.createDataRequestBasic(); var controls = this.getInstancesByTags(tags, true); return $.extend({ controls: controls }, dataBasic); }; this.createDataRequestReloadByControlDivIds = function (controlDivIds) { var dataBasic = EwCore.createDataRequestBasic(); var controls = this.getInstancesByControlDivIds(controlDivIds, true); return $.extend({ controls: controls }, dataBasic); }; this.jsonRequest = function (url, requestData, opts) { var error = (opts != undefined) ? opts.error : undefined; var success = (opts != undefined) ? opts.success : undefined; var complete = (opts != undefined) ? opts.complete : undefined; var postBackWriteContent = function (dataResponse) { if (dataResponse != undefined && dataResponse.controls != undefined) { for (var i = 0; i < dataResponse.controls.length; i++) { var partialRenderData = dataResponse.controls[i]; var $target = $("#" + partialRenderData.controlDivId); $target.html(partialRenderData.htmlContent); EwCore.ajaxContentAdded($target[0]); } } EwCore.processAjaxControlActionResponse(dataResponse); if (typeof success == 'function') { success(); } }; var ajaxParams = { type: 'POST', url: url, dataType: "json", contentType: 'application/json', data: JSON.stringify(requestData), success: postBackWriteContent, complete: function (xhr) { var location = xhr.getResponseHeader("Location"); if (location) { window.location = location; } else { if (typeof complete == 'function') { complete(); } } } }; if (error != undefined) { $.extend(ajaxParams, { error: error }); } $.ajax(ajaxParams); }; }; // ---------------------------------------------------------------- // EwDialog // ---------------------------------------------------------------- var EwDialogs = new function () { // to override by a plugin (custom dialogs design) this.customUserMessage = undefined; this.customErrorMessage = undefined; this.customConfirm = undefined; var checkOverrideFunctionExists = function (funcName) { // ReSharper disable UseOfImplicitGlobalInFunctionScope return (typeof EwDialogsOverride !== 'undefined' && EwDialogsOverride[funcName] != undefined); // ReSharper restore UseOfImplicitGlobalInFunctionScope }; var stripHtml = function (html) { return $("
" + EwUtils.htmlEncode(html) + "
").text(); }; this.errorMessage = function (msg) { if (this.customErrorMessage != undefined) { this.customErrorMessage(msg); } else { if (checkOverrideFunctionExists("errorMessage")) { // ReSharper disable UseOfImplicitGlobalInFunctionScope EwDialogsOverride.errorMessage(msg); // ReSharper restore UseOfImplicitGlobalInFunctionScope } else { alert(stripHtml(msg)); } } }; this.userMessage = function (msg) { if (this.customUserMessage != undefined) { this.customUserMessage(msg); } else { if (checkOverrideFunctionExists("userMessage")) { // ReSharper disable UseOfImplicitGlobalInFunctionScope EwDialogsOverride.userMessage(msg); // ReSharper restore UseOfImplicitGlobalInFunctionScope } else { alert(stripHtml(msg)); } } }; this.confirm = function (msg, opts) { if (this.customConfirm != undefined) { this.customConfirm(msg, opts); } else { if (checkOverrideFunctionExists("confirm")) { // ReSharper disable UseOfImplicitGlobalInFunctionScope EwDialogsOverride.confirm(msg, opts); // ReSharper restore UseOfImplicitGlobalInFunctionScope } else { if (confirm(stripHtml(msg))) { opts.submit(); } } } }; }; // ---------------------------------------------------------------- // AjaxForms // ---------------------------------------------------------------- var AjaxForms = (function ($, window) { 'use strict'; var fieldModelSelector = 'ajaxFormFieldModel', forms = [], formSelector = '*[data-ajax-form=true], form:not(#aspnetForm)', keys = { tab: 9, enter: 13, shift: 16, ctrl: 17, alt: 18 }, validators = {}, viewModelSelector = 'ajaxFormViewModel', getField = function (formModel, fieldName) { var field = formModel.fields[fieldName]; if (field === undefined) { throw 'Error: Unable to find field with name ' + fieldName + ' in current form (' + formModel.name + ')!'; } return field; }, getForm = function (element) { var $element = $(element); if (!$element.is(formSelector)) { $element = $element.closest(formSelector); } return $element; }, gotFocus = function (formModel) { var dateBlur = formModel.prevFieldBlur; if (dateBlur != null && new Date().getTime() - dateBlur < 20) { formModel.prevFieldBlur = null; var $prev = formModel.$prevField; var prevModel = $prev.data(fieldModelSelector); if (formModel.options.validationMode === 'focusOut' || (formModel.options.validationMode === 'focusOutWhenChanged' && prevModel.getValue() !== $prev.data("valueOnFocus"))) { prevModel.validate(); } } }, defaults = { autoErrorClass: true, autoValidatorClass: true, autoValidatorClassPrefix: 'val_', errorClass: 'error', onChangeVisibility: function (targetElement, visible) { if (visible) { $(targetElement).show(); } else { $(targetElement).hide(); } }, onRefreshLayout: function (formModel, initPhase) { var i, cond, visible, fn = initPhase ? defaults.onChangeVisibility : formModel.options.onChangeVisibility; for (i = 0; i < formModel.conditionals.length; i++) { cond = formModel.conditionals[i]; visible = eval(cond.condition); fn(cond.$element[0], visible); cond.$element.data('valEnabled', visible); } }, onValidateChanged: function (fieldElement, isValid, validatorName) { var $errorSpan, fieldName, errorClass = this.viewModel.options.errorClass; if (isValid) { if (this.$errorSpan !== undefined) { this.errorMessage = null; if (this.$errorSpan.data('valMessageFor') === undefined) { this.$errorSpan.remove(); this.$errorSpan = undefined; } else { this.$errorSpan.html(''); } } this.$errorMarkers.removeClass(errorClass); if (this.viewModel.options.autoErrorClass) { this.$forElements.removeClass(errorClass); this.$element.removeClass(errorClass); } } else { $errorSpan = this.$errorSpan; fieldName = this.$element.data('field'); if ($errorSpan === undefined) { $errorSpan = $('*[data-val-message-for="' + fieldName + '"]', getForm(fieldElement)); if ($errorSpan.length === 0) { $errorSpan = undefined; } } if ($errorSpan === undefined) { $errorSpan = $(''); $errorSpan.addClass(errorClass); this.$element.after($errorSpan); } if ($errorSpan === null) { throw 'unable create error span for field ' + fieldName; } this.errorMessage = this.validators[validatorName].message; $errorSpan.html(this.errorMessage); this.$errorSpan = $errorSpan; this.$errorMarkers.addClass(errorClass); if (this.viewModel.options.autoErrorClass) { this.$forElements.addClass(errorClass); this.$element.addClass(errorClass); } } }, onValidatorEnabledChanged: function (fieldElement, validatorName, enabled) { var field, validatorClass, $field = $(fieldElement), fieldName = $field.data('field'); if (!enabled) { this.options.onValidateChanged.call(this.fields[fieldName], fieldElement, true); } if (this.options.autoValidatorClass) { field = getField(this, fieldName); validatorClass = this.options.autoValidatorClassPrefix + validatorName; if (enabled) { field.$forElements.addClass(validatorClass); $field.addClass(validatorClass); } else { field.$forElements.removeClass(validatorClass); $field.removeClass(validatorClass); } } }, validateOnChange: false,validateOnKeyUp: false, validationMode: 'focusOut' //'focusOutWhenChanged' }, createFieldModel = function (formModel, $field) { var validatorName, msg, fieldName = $field.data('field'), fieldModel = { errorMessage: null, $element: $field, viewModel: formModel, validators: {}, $errorMarkers: $("*[data-val-marker='" + fieldName + "']"), $forElements: $("*[data-label-for='" + fieldName + "'],*[data-val-message-for='" + fieldName + "']"), getValue: function () { var evalFn, evalWrapper, value = $field.data('fieldValue'); if (value !== undefined) { return value; } evalFn = $field.data('fieldEval'); if (evalFn !== undefined) { // ReSharper disable UnusedParameter evalWrapper = function (field) { // ReSharper restore UnusedParameter value = eval(evalFn); }; evalWrapper(this.$element[0]); if (value !== undefined) { return value; } } if ($field.is('input')) { switch ($field.attr('type').toLowerCase()) { case 'checkbox': return $field.is(':checked'); } } return $field.val(); }, setValue: function (value) { var fieldSetFn; if ($field.data('fieldValue') !== undefined) { $field.data('fieldValue', value); return; } fieldSetFn = $field.data('fieldSet'); if (fieldSetFn !== undefined) { eval(fieldSetFn); return; } if ($field.is('input')) { switch ($field.attr('type').toLowerCase()) { case 'checkbox': { if (value === true) { $field.attr("checked", "checked"); } else { $field.attr("checked", null); } break; } } } $field.val(value); }, validate: function (data) { var i, valInst, wasValid = this.errorMessage === null, $el = this.$element, sortedValidators = []; this.errorMessage = null; while ($el.data(viewModelSelector) === undefined) { if ($el.data('valEnabled') === false) { return true; } $el = $el.parent(); } for (i in this.validators) { if (this.validators.hasOwnProperty(i)) { sortedValidators.push({ validator: this.validators[i], name: i }); } } sortedValidators = sortedValidators.sort(function (first, second) { return second.validator.priority - first.validator.priority; }); for (i = 0; i < sortedValidators.length; i++) { valInst = sortedValidators[i].validator; if (valInst.enabled && !valInst.func(this, data)) { if (wasValid || this.errorMessage !== sortedValidators[i].validator.message) { this.viewModel.options.onValidateChanged.call(this, $field[0], false, sortedValidators[i].name); } return false; } } if (!wasValid) { this.viewModel.options.onValidateChanged.call(this, $field[0], true); } return true; } }; for (validatorName in validators) { if (validators.hasOwnProperty(validatorName)) { msg = $field.data('val' + validatorName.charAt(0).toUpperCase() + validatorName.slice(1)); if (msg !== undefined) { fieldModel.validators[validatorName] = $.extend({}, validators[validatorName], { message: msg, enabled: true }); } } } $field.data(fieldModelSelector, fieldModel); $field.change(function () { var thisFieldModel = $(this).data(fieldModelSelector); if (thisFieldModel.viewModel.options.validateOnChange) { thisFieldModel.validate(); } if (fieldModel.$element.data('layoutChangeNotify') === true) { thisFieldModel.viewModel.options.onRefreshLayout(thisFieldModel.viewModel, false); } }); return fieldModel; }, initializeButtons = function ($target) { $('*[data-form-button]', $target).each(function () { $(this).click(function () { var $this = $(this), buttonAction = $this.data('formButton'), form = $this.ajaxForm(); if (form !== undefined && form !== null) { form.executeButtonAction(buttonAction, $this.data('initiator')); } return undefined; }).focus(function () { gotFocus($(this).ajaxForm()); }); }); }, initializeFields = function (formModel) { $('*[data-field]', $(formModel.form)).each(function () { var $field = $(this), fieldName = $field.data('field'), fieldModel = createFieldModel(formModel, $field); formModel.fields[fieldName] = fieldModel; $field.focus(function () { var $this = $(this); $this.data("valueOnFocus", $this.data(fieldModelSelector).getValue()); gotFocus(formModel); }).blur(function () { formModel.$prevField = $(this); formModel.prevFieldBlur = new Date().getTime(); }); if ($field.is('input') && ($field.attr('type').toLowerCase() === 'text' || $field.attr('type').toLowerCase() === 'password')) { $field.keydown(function (e) { var $this = $(this); if ((e.keyCode || e.which) === keys.enter) { $this.data("valueOnKeyDownEnter", $this.data(fieldModelSelector).getValue()); } }).keyup(function (e) { var $this = $(this), form = $this.ajaxForm(), thisFieldModel = $this.data(fieldModelSelector), valueEquals = thisFieldModel.getValue() === $this.data("valueOnKeyDownEnter"), keyCode = e.keyCode || e.which; if (keyCode === keys.enter) { if (valueEquals) { e.preventDefault(); if (form !== undefined && form !== null) { form.submit(); } } } else if (form.options.validateOnKeyUp && keyCode !== keys.tab && keyCode !== keys.shift && keyCode !== keys.ctrl && keyCode !== keys.alt) { if (!valueEquals) { thisFieldModel.validate({ initiator: "keyUp", keyCode: keyCode }); } } }); } }); }, initializeValidators = function (formModel) { var i, j, field; for (i in formModel.fields) { if (formModel.fields.hasOwnProperty(i)) { field = formModel.fields[i]; for (j in field.validators) { if (field.validators.hasOwnProperty(j)) { formModel.options.onValidatorEnabledChanged.call(formModel, field.$element[0], j, true); } } } } }, initializeVisibleConditions = function (formModel, $target) { $('*[data-form-visible-condition]', $target).each(function () { var $this = $(this), formVisibleCondition = $this.data('formVisibleCondition'); if (formVisibleCondition !== undefined && formVisibleCondition !== null) { formVisibleCondition = formVisibleCondition.replace(/\{:(.*?):\}/g, function (group, fieldName) { getField(formModel, fieldName); return 'formModel.fields["' + fieldName + '"].getValue()'; }); } formModel.conditionals.push({ $element: $this, condition: formVisibleCondition }); }); }, registerBasicButtonActions = function (formModel) { registerButtonAction(formModel, 'submit', function (initiator) { formModel.submit(initiator); }); registerButtonAction(formModel, 'reset', function () { formModel.reset(); }); registerButtonAction(formModel, 'validate', function () { formModel.validate(); }); }, registerBasicValidators = function () { registerValidator('required', 200, function (field, data) { if (data !== undefined) { return undefined; } var initialValue = field.$element.data('valRequiredInitialValue'); if (initialValue === undefined) { if (field.$element.is("input[type=checkbox]")) { initialValue = true; } else { initialValue = ''; } } return field.getValue() !== initialValue; }); registerValidator('length', 190, function (field, data) { if (data !== undefined) { return undefined; } var length = field.getValue().length, minLength = field.$element.data('valLengthMin'), maxLength = field.$element.data('valLengthMax'); return (minLength === undefined || parseInt(minLength, 10) <= length) && (maxLength === undefined || parseInt(maxLength, 10) >= length); }); registerValidator('email', 110, function (field, data) { if (data !== undefined) { return undefined; } var val = field.getValue(), regexEmail = /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2}|com|org|net|gov|mil|biz|edu|info|mobi|name|aero|asia|cat|coop|int|jobs|museum|post|pro|tel|travel|xxx)\b$/; return val === '' || regexEmail.test(val); }); registerValidator('regex', 110, function (field, data) { if (data !== undefined) { return undefined; } var val = field.getValue(), expr = field.$element.data('valRegexExpr'), match = expr.match(new RegExp('^/?(.*?)/?(g?i?m?y?)$')), regex = match[2] === undefined ? new RegExp(match[1]) : new RegExp(match[1], match[2]); return val === '' || regex.test(val); }); registerValidator('range', 110, function (field, data) { if (data !== undefined) { return undefined; } var val = field.getValue(), value = parseFloat(val), minValue = field.$element.data('valRangeMin'), maxValue = field.$element.data('valRangeMax'); return val === '' || ((minValue === undefined || parseFloat(minValue) <= value) && (maxValue === undefined || parseFloat(maxValue) >= value)); }); registerValidator('custom', 100, function (field, data) { if (data !== undefined) { return undefined; } var caller, value = field.getValue(), fnCustom = field.$element.data('valCustomCode'), result = true; if (fnCustom !== undefined) { // ReSharper disable UnusedParameter caller = function (fieldElement, actualValue) { // ReSharper restore UnusedParameter result = eval(fnCustom); }; caller(field.$element[0], value); } return result; }); }, registerButtonAction = function (formModel, actionName, func) { formModel.buttonActions[actionName] = func; }, registerValidator = function (name, priority, validateFunc) { validators[name] = { priority: priority, func: validateFunc }; }, readChangeVisibilityAttribute = function (formModel, $target, opts) { var override, base; override = $target.data('onChangeVisibility'); if (override !== undefined) { base = opts.onChangeVisibility; opts.onChangeVisibility = function (target, visible) { if (eval(override) !== false) { base.call(formModel, target, visible); } }; } }, readOptionsFromAttributes = function (formModel) { var override, opts = $.extend({}, defaults), $target = $(formModel.form); readChangeVisibilityAttribute(formModel, $target, opts); readValidateChangedAttribute(formModel, $target, opts); readRefreshLayoutAttribute($target, opts); readValidatorEnabledChangedAttribute(formModel, $target, opts); override = $target.data('autoErrorClass'); if (override !== undefined) { opts.autoErrorClass = override === 'true'; } override = $target.data('autoValidatorClass'); if (override !== undefined) { opts.autoValidatorClass = override === 'true'; } override = $target.data('autoValidatorClassPrefix'); if (override !== undefined) { opts.autoValidatorClassPrefix = override; } override = $target.data('errorClass'); if (override !== undefined) { opts.errorClass = override; } override = $target.data('validationMode'); if (override !== undefined) { opts.validationMode = override; } override = $target.data('validateOnKeyUp'); if (override !== undefined) { opts.validateOnKeyUp = override === 'true'; } override = $target.data('validateOnChange'); if (override !== undefined) { opts.validateOnChange = override === 'true'; } return opts; }, readRefreshLayoutAttribute = function ($target, opts) { var override, base; override = $target.data('onRefreshLayout'); if (override !== undefined) { base = opts.onRefreshLayout; opts.onRefreshLayout = function (formModel, initPhase) { if (eval(override) !== false) { base(formModel, initPhase); } }; } }, readValidateChangedAttribute = function (formModel, $target, opts) { var override, base, field; override = $target.data('onValidateChanged'); if (override !== undefined) { base = opts.onValidateChanged; opts.onValidateChanged = function (fieldElement, isValid, validatorName) { if (eval(override) !== false) { field = getField(formModel, $(fieldElement).data('field')); base.call(field, fieldElement, isValid, validatorName); } }; } }, readValidatorEnabledChangedAttribute = function (formModel, $target, opts) { var override, base; override = $target.data('onValidatorEnabledChanged'); if (override !== undefined) { base = opts.onValidatorEnabledChanged; opts.onValidatorEnabledChanged = function (fieldElement, validatorName, enabled) { if (eval(override) !== false) { base.call(formModel, fieldElement, validatorName, enabled); } }; } }, updateOptionsFromParams = function (formModel) { var i, data = $(formModel.form).data(); for (i in data) { if (data.hasOwnProperty(i)) { if (i.indexOf('option') === 0) { formModel.options[i.substr(6)] = data[i]; } } } }; // ReSharper disable InconsistentNaming function AjaxForm(formTarget, options) { // ReSharper restore InconsistentNaming registerBasicValidators(); var $target = $(formTarget), formName = $target.data('formName'); forms[formName] = this; $.extend(this, { form: formTarget, name: formName, fields: {}, buttonActions: {}, conditionals: [], $validationSummary: $('*[data-val-summary-for="' + formName + '"]') }); this.options = $.extend({}, readOptionsFromAttributes(this), options); this.$prevField = null, this.prevFieldBlur = null, registerBasicButtonActions(this); updateOptionsFromParams(this); initializeFields(this); initializeValidators(this); initializeButtons($target); initializeVisibleConditions(this, $target); this.options.onRefreshLayout(this, true); } $.extend(AjaxForm.prototype, { buildData: function () { var i, json = {}; for (i in this.fields) { if (this.fields.hasOwnProperty(i)) { json[i] = this.fields[i].getValue(); } } return json; }, executeButtonAction: function (action, initiator) { var handler = this.buttonActions[action]; if (handler === undefined || handler === null) { throw "Error: Unable resolve button action '" + action + "'!"; } handler.call(this, initiator); }, getFieldValue: function (fieldName) { var field = getField(this, fieldName); return field.getValue(); }, setFieldValue: function(fieldName, value) { var field = getField(this, fieldName); field.setValue(value); }, getFieldElement: function (fieldName) { var field = getField(this, fieldName); return field.$element[0]; }, refreshLayout: function () { this.options.onRefreshLayout(this, false); }, registerButtonAction: function (actionName, func) { registerButtonAction(this, actionName, func); }, reset: function () { $(':input', $(this.form)).filter('*[data-field]').each(function () { $(this).val(undefined); }); }, setValidatorEnabled: function (validatorName, enabled, fieldName) { var i, self = this, setValidator = function (field) { var validator = field.validators[validatorName]; if (validator !== undefined && validator !== null) { if (validator.enabled !== enabled) { validator.enabled = enabled; self.options.onValidatorEnabledChanged.call(self, field.$element[0], validatorName, enabled); } } }; if (fieldName === undefined) { for (i in this.fields) { if (this.fields.hasOwnProperty(i)) { setValidator(this.fields[i]); } } } else { setValidator(this.fields[fieldName]); } }, submit: function (initiator) { var actionUrl, handler, caller, location, $target = $(this.form); var submitData = this.submitGetData(initiator); if (submitData == undefined) { return; } actionUrl = $target.data('actionUrl'); if (actionUrl !== undefined) { $.ajax({ type: 'POST', url: actionUrl, dataType: 'json', contentType: 'application/json', data: JSON.stringify(submitData), success: function (data) { handler = $target.data('onSuccess'); if (handler !== undefined) { // ReSharper disable UnusedParameter // ReSharper disable DuplicatingLocalDeclaration caller = function (data, initiator, formModel) { // ReSharper restore DuplicatingLocalDeclaration // ReSharper restore UnusedParameter eval(handler); }; caller(data, initiator, this); } }, error: function (data) { handler = $target.data('onError'); if (handler !== undefined) { // ReSharper disable UnusedParameter // ReSharper disable DuplicatingLocalDeclaration caller = function (data, initiator, formModel) { // ReSharper restore DuplicatingLocalDeclaration // ReSharper restore UnusedParameter eval(handler); }; caller(data, initiator, this); } else { throw 'Error: Form ' + $target.data('formName') + ' submit error! - Response: ' + data.responseText; } }, complete: function (xhr) { location = xhr.getResponseHeader('Location'); if (location) { window.location = location; } } }); return; } handler = $target.data('onSubmit'); if (handler !== undefined) { // ReSharper disable UnusedParameter // ReSharper disable DuplicatingLocalDeclaration caller = function (data, initiator, formModel) { // ReSharper restore DuplicatingLocalDeclaration // ReSharper restore UnusedParameter eval(handler); }; caller(submitData.data, initiator, this); return; } throw 'Error: Can\'t submit form ' + $target.data('formName') + '!'; }, submitGetData: function (initiator) { if (!this.validate()) { return undefined; } return { data: this.buildData(), initiator: initiator }; }, validate: function (fieldName, data) { var i, handler, caller, errorMessage, ul, isFormValid = true, errors = []; if (fieldName !== undefined) { if (!this.fields[fieldName].validate(data)) { isFormValid = false; } } else { for (i in this.fields) { if (this.fields.hasOwnProperty(i)) { if (!this.fields[i].validate(data)) { isFormValid = false; } } } } handler = $(this.form).data('onValidate'); if (handler !== undefined) { // ReSharper disable UnusedParameter caller = function (formModel, isValid) { // ReSharper restore UnusedParameter if (!eval(handler)) { isFormValid = false; } }; caller(this, isFormValid); } this.$validationSummary.html(''); if (!isFormValid) { for (i in this.fields) { if (this.fields.hasOwnProperty(i)) { errorMessage = this.fields[i].errorMessage; if (errorMessage !== null && errorMessage !== undefined) { errors.push(errorMessage); } } } ul = $("
    "); $.each(errors, function (index, item) { $("
  • ").html(item).appendTo(ul); }); ul.appendTo(this.$validationSummary); } return isFormValid; } }); $.fn.ajaxForm = function () { var result, form, actual; this.each(function () { form = getForm(this); if (form !== undefined && form !== null) { actual = form.data(viewModelSelector); if (result !== undefined && actual !== result) { throw "Error: unable resolve single form from selected elements!"; } result = actual; } }); return result; }; $.fn.ajaxFormParse = function (options) { $(formSelector, this).each(function () { if (!$.data(this, viewModelSelector)) { $.data(this, viewModelSelector, new AjaxForm(this, options || {})); } }); $("*[data-form-button][data-form-name]", $(formSelector)).click(function () { var form = AjaxForms.getByName($(this).data("formName")); if (form !== undefined && form !== null) { form.executeButtonAction($(this).data("formButton"), $(this).data("initiator")); } }); }; $(function () { $(window.document).ajaxFormParse(); }); return { getByName: function (formName) { return forms[formName]; }, registerValidator: registerValidator }; }(jQuery, window)); // ---------------------------------------------------------------- // jQuery extensions // ---------------------------------------------------------------- (function ($) { // [name] is the name of the event "click", "mouseover", .. // same as you'd pass it to bind() // [fn] is the handler function $.fn.bindFirst = function (name, fn) { // bind as you normally would // don't want to miss out on any jQuery magic this.on(name, fn); // Thanks to a comment by @Martin, adding support for // namespaced events too. this.each(function () { var handlers = $._data(this, 'events')[name.split('.')[0]]; // take out the handler we just inserted from the end var handler = handlers.pop(); // move it at the beginning handlers.splice(0, 0, handler); }); }; $.fn.uniqueId = function (prefix, overwrite) { return this.each(function () { if (!this.id || overwrite) { this.id = EwCore.newUniqueId(prefix); } }); }; }(jQuery)); // ---------------------------------------------------------------- // Compatibility fixes // ---------------------------------------------------------------- if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (obj, start) { for (var i = (start || 0), j = this.length; i < j; i++) { if (this[i] === obj) { return i; } } return -1; }; }; // disable default ajax cache for get requests $(function () { $.ajaxSetup({ cache: false }); });