1/** 2 * Custom Input Scripts 3 * - multiselect 4 * - select 5 * - checkbox 6 * - radio 7 * 8 * Moved from classic greyd default.js 9 * was global default.js variable 'custom_inputs' 10 */ 11 12var greyd = greyd || {}; 13 14( function() { 15 jQuery( function() { 16 if ( typeof $ === 'undefined' ) $ = jQuery; 17 18 // do not init if classic theme is detected 19 if ( typeof theme !== 'undefined' ) return; 20 21 greyd.input.init(); 22 23 console.log( "Input Scripts: loaded" ); 24 } ); 25} )( jQuery ); 26 27 28/** 29 * init greyd.input var 30 */ 31greyd.input = greyd.input || {}; 32 33/** 34 * init all inputs 35 * multiselects, selects, checkboxes, radios 36 */ 37greyd.input.init = function() { 38 greyd.input.multiselects.init(); 39 greyd.input.selects.init(); 40 greyd.input.boxes.init(); 41} 42 43/** 44 * close all multiselect and select boxes in the document 45 * except the current select box 46 */ 47greyd.input.closeAll = function( current ) { 48 greyd.input.multiselects.closeAll(); 49 greyd.input.selects.closeAll(current); 50} 51 52 53/** 54 * Multiselect 55 */ 56greyd.input.multiselects = new function() { 57 58 /** 59 * Custom multiselects 60 */ 61 this.init = function() { 62 63 if ( typeof $ === 'undefined' ) $ = jQuery; 64 65 // console.log( "greyd.input.multiselects init" ); 66 67 $( ".greyd_form select[multiple]" ).each( function() { 68 greyd.input.multiselects.render( this ); 69 } ); 70 71 $( ".greyd_multiselect" ).each( function() { 72 var wrap = $( this ); 73 var input = wrap.find( ".input" ); 74 75 // open the dropdown 76 input.off( "click" ).on( "click", function( e ) { 77 e.stopPropagation(); 78 var open = wrap.hasClass( "open" ) ? false : true; 79 $( ".greyd_multiselect" ).each( function() { $( this ).removeClass( "open" ); } ); 80 greyd.input.closeAll(null); 81 if ( open ) { 82 wrap.addClass( "open" ); 83 $( document ).one( "click", function() { 84 wrap.removeClass( "open" ); 85 } ); 86 } 87 } ); 88 89 // toggle tag 90 var option = wrap.find( ".option" ); 91 option.off( "click" ).on( "click", function( e ) { 92 greyd.input.multiselects.toggleValue( this, e ); 93 } ); 94 95 } ); 96 97 // control multiselects and multiradio with keyboard 98 document.addEventListener( "keydown", e => { 99 100 const multiselectWrapper = e.target?.closest( ".greyd_multiselect" ); 101 if ( multiselectWrapper && multiselectWrapper?.querySelector( "input" ) ) { 102 103 if ( multiselectWrapper?.classList?.contains( "open" ) ) { 104 if ( e.key == "ArrowUp" || e.key == "ArrowDown" ) { 105 e.preventDefault(); 106 greyd.input.multiselects.focusNextItem( e ); 107 } 108 else if ( e.key == " " && e.target.classList.contains( "option" ) ) { 109 const option = e.target; 110 e.preventDefault(); 111 greyd.input.multiselects.toggleValue( option, e ); 112 } 113 else if ( 114 e.key == "Escape" || 115 e.key == " " && e.target.classList.contains( "input" ) 116 ) { 117 e.preventDefault(); 118 greyd.input.closeAll(null); 119 } 120 else if ( e.key == "Tab" ) { 121 const activeElement = document.activeElement; 122 if ( 123 ( e.shiftKey && e.target.classList.contains( "input" ) ) || 124 ( !e.shiftKey && !activeElement.nextElementSibling ) 125 ) { 126 greyd.input.closeAll(null); 127 } 128 } 129 } 130 else { 131 if ( 132 e.key == "Enter" || 133 e.key == " " && e.target.classList.contains( "input" ) 134 ) { 135 e.preventDefault(); 136 multiselectWrapper.classList.add( "open" ); 137 } 138 } 139 140 } 141 else if ( e.target?.closest( ".greyd_multiradio" ) ) { 142 143 if ( e.key == "ArrowUp" || e.key == "ArrowDown" ) { 144 // console.log(e); 145 e.preventDefault(); 146 const activeElement = document.activeElement; 147 if ( activeElement.nextElementSibling && e.key == "ArrowDown" ) { 148 activeElement.nextElementSibling.focus(); 149 } 150 else if ( activeElement.previousElementSibling && e.key == "ArrowUp" ) { 151 activeElement.previousElementSibling.focus(); 152 } 153 } 154 else if ( e.key == " " && e.target.classList.contains( "option" ) ) { 155 e.preventDefault(); 156 e.target.onclick(); 157 } 158 } 159 160 } ); 161 162 }; 163 164 /** 165 * Events 166 */ 167 this.toggleValue = function( elem, e ) { 168 e.stopPropagation(); 169 elem = $( elem ); 170 if ( elem.hasClass( 'selected' ) ) 171 this.removeValue( elem, e ); 172 else 173 this.addValue( elem, e ); 174 }; 175 176 this.removeValue = function( elem, e ) { 177 e.stopPropagation(); 178 elem = $( elem ); 179 var val = elem.data( 'value' ); 180 var wrap = elem.closest( '.greyd_multiselect' ); 181 182 // reset option 183 wrap.find( '.dropdown .option[data-value="' + val + '"]' ).removeClass( 'selected' ); 184 185 // remove tag 186 wrap.find( '.input .tag[data-value="' + val + '"]' ).remove(); 187 188 // remove value 189 var field = wrap.find( 'input' ); 190 var values = field.val().split( ',' ); 191 values = values.filter( function( v, i ) { 192 if ( v != "" && v != val && values.indexOf( v ) == i ) return true; 193 } ); 194 greyd.input.multiselects.setValue( field, values.join( ',' ) ); 195 // greyd.input.multiselects.setValue( field, field.val().replace(val, "") ); 196 }; 197 198 this.addValue = function( elem, e ) { 199 e.stopPropagation(); 200 elem = $( elem ); 201 var val = elem.data( 'value' ); 202 var name = elem.text(); 203 var wrap = elem.closest( '.greyd_multiselect' ); 204 var input = wrap.find( '.input' ); 205 206 // highlight option 207 wrap.find( '.dropdown .option[data-value="' + val + '"]' ).addClass( 'selected' ); 208 209 // append tag 210 // var tag = input.data('tag'); 211 var tag = decodeURIComponent( input.data( 'tag' ) ); 212 tag = tag.replace( "{value}", val ).replace( "{name}", name ); 213 input.find( '.tags' ).append( tag ); 214 215 // add value 216 var field = wrap.find( 'input' ); 217 var values = field.val().split( ',' ); 218 values.push( val ); 219 values = values.filter( function( v, i ) { 220 if ( v != "" && values.indexOf( v ) == i ) return true; 221 } ); 222 greyd.input.multiselects.setValue( field, values.join( ',' ) ); 223 // greyd.input.multiselects.setValue( field, field.val()+","+val ); 224 }; 225 226 this.setValue = function( field, value ) { 227 // cleanup double commas or commas at the end or beginning of the string 228 // field.val( value.replace(/,+/, ",").replace(/^,|,$/, "") ); 229 field.val( value ); 230 field.trigger( "change" ); 231 }; 232 233 this.render = function( select ) { 234 var placeholder = ''; 235 var options = {}; 236 var values = [];
237 var name = select.getAttribute( 'name' ); 238 var className = select.getAttribute( 'class' ); 239 var required = select.hasAttribute( 'required' ); 240 var tag_code = "<span class=\"tag\" data-value=\"{value}\" onclick=\"greyd.input.multiselects.removeValue(this, event);\">{name}<span class=\"icon_close\"></span></span>"; 241 242 $( select ).children( 'option' ).each( function() { 243 var txt = this.textContent; 244 var val = this.hasAttribute( 'value' ) ? this.getAttribute( 'value' ) : ''; 245 if ( val == '' && placeholder == '' ) { 246 placeholder = txt; 247 } else { 248 val = val == '' ? txt : val; 249 options[ val ] = txt; 250 if ( this.hasAttribute( 'selected' ) ) 251 values.push( val ); 252 } 253 } ); 254 255 // create wrapper 256 var wrap = document.createElement( "div" ); 257 wrap.setAttribute( "class", "greyd_multiselect" ); 258 select.after( wrap ); 259 260 // remove original select 261 select.parentNode.removeChild( select ); 262 263 // create input 264 var input = document.createElement( "input" ); 265 input.setAttribute( "type", "text" ); 266 input.setAttribute( "name", name ); 267 input.setAttribute( "id", name ); 268 input.setAttribute( "value", values.join( ',' ) ); 269 input.setAttribute( "tabindex", -1 ); 270 if ( required ) input.setAttribute( "required", "required" ); 271 wrap.appendChild( input ); 272 273 // create div.input 274 var div = document.createElement( "div" ); 275 div.setAttribute( "class", "input" + ( className ? ' ' + className : '' ) ); 276 277 // make it tabbable 278 div.setAttribute( "role", "listbox" ); 279 div.setAttribute( "tabindex", 0 ); 280 div.setAttribute( "aria-multiselectable", "true" ); 281 div.setAttribute( "data-tag", encodeURIComponent( tag_code ) ); 282 wrap.appendChild( div ); 283 284 // create tags 285 var tags = document.createElement( "span" ); 286 tags.setAttribute( "class", "tags" ); 287 tags.setAttribute( "data-placeholder", placeholder ); 288 div.appendChild( tags ); 289 if ( values.length > 0 ) { 290 values.forEach( function( value, i ) { 291 var tag = $.parseHTML( tag_code.replace( /{value}/, value ).replace( /{name}/, options[ value ] ) ); 292 $( tags ).append( tag ); 293 } ); 294 } 295 296 // create dropdown 297 var dropdown = document.createElement( "div" ); 298 dropdown.setAttribute( "class", "dropdown" ); 299 wrap.appendChild( dropdown ); 300 for ( var val in options ) { 301 302 // create option 303 var option = document.createElement( "div" ); 304 option.setAttribute( "class", "option" + ( values.indexOf( val ) >= 0 ? " selected" : "" ) ); 305 option.setAttribute( "data-value", val ); 306 307 // declare it as listbox child 308 option.setAttribute( "role", "option" ); 309 option.setAttribute( "tabindex", 0 ); 310 dropdown.appendChild( option ); 311 312 var icon = document.createElement( "span" ); 313 icon.setAttribute( "class", "icn" ); 314 option.appendChild( icon ); 315 316 option.innerHTML += options[ val ]; 317 } 318 }; 319 320 /** 321 * Helper function for multiselects to switch between options with ArrowUp & Arrow Down 322 * @param {event} 323 */ 324 this.focusNextItem = ( e ) => { 325 const key = e.key; 326 const target = e.target; 327 const activeElement = document.activeElement; 328 329 const selectWrapper = target.closest( ".greyd_multiselect" ); 330 const selectGroup = selectWrapper.querySelector( ".dropdown" ); 331 const items = selectGroup.children; 332 333 if ( activeElement.classList.contains( "input" ) && selectWrapper.classList.contains( "open" ) && key == "ArrowDown" ) { 334 items[ 0 ].focus(); 335 } else if ( activeElement.nextSibling && key == "ArrowDown" ) { 336 activeElement.nextSibling.focus(); 337 } else if ( activeElement.previousSibling && key == "ArrowUp" ) { 338 activeElement.previousSibling.focus(); 339 } else return; 340 }; 341 342 /** 343 * close all multiselect boxes in the document 344 */ 345 this.closeAll = function() { 346 var elements = document.getElementsByClassName("greyd_multiselect"); 347 for (var i = 0; i < elements.length; i++) { 348 elements[i].classList.remove("open"); 349 } 350 } 351}; 352 353/** 354 * normal Select 355 */ 356greyd.input.selects = new function() { 357 358 /** 359 * Custom selects 360 */ 361 this.init = function() { 362 var x, i, j, selElmnt, a, b, c; 363 // look for any elements with the class "custom-select": 364 x = document.getElementsByClassName("custom-select"); 365 // console.log(x); 366 // x = document.getElementsByTagName("select"); 367 for (i = 0; i < x.length; i++) { 368 // console.log(x[i]); 369 selElmnt = x[i].getElementsByTagName("select")[0]; 370 if (typeof selElmnt === 'undefined') continue; 371 var t = x[i].getElementsByClassName("select-selected")[0]; 372 if (typeof t !== 'undefined') continue; 373 // selElmnt = x[i]; 374 // for each element, create a new DIV that will act as the selected item: 375 a = document.createElement("DIV"); 376 a.setAttribute("class", "select-selected"); 377 if (selElmnt.hasAttribute("style")) a.setAttribute("style", selElmnt.getAttribute("style")); 378 if (selElmnt.hasAttribute("title")) a.setAttribute("title", selElmnt.getAttribute("title")); 379 a.setAttribute("onmouseover", selElmnt.getAttribute("onmouseover")); 380 a.setAttribute("onmouseout", selElmnt.getAttribute("onmouseout")); 381 a.innerHTML = "<span>" + selElmnt.options[selElmnt.selectedIndex].innerHTML + "</span>"; 382 // x[i].appendChild(a); 383 selElmnt.parentNode.insertBefore(a, selElmnt.nextSibling); 384 a.innerHTML += "<span class=\"select-icon\" aria-hidden=\"true\"></span>"; 385 // x[i].parentElement.appendChild(a); 386 // for each element, create a new DIV that will contain the option list: 387 b = document.createElement("DIV"); 388 b.setAttribute("class", "select-items select-hide"); 389 b.style.marginTop = '-'+selElmnt.style.marginBottom; 390 b.style.fontSize = selElmnt.style.fontSize; 391 b.style.fontWeight = selElmnt.style.fontWeight; 392 b.style.color = selElmnt.style.color; 393 b.style.background = selElmnt.style.background; 394 b.style.borderRadius = selElmnt.style.borderRadius; 395 b.style.borderColor = selElmnt.style.borderColor; 396 b.style.borderWidth = selElmnt.style.borderWidth; 397 b.style.borderLeftStyle = selElmnt.style.borderLeftStyle; 398 b.style.borderRightStyle = selElmnt.style.borderRightStyle; 399 b.style.borderTopStyle = selElmnt.style.borderTopStyle; 400 b.style.borderBottomStyle = selElmnt.style.borderBottomStyle; 401 402 // for each option in the original select element... 403 for (j = 0; j < selElmnt.length; j++) { 404 // create a new DIV that will act as an option item: 405 c = document.createElement("DIV"); 406 c.innerHTML = selElmnt.options[j].innerHTML; 407 c.dataset.value = selElmnt.options[j].getAttribute('value'); 408 409 // custom css and hover 410 if (selElmnt.getAttribute("onmouseover") && selElmnt.getAttribute("onmouseout")) { 411 c.style.color = selElmnt.style.color; 412 c.style.background = selElmnt.style.background; 413 c.style.paddingLeft = selElmnt.style.paddingLeft; 414 c.style.paddingRight = selElmnt.style.paddingRight; 415 c.style.paddingTop = "5px"; 416 c.style.paddingBottom = "5px"; 417 // make onmouseover 418 var t = selElmnt.getAttribute("onmouseover"); 419 t = t.split(' !important').join(''); 420 if (t.indexOf('color: ') > -1) { 421 t = t.split('color: '); 422 var color = t[1].split(';'); 423 var jscolor = "$(this).css('color','"+color[0]+"');"; 424 t = t[1]; 425 } 426 if (t.indexOf('background: ') > -1) { 427 t = t.split('background: '); 428 var background = t[1].split(';'); 429 var jsbackground = "$(this).css('background','"+background[0]+"');"; 430 } 431 // c.setAttribute("onmouseover", "$(this).css('color','"+color[0]+"');$(this).css('background','"+background[0]+"');"); 432 c.setAttribute("onmouseover", jscolor+jsbackground); 433 // make onmouseout 434 var t = selElmnt.getAttribute("onmouseout"); 435 t = t.split(' !important').join(''); 436 if (t.indexOf('color: ') > -1) { 437 t = t.split('color: '); 438 var color = t[1].split(';'); 439 var jscolor = "$(this).css('color','"+color[0]+"');"; 440 t = t[1]; 441 } 442 if (t.indexOf('background: ') > -1) { 443 t = t.split('background: '); 444 var background = t[1].split(';'); 445 var jsbackground = "$(this).css('background','"+background[0]+"');"; 446 } 447 // c.setAttribute("onmouseout", "$(this).css('color','"+color[0]+"');$(this).css('background','"+background[0]+"');"); 448 c.setAttribute("onmouseout", jscolor+jsbackground); 449 } 450 451 // when an item is clicked, update the original select box 452 c.addEventListener("click", function(e) { 453 const select = this.parentNode.parentNode.getElementsByTagName("select")[0]; 454 const index = [...this.parentElement.children].indexOf( this ); 455 select.selectedIndex = index; 456 select.dispatchEvent(new Event('change')); 457 // close the dropdown 458 this.parentNode.classList.add("select-hide"); 459 this.parentNode.previousSibling.classList.remove("select-arrow-active"); 460 }); 461 b.appendChild(c); 462 } 463 selElmnt.parentNode.insertBefore(b, a.nextSibling); 464 465 // when the select box is clicked, close any other select boxes, 466 a.addEventListener("click", function(e) { 467 // and open/close the current select box: 468 e.stopPropagation(); 469 greyd.input.closeAll(this); 470 // this.nextSibling.classList.toggle("select-hide"); 471 this.classList.toggle("select-arrow-active"); 472 $(this.nextSibling).toggleClass("select-hide"); 473 }); 474 475 // change the fake select when changing the real select 476 selElmnt.addEventListener("change", function(e) { 477 const select = this; 478 const fakeSelect = select.parentNode.getElementsByClassName('select-selected')[0]; 479 fakeSelect.innerHTML = "<span>" + select.options[select.selectedIndex].innerHTML + "</span>"; 480 fakeSelect.innerHTML += "<span class=\"select-icon\" aria-hidden=\"true\"></span>"; 481 const fakeOptions = select.parentNode.getElementsByClassName('select-items')[0]; 482 const selectedOptions = fakeOptions.getElementsByClassName("same-as-selected"); 483 selectedOptions.length && selectedOptions[0].classList.remove("same-as-selected"); 484 fakeOptions.children[ select.selectedIndex ].classList.add("same-as-selected"); 485 }) 486 } 487 // if the user clicks anywhere outside the select box, 488 // then close all select boxes: 489 document.addEventListener("click", greyd.input.closeAll); 490 } 491 492 /** 493 * close all select boxes in the document 494 * except the current select box 495 */ 496 this.closeAll = function( current ) { 497 var except = []; 498 var elements = document.getElementsByClassName("select-items"); 499 var selected = document.getElementsByClassName("select-selected"); 500 for ( var i=0; i<selected.length; i++ ) { 501 if ( selected[i] == current ) { 502 except.push(i); 503 } 504 else { 505 selected[i].classList.remove("select-arrow-active"); 506 } 507 } 508 for ( var i=0; i<elements.length; i++ ) { 509 if ( except.indexOf(i) ) { 510 elements[i].classList.add("select-hide"); 511 } 512 } 513 } 514 515}; 516 517/** 518 * radio buttons & checkboxes 519 */ 520greyd.input.boxes = new function() { 521 522 /** 523 * Custom radio buttons & checkboxes 524 */ 525 this.init = function() { 526 let inputs = document.getElementsByTagName("input"); 527 for ( var i=0; i<inputs.length; i++ ) { 528 // console.log(inputs[i]); 529 const input = inputs[i]; 530 531 // check if next sibling already is an empty span 532 const nextSibling = input.nextSibling; 533 if (nextSibling && nextSibling.tagName == "SPAN") continue; 534 535 const isCustomCheckbox = input.type == "checkbox" && input.closest('form.greyd_form') !== null; 536 const isCustomRadio = input.type == "radio" && ( input.closest('form.greyd_form') !== null || input.closest('form.greyd-search-form ') !== null ); 537 538 if ( isCustomCheckbox || isCustomRadio ) { 539 // console.log(input); 540 var lbl = document.createElement("span"); 541 if ( input.hasAttribute("title") ) { 542 lbl.setAttribute("title", input.getAttribute("title")); 543 } 544 input.parentNode.insertBefore( lbl, input.nextSibling ); 545 } 546 } 547 } 548 549};
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.