https://www.swhd.org/wp-content/plugins/wp-easycart/design/theme/b…/ec-text-input-rules.js?ver=6_0_1
1/** 2 * WP EasyCart - input rules for text / textarea modifier options ( 6.0.0 ). 3 * 4 * Templates print data attributes from wp_easycart_text_input_rules::attributes(): 5 * data-ec-text-case="upper|lower|title" 6 * data-ec-text-allowed="letters|letters_space|numbers|alnum|alnum_space" 7 * data-ec-min-length="5" data-ec-min-length-error="..." ( checked by ec-store.js on add to cart ) 8 * plus the native maxlength. This script applies them while the shopper types or 9 * pastes ( keeping the caret in place ), on focus, and before a form submits. 10 * The server applies the same rules again on add to cart. 11 * 12 * Order ( mirrors PHP ): case -> strip disallowed -> max length. 13 * No dependencies; events are delegated so late-loaded markup works too. 14 */ 15( function() { 16 'use strict'; 17 18 if ( window.wpeasycart_text_input_rules ) { 19 return; 20 } 21 22 var SELECTOR = 'input[data-ec-text-case],input[data-ec-text-allowed],textarea[data-ec-text-case],textarea[data-ec-text-allowed]'; 23 24 var UNICODE = ( function() { 25 try { 26 return new RegExp( '\\p{L}', 'u' ).test( String.fromCharCode( 0xE9 ) ); 27 } catch ( e ) { 28 return false; 29 } 30 } )(); 31 32 /* Older browsers: common Latin, Greek, Cyrillic, Hebrew, Arabic, Devanagari, Thai, CJK and Hangul ranges. */ 33 function range( a, b ) { 34 return String.fromCharCode( a ) + '-' + String.fromCharCode( b ); 35 } 36 var LETTERS = UNICODE ? '\\p{L}\\p{M}' : 'A-Za-z' + String.fromCharCode( 0xAA, 0xB5, 0xBA ) + 37 range( 0xC0, 0xD6 ) + range( 0xD8, 0xF6 ) + range( 0xF8, 0x2FF ) + range( 0x300, 0x36F ) + 38 range( 0x370, 0x3FF ) + range( 0x400, 0x52F ) + range( 0x531, 0x58F ) + range( 0x5D0, 0x5EA ) + 39 range( 0x620, 0x64A ) + range( 0x900, 0x97F ) + range( 0xE00, 0xE7F ) + range( 0x1E00, 0x1FFF ) + 40 range( 0x3040, 0x30FF ) + range( 0x3400, 0x4DBF ) + range( 0x4E00, 0x9FFF ) + range( 0xAC00, 0xD7AF ); 41 var FLAGS = UNICODE ? 'gu' : 'g'; 42 var cache = {}; 43 44 function disallowed( allowed, multiline ) { 45 var key = allowed + ( multiline ? '_m' : '' ); 46 if ( Object.prototype.hasOwnProperty.call( cache, key ) ) { 47 return cache[ key ]; 48 } 49 var re = null; 50 if ( 'letters' === allowed ) { 51 re = new RegExp( '[^' + LETTERS + ']', FLAGS ); 52 } else if ( 'letters_space' === allowed ) { 53 re = new RegExp( '[^' + LETTERS + ' ' + ( multiline ? '\\r\\n' : '' ) + ']', FLAGS ); 54 } else if ( 'numbers' === allowed ) { 55 re = /[^0-9]/g; 56 } else if ( 'alnum' === allowed ) { 57 re = new RegExp( '[^' + LETTERS + '0-9]', FLAGS ); 58 } else if ( 'alnum_space' === allowed ) { 59 re = new RegExp( '[^' + LETTERS + '0-9 ' + ( multiline ? '\\r\\n' : '' ) + ']', FLAGS ); 60 } 61 cache[ key ] = re; 62 return re; 63 } 64 65 function apply_case( value, c ) { 66 if ( 'upper' === c ) { 67 return value.toUpperCase(); 68 } 69 if ( 'lower' === c ) { 70 return value.toLowerCase(); 71 } 72 if ( 'title' === c ) { 73 return value.replace( /(^|\s)(\S)/g, function( m, space, ch ) { 74 return space + ch.toUpperCase(); 75 } ); 76 } 77 return value; 78 } 79 80 /* Case + allowed characters ( no length cap: used for the text before the caret too ). */ 81 function clean( value, el ) { 82 var re = disallowed( el.getAttribute( 'data-ec-text-allowed' ) || 'any', 'TEXTAREA' === el.tagName ); 83 value = apply_case( String( value ), el.getAttribute( 'data-ec-text-case' ) || 'none' ); 84 return re ? value.replace( re, '' ) : value; 85 } 86 87 function max_length( el ) { 88 var max = parseInt( el.getAttribute( 'maxlength' ), 10 ); 89 return max > 0 ? max : 0; 90 } 91 92 function fire( el, type ) { 93 var evt; 94 if ( 'function' === typeof window.Event ) { 95 evt = new Event( type, { bubbles: true } ); 96 } else { 97 evt = document.createEvent( 'Event' ); 98 evt.initEvent( type, true, false ); 99 } 100 el.dispatchEvent( evt ); 101 } 102 103 /* Normalise el.value in place; keeps the caret where the shopper expects it. */ 104 function normalize( el ) { 105 var value = el.value; 106 var next = clean( value, el ); 107 var max = max_length( el ); 108 if ( max && next.length > max ) { 109 next = next.slice( 0, max ); 110 } 111 if ( next === value ) { 112 return false; 113 } 114 var caret = null; 115 try { 116 if ( document.activeElement === el && 'number' === typeof el.selectionEnd ) { 117 caret = Math.min( clean( value.slice( 0, el.selectionEnd ), el ).length, next.length ); 118 } 119 } catch ( e ) { 120 caret = null; 121 } 122 el.value = next; 123 if ( null !== caret ) { 124 try { 125 el.setSelectionRange( caret, caret ); 126 } catch ( e2 ) {} 127 } 128 return true; 129 } 130 131 function matches( el ) { 132 if ( ! el || 1 !== el.nodeType ) { 133 return false; 134 } 135 var fn = el.matches || el.msMatchesSelector || el.webkitMatchesSelector; 136 return fn ? fn.call( el, SELECTOR ) : false; 137 } 138 139 var composing = false; 140 141 document.addEventListener( 'compositionstart', function( e ) { 142 if ( matches( e.target ) ) { 143 composing = true; 144 } 145 }, true ); 146 147 document.addEventListener( 'compositionend', function( e ) { 148 if ( matches( e.target ) ) { 149 composing = false; 150 normalize( e.target ); 151 } 152 }, true ); 153 154 document.addEventListener( 'input', function( e ) { 155 if ( ! composing && ! e.isComposing && matches( e.target ) ) { 156 normalize( e.target ); 157 } 158 }, true ); 159 160 /* 161 * Multi-character insertions ( paste, drop, autocomplete, a predictive-keyboard word ): 162 * clean the text before it lands so the native maxlength doesn't cut it first and 163 * then leave room unused once disallowed characters are removed. 164 */ 165 function insert_clean( el, text ) { 166 if ( 'string' !== typeof text || 'number' !== typeof el.selectionStart ) { 167 return false; 168 } 169 var start = el.selectionStart; 170 var end = el.selectionEnd; 171 var value = el.value; 172 if ( 'TEXTAREA' !== el.tagName ) { 173 text = text.replace( /[\r\n]+/g, ' ' ); 174 } 175 var before = clean( value.slice( 0, start ), el ); 176 var inserted = clean( value.slice( 0, start ) + text, el ).slice( before.length ); 177 var after = value.slice( end ); 178 var max = max_length( el ); 179 if ( max ) { 180 inserted = inserted.slice( 0, Math.max( 0, max - before.length - after.length ) ); 181 } 182 el.value = before + inserted + after; 183 normalize( el ); 184 var caret = Math.min( before.length + inserted.length, el.value.length ); 185 try { 186 el.setSelectionRange( caret, caret ); 187 } catch ( e2 ) {} 188 el.setAttribute( 'data-ec-text-pending-change', '1' ); 189 fire( el, 'input' ); 190 return true; 191 } 192 193 /* Would the native insertion lose characters to the allowed-characters rule? */ 194 function needs_clean_insert( el, text ) { 195 if ( 'string' !== typeof text || '' === text ) { 196 return false; 197 } 198 if ( 'TEXTAREA' !== el.tagName && /[\r\n]/.test( text ) ) { 199 return true; 200 } 201 var re = disallowed( el.getAttribute( 'data-ec-text-allowed' ) || 'any', 'TEXTAREA' === el.tagName ); 202 return !! re && text.replace( re, '' ) !== text; 203 } 204 205 document.addEventListener( 'paste', function( e ) { 206 var el = e.target; 207 if ( ! matches( el ) ) { 208 return; 209 } 210 var data = e.clipboardData || window.clipboardData; 211 if ( ! data || ! data.getData ) { 212 return; 213 } 214 var text = data.getData( 'text' ); 215 if ( needs_clean_insert( el, text ) && insert_clean( el, text ) ) { 216 e.preventDefault(); 217 } 218 }, true ); 219 220 document.addEventListener( 'beforeinput', function( e ) { 221 var el = e.target; 222 if ( composing || e.isComposing || ! matches( el ) ) { 223 return; 224 } 225 var text = null; 226 if ( 'insertText' === e.inputType || 'insertReplacementText' === e.inputType ) { 227 text = e.data; 228 if ( null === text && e.dataTransfer && e.dataTransfer.getData ) { 229 text = e.dataTransfer.getData( 'text/plain' ); 230 } 231 } else if ( 'insertFromDrop' === e.inputType && e.dataTransfer && e.dataTransfer.getData ) { 232 text = e.dataTransfer.getData( 'text/plain' ); 233 } 234 if ( e.cancelable && needs_clean_insert( el, text ) && insert_clean( el, text ) ) { 235 e.preventDefault(); 236 } 237 }, true ); 238 239 /* Scripted value changes don't always fire "change" on blur; ec-store.js recalculates prices on change. */ 240 document.addEventListener( 'focusout', function( e ) { 241 var el = e.target; 242 if ( matches( el ) && el.getAttribute( 'data-ec-text-pending-change' ) ) { 243 el.removeAttribute( 'data-ec-text-pending-change' ); 244 fire( el, 'change' ); 245 } 246 }, true ); 247 248 document.addEventListener( 'focusin', function( e ) { 249 if ( matches( e.target ) ) { 250 normalize( e.target ); 251 } 252 }, true ); 253 254 /* Autofill / scripted values that never fired an input event. */ 255 document.addEventListener( 'submit', function( e ) { 256 var form = e.target; 257 if ( ! form || ! form.querySelectorAll ) { 258 return; 259 } 260 var fields = form.querySelectorAll( SELECTOR ); 261 for ( var i = 0; i < fields.length; i++ ) { 262 normalize( fields[ i ] ); 263 } 264 }, true ); 265 266 function boot() { 267 var fields = document.querySelectorAll( SELECTOR ); 268 for ( var i = 0; i < fields.length; i++ ) { 269 normalize( fields[ i ] ); 270 } 271 272 if ( fields.length && ! document.getElementById( 'ec-text-input-rules-css' ) ) { 273 var style = document.createElement( 'style' ); 274 style.id = 'ec-text-input-rules-css'; 275 style.appendChild( document.createTextNode( '[data-ec-text-case]::placeholder{text-transform:none}[data-ec-text-case]::-webkit-input-placeholder{text-transform:none}' ) ); 276 ( document.head || document.documentElement ).appendChild( style ); 277 } 278 279 /* Server rejected an add to cart because the rules removed everything typed into a required option. */ 280 var match = /[?&]ec_option_error=(\d+)/.exec( window.location.search ); 281 if ( match ) { 282 var rows = document.querySelectorAll( '[id^="ec_details_adv_option_row_error_' + match[1] + '_"]' ); 283 /* Reason â the message attribute on the option's input ( text rules and file uploads ). */ 284 var reason = /[?&]ec_option_error_reason=([a-z_]+)/.exec( window.location.search ); 285 var message_attr = { 286 min_length: 'data-ec-min-length-error', 287 file_type: 'data-ec-error-file-type', 288 file_size: 'data-ec-error-file-size', 289 file_upload: 'data-ec-error-file-upload' 290 }[ reason ? reason[1] : '' ]; 291 for ( var r = 0; r < rows.length; r++ ) { 292 if ( message_attr ) { 293 var input = document.getElementById( rows[ r ].id.replace( 'ec_details_adv_option_row_error_', 'ec_option_adv_' ) ); 294 if ( input && input.getAttribute( message_attr ) ) { 295 rows[ r ].textContent = input.getAttribute( message_attr ); 296 } 297 } 298 rows[ r ].style.display = 'block'; 299 } 300 if ( rows.length && rows[0].scrollIntoView ) { 301 rows[0].scrollIntoView( { block: 'center' } ); 302 } 303 } 304 } 305 306 if ( 'loading' === document.readyState ) { 307 document.addEventListener( 'DOMContentLoaded', boot ); 308 } else { 309 boot(); 310 } 311 312 /* Characters short of the minimum length ( 0 when empty or long enough ). Surrogate pairs count once, like PHP mb_strlen. */ 313 function too_short( el ) { 314 if ( ! el ) { 315 return 0; 316 } 317 var min = parseInt( el.getAttribute( 'data-ec-min-length' ) || el.getAttribute( 'minlength' ), 10 );
318 var value = String( el.value || '' ).replace( /^\s+|\s+$/g, '' ); 319 if ( ! ( min > 0 ) || '' === value ) { 320 return 0; 321 } 322 var length = value.replace( /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_' ).length; 323 return length < min ? min - length : 0; 324 } 325 326 window.wpeasycart_text_input_rules = { 327 normalize: normalize, 328 clean: clean, 329 too_short: too_short 330 }; 331} )();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.