Commit 78d5e5c8 authored by Sven Franck's avatar Sven Franck

added form validation

parent 56ef7949
......@@ -1387,80 +1387,10 @@ var priv = {};
// form = document.getElementById(e.target.getAttribute("data-form"));
// target = e.target.href;
//
// e.preventDefault();
//
// if (util.testForClass(form.className), "validate") {
// valid = $(form).triggerHandler( "submitForm" );
// } else {
// valid = $(form).serialize();
// }
//
// // // TODO: how to update status?
// // if (valid !== false) {
// // jIO.util.ajax({
// // "url": "data/" + attachment + ".json", "dataType":"json"
// // })
// // }
// //
// // target = e.target.href,
// // id = form.id,
// // pointer = "response:" + id,
// // proceed = function (data, target) {
// // // changePage with fragment_cache pointer
// // $.mobile.changePage(target, {"transition": "slide", "data": data});
// // };
// //
// // // stop
// // // fetch and proceed
// // if (valid !== false) {
// // $.ajax({
// // "type":"POST",
// // "url": "foo.php",
// // "data": valid
// // }).done(function(data) {
// // // overwrite cache
// // // NOTE: FAKE data until working
// // priv.fragment_cache[id] = priv.getFakeRecords["payment"];
// // proceed(pointer, target);
// // }).fail(function(jqXHR) {
// // // fake it
// // priv.fragment_cache[id] = priv.getFakeRecords["payment"];
// // proceed(pointer, target);
// // });
// // }
// // });
// // }
// // });
// PAGE BINDINGS
.end()
// form validation initializer
.find("form")
// .filter(function () {
// return this.getAttribute("data-bound") !== true;
// })
// .each(function (i, element) {
// element.setAttribute("data-bound", true);
// // TODO: how to add custom validations here?
// // TODO: async?
// $(element).validVal({
// validate: {
// onKeyup: "valid",
// onBlur: "valid"
// },
// form: {
// onInvalid: function() { return;}
// }
// });
// })
// .end()
......
......@@ -43,6 +43,14 @@
// TODO: remove duplicate code for pagination
erp5.map_actions = {
/**
* submit a form
* @method submit
* @param {object} obj Action Object
*/
submit: function (obj) {
init.submit(obj);
},
/**
* show jumps popup
* @method jump
......@@ -246,7 +254,7 @@
// TODO: how to get count? run query on underlying_portal_type with
// category? Lot of queries
// TODO: add remaining mapping!
// TODO: still bad, although "categories_" is out!
// TODO: bad because not generic! Try!!!
mapListItems: function (items, replace) {
var property, obj, i, item, clean, value, store, split, host, arr = [];
......@@ -290,13 +298,13 @@
obj.left = {};
obj.left[item[property]] = (store !== undefined ? store : false);
break;
case "image":
case "image_url":
if (obj.left !== undefined) {
if (!obj.left["image"]) {
if (obj.left["image"] === false) {
obj.left["image"] = item[property];
obj.left["alt"] = item["title"];
}
if (!obj.left["icon"]) {
if (obj.left["icon"] === false) {
obj.left["icon"] = item[property];
}
} else {
......@@ -363,15 +371,13 @@
spec.properties.maximum_length) {
validation_list += "|max_length:" +
(prevail.properties.maximum_length ||
spec.properties.maximum_length) + "&truncate" +
spec.properties.maximum_length) + "&truncate:" +
(prevail.properties.truncate || spec.properties.truncate);
}
// unknown selection?
// type-related validation, element and type definition
console.log("MAPPING")
console.log(spec.type)
switch (spec.type) {
case "StringField":
case "RelationStringField":
......@@ -385,6 +391,10 @@
type = "password";
clear = true;
break;
case "CheckboxField":
element = "input";
type = "checkbox";
break;
case "MultiListField":
case "ListField":
case "ParallelListField":
......@@ -735,6 +745,11 @@
* @return {object} fragment HTML object containing the elements
*/
// TODO: refactor. Remove hack!!!!
// TODO: this is crap because it adds first/last which in case of
// wrapping elements ends up on the wrong element, so do it either before
// or after!
// TODO: this function is crap, either do all looping here (listview...)
// or nothing
// NOTE: this can only handle fully described elements in an array (no li!)
factory.util.generateFromArray = function (spec, type, hack, reference) {
var i,
......@@ -757,7 +772,8 @@
}
// class string
if (element.direct && type !== "controlbar") {
if ((element.type !== "input" && element.type !== "select")
&& element.direct && type !== "controlbar") {
order = i === 0 ? " ui-first-child " :
(i === (spec.length - 1) ? " ui-last-child " : " ");
......@@ -1216,10 +1232,13 @@
* @param {object} spec Configuration for controlgroup
* @return controlgroup
*/
// TODO: crap to use both layout and children!
factory.generateForm = function (spec) {
var i,
j,
k,
layout,
element,
container,
area,
field,
......@@ -1228,24 +1247,22 @@
doc,
id,
config,
fragment = factory.util.wrapInForm(spec.form);
fragment = factory.util.wrapInForm(spec.form),
wrap = function (area) {
return factory.generateElement(
"div", {"className": "span_" + area}
);
};
// form fields = layout
for (i = 0; i < spec.layout.length; i += 1) {
layout = spec.layout[i];
area = layout.position === "center" ? 2 : 1;
container = factory.generateElement(
"div", {"className": "span_" + area}
);
container = wrap(area);
for (j = 0; j < layout.fieldlist.length; j += 1) {
console.log(j)
console.log(layout)
field = layout.fieldlist[j];
console.log(field)
console.log(spec.fields)
config = spec.fields[field.title];
console.log("so config is")
console.log(config)
overrides = field.overrides;
doc = spec.data || undefined;
value = doc ? (doc[field.title]) : undefined;
......@@ -1259,8 +1276,7 @@
default: position = undefined;
break;
}
console.log("this should be a proper config")
console.log(config)
// generate and append
container.appendChild(factory.generateFormElement(
factory.map_utils.mapFormField(config, overrides, value),
......@@ -1271,7 +1287,17 @@
}
fragment.appendChild(container);
}
console.log(fragment)
// children (default to fullscreen)
for (k = 0; k < spec.children.length; k += 1) {
// pass reference
element = spec.children[k];
element.reference = spec.form;
container = wrap(2);
container.appendChild(factory.util.forward(element));
}
fragment.appendChild(container);
return fragment;
};
......@@ -1525,8 +1551,8 @@
list = factory.generateElement(
config.numbered ? "ol" : "ul",
{
"className": "ui-listview " + spec.class_list + " " +
(config.inset ? "ui-listview-inset ui-corner-all ui-shadow " : "")
"className": "ui-listview " + (spec.class_list || "") +
(config.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "")
},
{
"data-role":"listview",
......@@ -1569,7 +1595,6 @@
last = auto;
}
}
// list item
item = factory.generateElement(
"li",
......@@ -2246,16 +2271,23 @@
action,
clear,
map,
theme,
icon_string,
input_type,
container_classes = "",
label_classes = "",
need_text_node,
container_class_list = "",
label_class_list = "",
index = "",
disabled = "",
readonly = "",
active = "off",
text = "text";
// shim missing logic
if (config.logic === undefined) {
config.logic = {};
}
// first/last in group of elements
if (position) {
switch (position) {
......@@ -2268,6 +2300,11 @@
};
}
// theme
if (config.attributes["data-theme"] !== undefined) {
theme = " ui-btn-" + config.attributes["data-theme"];
}
// enhanced!
config.attributes["data-enhanced"] = true;
......@@ -2320,22 +2357,27 @@
if (input_type) {
switch (input_type) {
case "radio":
container_classes = "ui-" + config.attributes.type;
container_class_list = "ui-" + config.attributes.type;
label_inside = true;
label_classes = "ui-btn ui-corner-all ui-btn-inherit" +
label_class_list = "ui-btn ui-corner-all ui-btn-inherit" +
" ui-btn-icon-left ui-icon-radio-" + active + " ui-radio-" +
active + index + disabled + readonly;
config.attributes["data-cacheval"] = true;
break;
case "checkbox":
icon_string = factory.generateIconClassString(config, "checkbox");
container_classes = "ui-"+ config.attributes.type;
container_class_list = "ui-"+ config.attributes.type;
label_inside = true;
label_classes = "ui-btn ui-corner-all ui-btn-inherit " +
label_class_list = "ui-btn ui-corner-all ui-btn-inherit " +
icon_string + " ui-checkbox-" + active + index + disabled
+ readonly;
config.attributes["data-cacheval"] = false;
break;
case "submit":
case "reset":
container_class_list = "ui-btn ui-input-btn";
need_text_node = true;
break;
// covers all JQM text input types and excludes select/textarea...
default:
if (input_type !== "hidden") {
......@@ -2344,13 +2386,14 @@
config.attributes["data-type"] = "search";
text = "search";
}
container_classes = "ui-input-" + text + " ui-body-inherit " +
" ui-corner-all ui-shadow-inset" + disabled + readonly +
(action || "") + (clear || "");
container_class_list = "ui-input-" + text + " ui-body-inherit ";
}
break;
}
}
// assemble
container_class_list += " ui-corner-all ui-shadow-inset" +
disabled + readonly + (action || "") + (clear || "") + (theme || "");
// container
if (config.type === "textarea" || config.logic.type === "hidden") {
......@@ -2362,22 +2405,23 @@
// select
if (config.type === "select") {
icon_string = factory.generateIconClassString(config);
container_classes = "ui-" + config.type;
container_class_list = "ui-" + config.type;
element_target = factory.generateElement(
"div",
{
"id": config.direct.id + "-button",
"className":"ui-btn ui-corner-all ui-shadow " + icon_string
"className":icon_string + " ui-btn ui-corner-all ui-shadow " +
(config.logic.wrapper_class_list || "")
},
{"data-enhanced":"true"}
);
element_reverse = true;
}
// queued commands (need container_classes to be set)
// queued commands (need container_class_list to be set)
if (wrap_in_container) {
container = factory.generateElement(
"div", {"className": container_classes}
"div", {"className": container_class_list}
);
}
if (label_inside) {
......@@ -2386,17 +2430,21 @@
label_target = wrapper;
}
// label - always label for validatiy, no label means "hide" not "skip"!
label_target.appendChild(factory.generateElement(
"label",
{
"className": label_classes + " translate" +
((label === undefined || config.logic.text === "") ?
" ui-hidden-accessible" : "")
},
{"for": config.direct.id, "data-i18n": config.logic.label_i18n || ""},
{"text": config.logic.label || ""}
));
// label - always label for validity, no label means "hide" not "skip"!
// NOTE: for submit and reset buttons don't add a label because we don't
// need an id/name
if (need_text_node === undefined) {
label_target.appendChild(factory.generateElement(
"label",
{
"className": label_class_list + " translate" +
((label === undefined || config.logic.text === "") ?
" ui-hidden-accessible" : "")
},
{"for": config.direct.id, "data-i18n": config.logic.label_i18n || ""},
{"text": config.logic.label || ""}
));
}
// target
if (element_reverse) {
......@@ -2405,6 +2453,11 @@
element_target = container;
}
// text node
if (need_text_node) {
element_target.appendChild(document.createTextNode(config.direct.value));
}
// ELEMENT
element_target.appendChild(factory.generateElement(
config.type,
......@@ -2623,6 +2676,80 @@
/* UTILS */
/* ====================================================================== */
/**
* IE8 compatible custom event listener
* @method listenToEvent
* @param {object} element Element to attach listener to
* @param {string} eventName Event to listen for
* @param {method} callback Method to run on event
* @param {boolean} custom Flag for custom event
*/
// WARNING: make sure this works in IE8!
util.listenToEvent = function (element, event_name, callback, custom) {
var old_ie = document.addEventListener,
target = element || ((custom && old_ie) ?
(document.documentElement) : document);
if (old_ie) {
if (custom) {
target.attachEvent('onpropertychange', function (e) {
if(e.propertyName == event_name) {
callback();
}
});
} else {
target.attachEvent("on" + event, attachHandler);
}
} else {
console.log("adding listener")
console.log(target)
target.addEventListener(event_name, callback, false);
}
};
/**
* IE8 compatible custom event trigger
* @method triggerEvent
* @param {object} element Element to attach listener to
* @param {string} event_name Event to trigger
* @param {boolean} custom Flag for custom event
*/
util.triggerEvent = function (element, event_name, custom) {
var event,
old_ie = document.addEventListener,
target = element || ((custom && old_ie) ?
(document.documentElement) : document);
if (document.createEvent) {
event = document.createEvent('Event');
event.initEvent(event_name, true, true);
target.dispatchEvent(event);
} else {
event = target[event_name];
event += 1;
}
};
/**
* IE8 compatible removal of event listeners
* @method removeEvent
* @param {object} element Element to attach listener to
* @param {string} eventName Event to listen for
* @param {method} callback Method to run on event
* @param {boolean} custom Flag for custom event
*/
util.removeEvent = function (element, event_name, callback, custom) {
var old_ie = document.addEventListener,
target = element || ((custom && old_ie) ?
(document.documentElement) : document);
if (target.removeEventListener) {
target.removeEventListener(event_name, callback, false);
} else {
target.detachEvent('onpropertychange', callback);
}
};
/**
* Load content into a global or local popup
* @method loadPopupContents
......@@ -3270,7 +3397,8 @@
if (type === "click" && e.target.getAttribute("data-rel") === null) {
e.preventDefault();
if (type === "click" && (tag === "SELECT" || tag === "INPUT")) {
if (type === "click" && (tag === "SELECT" ||
(tag === "INPUT" && e.target.type !== "submit"))) {
return;
}
}
......@@ -3432,6 +3560,56 @@
.fail(util.errorHandler);
};
/**
* Handler for form submission
* @method submit
* @param {object} config Action Object
*/
// NOTE: we always validate! to skip validation test for class on form
init.submit = function (config) {
var form_element = document.getElementById(config.id),
valid = $(form_element).triggerHandler( "submitForm" );
// // TODO: how to update status?
// if (valid !== false) {
// jIO.util.ajax({
// "url": "data/" + attachment + ".json", "dataType":"json"
// })
// }
//
// target = e.target.href,
// id = form.id,
// pointer = "response:" + id,
// proceed = function (data, target) {
// // changePage with fragment_cache pointer
// $.mobile.changePage(target, {"transition": "slide", "data": data});
// };
//
// // stop
//
// // fetch and proceed
// if (valid !== false) {
// $.ajax({
// "type":"POST",
// "url": "foo.php",
// "data": valid
// }).done(function(data) {
// // overwrite cache
// // NOTE: FAKE data until working
// priv.fragment_cache[id] = priv.getFakeRecords["payment"];
// proceed(pointer, target);
// }).fail(function(jqXHR) {
// // fake it
// priv.fragment_cache[id] = priv.getFakeRecords["payment"];
// proceed(pointer, target);
// });
// }
// });
// }
};
/**
* Handler for pagination
* @method paginate
......@@ -3534,27 +3712,81 @@
};
/**
* Set page bindings
* Set bindings on page specific elements after content has been appended
* @method setPageBindings
* @param {object} e Custom event object
*/
init.setPageBindings = function (e) {
$(document)
// disable JQM filters
.find("[data-filter='true']")
.filter(function () {
return this.getAttribute("data-bound") !== true;
})
.each(function (i, element) {
element.setAttribute("data-bound", true);
// TODO: add local popups!
init.setPageBindings = function () {
var i,
j,
form_element,
filterable,
form_list = document.getElementsByTagName("form"),
filter_list = document.querySelectorAll("[data-filter]");
$(element).on("filterablebeforefilter", function (e) {
// disable default filtering of JQM filterable
for (i = 0; i < filter_list.length; i += 1) {
filterable = filter_list[i];
if (filterable.getAttribute("data-bound") === null) {
filterable.setAttribute("data-bound", true);
// TODO: javascript-able?
$(filterable).on("filterablebeforefilter", function (e) {
e.preventDefault();
});
});
}
}
// add validation to all forms
for (j = 0; j < form_list.length; j += 1) {
form_element = form_list[j];
if (form_element.getAttribute("data-bound") === null) {
form_element.setAttribute("data-bound", true);
// TODO: javascript-able?
// NOTE: the script is mapped to validval, so replacing it
// requires it to add a different plugin here as well as
// updating all data-vv fields being set in mapFormField() and
// generateFormElement
$(form_element).validVal({
validate: {
onKeyup: "valid",
onBlur: "valid"
},
form: {
onInvalid: function(invalid_field_list) {
console.log("CALLING oninvalid")
var i,
invalid_field,
invalid_field_label,
undo = function (element) {
util.removeEvent(element, "click", undo);
};
for (i = 0; i < invalid_field_list.length; i += 1) {
invalid_field = invalid_field_list[i];
switch (invalid_field.type) {
case "checkbox":
case "radio":
// console.log("FOUND A CHECKBOX OR RADIO")
// invalid_field_label = invalid_field.previousSibling;
// invalid_field_label.className += " invalid";
// util.listenToEvent(
// invalid_field_label, "click", undo(invalid_field_label)
// );
break;
}
}
return;
}
}
});
}
}
// TODO: FIND local popups!
};
......@@ -3589,15 +3821,15 @@
if (document.getElementById(raw_url.split("#").pop()) ||
raw_url === $.mobile.getDocumentUrl() ||
data.options.role === "popup") {
console.log("let JQM go")
// console.log("let JQM go")
return;
}
if (document.getElementById(config.id)) {
console.log("stop JQM")
// console.log("stop JQM")
e.preventDefault();
return;
} else {
console.log("HIJACK and stop JQM")
// console.log("HIJACK and stop JQM")
handle = true;
e.preventDefault();
}
......@@ -3614,7 +3846,7 @@
}
init.fetchPageLayouts("settings", config.layout_identifier)
.then(function (reply) {
init.setPageElements(config, util.parseIfNeeded(reply), create);
return init.setPageElements(config, util.parseIfNeeded(reply), create);
})
.then(init.setPageBindings)
.fail(util.errorHandler);
......@@ -3839,8 +4071,7 @@
delete baggage.constructor;
baggage.state.selected = create === false ? (baggage.state.selected) : undefined;
selector.state = baggage.state;
console.log("DONE generating")
console.log(selector)
// TODO: not working - element is fragment or html element
init.updateInfoFields(
element, baggage.state.query, baggage.state.total
......@@ -3874,11 +4105,9 @@
} else {
document.body.appendChild(page);
}
console.log("let's go to")
console.log(config.id)
// JQM treatment
$(document).enhanceWithin();
console.log("CHANGING PAGE")
$.mobile.changePage("#" + config.id);
} else {
......@@ -4080,6 +4309,12 @@
};
})
// block form submits
.on("submit", "form", function (e) {
e.preventDefault();
return false;
})
// global actions
.on("click change keyup input", ".action", function (e) {
var val, last,
......@@ -4089,7 +4324,6 @@
// delay all input field actions allowing user to type/select
if (element.tagName === "INPUT" &&
(type !== "button" && type !== "checkbox")) {
val = element.value,
last = element.getAttribute("data-last")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment