Class: Dommy::ValidityState
- Inherits:
-
Object
- Object
- Dommy::ValidityState
- Defined in:
- lib/dommy/html_elements.rb
Overview
ValidityState — computes constraint-validation flags from the
host control's current attributes and value. Bound to a single
host control; reads dynamically on every access so attribute
changes between calls are reflected.
Flags follow the HTML spec; badInput is always false (we'd need
the browser's number parser to detect "12abc" in a type=number).
Constant Summary collapse
- FLAGS =
%w[ valueMissing typeMismatch patternMismatch tooLong tooShort rangeUnderflow rangeOverflow stepMismatch badInput customError ] .freeze
- EMAIL_RE =
The exact WHATWG "valid email address" production.
%r{\A[a-zA-Z0-9.!\#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z}- URL_SCHEMES =
%w[http:// https:// ftp://].freeze
Instance Method Summary collapse
-
#__js_get__(key) ⇒ Object
---- Bridge protocol ----.
-
#bad_input ⇒ Object
badInputflags input that the user agent couldn't convert to the host control's expected type. - #custom_error ⇒ Object
-
#host_barred? ⇒ Boolean
A control that is disabled or readonly is barred from constraint validation — none of the "suffering from" flags apply.
-
#initialize(host = nil) ⇒ ValidityState
constructor
A new instance of ValidityState.
- #pattern_mismatch ⇒ Object
- #range_overflow ⇒ Object
- #range_underflow ⇒ Object
-
#reversed_range?(min, max) ⇒ Boolean
A reversed range only exists for the periodic
timedomain with min > max. - #step_mismatch ⇒ Object
-
#too_long ⇒ Object
tooLong / tooShort apply ONLY when the value was last changed by a USER EDIT (not a script assignment), per the WHATWG "suffering from being too long/short" definitions.
- #too_short ⇒ Object
- #type_mismatch ⇒ Object
- #valid ⇒ Object
- #valid_float?(s) ⇒ Boolean
- #value_missing ⇒ Object
Constructor Details
#initialize(host = nil) ⇒ ValidityState
Returns a new instance of ValidityState.
1720 1721 1722 |
# File 'lib/dommy/html_elements.rb', line 1720 def initialize(host = nil) @host = host end |
Instance Method Details
#__js_get__(key) ⇒ Object
---- Bridge protocol ----
1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 |
# File 'lib/dommy/html_elements.rb', line 1923 def __js_get__(key) case key when "valueMissing" value_missing when "typeMismatch" type_mismatch when "patternMismatch" pattern_mismatch when "tooLong" too_long when "tooShort" too_short when "rangeUnderflow" range_underflow when "rangeOverflow" range_overflow when "stepMismatch" step_mismatch when "badInput" bad_input when "customError" custom_error when "valid" valid else Bridge::ABSENT end end |
#bad_input ⇒ Object
badInput flags input that the user agent couldn't convert to
the host control's expected type. For Dommy this is meaningful
for type=number/range (raw string not a finite float) and
type=color (not a #rrggbb literal).
1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 |
# File 'lib/dommy/html_elements.rb', line 1880 def bad_input return false unless @host raw = @host.respond_to?(:raw_value) ? @host.raw_value : host_value raw = raw.to_s return false if raw.empty? case host_type when "number", "range" !valid_float?(raw) when "color" !raw.strip.downcase.match?(/\A#[0-9a-f]{6}\z/) else false end end |
#custom_error ⇒ Object
1904 1905 1906 |
# File 'lib/dommy/html_elements.rb', line 1904 def custom_error !.empty? end |
#host_barred? ⇒ Boolean
A control that is disabled or readonly is barred from constraint validation — none of the "suffering from" flags apply.
1728 1729 1730 1731 1732 1733 1734 |
# File 'lib/dommy/html_elements.rb', line 1728 def return false unless @host disabled = host_attr_present?("disabled") readonly = @host.respond_to?(:readonly) ? @host.readonly : host_attr_present?("readonly") disabled || readonly end |
#pattern_mismatch ⇒ Object
1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 |
# File 'lib/dommy/html_elements.rb', line 1797 def pattern_mismatch return false unless @host pat = host_attr_value("pattern").to_s return false if pat.empty? v = host_value.to_s return false if v.empty? # The pattern must be a valid regex ON ITS OWN — validate it before # anchoring, so an unbalanced `a)(b` (which the `(?:…)` wrapper would # otherwise balance) is correctly discarded rather than silently matched. Regexp.new(pat) !Regexp.new("\\A(?:#{pat})\\z").match?(v) rescue RegexpError false end |
#range_overflow ⇒ Object
1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 |
# File 'lib/dommy/html_elements.rb', line 1844 def range_overflow return false unless numeric_host? max = @host.max_as_number return false if max.nil? num = @host.value_as_number return false if num.nan? min = @host.min_as_number return num > max && num < min if reversed_range?(min, max) num > max end |
#range_underflow ⇒ Object
1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 |
# File 'lib/dommy/html_elements.rb', line 1827 def range_underflow return false unless numeric_host? min = @host.min_as_number return false if min.nil? num = @host.value_as_number return false if num.nan? # A `time` control has a periodic domain: min > max means a REVERSED range # whose accepted values are `>= min` OR `<= max`, so both underflow and # overflow hold for a value in the excluded gap (max, min). max = @host.max_as_number return num > max && num < min if reversed_range?(min, max) num < min end |
#reversed_range?(min, max) ⇒ Boolean
A reversed range only exists for the periodic time domain with min > max.
1859 1860 1861 |
# File 'lib/dommy/html_elements.rb', line 1859 def reversed_range?(min, max) host_type == "time" && min && max && min > max end |
#step_mismatch ⇒ Object
1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 |
# File 'lib/dommy/html_elements.rb', line 1863 def step_mismatch return false unless numeric_host? step = @host.allowed_value_step return false if step.nil? num = @host.value_as_number return false if num.nan? ratio = (num - @host.validation_step_base) / step (ratio - ratio.round).abs > 1e-7 end |
#too_long ⇒ Object
tooLong / tooShort apply ONLY when the value was last changed by a USER EDIT (not a script assignment), per the WHATWG "suffering from being too long/short" definitions. Dommy has no interactive text entry, so a value is never user-edited and these constraints never fire.
1819 1820 1821 |
# File 'lib/dommy/html_elements.rb', line 1819 def too_long false end |
#too_short ⇒ Object
1823 1824 1825 |
# File 'lib/dommy/html_elements.rb', line 1823 def too_short false end |
#type_mismatch ⇒ Object
1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 |
# File 'lib/dommy/html_elements.rb', line 1776 def type_mismatch return false unless @host v = host_value.to_s return false if v.empty? case host_type when "email" # A `multiple` email is a comma-separated list; every part must be valid. if host_attr_present?("multiple") v.split(",", -1).any? { |part| !part.strip.match?(EMAIL_RE) } else !v.match?(EMAIL_RE) end when "url" URL_SCHEMES.none? { |s| v.start_with?(s) } else false end end |
#valid ⇒ Object
1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 |
# File 'lib/dommy/html_elements.rb', line 1908 def valid !(value_missing || type_mismatch || pattern_mismatch || too_long || too_short || range_underflow || range_overflow || step_mismatch || bad_input || custom_error) end |
#valid_float?(s) ⇒ Boolean
1897 1898 1899 1900 1901 1902 |
# File 'lib/dommy/html_elements.rb', line 1897 def valid_float?(s) Float(s) true rescue ArgumentError, TypeError false end |
#value_missing ⇒ Object
1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 |
# File 'lib/dommy/html_elements.rb', line 1736 def value_missing return false unless @host && host_attr_present?("required") case host_type when "checkbox" # The checkbox/radio "being missing" flag reflects checkedness even when # the control is barred (only willValidate gates participation). !host_checked? when "radio" # An unnamed radio is not part of a group and is never missing. return false if @host.respond_to?(:get_attribute) && @host.get_attribute("name").to_s.empty? # A required radio is missing only when NO member of its group (same # name/form owner/tree) is checked — using runtime checkedness. if @host.respond_to?(:radio_group_members) @host.radio_group_members.none? { |r| r.respond_to?(:checked) ? r.checked : false } else !host_checked? end when "file" files = @host.respond_to?(:files) ? @host.files : nil files.nil? || files.length.zero? when "select-one", "select-multiple" # A required select is missing when its selected option has an empty # value (the placeholder label option); the flag isn't barred by disabled. @host.respond_to?(:value) && @host.value.to_s.empty? else # Text-like controls only "suffer from being missing" when mutable. return false if # A date/number type with an unparseable value has no value (its # sanitized value is empty), so it counts as missing. if @host.respond_to?(:numeric_value_type?) && @host.send(:numeric_value_type?) @host.value_as_number.nan? else host_value.to_s.empty? end end end |