@joystick.js/ui-canary 0.0.0-canary.28 → 0.0.0-canary.280
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.js +15 -0
- package/dist/index.js +20 -20
- package/increment_version.js +3 -0
- package/index.html +7 -0
- package/package.json +9 -13
- package/src/accounts/authenticated.js +8 -0
- package/src/accounts/index.js +21 -0
- package/src/accounts/login.js +7 -0
- package/src/accounts/logout.js +8 -0
- package/src/accounts/recover_password.js +7 -0
- package/src/accounts/request.js +70 -0
- package/src/accounts/reset_password.js +7 -0
- package/src/accounts/signup.js +7 -0
- package/src/accounts/user.js +8 -0
- package/src/api/get.js +73 -0
- package/src/api/index.js +10 -0
- package/src/api/set.js +83 -0
- package/src/attach_joystick_to_window.js +31 -0
- package/src/cache/class.js +98 -0
- package/src/cache/index.js +5 -0
- package/src/component/class.js +365 -0
- package/src/component/css/compile.js +108 -0
- package/src/component/data/compile.js +44 -0
- package/src/component/data/fetch.js +12 -0
- package/src/component/data/load_data_from_window.js +14 -0
- package/src/component/dom/is_valid_attribute_name.js +11 -0
- package/src/component/events/attach_event_listener_for_element.js +32 -0
- package/src/component/events/serialize.js +12 -0
- package/src/component/index.js +17 -0
- package/src/component/methods/compile.js +28 -0
- package/src/component/props/compile.js +21 -0
- package/src/component/props/compile_default.js +21 -0
- package/src/component/register_component_options.js +44 -0
- package/src/component/render_methods/component.js +54 -0
- package/src/component/render_methods/component_ssr.js +22 -0
- package/src/component/render_methods/each.js +19 -0
- package/src/component/render_methods/i18n.js +50 -0
- package/src/component/render_methods/index.js +19 -0
- package/src/component/render_methods/when.js +20 -0
- package/src/component/state/compile.js +21 -0
- package/src/component/url/compile.js +39 -0
- package/src/component/validate_component_options/has_required_options.js +9 -0
- package/src/component/validate_component_options/index.js +16 -0
- package/src/component/validate_component_options/required_options.js +5 -0
- package/src/component/virtual_dom/diff.js +61 -0
- package/src/component/virtual_dom/diff_attributes.js +37 -0
- package/src/component/virtual_dom/diff_children.js +74 -0
- package/src/component/virtual_dom/element_patch_functions.js +31 -0
- package/src/component/virtual_dom/map_patch_functions_to_nodes.js +14 -0
- package/src/component/virtual_dom/render_virtual_dom_to_dom.js +37 -0
- package/src/external/get.js +9 -0
- package/src/external/track.js +10 -0
- package/src/forms/validate.js +306 -0
- package/src/forms/validators/credit_card.js +19 -0
- package/src/forms/validators/custom.js +5 -0
- package/src/forms/validators/email.js +12 -0
- package/src/forms/validators/equals.js +5 -0
- package/src/forms/validators/index.js +43 -0
- package/src/forms/validators/matches.js +5 -0
- package/src/forms/validators/max_length.js +5 -0
- package/src/forms/validators/min_length.js +5 -0
- package/src/forms/validators/phone.js +14 -0
- package/src/forms/validators/postal_code.js +204 -0
- package/src/forms/validators/regex.js +5 -0
- package/src/forms/validators/required.js +13 -0
- package/src/forms/validators/semver.js +14 -0
- package/src/forms/validators/slug.js +12 -0
- package/src/forms/validators/strong_password.js +12 -0
- package/src/forms/validators/url.js +12 -0
- package/src/forms/validators/vat.js +54 -0
- package/src/index.js +55 -0
- package/src/lib/constants.js +13 -0
- package/src/lib/debounce.js +9 -0
- package/src/lib/generate_cookie_header.js +7 -0
- package/src/lib/generate_id.js +15 -0
- package/src/lib/get_joystick_environment.js +13 -0
- package/src/lib/log_request_errors.js +15 -0
- package/src/lib/nested_object_diff.js +164 -0
- package/src/lib/parse_json.js +12 -0
- package/src/lib/throw_framework_error.js +5 -0
- package/src/lib/types.js +59 -0
- package/src/mount/index.js +45 -0
- package/src/test/create_file.js +10 -0
- package/src/test/index.js +13 -0
- package/src/test/track_function_call.js +16 -0
- package/src/tree/add_node_to_tree.js +6 -0
- package/src/tree/clean_up.js +11 -0
- package/src/tree/get_child_instance_ids.js +7 -0
- package/src/tree/get_children_from_tree.js +25 -0
- package/src/tree/get_css_from_tree.js +43 -0
- package/src/tree/get_event_listeners_for_nodes.js +15 -0
- package/src/tree/get_node_from_tree.js +7 -0
- package/src/tree/get_parent_from_tree.js +8 -0
- package/src/tree/jobs/index.js +201 -0
- package/src/tree/jobs/run.js +11 -0
- package/src/tree/replace_child_in_vdom.js +18 -0
- package/src/upload/index.js +72 -0
- package/src/websockets/client.js +125 -0
- package/src/websockets/register_on_component.js +27 -0
- package/test.js +145 -0
- package/README.md +0 -8
- package/_package.json +0 -26
- package/canary.js +0 -12
- package/dist/accounts/authenticated.js +0 -1
- package/dist/accounts/index.js +0 -1
- package/dist/accounts/login.js +0 -1
- package/dist/accounts/logout.js +0 -1
- package/dist/accounts/recoverPassword.js +0 -1
- package/dist/accounts/request.js +0 -1
- package/dist/accounts/resetPassword.js +0 -1
- package/dist/accounts/signup.js +0 -1
- package/dist/accounts/user.js +0 -1
- package/dist/api/get.js +0 -1
- package/dist/api/index.js +0 -1
- package/dist/api/set.js +0 -1
- package/dist/attachJoystickToWindow.js +0 -1
- package/dist/cache/class.js +0 -1
- package/dist/cache/index.js +0 -1
- package/dist/component/class/css/compile.js +0 -21
- package/dist/component/class/css/prefix.js +0 -1
- package/dist/component/class/data/compile.js +0 -1
- package/dist/component/class/data/fetch.js +0 -1
- package/dist/component/class/data/findComponentDataFromSSR.js +0 -1
- package/dist/component/class/data/loadDataFromWindow.js +0 -1
- package/dist/component/class/events/_registerListeners.js +0 -1
- package/dist/component/class/events/_unregisterListeners.js +0 -1
- package/dist/component/class/events/registerListeners.js +0 -1
- package/dist/component/class/events/serialize.js +0 -1
- package/dist/component/class/events/unregisterListeners.js +0 -1
- package/dist/component/class/index.js +0 -21
- package/dist/component/class/lifecycle/compile.js +0 -1
- package/dist/component/class/methods/compile.js +0 -1
- package/dist/component/class/options/allowedComponentOptions.js +0 -1
- package/dist/component/class/options/allowedDOMEvents.js +0 -1
- package/dist/component/class/options/allowedLifecycleMethods.js +0 -1
- package/dist/component/class/options/defaultLifecycleMethods.js +0 -1
- package/dist/component/class/options/hasAllRequiredOptions.js +0 -1
- package/dist/component/class/options/registerOptions.js +0 -21
- package/dist/component/class/options/requiredOptions.js +0 -1
- package/dist/component/class/options/validateOptions.js +0 -1
- package/dist/component/class/options/validators/css.js +0 -1
- package/dist/component/class/options/validators/events.js +0 -1
- package/dist/component/class/options/validators/index.js +0 -1
- package/dist/component/class/options/validators/lifecycle.js +0 -1
- package/dist/component/class/options/validators/methods.js +0 -1
- package/dist/component/class/options/validators/name.js +0 -1
- package/dist/component/class/options/validators/render.js +0 -1
- package/dist/component/class/options/validators/websockets.js +0 -1
- package/dist/component/class/options/validators/wrapper.js +0 -1
- package/dist/component/class/props/compile.js +0 -1
- package/dist/component/class/props/compileDefault.js +0 -1
- package/dist/component/class/render/clearTimersOnChildren.js +0 -1
- package/dist/component/class/render/forMount.js +0 -1
- package/dist/component/class/render/getExistingPropsMap.js +0 -1
- package/dist/component/class/render/getExistingStateMap.js +0 -1
- package/dist/component/class/render/getUpdatedDOM.js +0 -1
- package/dist/component/class/render/sanitizeHTML.js +0 -1
- package/dist/component/class/render/toHTML.js +0 -1
- package/dist/component/class/render/wrapHTML.js +0 -1
- package/dist/component/class/renderMethods/compile.js +0 -1
- package/dist/component/class/renderMethods/component.js +0 -1
- package/dist/component/class/renderMethods/dragDrop/attachEventListeners.js +0 -1
- package/dist/component/class/renderMethods/dragDrop/concept.js +0 -0
- package/dist/component/class/renderMethods/dragDrop/draggable.js +0 -1
- package/dist/component/class/renderMethods/dragDrop/droppable.js +0 -1
- package/dist/component/class/renderMethods/dragDrop/index.js +0 -1
- package/dist/component/class/renderMethods/dragDrop/isBefore.js +0 -1
- package/dist/component/class/renderMethods/each.js +0 -1
- package/dist/component/class/renderMethods/i18n.js +0 -1
- package/dist/component/class/renderMethods/index.js +0 -1
- package/dist/component/class/renderMethods/when.js +0 -1
- package/dist/component/class/state/compile.js +0 -1
- package/dist/component/class/url/compile.js +0 -1
- package/dist/component/class/virtualDOM/build.js +0 -1
- package/dist/component/class/virtualDOM/buildTree.js +0 -1
- package/dist/component/class/virtualDOM/diff/attributes.js +0 -1
- package/dist/component/class/virtualDOM/diff/children.js +0 -1
- package/dist/component/class/virtualDOM/diff/elementPatchFunctions.js +0 -1
- package/dist/component/class/virtualDOM/diff/index.js +0 -1
- package/dist/component/class/virtualDOM/diff/mapPatchFunctionsToNodes.js +0 -1
- package/dist/component/class/virtualDOM/renderTreeToDOM.js +0 -1
- package/dist/component/index.js +0 -21
- package/dist/component/renderComponentToHTML.js +0 -1
- package/dist/component/tree/addChildToParent.js +0 -1
- package/dist/component/tree/clearChildrenOnParent.js +0 -1
- package/dist/component/tree/findComponentInTreeByField.js +0 -1
- package/dist/component/tree/replaceChildInVDOMTree.js +0 -1
- package/dist/component/tree/updateParentInstanceInTree.js +0 -1
- package/dist/css/getCSSFromTree.js +0 -21
- package/dist/css/update.js +0 -21
- package/dist/forms/validate.js +0 -1
- package/dist/forms/validators/creditCard.js +0 -1
- package/dist/forms/validators/email.js +0 -1
- package/dist/forms/validators/equals.js +0 -1
- package/dist/forms/validators/index.js +0 -1
- package/dist/forms/validators/matches.js +0 -1
- package/dist/forms/validators/maxLength.js +0 -1
- package/dist/forms/validators/minLength.js +0 -1
- package/dist/forms/validators/phone.js +0 -1
- package/dist/forms/validators/postalCode.js +0 -1
- package/dist/forms/validators/required.js +0 -1
- package/dist/forms/validators/semVer.js +0 -1
- package/dist/forms/validators/slug.js +0 -1
- package/dist/forms/validators/strongPassword.js +0 -1
- package/dist/forms/validators/types.js +0 -1
- package/dist/forms/validators/url.js +0 -1
- package/dist/forms/validators/vat.js +0 -1
- package/dist/html/index.js +0 -1
- package/dist/lib/addToQueue.js +0 -1
- package/dist/lib/assignOptionsToInstance.js +0 -1
- package/dist/lib/constants.js +0 -1
- package/dist/lib/debounce.js +0 -1
- package/dist/lib/generateId.js +0 -1
- package/dist/lib/isNode.js +0 -1
- package/dist/lib/isValidAttributeName.js +0 -1
- package/dist/lib/logRequestErrors.js +0 -1
- package/dist/lib/parseJSON.js +0 -1
- package/dist/lib/processQueue.js +0 -1
- package/dist/lib/queueArray.js +0 -1
- package/dist/lib/serializeEvents.js +0 -1
- package/dist/lib/stringContainsHTML.js +0 -1
- package/dist/lib/throttle.js +0 -1
- package/dist/lib/throwFrameworkError.js +0 -1
- package/dist/lib/types.js +0 -1
- package/dist/lib/windowIsUndefined.js +0 -1
- package/dist/mount/appendToTarget.js +0 -1
- package/dist/mount/index.js +0 -1
- package/dist/mount/initializeJoystickComponentTree.js +0 -1
- package/dist/overrideTimers.js +0 -1
- package/dist/upload/index.js +0 -1
- package/dist/websockets/client.js +0 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import diff_virtual_dom_nodes from './diff.js';
|
|
2
|
+
import map_patch_functions_to_nodes from './map_patch_functions_to_nodes.js';
|
|
3
|
+
import render_virtual_dom_to_dom from './render_virtual_dom_to_dom.js';
|
|
4
|
+
|
|
5
|
+
const append_new_children = (new_children_functions = [], parent_node) => {
|
|
6
|
+
for (let i = 0; i < new_children_functions?.length; i += 1) {
|
|
7
|
+
const patch_function = new_children_functions[i];
|
|
8
|
+
|
|
9
|
+
if (patch_function && typeof patch_function === "function") {
|
|
10
|
+
patch_function(parent_node);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const patch_child_nodes = (patch_node_map = []) => {
|
|
16
|
+
for (let i = 0; i < patch_node_map?.length; i += 1) {
|
|
17
|
+
const [patch_function, child_node] = patch_node_map[i];
|
|
18
|
+
if (patch_function && typeof patch_function === "function") {
|
|
19
|
+
patch_function(child_node);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const get_new_children_functions = (old_virtual_children = [], new_virtual_children = []) => {
|
|
25
|
+
const new_children_functions = [];
|
|
26
|
+
const new_children = new_virtual_children.slice(old_virtual_children.length);
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < new_children?.length; i += 1) {
|
|
29
|
+
const new_child = new_children[i];
|
|
30
|
+
const new_child_function = (node) => {
|
|
31
|
+
const new_dom_node = render_virtual_dom_to_dom(new_child);
|
|
32
|
+
node.appendChild(new_dom_node);
|
|
33
|
+
return node;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
new_children_functions.push(new_child_function);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return new_children_functions;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const get_patches_for_existing_children = (old_virtual_children = [], new_virtual_children = []) => {
|
|
43
|
+
const child_patch_functions = [];
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < old_virtual_children?.length; i += 1) {
|
|
46
|
+
const old_virtual_child = old_virtual_children[i];
|
|
47
|
+
|
|
48
|
+
const child_patch_function = diff_virtual_dom_nodes(
|
|
49
|
+
old_virtual_child,
|
|
50
|
+
new_virtual_children[i],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
child_patch_functions.push(child_patch_function);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return child_patch_functions;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const diff_children = (old_virtual_children = [], new_virtual_children = []) => {
|
|
60
|
+
const child_patch_functions = get_patches_for_existing_children(old_virtual_children, new_virtual_children);
|
|
61
|
+
const new_children_functions = get_new_children_functions(old_virtual_children, new_virtual_children);
|
|
62
|
+
|
|
63
|
+
return (parent_node) => {
|
|
64
|
+
if (parent_node) {
|
|
65
|
+
const patch_node_map = map_patch_functions_to_nodes(child_patch_functions, parent_node.childNodes);
|
|
66
|
+
patch_child_nodes(patch_node_map);
|
|
67
|
+
append_new_children(new_children_functions, parent_node);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return parent_node;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default diff_children;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import diff_attributes from "./diff_attributes.js";
|
|
2
|
+
import diff_children from "./diff_children.js";
|
|
3
|
+
|
|
4
|
+
const element_patch_functions = {
|
|
5
|
+
select: (old_virtual_node, new_virtual_node) => {
|
|
6
|
+
// NOTE: This properly handles re-rendering selects without breaking the
|
|
7
|
+
// value. Not ideal, but this makes the behavior stable/predictable.
|
|
8
|
+
return (node) => {
|
|
9
|
+
const old_value = node.value;
|
|
10
|
+
node.replaceChildren();
|
|
11
|
+
|
|
12
|
+
diff_attributes(old_virtual_node.attributes, new_virtual_node.attributes)(node),
|
|
13
|
+
diff_children([], new_virtual_node.children)(node);
|
|
14
|
+
|
|
15
|
+
const old_value_exists = node.querySelector(`option[value="${old_value}"]`);
|
|
16
|
+
|
|
17
|
+
if (old_value_exists) {
|
|
18
|
+
node.value = old_value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!old_value_exists) {
|
|
22
|
+
const first_option = node.querySelector('option');
|
|
23
|
+
node.value = first_option?.value || '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return node;
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default element_patch_functions;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const map_patch_functions_to_nodes = (patch_functions = [], nodes = []) => {
|
|
2
|
+
const map = [];
|
|
3
|
+
const longest_list_length = Math.max(patch_functions.length, nodes.length);
|
|
4
|
+
|
|
5
|
+
for (let i = 0; i < longest_list_length; i += 1) {
|
|
6
|
+
const patch_function = patch_functions[i];
|
|
7
|
+
const node = nodes[i];
|
|
8
|
+
map.push([patch_function, node]);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return map;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default map_patch_functions_to_nodes;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import is_valid_attribute_name from "../dom/is_valid_attribute_name.js";
|
|
2
|
+
import types from '../../lib/types.js';
|
|
3
|
+
|
|
4
|
+
const render_dom_node = (virtual_dom_node = {}) => {
|
|
5
|
+
const element = document.createElement(virtual_dom_node.tag_name);
|
|
6
|
+
const attributes = Object.entries(virtual_dom_node.attributes);
|
|
7
|
+
|
|
8
|
+
for (let i = 0; i < attributes.length; i += 1) {
|
|
9
|
+
const [key, value] = attributes[i];
|
|
10
|
+
if (is_valid_attribute_name(key)) {
|
|
11
|
+
element.setAttribute(key, value);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < virtual_dom_node?.children?.length; i += 1) {
|
|
16
|
+
const child_virtual_dom_node = virtual_dom_node?.children[i];
|
|
17
|
+
|
|
18
|
+
if (child_virtual_dom_node) {
|
|
19
|
+
const child_dom_node = render_virtual_dom_to_dom(child_virtual_dom_node);
|
|
20
|
+
element.appendChild(child_dom_node);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return element;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const render_virtual_dom_to_dom = (virtual_dom_node = null) => {
|
|
28
|
+
// NOTE: buildVirtualDOM (./build) can potentially return a string if the node
|
|
29
|
+
// being rendered is text.
|
|
30
|
+
if (types.is_string(virtual_dom_node)) {
|
|
31
|
+
return document.createTextNode(virtual_dom_node);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return render_dom_node(virtual_dom_node);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default render_virtual_dom_to_dom;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const track = (external_library_name = '', external_data = {}) => {
|
|
2
|
+
if (typeof window !== 'undefined') {
|
|
3
|
+
joystick._external[external_library_name] = {
|
|
4
|
+
...(joystick._external[external_library_name] || {}),
|
|
5
|
+
...external_data,
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default track;
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import debounce from "../lib/debounce.js";
|
|
2
|
+
import validators from "./validators/index.js";
|
|
3
|
+
|
|
4
|
+
class ValidateForm {
|
|
5
|
+
constructor(form, options = {}) {
|
|
6
|
+
this.fields_to_listen_to_for_changes = [
|
|
7
|
+
"checkbox",
|
|
8
|
+
"color",
|
|
9
|
+
"date",
|
|
10
|
+
"datetime-local",
|
|
11
|
+
"email",
|
|
12
|
+
"file",
|
|
13
|
+
"hidden",
|
|
14
|
+
"month",
|
|
15
|
+
"number",
|
|
16
|
+
"password",
|
|
17
|
+
"radio",
|
|
18
|
+
"range",
|
|
19
|
+
"search",
|
|
20
|
+
"tel",
|
|
21
|
+
"text",
|
|
22
|
+
"time",
|
|
23
|
+
"url",
|
|
24
|
+
"week",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
this.default_validation_errors = {
|
|
28
|
+
credit_card: () => "Must be a valid credit card number.",
|
|
29
|
+
creditCard: () => "Must be a valid credit card number.",
|
|
30
|
+
custom: () => "Must return true.",
|
|
31
|
+
email: () => "Must be a valid email.",
|
|
32
|
+
equals: (rule) => `Value must equal ${rule}.`,
|
|
33
|
+
matches: (rule) => `Field must match ${rule}.`,
|
|
34
|
+
max_length: (rule) => `Field value can be no greater than ${rule}.`,
|
|
35
|
+
maxLength: (rule) => `Field value can be no greater than ${rule}.`,
|
|
36
|
+
min_length: (rule) => `Field value can be no less than ${rule}.`,
|
|
37
|
+
minLength: (rule) => `Field value can be no less than ${rule}.`,
|
|
38
|
+
phone: () => `Field value must be a valid telephone number.`,
|
|
39
|
+
postal_code: () => `Field value must be a valid postal code.`,
|
|
40
|
+
postalCode: () => `Field value must be a valid postal code.`,
|
|
41
|
+
regex: () => "Must match regex.",
|
|
42
|
+
required: () => "This field is required.",
|
|
43
|
+
semver: () => `Field value must be a valid semantic version.`,
|
|
44
|
+
semVer: () => `Field value must be a valid semantic version.`,
|
|
45
|
+
slug: () => `Field value must be a valid URL slug.`,
|
|
46
|
+
strong_password: () => `Field value must be a valid password.`,
|
|
47
|
+
strongPassword: () => `Field value must be a valid password.`,
|
|
48
|
+
url: () => `Field value must be a valid URL.`,
|
|
49
|
+
vat: () => `Field value must be a valid VAT code.`,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
if (!form) {
|
|
53
|
+
console.warn(
|
|
54
|
+
"[validate_form] Must pass an HTML <form></form> element to validate."
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.form = form;
|
|
59
|
+
this.set_options(options);
|
|
60
|
+
this.attach_event_listeners();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
set_options(options = {}) {
|
|
64
|
+
this.rules = options.rules || {};
|
|
65
|
+
this.messages = options.messages || {};
|
|
66
|
+
this.onSubmit = options.onSubmit;
|
|
67
|
+
this.on_render_error = options.on_render_error || options.onRenderError;
|
|
68
|
+
this.fields = this.serialize();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
update_options(options = {}) {
|
|
72
|
+
this.set_options(options);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
serialize() {
|
|
76
|
+
if (this.form) {
|
|
77
|
+
const fields = Object.keys(this.rules).map((name) => {
|
|
78
|
+
const element = this.form.querySelector(`[name="${name}"]`);
|
|
79
|
+
const type = element?.type;
|
|
80
|
+
const listen_for_changes = this.fields_to_listen_to_for_changes.includes(type);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
listen_for_changes,
|
|
84
|
+
type,
|
|
85
|
+
name,
|
|
86
|
+
element,
|
|
87
|
+
validations: Object.entries(this.rules[name])
|
|
88
|
+
.map(([validation_name, validation_rule]) => {
|
|
89
|
+
return {
|
|
90
|
+
name: validation_name,
|
|
91
|
+
rule: validation_rule,
|
|
92
|
+
valid: false,
|
|
93
|
+
};
|
|
94
|
+
})
|
|
95
|
+
.sort((v1, v2) => {
|
|
96
|
+
if (v1.name > v2.name) {
|
|
97
|
+
return 1;
|
|
98
|
+
} else {
|
|
99
|
+
return -1;
|
|
100
|
+
}
|
|
101
|
+
}),
|
|
102
|
+
errorMessages: this.messages[name] ? Object.keys(this.messages[name]) : {},
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return fields?.filter((field) => {
|
|
107
|
+
return !!field?.element;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
attach_event_listeners() {
|
|
113
|
+
if (this.form) {
|
|
114
|
+
const submit_event_listener = (event) => {
|
|
115
|
+
event.stopPropagation();
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
|
|
118
|
+
const is_valid = this.validate();
|
|
119
|
+
|
|
120
|
+
if (is_valid && this.onSubmit) {
|
|
121
|
+
this.onSubmit();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
if (this.form.listeners?.length > 0) {
|
|
126
|
+
for (let i = 0; i < this.form.listeners.length; i += 1) {
|
|
127
|
+
const listener = this.form.listeners[i];
|
|
128
|
+
this.form.removeEventListener("submit", listener);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.form.addEventListener("submit", submit_event_listener);
|
|
133
|
+
this.form.listeners = [
|
|
134
|
+
...(this.form.listeners || []),
|
|
135
|
+
submit_event_listener,
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < this?.fields?.length; i += 1) {
|
|
140
|
+
const field = this?.fields[i];
|
|
141
|
+
if (field?.element && field?.listen_for_changes) {
|
|
142
|
+
const field_event_listener = () => {
|
|
143
|
+
debounce(() => {
|
|
144
|
+
this.validate(field.name);
|
|
145
|
+
}, 100);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
field.element.removeEventListener("input", field_event_listener);
|
|
149
|
+
field.element.addEventListener("input", field_event_listener);
|
|
150
|
+
|
|
151
|
+
field.element.removeEventListener("change", field_event_listener);
|
|
152
|
+
field.element.addEventListener("change", field_event_listener);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
validate(fieldName = null) {
|
|
158
|
+
if (!fieldName) {
|
|
159
|
+
this.clear_existing_errors();
|
|
160
|
+
|
|
161
|
+
for (let i = 0; i < this.fields.length; i += 1) {
|
|
162
|
+
const field = this.fields[i];
|
|
163
|
+
this.validate_field(field);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return this.check_if_valid();
|
|
167
|
+
} else {
|
|
168
|
+
const field = this.fields.find((field) => field.name === fieldName);
|
|
169
|
+
this.clear_existing_error(fieldName);
|
|
170
|
+
this.validate_field(field);
|
|
171
|
+
return this.check_if_valid();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
check_if_valid() {
|
|
176
|
+
const fields_with_validations = this.fields.map((field) => {
|
|
177
|
+
return field.validations.some((validation) => {
|
|
178
|
+
return !validation.valid;
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return !fields_with_validations.includes(true);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
validate_field(field) {
|
|
186
|
+
const field_input = this.form.querySelector(`[name="${field.name}"]`);
|
|
187
|
+
const is_checked = ["checkbox", "radio"].includes(field.type);
|
|
188
|
+
const value = !is_checked ? field?.element?.value?.trim() : null;
|
|
189
|
+
const checked = is_checked ? field?.element?.checked : null;
|
|
190
|
+
|
|
191
|
+
for (let i = 0; i < field.validations.length; i += 1) {
|
|
192
|
+
const validation = field.validations[i];
|
|
193
|
+
if (
|
|
194
|
+
!this.is_valid_value(is_checked, is_checked ? checked : value, validation)
|
|
195
|
+
) {
|
|
196
|
+
const error_message = this.messages[field.name] && this.messages[field.name][validation.name];
|
|
197
|
+
this.mark_validation_as_invalid(field, validation.name);
|
|
198
|
+
this.render_error(
|
|
199
|
+
field.element,
|
|
200
|
+
error_message || this.default_validation_errors[validation.name](validation.rule, value)
|
|
201
|
+
);
|
|
202
|
+
} else {
|
|
203
|
+
this.mark_validation_as_valid(field, validation.name);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const has_invalid_validations = field.validations.some((validation) => {
|
|
208
|
+
return !validation.valid;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (has_invalid_validations) {
|
|
212
|
+
field_input.classList.add("error");
|
|
213
|
+
field_input.focus();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!has_invalid_validations) {
|
|
217
|
+
field_input.classList.remove("error");
|
|
218
|
+
this.clear_existing_error(field.name);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return !has_invalid_validations;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
mark_validation_as_invalid(field, validation) {
|
|
225
|
+
const updated_validations = [...field.validations];
|
|
226
|
+
const validation_to_mark = updated_validations.find(
|
|
227
|
+
(updated_validation) => updated_validation.name === validation
|
|
228
|
+
);
|
|
229
|
+
validation_to_mark.valid = false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
mark_validation_as_valid(field, validation) {
|
|
233
|
+
const updated_validations = [...field.validations];
|
|
234
|
+
const validation_to_mark = updated_validations.find(
|
|
235
|
+
(updated_validation) => updated_validation.name === validation
|
|
236
|
+
);
|
|
237
|
+
validation_to_mark.valid = true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
is_valid_value(is_checked, value, validation) {
|
|
241
|
+
const validator = validators[validation.name];
|
|
242
|
+
|
|
243
|
+
if (validator) {
|
|
244
|
+
return validator(validation.rule, value, { is_checked });
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
clear_existing_errors() {
|
|
251
|
+
if (this.form) {
|
|
252
|
+
const error_elements = this.form.querySelectorAll(".input-hint.error");
|
|
253
|
+
for (let i = 0; i < error_elements?.length; i += 1) {
|
|
254
|
+
const error_element = error_elements[i];
|
|
255
|
+
error_element.parentNode.removeChild(error_element);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
clear_existing_error(name = "") {
|
|
261
|
+
const existing_error = document.getElementById(`error-${name}`);
|
|
262
|
+
if (existing_error) {
|
|
263
|
+
existing_error.parentNode.removeChild(existing_error);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
render_error(element, message = "") {
|
|
268
|
+
if (element) {
|
|
269
|
+
this.clear_existing_error(element.name);
|
|
270
|
+
|
|
271
|
+
// NOTE: Developer has provided an on_render_error hook. Prefer that here
|
|
272
|
+
// instead of doing the default error rendering.
|
|
273
|
+
if (typeof this.on_render_error === 'function') {
|
|
274
|
+
return this.on_render_error(element, message);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const error = document.createElement("p");
|
|
278
|
+
|
|
279
|
+
error.classList.add("input-hint");
|
|
280
|
+
error.classList.add("error");
|
|
281
|
+
error.setAttribute("id", `error-${element.name}`);
|
|
282
|
+
error.innerText = message;
|
|
283
|
+
|
|
284
|
+
element.after(error);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const validate_form = (form, options) => {
|
|
290
|
+
return new Promise((resolve, reject) => {
|
|
291
|
+
try {
|
|
292
|
+
const validation = new ValidateForm(form, options);
|
|
293
|
+
const is_valid = validation.validate();
|
|
294
|
+
|
|
295
|
+
if (is_valid) {
|
|
296
|
+
resolve();
|
|
297
|
+
} else {
|
|
298
|
+
reject();
|
|
299
|
+
}
|
|
300
|
+
} catch (exception) {
|
|
301
|
+
console.warn(exception);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
export default validate_form;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import types from "../../lib/types.js";
|
|
2
|
+
|
|
3
|
+
const regex = /^(?:4[0-9]{12}(?:[0-9]{3,6})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12,15}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11}|6[27][0-9]{14}|^(81[0-9]{14,17}))$/;
|
|
4
|
+
|
|
5
|
+
const credit_card = (rule, value = "") => {
|
|
6
|
+
if (!value) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (value && !types.is_string(value)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const card_number = value ? value.replace(/[- ]+/g, '') : '';
|
|
15
|
+
|
|
16
|
+
return rule === true ? card_number.match(new RegExp(regex)) : !card_number.match(new RegExp(regex));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default credit_card;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email Regex
|
|
3
|
+
* https://emailregex.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const regex = /^((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))$/;
|
|
7
|
+
|
|
8
|
+
const email = (rule, value = "") => {
|
|
9
|
+
return rule === true ? !!value.match(regex) : !value.match(regex);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default email;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import credit_card from './credit_card.js';
|
|
2
|
+
import custom from './custom.js';
|
|
3
|
+
import email from './email.js';
|
|
4
|
+
import equals from './equals.js';
|
|
5
|
+
import matches from './matches.js';
|
|
6
|
+
import max_length from './max_length.js';
|
|
7
|
+
import min_length from './min_length.js';
|
|
8
|
+
import phone from './phone.js';
|
|
9
|
+
import postal_code from './postal_code.js';
|
|
10
|
+
import regex from './regex.js';
|
|
11
|
+
import required from './required.js';
|
|
12
|
+
import semver from './semver.js';
|
|
13
|
+
import slug from './slug.js';
|
|
14
|
+
import strong_password from './strong_password.js';
|
|
15
|
+
import url from './url.js';
|
|
16
|
+
import vat from './vat.js';
|
|
17
|
+
|
|
18
|
+
const validators = {
|
|
19
|
+
creditCard: credit_card,
|
|
20
|
+
credit_card,
|
|
21
|
+
custom,
|
|
22
|
+
email,
|
|
23
|
+
equals,
|
|
24
|
+
matches,
|
|
25
|
+
maxLength: max_length,
|
|
26
|
+
max_length,
|
|
27
|
+
minLength: min_length,
|
|
28
|
+
min_length,
|
|
29
|
+
phone,
|
|
30
|
+
postalCode: postal_code,
|
|
31
|
+
postal_code,
|
|
32
|
+
regex,
|
|
33
|
+
required,
|
|
34
|
+
semVer: semver,
|
|
35
|
+
semver,
|
|
36
|
+
slug,
|
|
37
|
+
strongPassword: strong_password,
|
|
38
|
+
strong_password,
|
|
39
|
+
url,
|
|
40
|
+
vat,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default validators;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phone
|
|
3
|
+
* https://ihateregex.io/expr/phone/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const regex = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/;
|
|
7
|
+
|
|
8
|
+
const phone = (rule, value = "") => {
|
|
9
|
+
return rule === true
|
|
10
|
+
? !!value.match(regex)
|
|
11
|
+
: !value.match(regex);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default phone;
|