{"translation-revision-date":"2025-01-09T20:01:27+00:00","generator":"WP-CLI\/2.11.0","source":"src\/Components\/Dashboard\/QuickAccess.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"en","plural-forms":"nplurals=2; plural=(n != 1);"},"Quick Access":["Acceso r\u00e1pido"],"Contact Us":["Cont\u00e1ctanos"],"Help Centre":["Centro de ayuda"],"Request a Feature":["Solicitar una caracter\u00edstica"]}}}.form-control .select2-choice { border: 0; border-radius: 2px; } .form-control .select2-choice .select2-arrow { border-radius: 0 2px 2px 0; } .form-control.select2-container { height: auto !important; padding: 0; } .form-control.select2-container.select2-dropdown-open { border-color: #5897FB; border-radius: 3px 3px 0 0; } .form-control .select2-container.select2-dropdown-open .select2-choices { border-radius: 3px 3px 0 0; } .form-control.select2-container .select2-choices { border: 0 !important; border-radius: 3px; } .control-group.warning .select2-container .select2-choice, .control-group.warning .select2-container .select2-choices, .control-group.warning .select2-container-active .select2-choice, .control-group.warning .select2-container-active .select2-choices, .control-group.warning .select2-dropdown-open.select2-drop-above .select2-choice, .control-group.warning .select2-dropdown-open.select2-drop-above .select2-choices, .control-group.warning .select2-container-multi.select2-container-active .select2-choices { border: 1px solid #C09853 !important; } .control-group.warning .select2-container .select2-choice div { border-left: 1px solid #C09853 !important; background: #FCF8E3 !important; } .control-group.error .select2-container .select2-choice, .control-group.error .select2-container .select2-choices, .control-group.error .select2-container-active .select2-choice, .control-group.error .select2-container-active .select2-choices, .control-group.error .select2-dropdown-open.select2-drop-above .select2-choice, .control-group.error .select2-dropdown-open.select2-drop-above .select2-choices, .control-group.error .select2-container-multi.select2-container-active .select2-choices { border: 1px solid #B94A48 !important; } .control-group.error .select2-container .select2-choice div { border-left: 1px solid #B94A48 !important; background: #F2DEDE !important; } .control-group.info .select2-container .select2-choice, .control-group.info .select2-container .select2-choices, .control-group.info .select2-container-active .select2-choice, .control-group.info .select2-container-active .select2-choices, .control-group.info .select2-dropdown-open.select2-drop-above .select2-choice, .control-group.info .select2-dropdown-open.select2-drop-above .select2-choices, .control-group.info .select2-container-multi.select2-container-active .select2-choices { border: 1px solid #3A87AD !important; } .control-group.info .select2-container .select2-choice div { border-left: 1px solid #3A87AD !important; background: #D9EDF7 !important; } .control-group.success .select2-container .select2-choice, .control-group.success .select2-container .select2-choices, .control-group.success .select2-container-active .select2-choice, .control-group.success .select2-container-active .select2-choices, .control-group.success .select2-dropdown-open.select2-drop-above .select2-choice, .control-group.success .select2-dropdown-open.select2-drop-above .select2-choices, .control-group.success .select2-container-multi.select2-container-active .select2-choices { border: 1px solid #468847 !important; } .control-group.success .select2-container .select2-choice div { border-left: 1px solid #468847 !important; background: #DFF0D8 !important; } /* global redux_check_intro */ /** * Description */ ( function( $ ) { 'use strict'; $(function() { $( '#theme-check > h2' ).html( $( '#theme-check > h2' ).html() + ' with Redux Theme-Check' ); if ( 'undefined' !== typeof redux_check_intro ) { $( '#theme-check .theme-check' ).append( redux_check_intro.text ); } $( '#theme-check form' ).append( '   Extra WP.org Requirements.' ); } ); }( jQuery ) ); .badge{align-items:center;border-radius:3px;box-sizing:border-box;display:inline-flex;font-size:12px;font-weight:700;gap:5px;justify-content:center;line-height:1;min-block-size:24px;min-inline-size:43px;padding-block:0;padding-inline:3px;text-align:center;text-transform:uppercase}.row-actions .badge{color:#8c8c8c;font-weight:500;padding-inline:0;text-transform:capitalize}.badge .dashicons{block-size:18px;font-size:18px;inline-size:18px}.network-shared{color:#2271b1;cursor:help;font-size:22px;inline-size:100%}.small-badge{block-size:auto;font-size:smaller;inline-size:auto;padding-block:0;padding-inline:.5em}.wp-core-ui .button.nav-tab-button{align-items:center;align-self:center;background:#f6f7f7;border-color:#f6f7f7;color:#a7aaad;display:flex;float:inline-end;gap:2px;justify-content:center;margin-inline-start:.5em}.badge.php-badge,.nav-tab-inactive:hover .badge.php-badge,:hover>.inverted-badges .php-badge{background-color:#1d97c6;color:#fff}.badge.php-badge:hover{background-color:#1a86b0;color:#fff}.badge.html-badge,.nav-tab-inactive:hover .badge.html-badge,:hover>.inverted-badges .html-badge{background-color:#ef6a36;color:#fff}.badge.html-badge:hover{background-color:#ed581e;color:#fff}.badge.css-badge,.nav-tab-inactive:hover .badge.css-badge,:hover>.inverted-badges .css-badge{background-color:#9b59b6;color:#fff}.badge.css-badge:hover{background-color:#8f4bab;color:#fff}.badge.js-badge,.nav-tab-inactive:hover .badge.js-badge,:hover>.inverted-badges .js-badge{background-color:#ffeb3b;color:#1c1f20}.badge.js-badge:hover{background-color:#ffe822;color:#1c1f20}.badge.cond-badge,.nav-tab-inactive:hover .badge.cond-badge,:hover>.inverted-badges .cond-badge{background-color:#2eae95;color:#fff}.badge.cond-badge:hover{background-color:#299a84;color:#fff}.badge.core-badge,.nav-tab-inactive:hover .badge.core-badge,:hover>.inverted-badges .core-badge{background-color:#61c5cb;color:#fff}.badge.core-badge:hover{background-color:#4ebec5;color:#fff}.badge.pro-badge,.nav-tab-inactive:hover .badge.pro-badge,:hover>.inverted-badges .pro-badge{background-color:#f7e8e3;color:#df9279}.badge.pro-badge:hover{background-color:#f1d8cf;color:#df9279}.badge.cloud-badge,.nav-tab-inactive:hover .badge.cloud-badge,:hover>.inverted-badges .cloud-badge{background-color:#00bcd4;color:#fff}.badge.cloud-badge:hover{background-color:#00a5bb;color:#fff}.badge.bundles-badge,.nav-tab-inactive:hover .badge.bundles-badge,:hover>.inverted-badges .bundles-badge{background-color:#50575e;color:#fff}.badge.bundles-badge:hover{background-color:#444a50;color:#fff}.badge.cloud_search-badge,.nav-tab-inactive:hover .badge.cloud_search-badge,:hover>.inverted-badges .cloud_search-badge{background-color:#ff9800;color:#fff}.badge.cloud_search-badge:hover{background-color:#e68900;color:#fff}.badge.private-badge,.nav-tab-inactive:hover .badge.private-badge,:hover>.inverted-badges .private-badge{background-color:#f7e6be;color:#ca961b}.badge.private-badge:hover{background-color:#f4dda7;color:#ca961b}.badge.public-badge,.nav-tab-inactive:hover .badge.public-badge,:hover>.inverted-badges .public-badge{background-color:#dbebf7;color:#2271b1}.badge.public-badge:hover{background-color:#c6dff2;color:#2271b1}.badge.success-badge,.nav-tab-inactive:hover .badge.success-badge,:hover>.inverted-badges .success-badge{background-color:#d3e8d1;color:#447340}.badge.success-badge:hover{background-color:#c3e0c0;color:#447340}.badge.failure-badge,.nav-tab-inactive:hover .badge.failure-badge,:hover>.inverted-badges .failure-badge{background-color:#fad7c1;color:#a24b16}.badge.failure-badge:hover{background-color:#f8c8a9;color:#a24b16}.badge.info-badge,.nav-tab-inactive:hover .badge.info-badge,:hover>.inverted-badges .info-badge{background-color:#d2e6f4;color:#2b71a3}.badge.info-badge:hover{background-color:#bedbef;color:#2b71a3}.badge.neutral-badge,.nav-tab-inactive:hover .badge.neutral-badge,:hover>.inverted-badges .neutral-badge{background-color:#e2e5e5;color:#6c7e7e}.badge.neutral-badge:hover{background-color:#d5d9d9;color:#6c7e7e}.badge.special-badge,.nav-tab-inactive:hover .badge.special-badge,:hover>.inverted-badges .special-badge{background-color:#dfc5ef;color:#6e249c}.badge.special-badge:hover{background-color:#d4b1e9;color:#6e249c}.inverted-badges .badge,.nav-tab-inactive .badge{background-color:#a7aaad;border-color:#fff!important;color:#fff}.inverted-badges .badge .dashicons,.nav-tab-inactive .badge .dashicons{color:#fff}.nav-tab-inactive .badge.pro-badge{background-color:#f7e8e3;color:#df9279}.nav-tab-inactive:hover .dashicons-external,.nav-tab-inactive:hover.button{color:#3c434a}.nav-tab-inactive:hover .badge.pro-badge{background-color:#df9279;color:#f7e8e3}.nav-tab-inactive{background:transparent}.nav-tab-button .dashicons-external{color:#666;font-size:15px}.snippet-activation-switch,.snippet-execution-button,input[type=checkbox].switch{display:block;position:relative}.snippet-activation-switch,input[type=checkbox].switch{-webkit-appearance:none;-moz-appearance:none;appearance:none;block-size:19px;border:1px solid #789;border-radius:34px;box-sizing:border-box;cursor:pointer;inline-size:32px;margin:0;outline:0;text-align:start}.snippet-activation-switch:before,input[type=checkbox].switch:before{background-color:#789;block-size:13px;border-radius:50%;content:"";display:inline-block;inline-size:13px;margin:2px;transition:all .1s}.active-snippet .snippet-activation-switch,input[type=checkbox].switch:checked{background-color:#0073aa}.active-snippet .snippet-activation-switch:before,input[type=checkbox].switch:checked:before{background-color:#fff;transform:translateX(calc(100%*var(--cs-direction-multiplier)))}.erroneous-snippet .snippet-activation-switch:before{color:#bbb;content:"!";font-weight:700;line-height:1;text-align:center;transform:translateX(calc(50%*var(--cs-direction-multiplier)))}a.snippet-condition-count{align-items:center;block-size:29px;border:1.8px solid;border-radius:50%;box-sizing:border-box;display:flex;inline-size:29px;justify-content:center}.inactive-snippet a.snippet-condition-count{color:#ccc}.active-snippet a.snippet-condition-count{color:#2271b1;font-weight:700}.snippet-execution-button{block-size:0;border-block:9px solid transparent;border-inline-start:10px solid #ccc;inline-size:0;margin-block-start:9px;margin-inline-start:11px}.snippet-execution-button:before{border:1.8px solid #ccc;border-radius:50%;content:"";inset:-14px -8px -14px -21px;position:absolute;z-index:2}.snippet-execution-button:hover{border-inline-start-color:#2271b1;transition:border-inline-start-color .6s}.snippet-execution-button:hover:before{border-color:#2271b1;transition:border-color .6s}:root{--cs-direction-multiplier:1}body.rtl,html[dir=rtl]{--cs-direction-multiplier:-1}.code-snippets-select input[type=text]:focus{box-shadow:none}.help-tooltip{border-block-end:1px dotted;display:inline-flex;flex-direction:column;justify-content:center;position:relative;vertical-align:middle}.help-tooltip-anchor{background:transparent!important;cursor:help;display:inline-block;font-size:10px;padding-block:.3em 0;padding-inline:.3em}.tooltip{cursor:help!important;display:inline-block;position:relative}.tooltip .dashicons{color:#789}.tooltip.badge{display:inline-flex}.tooltip.badge .dashicons{color:inherit}.tooltip-content,.tooltip:before{opacity:0;pointer-events:none;position:absolute;transform:translateZ(0);transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71,1.7,.77,1.24);visibility:hidden}.tooltip:before{background:transparent;border:6px solid transparent;content:"";z-index:1001}.tooltip-inline:before{inset-block-start:4px}.tooltip-content{-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:rgba(51,51,51,.9);border-radius:6px;color:#fff;font-size:small;font-weight:400;min-inline-size:200px;padding:8px;position:absolute;text-transform:none;z-index:1000}.tooltip-block .tooltip-content{margin-inline-start:-80px}.tooltip-inline .tooltip-content{margin-block-end:-16px;margin-inline-start:0}.tooltip-inline.tooltip-start .tooltip-content{inset-block-start:-50%}.tooltip-block.tooltip-start:before{border-block-start-color:rgba(51,51,51,.9);margin-block-end:-12px;margin-inline-start:-6px}.tooltip-block.tooltip-start .tooltip-content,.tooltip-block.tooltip-start:before{inset-block-end:100%;inset-inline-start:50%}.tooltip-block.tooltip-end:before{border-block-end-color:rgba(51,51,51,.9);border-block-start-color:transparent;margin-block:-12px 0}.tooltip-block.tooltip-end .tooltip-content,.tooltip-block.tooltip-end:before{inset-block:100% auto;margin-inline-start:25%}.tooltip-block.tooltip-end .tooltip-content{inset-inline-end:-70%}.tooltip-inline.tooltip-start:before{border-block-start-color:transparent;border-inline-start-color:rgba(51,51,51,.9);inset-block-end:50%;margin-block-end:0;margin-inline:0 -12px}.tooltip-inline.tooltip-start .tooltip-content,.tooltip-inline.tooltip-start:before{inset-inline:auto 100%}.tooltip-inline.tooltip-end:before{border-block-start-color:transparent;border-inline-end-color:rgba(51,51,51,.9);margin-block-end:0;margin-inline-start:-12px}.tooltip-inline.tooltip-end .tooltip-content,.tooltip-inline.tooltip-end:before{inset-inline-start:100%}.tooltip-inline.tooltip-end .tooltip-content{inset-block-start:-50%}.tooltip:focus .tooltip-content,.tooltip:focus:before,.tooltip:hover .tooltip-content,.tooltip:hover:before{opacity:1;visibility:visible}.tooltip-block.tooltip-start:focus .tooltip-content,.tooltip-block.tooltip-start:focus:before,.tooltip-block.tooltip-start:hover .tooltip-content,.tooltip-block.tooltip-start:hover:before{transform:translateY(-10px)}.tooltip-block.tooltip-end:focus .tooltip-content,.tooltip-block.tooltip-end:focus:before,.tooltip-block.tooltip-end:hover .tooltip-content,.tooltip-block.tooltip-end:hover:before{transform:translateY(10px)}.tooltip-inline.tooltip-end:focus .tooltip-content,.tooltip-inline.tooltip-end:focus:before,.tooltip-inline.tooltip-end:hover .tooltip-content,.tooltip-inline.tooltip-end:hover:before{transform:translateX(calc(10px*var(--cs-direction-multiplier)))}.tooltip-inline.tooltip-start:focus .tooltip-content,.tooltip-inline.tooltip-start:focus:before,.tooltip-inline.tooltip-start:hover .tooltip-content,.tooltip-inline.tooltip-start:hover:before{transform:translateX(calc(-10px*var(--cs-direction-multiplier)))}.cloud-legend-tooltip h3{color:#fff;font-size:16px;text-align:center}.cloud-legend-tooltip td{vertical-align:top}.cloud-search-info{text-align:justify}.cloud-search-info small{color:#646970;float:inline-end}.thickbox-code-viewer{background-color:#f6f6f6;border-radius:10px;min-block-size:250px;padding:20px}#snippet-code-thickbox{display:block;inline-size:100%}.no-results{font-size:15px}.dashicons.cloud-synced{color:#00bcd4}.dashicons.cloud-downloaded{color:#e91e63}.dashicons.cloud-not-downloaded{color:#9e9e9e}.dashicons.cloud-update{color:#ff9800}.cloud_update a{color:#ff9800!important;text-decoration:underline}.updated.column-updated span{-webkit-text-decoration:dotted underline;text-decoration:dotted underline}td.column-name .cloud-icon{margin-inline-end:3px}.cloud-snippet-download{color:#2271b1!important}.cloud-snippet-downloaded,.cloud-snippet-preview-style{color:#616161!important}.cloud-snippet-update{color:#ff9800!important}#cloud-search-form{margin-block:30px;text-align:center}.input-group{align-items:stretch;display:flex;flex-wrap:wrap;margin-block:0;margin-inline:auto;max-inline-size:900px;position:relative}#cloud_search{background-clip:padding-box;border-radius:0;color:#495057;display:block;flex:1 1 auto;font-size:1rem;inline-size:1%;margin-block-end:0;padding-block:.375rem;padding-inline:.75rem;position:relative;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}#cloud_search:focus{border:1px solid #8c8f94;box-shadow:none;outline:0}#cloud-select-prepend{background-color:#f6f7f7;border-color:#2271b1;border-end-end-radius:0;border-start-end-radius:0;color:#2271b1;margin-inline-end:-3px;position:relative;z-index:2}#cloud-select-prepend:hover{background-color:#f0f0f1;border-color:#0a4b78;color:#0a4b78}#cloud-search-submit{align-items:center;display:flex;justify-content:center;margin-inline-start:-3px;padding-block:0;padding-inline:15px}.cloud-search{margin-inline-start:5px}.bundle-group{display:flex;flex-wrap:nowrap;gap:5px;justify-content:space-between;margin-block-start:10px}#cloud-bundles{color:#495057;display:flex;flex:1 1 auto;font-size:1rem;inline-size:50%;padding-block:.375rem;padding-inline:.75rem;position:relative}#cloud-bundle-show{inline-size:10%}#cloud-bundle-run{inline-size:15%}#bundle_share_name{color:#495057;font-size:1rem;inline-size:25%}.heading-box{margin:auto;max-inline-size:900px;padding-block-end:1rem}.cloud-search-heading{font-size:23px;font-weight:400;line-height:1.3;margin-block-end:0;padding-block:9px 4px;padding-inline:0;text-align:center}.cloud-badge.ai-icon{color:#b22222;font-size:12px;margin-inline-start:5px;padding:3px}.cloud-search-card-bottom{min-block-size:40px}#cloud-search-results .cloud-snippets #the-list{display:flex;flex-wrap:wrap;justify-content:center}#cloud-search-results .cloud-snippets #the-list .plugin-card{display:flex;flex-direction:column;justify-content:space-between}#cloud-search-results .cloud-snippets #the-list .plugin-card .cloud-meta-row{align-items:center;display:flex;flex-grow:1;justify-content:space-between}#cloud-search-results .cloud-snippets #the-list .plugin-card .column-name{display:flex;justify-content:space-between}#cloud-search-results .cloud-snippets #the-list .plugin-card .column-name h3{display:inline-flex;flex-shrink:1}#cloud-search-results .cloud-snippets #the-list .plugin-card .column-name .title-icon{block-size:90px;margin-block-start:-7px}#cloud-search-results .cloud-snippets #the-list .plugin-card .column-votes{display:inline-flex;gap:3px}#cloud-search-results .cloud-snippets #the-list .plugin-card .column-votes:hover .thumbs-up{stroke:#059669;fill:#6ee7b7;animation:thumb 1s ease-in-out infinite}#cloud-search-results .cloud-snippets #the-list .plugin-card .column-votes .num-votes{align-items:flex-end;display:inline-flex}#cloud-search-results .cloud-snippets #the-list .action-buttons{align-items:flex-end;margin:0}#cloud-search-results .cloud-snippets #the-list .action-buttons .button{inline-size:100%;text-align:center}.cloud-snippets #the-list .column-download{display:flex;flex-flow:column;text-align:end}.cloud-snippets #the-list .column-download li{list-style:none}.cloud-connect-wrap{align-items:center;display:flex;float:inline-end;gap:5px;justify-content:space-between;margin-block:0;margin-inline:3px;max-block-size:35px}.cloud-table>tbody>tr{block-size:80px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.1)}.cloud-table>tbody>tr>td{max-inline-size:250px}.cloud-table tbody .active-snippet .column-name{font-weight:400;max-inline-size:400px;white-space:normal!important}.cloud-table td .no-results{color:#e32121;margin-block-start:15px;text-align:center}.cloud-status-dot{background-color:#ce0000;block-size:10px;border-radius:50%;inline-size:10px}.cloud-connect-active .cloud-status-dot{background-color:#25a349}.cloud-connect-text{color:#ce0000}.cloud-connect-active .cloud-connect-text{color:#2e7d32}.thumbs-up{block-size:1.25rem;inline-size:1.25rem;transform-origin:bottom left}.thumbs-up:hover{stroke:#059669;fill:#6ee7b7}.plugin-card-bottom{align-items:center;display:flex;overflow:visible!important}.beta-test-notice{margin-block-start:20px}.highlight-yellow{background:#fefdba;border-radius:3px;padding:3px}@keyframes thumb{0%{transform:rotate(0)}33%{transform:rotate(7deg)}66%{transform:rotate(-15deg)}90%{transform:rotate(5deg)}to{transform:rotate(0)}}.column-name .dashicons,.column-type .dashicons{block-size:16px;font-size:16px;inline-size:16px;vertical-align:middle}.column-name .dashicons-clock,.column-type .dashicons-clock{vertical-align:middle}.active-snippet .column-name>.snippet-name{font-weight:600}.active-snippet td,.active-snippet th{background-color:rgba(120,200,230,.06)}.active-snippet th.check-column{border-inline-start:2px solid #2ea2cc}.column-priority input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:none;box-shadow:none;color:#666;inline-size:4em;text-align:center}.column-priority input:active,.column-priority input:focus,.column-priority input:hover{background-color:#f5f5f5;background-color:rgba(0,0,0,.1);border-radius:6px;color:#000}.column-priority input:disabled{color:inherit}.clear-filters{vertical-align:baseline!important}.snippets td.column-id{text-align:center}.snippets tr{background:#fff}.snippets ol,.snippets ul{margin-block:0 1.5em;margin-inline:1.5em 0}.snippets ul{list-style:disc}.snippets th.sortable a,.snippets th.sorted a{display:flex;flex-direction:row}.snippets .row-actions{color:#ddd;inset-inline-start:0;position:relative}.snippets .column-activate{padding-inline-end:0!important}.snippets .clear-filters{vertical-align:middle}.snippets tfoot th.check-column{padding-block:13px 0;padding-inline:3px 0}.snippets .inactive-snippet th.check-column,.snippets tfoot th.check-column,.snippets thead th.check-column{padding-inline-start:5px}.snippets .active-snippet td,.snippets .active-snippet th,.snippets .inactive-snippet td,.snippets .inactive-snippet th{border:none;box-shadow:inset 0 -1px 0 rgba(0,0,0,.1);padding-block:10px;padding-inline:9px}.snippets tr.active-snippet+tr.inactive-snippet td,.snippets tr.active-snippet+tr.inactive-snippet th{border-block-start:1px solid rgba(0,0,0,.03);box-shadow:inset 0 1px 0 rgba(0,0,0,.02),inset 0 -1px 0 #e1e1e1}.snippets #all-snippets-table a.delete:hover,.snippets #search-snippets-table a.delete:hover,.snippets a.delete:hover{border-block-end:1px solid red;color:red}#wpbody-content .snippets .column-name{white-space:nowrap}td.column-description{max-inline-size:700px}td.column-description pre{white-space:unset}.inactive-snippet a{color:#579}.inactive-snippet a:active,.inactive-snippet a:focus,.inactive-snippet a:hover{color:#819db9}@media(width <= 782px){p.search-box{block-size:auto;float:inline-start;margin-block:1em 0;margin-inline:0;position:static}}.wp-list-table .is-expanded td.column-activate.activate{display:table-cell!important}.nav-tab-wrapper+.subsubsub,p.search-box{margin-block:10px 0;margin-inline:0}.snippet-type-description{border-block-end:1px solid #ccc;margin:0;padding-block:1em;padding-inline:0}.code-snippets-notice a.notice-dismiss{text-decoration:none}.refresh-button-container{align-items:center;display:flex;gap:7px;justify-content:flex-start;margin-block:15px -39px}#refresh-button{font-size:20px;inline-size:30px;line-height:1.4;padding:0}.wrap h2.nav-tab-wrapper .nav-tab{align-items:center;display:flex;flex-flow:row wrap;gap:8px}@media(width <= 1190px){.nav-tab .snippet-label{display:none}} /*! elementor-pro - v3.29.0 - 04-06-2025 */ .elementor-slides .swiper-slide-bg{background-position:50%;background-repeat:no-repeat;background-size:cover;min-height:100%;min-width:100%}.elementor-slides .swiper-slide-inner{background-position:50%;background-repeat:no-repeat;bottom:0;left:0;margin:auto;padding:50px;position:absolute;right:0;top:0}.elementor-slides .swiper-slide-inner,.elementor-slides .swiper-slide-inner:hover{color:#fff;display:flex}.elementor-slides .swiper-slide-inner .elementor-background-overlay{bottom:0;left:0;position:absolute;right:0;top:0;z-index:0}.elementor-slides .swiper-slide-inner .elementor-slide-content{position:relative;width:100%;z-index:1}.elementor-slides .swiper-slide-inner .elementor-slide-heading{font-size:35px;font-weight:700;line-height:1}.elementor-slides .swiper-slide-inner .elementor-slide-description{font-size:17px;line-height:1.4}.elementor-slides .swiper-slide-inner .elementor-slide-description:not(:last-child),.elementor-slides .swiper-slide-inner .elementor-slide-heading:not(:last-child){margin-bottom:30px}.elementor-slides .swiper-slide-inner .elementor-slide-button{background:transparent;border:2px solid #fff;color:#fff;display:inline-block}.elementor-slides .swiper-slide-inner .elementor-slide-button,.elementor-slides .swiper-slide-inner .elementor-slide-button:hover{background:transparent;color:inherit;text-decoration:none}.elementor--v-position-top .swiper-slide-inner{align-items:flex-start}.elementor--v-position-bottom .swiper-slide-inner{align-items:flex-end}.elementor--v-position-middle .swiper-slide-inner{align-items:center}.elementor--h-position-left .swiper-slide-inner{justify-content:flex-start}.elementor--h-position-right .swiper-slide-inner{justify-content:flex-end}.elementor--h-position-center .swiper-slide-inner{justify-content:center}body.rtl .elementor-widget-slides .elementor-swiper-button-next{left:10px;right:auto}body.rtl .elementor-widget-slides .elementor-swiper-button-prev{left:auto;right:10px}.elementor-slides-wrapper div:not(.swiper-slide)>.swiper-slide-inner{display:none}@media (max-width:767px){.elementor-slides .swiper-slide-inner{padding:30px}.elementor-slides .swiper-slide-inner .elementor-slide-heading{font-size:23px;line-height:1;margin-bottom:15px}.elementor-slides .swiper-slide-inner .elementor-slide-description{font-size:13px;line-height:1.4;margin-bottom:15px}}tinymce.Resource.add('tinymce.html-i18n.help-keynav.kk', '

Пернетақта навигациясын бастау

\n' + '\n' + '
\n' + '
Мәзір жолағын фокустау
\n' + '
Windows немесе Linux: Alt+F9
\n' + '
macOS: ⌥F9
\n' + '
Құралдар тақтасын фокустау
\n' + '
Windows немесе Linux: Alt+F10
\n' + '
macOS: ⌥F10
\n' + '
Төменгі деректемені фокустау
\n' + '
Windows немесе Linux: Alt+F11
\n' + '
macOS: ⌥F11
\n' + '
Мәтінмәндік құралдар тақтасын фокустау
\n' + '
Windows, Linux немесе macOS: Ctrl+F9\n' + '
\n' + '\n' + '

Навигация бөлектелетін немесе Төменгі деректеме элементінің жолындағы бірінші элемент жағдайында асты сызылатын\n' + ' бірінші ПИ элементінен басталады.

\n' + '\n' + '

ПИ бөлімдері арасында навигациялау

\n' + '\n' + '

Бір ПИ бөлімінен келесісіне өту үшін Tab пернесін басыңыз.

\n' + '\n' + '

Бір ПИ бөлімінен алдыңғысына өту үшін Shift+Tab пернесін басыңыз.

\n' + '\n' + '

Осы ПИ бөлімдерінің Tab реті:

\n' + '\n' + '
    \n' + '
  1. Мәзір жолағы
  2. \n' + '
  3. Әрбір құралдар тақтасы тобы
  4. \n' + '
  5. Бүйірлік жолақ
  6. \n' + '
  7. Төменгі деректемедегі элемент жолы
  8. \n' + '
  9. Төменгі деректемедегі сөздер санын ауыстыру түймесі
  10. \n' + '
  11. Төменгі деректемедегі брендингтік сілтеме
  12. \n' + '
  13. Төменгі деректемедегі редактор өлшемін өзгерту тұтқасы
  14. \n' + '
\n' + '\n' + '

ПИ бөлімі көрсетілмесе, ол өткізіп жіберіледі.

\n' + '\n' + '

Төменгі деректемеде пернетақта навигациясының фокусы болса және бүйірлік жолақ көрінбесе, Shift+Tab тіркесімін басу әрекеті\n' + ' фокусты соңғысы емес, бірінші құралдар тақтасы тобына жылжытады.

\n' + '\n' + '

ПИ бөлімдерінде навигациялау

\n' + '\n' + '

Бір ПИ элементінен келесісіне өту үшін Arrow (Көрсеткі) пернесін басыңыз.

\n' + '\n' + '

Left (Сол жақ) және Right (Оң жақ) көрсеткі пернелері

\n' + '\n' + '\n' + '\n' + '

Down (Төмен) және Up (Жоғары) көрсеткі пернелері

\n' + '\n' + '\n' + '\n' + '

Фокусталған ПИ бөліміндегі Arrow (Көрсеткі) пернелерінің циклі.

\n' + '\n' + '

Ашық мәзірді жабу үшін ішкі мәзірді ашып немесе ашылмалы мәзірді ашып, Esc пернесін басыңыз.

\n' + '\n' + '

Ағымдағы фокус белгілі бір ПИ бөлімінің «үстінде» болса, Esc пернесін басу әрекеті пернетақта\n' + ' навигациясын толығымен жабады.

\n' + '\n' + '

Мәзір элементін немесе құралдар тақтасы түймесін орындау

\n' + '\n' + '

Қажетті мәзір элементі немесе құралдар тақтасы түймесі бөлектелген кезде, элементті орындау үшін Return (Қайтару), Enter (Енгізу)\n' + ' немесе Space bar (Бос орын) пернесін басыңыз.

\n' + '\n' + '

Белгіленбеген диалог терезелерін навигациялау

\n' + '\n' + '

Белгіленбеген диалог терезелерінде диалог терезесі ашылған кезде бірінші интерактивті құрамдас фокусталады.

\n' + '\n' + '

Tab немесе Shift+Tab пернесін басу арқылы интерактивті диалог терезесінің құрамдастары арасында навигациялаңыз.

\n' + '\n' + '

Белгіленген диалог терезелерін навигациялау

\n' + '\n' + '

Белгіленген диалог терезелерінде диалог терезесі ашылған кезде қойынды мәзіріндегі бірінші түйме фокусталады.

\n' + '\n' + '

Tab немесе\n' + ' Shift+Tab пернесін басу арқылы осы диалог терезесі қойындысының интерактивті құрамдастары арасында навигациялаңыз.

\n' + '\n' + '

Қойынды мәзірінің фокусын беру арқылы басқа диалог терезесінің қойындысына ауысып, тиісті Arrow (Көрсеткі)\n' + ' пернесін басу арқылы қолжетімді қойындылар арасында айналдыруға болады.

\n'); "use strict";var WPMailSMTPConnect=window.WPMailSMTPConnect||function(n,e){var c={$connectBtn:e("#wp-mail-smtp-setting-upgrade-license-button"),$connectKey:e("#wp-mail-smtp-setting-upgrade-license-key")},o={init:function(){e(o.ready)},ready:function(){o.events()},events:function(){o.connectBtnClick()},connectBtnClick:function(){c.$connectBtn.on("click",function(){o.gotoUpgradeUrl()})},proAlreadyInstalled:function(t){return{title:wp_mail_smtp_connect.text.almost_done,content:t.data.message,icon:'">"+t.status+" "+t.statusText+" "+t.responseText,icon:'"> 0 ) { show_notice( $notices ); } // Notify plugins that the cart was emptied. $( document.body ).trigger( 'wc_cart_emptied' ); } else { // If the checkout is also displayed on this page, trigger update event. if ( $( '.woocommerce-checkout' ).length ) { $( document.body ).trigger( 'update_checkout' ); } // Store the old coupon error message and value before the // .woocommerce-cart-form is replaced with the new form. var $old_coupon_field_val = $( '#coupon_code' ).val(); var $old_coupon_error_msg = $( '#coupon_code' ) .closest( '.coupon' ) .find( '.coupon-error-notice' ); $( '.woocommerce-cart-form' ).replaceWith( $new_form ); $( '.woocommerce-cart-form' ) .find( ':input[name="update_cart"]' ) .prop( 'disabled', true ); if ( preserve_notices && $old_coupon_error_msg.length > 0 ) { var $new_coupon_field = $( '.woocommerce-cart-form' ).find( '#coupon_code' ); var $new_coupon_field_wrapper = $new_coupon_field.closest( '.coupon' ); $new_coupon_field.val( $old_coupon_field_val ); // The coupon input with error needs to be focused before adding the live region // with the error message, otherwise the screen reader won't read it. $new_coupon_field.focus(); show_coupon_error( $old_coupon_error_msg, $new_coupon_field_wrapper, true ); } if ( $notices.length > 0 ) { show_notice( $notices ); } update_cart_totals_div( $new_totals ); } $( document.body ).trigger( 'updated_wc_div' ); }; /** * Update the .cart_totals div with a string of html. * * @param {String} html_str The HTML string with which to replace the div. */ var update_cart_totals_div = function ( html_str ) { $( '.cart_totals' ).replaceWith( html_str ); $( document.body ).trigger( 'updated_cart_totals' ); }; /** * Shows new notices on the page. * * @param {Object} The Notice HTML Element in string or object form. */ var show_notice = function ( html_element, $target ) { if ( ! $target ) { $target = $( '.woocommerce-notices-wrapper:first' ) || $( '.wc-empty-cart-message' ).closest( '.woocommerce' ) || $( '.woocommerce-cart-form' ); } $target.prepend( html_element ); }; /** * Shows coupon form errors. * * @param {string|object} html_element The HTML string response after applying an invalid coupon or a jQuery element. * @param {Object} $target Coupon field wrapper jQuery element. * @param {boolean} is_live_region Whether role="alert" should be added or not. */ var show_coupon_error = function ( html_element, $target, is_live_region ) { if ( $target.length === 0 ) { return; } var $coupon_error_el = ''; if ( typeof html_element === 'string' ) { var msg = $( $.parseHTML( html_element ) ).text().trim(); if ( msg === '' ) { return; } $coupon_error_el = $( '

' + msg + '

' ); } else { $coupon_error_el = html_element; } if ( is_live_region ) { $coupon_error_el.attr( 'role', 'alert' ); } $target.find( '#coupon_code' ) .addClass( 'has-error' ) .attr( 'aria-invalid', 'true' ) .attr( 'aria-describedby', 'coupon-error-notice' ); $target.append( $coupon_error_el ); }; /** * Object to handle AJAX calls for cart shipping changes. */ var cart_shipping = { /** * Initialize event handlers and UI state. */ init: function ( cart ) { this.cart = cart; this.toggle_shipping = this.toggle_shipping.bind( this ); this.shipping_method_selected = this.shipping_method_selected.bind( this ); this.shipping_calculator_submit = this.shipping_calculator_submit.bind( this ); $( document ).on( 'click', '.shipping-calculator-button', this.toggle_shipping ); $( document ).on( 'change', 'select.shipping_method, :input[name^=shipping_method]', this.shipping_method_selected ); $( document ).on( 'submit', 'form.woocommerce-shipping-calculator', this.shipping_calculator_submit ); $( '.shipping-calculator-form' ).hide(); }, /** * Toggle Shipping Calculator panel */ toggle_shipping: function ( event ) { var $target = $( event.currentTarget ); $( '.shipping-calculator-form' ).slideToggle( 'slow', function () { var self = this; setTimeout( function () { var $form = $( self ); $target.attr( 'aria-expanded', $form.is( ':visible' ) ? 'true' : 'false' ); }, 0 ); } ); $( 'select.country_to_state, input.country_to_state' ).trigger( 'change' ); $( document.body ).trigger( 'country_to_state_changed' ); // Trigger select2 to load. return false; }, /** * Handles when a shipping method is selected. */ shipping_method_selected: function ( event ) { var shipping_methods = {}; // eslint-disable-next-line max-len $( 'select.shipping_method, :input[name^=shipping_method][type=radio]:checked, :input[name^=shipping_method][type=hidden]' ).each( function () { shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val(); } ); block( $( 'div.cart_totals' ) ); var data = { security: wc_cart_params.update_shipping_method_nonce, shipping_method: shipping_methods, }; $.ajax( { type: 'post', url: get_url( 'update_shipping_method' ), data: data, dataType: 'html', success: function ( response ) { update_cart_totals_div( response ); var newCurrentTarget = document.getElementById( event.currentTarget.id ); if ( newCurrentTarget ) { newCurrentTarget.focus(); } }, complete: function () { unblock( $( 'div.cart_totals' ) ); $( document.body ).trigger( 'updated_shipping_method' ); }, } ); }, /** * Handles a shipping calculator form submit. * * @param {Object} evt The JQuery event. */ shipping_calculator_submit: function ( evt ) { evt.preventDefault(); var $form = $( evt.currentTarget ); block( $( 'div.cart_totals' ) ); block( $form ); // Provide the submit button value because wc-form-handler expects it. $( '' ) .attr( 'type', 'hidden' ) .attr( 'name', 'calc_shipping' ) .attr( 'value', 'x' ) .appendTo( $form ); // Make call to actual form post URL. $.ajax( { type: $form.attr( 'method' ), url: $form.attr( 'action' ), data: $form.serialize(), dataType: 'html', success: function ( response ) { update_wc_div( response ); }, complete: function () { unblock( $form ); unblock( $( 'div.cart_totals' ) ); }, } ); }, }; /** * Object to handle cart UI. */ var cart = { /** * Initialize cart UI events. */ init: function () { this.update_cart_totals = this.update_cart_totals.bind( this ); this.input_keypress = this.input_keypress.bind( this ); this.cart_submit = this.cart_submit.bind( this ); this.submit_click = this.submit_click.bind( this ); this.apply_coupon = this.apply_coupon.bind( this ); this.remove_coupon_clicked = this.remove_coupon_clicked.bind( this ); this.remove_coupon_error = this.remove_coupon_error.bind( this ); this.quantity_update = this.quantity_update.bind( this ); this.item_remove_clicked = this.item_remove_clicked.bind( this ); this.item_restore_clicked = this.item_restore_clicked.bind( this ); this.update_cart = this.update_cart.bind( this ); $( document ).on( 'wc_update_cart added_to_cart', function () { cart.update_cart.apply( cart, [].slice.call( arguments, 1 ) ); } ); $( document ).on( 'click', '.woocommerce-cart-form :input[type=submit]', this.submit_click ); $( document ).on( 'keypress', '.woocommerce-cart-form :input[type=number]', this.input_keypress ); $( document ).on( 'submit', '.woocommerce-cart-form', this.cart_submit ); $( document ).on( 'click', 'a.woocommerce-remove-coupon', this.remove_coupon_clicked ); $( document ).on( 'click', '.woocommerce-cart-form .product-remove > a', this.item_remove_clicked ); $( document ).on( 'click', '.woocommerce-cart .restore-item', this.item_restore_clicked ); $( document ).on( 'change input', '.woocommerce-cart-form .cart_item :input', this.input_changed ); $( document ).on( 'blur change input', '#coupon_code', this.remove_coupon_error ); $( '.woocommerce-cart-form :input[name="update_cart"]' ).prop( 'disabled', true ); }, /** * After an input is changed, enable the update cart button. */ input_changed: function () { $( '.woocommerce-cart-form :input[name="update_cart"]' ).prop( 'disabled', false ); }, /** * Update entire cart via ajax. */ update_cart: function ( preserve_notices ) { var $form = $( '.woocommerce-cart-form' ); block( $form ); block( $( 'div.cart_totals' ) ); // Make call to actual form post URL. $.ajax( { type: $form.attr( 'method' ), url: $form.attr( 'action' ), data: $form.serialize(), dataType: 'html', success: function ( response ) { update_wc_div( response, preserve_notices ); }, complete: function () { unblock( $form ); unblock( $( 'div.cart_totals' ) ); $.scroll_to_notices( $( '[role="alert"]' ) ); }, } ); }, /** * Update the cart after something has changed. */ update_cart_totals: function () { block( $( 'div.cart_totals' ) ); $.ajax( { url: get_url( 'get_cart_totals' ), dataType: 'html', success: function ( response ) { update_cart_totals_div( response ); }, complete: function () { unblock( $( 'div.cart_totals' ) ); }, } ); }, /** * Handle the key for quantity fields. * * @param {Object} evt The JQuery event * * For IE, if you hit enter on a quantity field, it makes the * document.activeElement the first submit button it finds. * For us, that is the Apply Coupon button. This is required * to catch the event before that happens. */ input_keypress: function ( evt ) { // Catch the enter key and don't let it submit the form. if ( 13 === evt.keyCode ) { var $form = $( evt.currentTarget ).parents( 'form' ); try { // If there are no validation errors, handle the submit. if ( $form[ 0 ].checkValidity() ) { evt.preventDefault(); this.cart_submit( evt ); } } catch ( err ) { evt.preventDefault(); this.cart_submit( evt ); } } }, /** * Handle cart form submit and route to correct logic. * * @param {Object} evt The JQuery event */ cart_submit: function ( evt ) { var $submit = $( document.activeElement ), $clicked = $( ':input[type=submit][clicked=true]' ), $form = $( evt.currentTarget ); // For submit events, currentTarget is form. // For keypress events, currentTarget is input. if ( ! $form.is( 'form' ) ) { $form = $( evt.currentTarget ).parents( 'form' ); } if ( 0 === $form.find( '.woocommerce-cart-form__contents' ).length ) { return; } if ( is_blocked( $form ) ) { return false; } if ( $clicked.is( ':input[name="update_cart"]' ) || $submit.is( 'input.qty' ) ) { evt.preventDefault(); this.quantity_update( $form ); } else if ( $clicked.is( ':input[name="apply_coupon"]' ) || $submit.is( '#coupon_code' ) ) { evt.preventDefault(); this.apply_coupon( $form ); } }, /** * Special handling to identify which submit button was clicked. * * @param {Object} evt The JQuery event */ submit_click: function ( evt ) { $( ':input[type=submit]', $( evt.target ).parents( 'form' ) ).removeAttr( 'clicked' ); $( evt.target ).attr( 'clicked', 'true' ); }, /** * Apply Coupon code * * @param {JQuery Object} $form The cart form. */ apply_coupon: function ( $form ) { block( $form ); var cart = this; var $text_field = $( '#coupon_code' ); var coupon_code = $text_field.val(); var data = { security: wc_cart_params.apply_coupon_nonce, coupon_code: coupon_code, }; $.ajax( { type: 'POST', url: get_url( 'apply_coupon' ), data: data, dataType: 'html', success: function ( response ) { $( '.woocommerce-error, .woocommerce-message, .woocommerce-info, ' + '.is-error, .is-info, .is-success, .coupon-error-notice' ).remove(); // We only want to show coupon notices if they are not errors. // Coupon errors are shown under the input. if ( response.indexOf( 'woocommerce-error' ) === -1 && response.indexOf( 'is-error' ) === -1 ) { show_notice( response ); } else { var $coupon_wrapper = $text_field.closest( '.coupon' ); if ( $coupon_wrapper.length > 0 ) { show_coupon_error( response, $coupon_wrapper, false ); } } $( document.body ).trigger( 'applied_coupon', [ coupon_code, ] ); }, complete: function () { unblock( $form ); cart.update_cart( true ); }, } ); }, /** * Handle when a remove coupon link is clicked. * * @param {Object} evt The JQuery event */ remove_coupon_clicked: function ( evt ) { evt.preventDefault(); var cart = this; var $wrapper = $( evt.currentTarget ).closest( '.cart_totals' ); var coupon = $( evt.currentTarget ).attr( 'data-coupon' ); block( $wrapper ); var data = { security: wc_cart_params.remove_coupon_nonce, coupon: coupon, }; $.ajax( { type: 'POST', url: get_url( 'remove_coupon' ), data: data, dataType: 'html', success: function ( response ) { $( '.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success' ).remove(); show_notice( response ); $( document.body ).trigger( 'removed_coupon', [ coupon ] ); unblock( $wrapper ); }, complete: function () { cart.update_cart( true ); }, } ); }, /** * Handle when the coupon input loses focus. * * @param {Object} evt The JQuery event */ remove_coupon_error: function ( evt ) { $( evt.currentTarget ) .removeClass( 'has-error' ) .removeAttr( 'aria-invalid' ) .removeAttr( 'aria-describedby' ) .closest( '.coupon' ) .find( '.coupon-error-notice' ) .remove(); }, /** * Handle a cart Quantity Update * * @param {JQuery Object} $form The cart form. */ quantity_update: function ( $form ) { block( $form ); block( $( 'div.cart_totals' ) ); // Provide the submit button value because wc-form-handler expects it. $( '' ) .attr( 'type', 'hidden' ) .attr( 'name', 'update_cart' ) .attr( 'value', 'Update Cart' ) .appendTo( $form ); // Make call to actual form post URL. $.ajax( { type: $form.attr( 'method' ), url: $form.attr( 'action' ), data: $form.serialize(), dataType: 'html', success: function ( response ) { update_wc_div( response ); }, complete: function () { unblock( $form ); unblock( $( 'div.cart_totals' ) ); $.scroll_to_notices( $( '[role="alert"]' ) ); }, } ); }, /** * Handle when a remove item link is clicked. * * @param {Object} evt The JQuery event */ item_remove_clicked: function ( evt ) { evt.preventDefault(); var $a = $( evt.currentTarget ); var $form = $a.parents( 'form' ); block( $form ); block( $( 'div.cart_totals' ) ); $.ajax( { type: 'GET', url: $a.attr( 'href' ), dataType: 'html', success: function ( response ) { update_wc_div( response ); }, complete: function () { unblock( $form ); unblock( $( 'div.cart_totals' ) ); $.scroll_to_notices( $( '[role="alert"]' ) ); }, } ); }, /** * Handle when a restore item link is clicked. * * @param {Object} evt The JQuery event */ item_restore_clicked: function ( evt ) { evt.preventDefault(); var $a = $( evt.currentTarget ); var $form = $( 'form.woocommerce-cart-form' ); block( $form ); block( $( 'div.cart_totals' ) ); $.ajax( { type: 'GET', url: $a.attr( 'href' ), dataType: 'html', success: function ( response ) { update_wc_div( response ); }, complete: function () { unblock( $form ); unblock( $( 'div.cart_totals' ) ); }, } ); }, }; cart_shipping.init( cart ); cart.init(); } ); /*! * jQuery.extendext 1.0.0 * * Copyright 2014-2019 Damien "Mistic" Sorel (http://www.strangeplanet.fr) * Licensed under MIT (http://opensource.org/licenses/MIT) * * Based on jQuery.extend by jQuery Foundation, Inc. and other contributors */ (function (root, factory) { if (typeof define === 'function' && define.amd) { define('jquery-extendext', ['jquery'], factory); } else if (typeof module === 'object' && module.exports) { module.exports = factory(require('jquery')); } else { factory(root.jQuery); } }(this, function ($) { "use strict"; $.extendext = function () { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, arrayMode = 'default'; // Handle a deep copy situation if (typeof target === "boolean") { deep = target; // Skip the boolean and the target target = arguments[i++] || {}; } // Handle array mode parameter if (typeof target === "string") { arrayMode = target.toLowerCase(); if (arrayMode !== 'concat' && arrayMode !== 'replace' && arrayMode !== 'extend') { arrayMode = 'default'; } // Skip the string param target = arguments[i++] || {}; } // Handle case when target is a string or something (possible in deep copy) if (typeof target !== "object" && !$.isFunction(target)) { target = {}; } // Extend jQuery itself if only one argument is passed if (i === length) { target = this; i--; } for (; i < length; i++) { // Only deal with non-null/undefined values if ((options = arguments[i]) !== null) { // Special operations for arrays if ($.isArray(options) && arrayMode !== 'default') { clone = target && $.isArray(target) ? target : []; switch (arrayMode) { case 'concat': target = clone.concat($.extend(deep, [], options)); break; case 'replace': target = $.extend(deep, [], options); break; case 'extend': options.forEach(function (e, i) { if (typeof e === 'object') { var type = $.isArray(e) ? [] : {}; clone[i] = $.extendext(deep, arrayMode, clone[i] || type, e); } else if (clone.indexOf(e) === -1) { clone.push(e); } }); target = clone; break; } } else { // Extend the base object for (name in options) { copy = options[name]; // Prevent never-ending loop if (name === '__proto__' || target === copy) { continue; } // Recurse if we're merging plain objects or arrays if (deep && copy && ( $.isPlainObject(copy) || (copyIsArray = $.isArray(copy)) )) { src = target[name]; // Ensure proper type for the source value if ( copyIsArray && !Array.isArray( src ) ) { clone = []; } else if ( !copyIsArray && !$.isPlainObject( src ) ) { clone = {}; } else { clone = src; } copyIsArray = false; // Never move original objects, clone them target[name] = $.extendext(deep, arrayMode, clone, copy); // Don't bring in undefined values } else if (copy !== undefined) { target[name] = copy; } } } } } // Return the modified object return target; }; })); /*! * jQuery QueryBuilder 2.7.0 * Copyright 2014-2023 Damien "Mistic" Sorel (http://www.strangeplanet.fr) * Licensed under MIT (https://opensource.org/licenses/MIT) */ (function(root, factory) { if (typeof define == 'function' && define.amd) { define('query-builder', ['jquery', 'jquery-extendext'], factory); } else if (typeof module === 'object' && module.exports) { module.exports = factory(require('jquery'), require('jquery-extendext')); } else { factory(root.jQuery); } }(this, function($) { "use strict"; /** * @typedef {object} Filter * @memberof QueryBuilder * @description See {@link http://querybuilder.js.org/index.html#filters} */ /** * @typedef {object} Operator * @memberof QueryBuilder * @description See {@link http://querybuilder.js.org/index.html#operators} */ /** * @param {jQuery} $el * @param {object} options - see {@link http://querybuilder.js.org/#options} * @constructor */ var QueryBuilder = function($el, options) { $el[0].queryBuilder = this; /** * Element container * @member {jQuery} * @readonly */ this.$el = $el; /** * Configuration object * @member {object} * @readonly */ this.settings = $.extendext(true, 'replace', {}, QueryBuilder.DEFAULTS, options); /** * Internal model * @member {Model} * @readonly */ this.model = new Model(); /** * Internal status * @member {object} * @property {string} id - id of the container * @property {boolean} generated_id - if the container id has been generated * @property {int} group_id - current group id * @property {int} rule_id - current rule id * @property {boolean} has_optgroup - if filters have optgroups * @property {boolean} has_operator_optgroup - if operators have optgroups * @readonly * @private */ this.status = { id: null, generated_id: false, group_id: 0, rule_id: 0, has_optgroup: false, has_operator_optgroup: false }; /** * List of filters * @member {QueryBuilder.Filter[]} * @readonly */ this.filters = this.settings.filters; /** * List of icons * @member {object.} * @readonly */ this.icons = this.settings.icons; /** * List of operators * @member {QueryBuilder.Operator[]} * @readonly */ this.operators = this.settings.operators; /** * List of templates * @member {object.} * @readonly */ this.templates = this.settings.templates; /** * Plugins configuration * @member {object.} * @readonly */ this.plugins = this.settings.plugins; /** * Translations object * @member {object} * @readonly */ this.lang = null; // translations : english << 'lang_code' << custom if (QueryBuilder.regional['en'] === undefined) { Utils.error('Config', '"i18n/en.js" not loaded.'); } this.lang = $.extendext(true, 'replace', {}, QueryBuilder.regional['en'], QueryBuilder.regional[this.settings.lang_code], this.settings.lang); // "allow_groups" can be boolean or int if (this.settings.allow_groups === false) { this.settings.allow_groups = 0; } else if (this.settings.allow_groups === true) { this.settings.allow_groups = -1; } // init templates Object.keys(this.templates).forEach(function(tpl) { if (!this.templates[tpl]) { this.templates[tpl] = QueryBuilder.templates[tpl]; } if (typeof this.templates[tpl] !== 'function') { throw new Error(`Template ${tpl} must be a function`); } }, this); // ensure we have a container id if (!this.$el.attr('id')) { this.$el.attr('id', 'qb_' + Math.floor(Math.random() * 99999)); this.status.generated_id = true; } this.status.id = this.$el.attr('id'); // INIT this.$el.addClass('query-builder form-inline'); this.filters = this.checkFilters(this.filters); this.operators = this.checkOperators(this.operators); this.bindEvents(); this.initPlugins(); }; $.extend(QueryBuilder.prototype, /** @lends QueryBuilder.prototype */ { /** * Triggers an event on the builder container * @param {string} type * @returns {$.Event} */ trigger: function(type) { var event = new $.Event(this._tojQueryEvent(type), { builder: this }); this.$el.triggerHandler(event, Array.prototype.slice.call(arguments, 1)); return event; }, /** * Triggers an event on the builder container and returns the modified value * @param {string} type * @param {*} value * @returns {*} */ change: function(type, value) { var event = new $.Event(this._tojQueryEvent(type, true), { builder: this, value: value }); this.$el.triggerHandler(event, Array.prototype.slice.call(arguments, 2)); return event.value; }, /** * Attaches an event listener on the builder container * @param {string} type * @param {function} cb * @returns {QueryBuilder} */ on: function(type, cb) { this.$el.on(this._tojQueryEvent(type), cb); return this; }, /** * Removes an event listener from the builder container * @param {string} type * @param {function} [cb] * @returns {QueryBuilder} */ off: function(type, cb) { this.$el.off(this._tojQueryEvent(type), cb); return this; }, /** * Attaches an event listener called once on the builder container * @param {string} type * @param {function} cb * @returns {QueryBuilder} */ once: function(type, cb) { this.$el.one(this._tojQueryEvent(type), cb); return this; }, /** * Appends `.queryBuilder` and optionally `.filter` to the events names * @param {string} name * @param {boolean} [filter=false] * @returns {string} * @private */ _tojQueryEvent: function(name, filter) { return name.split(' ').map(function(type) { return type + '.queryBuilder' + (filter ? '.filter' : ''); }).join(' '); } }); /** * Allowed types and their internal representation * @type {object.} * @readonly * @private */ QueryBuilder.types = { 'string': 'string', 'integer': 'number', 'double': 'number', 'date': 'datetime', 'time': 'datetime', 'datetime': 'datetime', 'boolean': 'boolean' }; /** * Allowed inputs * @type {string[]} * @readonly * @private */ QueryBuilder.inputs = [ 'text', 'number', 'textarea', 'radio', 'checkbox', 'select' ]; /** * Runtime modifiable options with `setOptions` method * @type {string[]} * @readonly * @private */ QueryBuilder.modifiable_options = [ 'display_errors', 'allow_groups', 'allow_empty', 'default_condition', 'default_filter' ]; /** * CSS selectors for common components * @type {object.} * @readonly */ QueryBuilder.selectors = { group_container: '.rules-group-container', rule_container: '.rule-container', filter_container: '.rule-filter-container', operator_container: '.rule-operator-container', value_container: '.rule-value-container', error_container: '.error-container', condition_container: '.rules-group-header .group-conditions', rule_header: '.rule-header', group_header: '.rules-group-header', group_actions: '.group-actions', rule_actions: '.rule-actions', rules_list: '.rules-group-body>.rules-list', group_condition: '.rules-group-header [name$=_cond]', rule_filter: '.rule-filter-container [name$=_filter]', rule_operator: '.rule-operator-container [name$=_operator]', rule_value: '.rule-value-container [name*=_value_]', add_rule: '[data-add=rule]', delete_rule: '[data-delete=rule]', add_group: '[data-add=group]', delete_group: '[data-delete=group]' }; /** * Template strings (see template.js) * @type {object.} * @readonly */ QueryBuilder.templates = {}; /** * Localized strings (see i18n/) * @type {object.} * @readonly */ QueryBuilder.regional = {}; /** * Default operators * @type {object.} * @readonly */ QueryBuilder.OPERATORS = { equal: { type: 'equal', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean'] }, not_equal: { type: 'not_equal', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean'] }, in: { type: 'in', nb_inputs: 1, multiple: true, apply_to: ['string', 'number', 'datetime'] }, not_in: { type: 'not_in', nb_inputs: 1, multiple: true, apply_to: ['string', 'number', 'datetime'] }, less: { type: 'less', nb_inputs: 1, multiple: false, apply_to: ['number', 'datetime'] }, less_or_equal: { type: 'less_or_equal', nb_inputs: 1, multiple: false, apply_to: ['number', 'datetime'] }, greater: { type: 'greater', nb_inputs: 1, multiple: false, apply_to: ['number', 'datetime'] }, greater_or_equal: { type: 'greater_or_equal', nb_inputs: 1, multiple: false, apply_to: ['number', 'datetime'] }, between: { type: 'between', nb_inputs: 2, multiple: false, apply_to: ['number', 'datetime'] }, not_between: { type: 'not_between', nb_inputs: 2, multiple: false, apply_to: ['number', 'datetime'] }, begins_with: { type: 'begins_with', nb_inputs: 1, multiple: false, apply_to: ['string'] }, not_begins_with: { type: 'not_begins_with', nb_inputs: 1, multiple: false, apply_to: ['string'] }, contains: { type: 'contains', nb_inputs: 1, multiple: false, apply_to: ['string'] }, not_contains: { type: 'not_contains', nb_inputs: 1, multiple: false, apply_to: ['string'] }, ends_with: { type: 'ends_with', nb_inputs: 1, multiple: false, apply_to: ['string'] }, not_ends_with: { type: 'not_ends_with', nb_inputs: 1, multiple: false, apply_to: ['string'] }, is_empty: { type: 'is_empty', nb_inputs: 0, multiple: false, apply_to: ['string'] }, is_not_empty: { type: 'is_not_empty', nb_inputs: 0, multiple: false, apply_to: ['string'] }, is_null: { type: 'is_null', nb_inputs: 0, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean'] }, is_not_null: { type: 'is_not_null', nb_inputs: 0, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean'] } }; /** * Default configuration * @type {object} * @readonly */ QueryBuilder.DEFAULTS = { filters: [], plugins: [], sort_filters: false, display_errors: true, allow_groups: -1, allow_empty: false, conditions: ['AND', 'OR'], default_condition: 'AND', inputs_separator: ' , ', select_placeholder: '------', display_empty_filter: true, default_filter: null, optgroups: {}, default_rule_flags: { filter_readonly: false, operator_readonly: false, value_readonly: false, no_delete: false }, default_group_flags: { condition_readonly: false, no_add_rule: false, no_add_group: false, no_delete: false }, templates: { group: null, rule: null, filterSelect: null, operatorSelect: null, ruleValueSelect: null }, lang_code: 'en', lang: {}, operators: [ 'equal', 'not_equal', 'in', 'not_in', 'less', 'less_or_equal', 'greater', 'greater_or_equal', 'between', 'not_between', 'begins_with', 'not_begins_with', 'contains', 'not_contains', 'ends_with', 'not_ends_with', 'is_empty', 'is_not_empty', 'is_null', 'is_not_null' ], icons: { add_group: 'glyphicon glyphicon-plus-sign', add_rule: 'glyphicon glyphicon-plus', remove_group: 'glyphicon glyphicon-remove', remove_rule: 'glyphicon glyphicon-remove', error: 'glyphicon glyphicon-warning-sign' } }; /** * @module plugins */ /** * Definition of available plugins * @type {object.} */ QueryBuilder.plugins = {}; /** * Gets or extends the default configuration * @param {object} [options] - new configuration * @returns {undefined|object} nothing or configuration object (copy) */ QueryBuilder.defaults = function(options) { if (typeof options == 'object') { $.extendext(true, 'replace', QueryBuilder.DEFAULTS, options); } else if (typeof options == 'string') { if (typeof QueryBuilder.DEFAULTS[options] == 'object') { return $.extend(true, {}, QueryBuilder.DEFAULTS[options]); } else { return QueryBuilder.DEFAULTS[options]; } } else { return $.extend(true, {}, QueryBuilder.DEFAULTS); } }; /** * Registers a new plugin * @param {string} name * @param {function} fct - init function * @param {object} [def] - default options */ QueryBuilder.define = function(name, fct, def) { QueryBuilder.plugins[name] = { fct: fct, def: def || {} }; }; /** * Adds new methods to QueryBuilder prototype * @param {object.} methods */ QueryBuilder.extend = function(methods) { $.extend(QueryBuilder.prototype, methods); }; /** * Initializes plugins for an instance * @throws ConfigError * @private */ QueryBuilder.prototype.initPlugins = function() { if (!this.plugins) { return; } if ($.isArray(this.plugins)) { var tmp = {}; this.plugins.forEach(function(plugin) { tmp[plugin] = null; }); this.plugins = tmp; } Object.keys(this.plugins).forEach(function(plugin) { if (plugin in QueryBuilder.plugins) { this.plugins[plugin] = $.extend(true, {}, QueryBuilder.plugins[plugin].def, this.plugins[plugin] || {} ); QueryBuilder.plugins[plugin].fct.call(this, this.plugins[plugin]); } else { Utils.error('Config', 'Unable to find plugin "{0}"', plugin); } }, this); }; /** * Returns the config of a plugin, if the plugin is not loaded, returns the default config. * @param {string} name * @param {string} [property] * @throws ConfigError * @returns {*} */ QueryBuilder.prototype.getPluginOptions = function(name, property) { var plugin; if (this.plugins && this.plugins[name]) { plugin = this.plugins[name]; } else if (QueryBuilder.plugins[name]) { plugin = QueryBuilder.plugins[name].def; } if (plugin) { if (property) { return plugin[property]; } else { return plugin; } } else { Utils.error('Config', 'Unable to find plugin "{0}"', name); } }; /** * Final initialisation of the builder * @param {object} [rules] * @fires QueryBuilder.afterInit * @private */ QueryBuilder.prototype.init = function(rules) { /** * When the initilization is done, just before creating the root group * @event afterInit * @memberof QueryBuilder */ this.trigger('afterInit'); if (rules) { this.setRules(rules); delete this.settings.rules; } else { this.setRoot(true); } }; /** * Checks the configuration of each filter * @param {QueryBuilder.Filter[]} filters * @returns {QueryBuilder.Filter[]} * @throws ConfigError */ QueryBuilder.prototype.checkFilters = function(filters) { var definedFilters = []; if (!filters || filters.length === 0) { Utils.error('Config', 'Missing filters list'); } filters.forEach(function(filter, i) { if (!filter.id) { Utils.error('Config', 'Missing filter {0} id', i); } if (definedFilters.indexOf(filter.id) != -1) { Utils.error('Config', 'Filter "{0}" already defined', filter.id); } definedFilters.push(filter.id); if (!filter.type) { filter.type = 'string'; } else if (!QueryBuilder.types[filter.type]) { Utils.error('Config', 'Invalid type "{0}"', filter.type); } if (!filter.input) { filter.input = QueryBuilder.types[filter.type] === 'number' ? 'number' : 'text'; } else if (typeof filter.input != 'function' && QueryBuilder.inputs.indexOf(filter.input) == -1) { Utils.error('Config', 'Invalid input "{0}"', filter.input); } if (filter.operators) { filter.operators.forEach(function(operator) { if (typeof operator != 'string') { Utils.error('Config', 'Filter operators must be global operators types (string)'); } }); } if (!filter.field) { filter.field = filter.id; } if (!filter.label) { filter.label = filter.field; } if (!filter.optgroup) { filter.optgroup = null; } else { this.status.has_optgroup = true; // register optgroup if needed if (!this.settings.optgroups[filter.optgroup]) { this.settings.optgroups[filter.optgroup] = filter.optgroup; } } switch (filter.input) { case 'radio': case 'checkbox': if (!filter.values || filter.values.length < 1) { Utils.error('Config', 'Missing filter "{0}" values', filter.id); } break; case 'select': var cleanValues = []; filter.has_optgroup = false; Utils.iterateOptions(filter.values, function(value, label, optgroup) { cleanValues.push({ value: value, label: label, optgroup: optgroup || null }); if (optgroup) { filter.has_optgroup = true; // register optgroup if needed if (!this.settings.optgroups[optgroup]) { this.settings.optgroups[optgroup] = optgroup; } } }.bind(this)); if (filter.has_optgroup) { filter.values = Utils.groupSort(cleanValues, 'optgroup'); } else { filter.values = cleanValues; } if (filter.placeholder) { if (filter.placeholder_value === undefined) { filter.placeholder_value = -1; } filter.values.forEach(function(entry) { if (entry.value == filter.placeholder_value) { Utils.error('Config', 'Placeholder of filter "{0}" overlaps with one of its values', filter.id); } }); } break; } }, this); if (this.settings.sort_filters) { if (typeof this.settings.sort_filters == 'function') { filters.sort(this.settings.sort_filters); } else { var self = this; filters.sort(function(a, b) { return self.translate(a.label).localeCompare(self.translate(b.label)); }); } } if (this.status.has_optgroup) { filters = Utils.groupSort(filters, 'optgroup'); } return filters; }; /** * Checks the configuration of each operator * @param {QueryBuilder.Operator[]} operators * @returns {QueryBuilder.Operator[]} * @throws ConfigError */ QueryBuilder.prototype.checkOperators = function(operators) { var definedOperators = []; operators.forEach(function(operator, i) { if (typeof operator == 'string') { if (!QueryBuilder.OPERATORS[operator]) { Utils.error('Config', 'Unknown operator "{0}"', operator); } operators[i] = operator = $.extendext(true, 'replace', {}, QueryBuilder.OPERATORS[operator]); } else { if (!operator.type) { Utils.error('Config', 'Missing "type" for operator {0}', i); } if (QueryBuilder.OPERATORS[operator.type]) { operators[i] = operator = $.extendext(true, 'replace', {}, QueryBuilder.OPERATORS[operator.type], operator); } if (operator.nb_inputs === undefined || operator.apply_to === undefined) { Utils.error('Config', 'Missing "nb_inputs" and/or "apply_to" for operator "{0}"', operator.type); } } if (definedOperators.indexOf(operator.type) != -1) { Utils.error('Config', 'Operator "{0}" already defined', operator.type); } definedOperators.push(operator.type); if (!operator.optgroup) { operator.optgroup = null; } else { this.status.has_operator_optgroup = true; // register optgroup if needed if (!this.settings.optgroups[operator.optgroup]) { this.settings.optgroups[operator.optgroup] = operator.optgroup; } } }, this); if (this.status.has_operator_optgroup) { operators = Utils.groupSort(operators, 'optgroup'); } return operators; }; /** * Adds all events listeners to the builder * @private */ QueryBuilder.prototype.bindEvents = function() { var self = this; var Selectors = QueryBuilder.selectors; // group condition change this.$el.on('change.queryBuilder', Selectors.group_condition, function() { if ($(this).is(':checked')) { var $group = $(this).closest(Selectors.group_container); self.getModel($group).condition = $(this).val(); } }); // rule filter change this.$el.on('change.queryBuilder', Selectors.rule_filter, function() { var $rule = $(this).closest(Selectors.rule_container); self.getModel($rule).filter = self.getFilterById($(this).val()); }); // rule operator change this.$el.on('change.queryBuilder', Selectors.rule_operator, function() { var $rule = $(this).closest(Selectors.rule_container); self.getModel($rule).operator = self.getOperatorByType($(this).val()); }); // add rule button this.$el.on('click.queryBuilder', Selectors.add_rule, function() { var $group = $(this).closest(Selectors.group_container); self.addRule(self.getModel($group)); }); // delete rule button this.$el.on('click.queryBuilder', Selectors.delete_rule, function() { var $rule = $(this).closest(Selectors.rule_container); self.deleteRule(self.getModel($rule)); }); if (this.settings.allow_groups !== 0) { // add group button this.$el.on('click.queryBuilder', Selectors.add_group, function() { var $group = $(this).closest(Selectors.group_container); self.addGroup(self.getModel($group)); }); // delete group button this.$el.on('click.queryBuilder', Selectors.delete_group, function() { var $group = $(this).closest(Selectors.group_container); self.deleteGroup(self.getModel($group)); }); } // model events this.model.on({ 'drop': function(e, node) { node.$el.remove(); self.refreshGroupsConditions(); }, 'add': function(e, parent, node, index) { if (index === 0) { node.$el.prependTo(parent.$el.find('>' + QueryBuilder.selectors.rules_list)); } else { node.$el.insertAfter(parent.rules[index - 1].$el); } self.refreshGroupsConditions(); }, 'move': function(e, node, group, index) { node.$el.detach(); if (index === 0) { node.$el.prependTo(group.$el.find('>' + QueryBuilder.selectors.rules_list)); } else { node.$el.insertAfter(group.rules[index - 1].$el); } self.refreshGroupsConditions(); }, 'update': function(e, node, field, value, oldValue) { if (node instanceof Rule) { switch (field) { case 'error': self.updateError(node); break; case 'flags': self.applyRuleFlags(node); break; case 'filter': self.updateRuleFilter(node, oldValue); break; case 'operator': self.updateRuleOperator(node, oldValue); break; case 'value': self.updateRuleValue(node, oldValue); break; } } else { switch (field) { case 'error': self.updateError(node); break; case 'flags': self.applyGroupFlags(node); break; case 'condition': self.updateGroupCondition(node, oldValue); break; } } } }); }; /** * Creates the root group * @param {boolean} [addRule=true] - adds a default empty rule * @param {object} [data] - group custom data * @param {object} [flags] - flags to apply to the group * @returns {Group} root group * @fires QueryBuilder.afterAddGroup */ QueryBuilder.prototype.setRoot = function(addRule, data, flags) { addRule = (addRule === undefined || addRule === true); var group_id = this.nextGroupId(); var $group = $($.parseHTML(this.getGroupTemplate(group_id, 1))); this.$el.append($group); this.model.root = new Group(null, $group); this.model.root.model = this.model; this.model.root.data = data; this.model.root.flags = $.extend({}, this.settings.default_group_flags, flags); this.model.root.condition = this.settings.default_condition; this.trigger('afterAddGroup', this.model.root); if (addRule) { this.addRule(this.model.root); } return this.model.root; }; /** * Adds a new group * @param {Group} parent * @param {boolean} [addRule=true] - adds a default empty rule * @param {object} [data] - group custom data * @param {object} [flags] - flags to apply to the group * @returns {Group} * @fires QueryBuilder.beforeAddGroup * @fires QueryBuilder.afterAddGroup */ QueryBuilder.prototype.addGroup = function(parent, addRule, data, flags) { addRule = (addRule === undefined || addRule === true); var level = parent.level + 1; /** * Just before adding a group, can be prevented. * @event beforeAddGroup * @memberof QueryBuilder * @param {Group} parent * @param {boolean} addRule - if an empty rule will be added in the group * @param {int} level - nesting level of the group, 1 is the root group */ var e = this.trigger('beforeAddGroup', parent, addRule, level); if (e.isDefaultPrevented()) { return null; } var group_id = this.nextGroupId(); var $group = $(this.getGroupTemplate(group_id, level)); var model = parent.addGroup($group); model.data = data; model.flags = $.extend({}, this.settings.default_group_flags, flags); model.condition = this.settings.default_condition; /** * Just after adding a group * @event afterAddGroup * @memberof QueryBuilder * @param {Group} group */ this.trigger('afterAddGroup', model); /** * After any change in the rules * @event rulesChanged * @memberof QueryBuilder */ this.trigger('rulesChanged'); if (addRule) { this.addRule(model); } return model; }; /** * Tries to delete a group. The group is not deleted if at least one rule is flagged `no_delete`. * @param {Group} group * @returns {boolean} if the group has been deleted * @fires QueryBuilder.beforeDeleteGroup * @fires QueryBuilder.afterDeleteGroup */ QueryBuilder.prototype.deleteGroup = function(group) { if (group.isRoot()) { return false; } /** * Just before deleting a group, can be prevented * @event beforeDeleteGroup * @memberof QueryBuilder * @param {Group} parent */ var e = this.trigger('beforeDeleteGroup', group); if (e.isDefaultPrevented()) { return false; } var del = true; group.each('reverse', function(rule) { del &= this.deleteRule(rule); }, function(group) { del &= this.deleteGroup(group); }, this); if (del) { group.drop(); /** * Just after deleting a group * @event afterDeleteGroup * @memberof QueryBuilder */ this.trigger('afterDeleteGroup'); this.trigger('rulesChanged'); } return del; }; /** * Performs actions when a group's condition changes * @param {Group} group * @param {object} previousCondition * @fires QueryBuilder.afterUpdateGroupCondition * @private */ QueryBuilder.prototype.updateGroupCondition = function(group, previousCondition) { group.$el.find('>' + QueryBuilder.selectors.group_condition).each(function() { var $this = $(this); $this.prop('checked', $this.val() === group.condition); $this.parent().toggleClass('active', $this.val() === group.condition); }); /** * After the group condition has been modified * @event afterUpdateGroupCondition * @memberof QueryBuilder * @param {Group} group * @param {object} previousCondition */ this.trigger('afterUpdateGroupCondition', group, previousCondition); this.trigger('rulesChanged'); }; /** * Updates the visibility of conditions based on number of rules inside each group * @private */ QueryBuilder.prototype.refreshGroupsConditions = function() { (function walk(group) { if (!group.flags || (group.flags && !group.flags.condition_readonly)) { group.$el.find('>' + QueryBuilder.selectors.group_condition).prop('disabled', group.rules.length <= 1) .parent().toggleClass('disabled', group.rules.length <= 1); } group.each(null, function(group) { walk(group); }, this); }(this.model.root)); }; /** * Adds a new rule * @param {Group} parent * @param {object} [data] - rule custom data * @param {object} [flags] - flags to apply to the rule * @returns {Rule} * @fires QueryBuilder.beforeAddRule * @fires QueryBuilder.afterAddRule * @fires QueryBuilder.changer:getDefaultFilter */ QueryBuilder.prototype.addRule = function(parent, data, flags) { /** * Just before adding a rule, can be prevented * @event beforeAddRule * @memberof QueryBuilder * @param {Group} parent */ var e = this.trigger('beforeAddRule', parent); if (e.isDefaultPrevented()) { return null; } var rule_id = this.nextRuleId(); var $rule = $($.parseHTML(this.getRuleTemplate(rule_id))); var model = parent.addRule($rule); model.data = data; model.flags = $.extend({}, this.settings.default_rule_flags, flags); /** * Just after adding a rule * @event afterAddRule * @memberof QueryBuilder * @param {Rule} rule */ this.trigger('afterAddRule', model); this.trigger('rulesChanged'); this.createRuleFilters(model); if (this.settings.default_filter || !this.settings.display_empty_filter) { /** * Modifies the default filter for a rule * @event changer:getDefaultFilter * @memberof QueryBuilder * @param {QueryBuilder.Filter} filter * @param {Rule} rule * @returns {QueryBuilder.Filter} */ model.filter = this.change('getDefaultFilter', this.getFilterById(this.settings.default_filter || this.filters[0].id), model ); } return model; }; /** * Tries to delete a rule * @param {Rule} rule * @returns {boolean} if the rule has been deleted * @fires QueryBuilder.beforeDeleteRule * @fires QueryBuilder.afterDeleteRule */ QueryBuilder.prototype.deleteRule = function(rule) { if (rule.flags.no_delete) { return false; } /** * Just before deleting a rule, can be prevented * @event beforeDeleteRule * @memberof QueryBuilder * @param {Rule} rule */ var e = this.trigger('beforeDeleteRule', rule); if (e.isDefaultPrevented()) { return false; } rule.drop(); /** * Just after deleting a rule * @event afterDeleteRule * @memberof QueryBuilder */ this.trigger('afterDeleteRule'); this.trigger('rulesChanged'); return true; }; /** * Creates the filters for a rule * @param {Rule} rule * @fires QueryBuilder.changer:getRuleFilters * @fires QueryBuilder.afterCreateRuleFilters * @private */ QueryBuilder.prototype.createRuleFilters = function(rule) { /** * Modifies the list a filters available for a rule * @event changer:getRuleFilters * @memberof QueryBuilder * @param {QueryBuilder.Filter[]} filters * @param {Rule} rule * @returns {QueryBuilder.Filter[]} */ var filters = this.change('getRuleFilters', this.filters, rule); var $filterSelect = $($.parseHTML(this.getRuleFilterSelect(rule, filters))); rule.$el.find(QueryBuilder.selectors.filter_container).html($filterSelect); /** * After creating the dropdown for filters * @event afterCreateRuleFilters * @memberof QueryBuilder * @param {Rule} rule */ this.trigger('afterCreateRuleFilters', rule); this.applyRuleFlags(rule); }; /** * Creates the operators for a rule and init the rule operator * @param {Rule} rule * @fires QueryBuilder.afterCreateRuleOperators * @private */ QueryBuilder.prototype.createRuleOperators = function(rule) { var $operatorContainer = rule.$el.find(QueryBuilder.selectors.operator_container).empty(); if (!rule.filter) { return; } var operators = this.getOperators(rule.filter); var $operatorSelect = $($.parseHTML(this.getRuleOperatorSelect(rule, operators))); $operatorContainer.html($operatorSelect); // set the operator without triggering update event if (rule.filter.default_operator) { rule.__.operator = this.getOperatorByType(rule.filter.default_operator); } else { rule.__.operator = operators[0]; } rule.$el.find(QueryBuilder.selectors.rule_operator).val(rule.operator.type); /** * After creating the dropdown for operators * @event afterCreateRuleOperators * @memberof QueryBuilder * @param {Rule} rule * @param {QueryBuilder.Operator[]} operators - allowed operators for this rule */ this.trigger('afterCreateRuleOperators', rule, operators); this.applyRuleFlags(rule); }; /** * Creates the main input for a rule * @param {Rule} rule * @fires QueryBuilder.afterCreateRuleInput * @private */ QueryBuilder.prototype.createRuleInput = function(rule) { var $valueContainer = rule.$el.find(QueryBuilder.selectors.value_container).empty(); rule.__.value = undefined; if (!rule.filter || !rule.operator || rule.operator.nb_inputs === 0) { return; } var self = this; var $inputs = $(); var filter = rule.filter; for (var i = 0; i < rule.operator.nb_inputs; i++) { var $ruleInput = $($.parseHTML($.trim(this.getRuleInput(rule, i)))); if (i > 0) $valueContainer.append(this.settings.inputs_separator); $valueContainer.append($ruleInput); $inputs = $inputs.add($ruleInput); } $valueContainer.css('display', ''); $inputs.on('change ' + (filter.input_event || ''), function() { if (!rule._updating_input) { rule._updating_value = true; rule.value = self.getRuleInputValue(rule); rule._updating_value = false; } }); if (filter.plugin) { $inputs[filter.plugin](filter.plugin_config || {}); } /** * After creating the input for a rule and initializing optional plugin * @event afterCreateRuleInput * @memberof QueryBuilder * @param {Rule} rule */ this.trigger('afterCreateRuleInput', rule); if (filter.default_value !== undefined) { rule.value = filter.default_value; } else { rule._updating_value = true; rule.value = self.getRuleInputValue(rule); rule._updating_value = false; } this.applyRuleFlags(rule); }; /** * Performs action when a rule's filter changes * @param {Rule} rule * @param {object} previousFilter * @fires QueryBuilder.afterUpdateRuleFilter * @private */ QueryBuilder.prototype.updateRuleFilter = function(rule, previousFilter) { this.createRuleOperators(rule); this.createRuleInput(rule); rule.$el.find(QueryBuilder.selectors.rule_filter).val(rule.filter ? rule.filter.id : '-1'); // clear rule data if the filter changed if (previousFilter && rule.filter && previousFilter.id !== rule.filter.id) { rule.data = undefined; } /** * After the filter has been updated and the operators and input re-created * @event afterUpdateRuleFilter * @memberof QueryBuilder * @param {Rule} rule * @param {object} previousFilter */ this.trigger('afterUpdateRuleFilter', rule, previousFilter); this.trigger('rulesChanged'); }; /** * Performs actions when a rule's operator changes * @param {Rule} rule * @param {object} previousOperator * @fires QueryBuilder.afterUpdateRuleOperator * @private */ QueryBuilder.prototype.updateRuleOperator = function(rule, previousOperator) { var $valueContainer = rule.$el.find(QueryBuilder.selectors.value_container); if (!rule.operator || rule.operator.nb_inputs === 0) { $valueContainer.hide(); rule.__.value = undefined; } else { $valueContainer.css('display', ''); if ($valueContainer.is(':empty') || !previousOperator || rule.operator.nb_inputs !== previousOperator.nb_inputs || rule.operator.optgroup !== previousOperator.optgroup ) { this.createRuleInput(rule); } } if (rule.operator) { rule.$el.find(QueryBuilder.selectors.rule_operator).val(rule.operator.type); // refresh value if the format changed for this operator rule.__.value = this.getRuleInputValue(rule); } /** * After the operator has been updated and the input optionally re-created * @event afterUpdateRuleOperator * @memberof QueryBuilder * @param {Rule} rule * @param {object} previousOperator */ this.trigger('afterUpdateRuleOperator', rule, previousOperator); this.trigger('rulesChanged'); }; /** * Performs actions when rule's value changes * @param {Rule} rule * @param {object} previousValue * @fires QueryBuilder.afterUpdateRuleValue * @private */ QueryBuilder.prototype.updateRuleValue = function(rule, previousValue) { if (!rule._updating_value) { this.setRuleInputValue(rule, rule.value); } /** * After the rule value has been modified * @event afterUpdateRuleValue * @memberof QueryBuilder * @param {Rule} rule * @param {*} previousValue */ this.trigger('afterUpdateRuleValue', rule, previousValue); this.trigger('rulesChanged'); }; /** * Changes a rule's properties depending on its flags * @param {Rule} rule * @fires QueryBuilder.afterApplyRuleFlags * @private */ QueryBuilder.prototype.applyRuleFlags = function(rule) { var flags = rule.flags; var Selectors = QueryBuilder.selectors; rule.$el.find(Selectors.rule_filter).prop('disabled', flags.filter_readonly); rule.$el.find(Selectors.rule_operator).prop('disabled', flags.operator_readonly); rule.$el.find(Selectors.rule_value).prop('disabled', flags.value_readonly); if (flags.no_delete) { rule.$el.find(Selectors.delete_rule).remove(); } /** * After rule's flags has been applied * @event afterApplyRuleFlags * @memberof QueryBuilder * @param {Rule} rule */ this.trigger('afterApplyRuleFlags', rule); }; /** * Changes group's properties depending on its flags * @param {Group} group * @fires QueryBuilder.afterApplyGroupFlags * @private */ QueryBuilder.prototype.applyGroupFlags = function(group) { var flags = group.flags; var Selectors = QueryBuilder.selectors; group.$el.find('>' + Selectors.group_condition).prop('disabled', flags.condition_readonly) .parent().toggleClass('readonly', flags.condition_readonly); if (flags.no_add_rule) { group.$el.find(Selectors.add_rule).remove(); } if (flags.no_add_group) { group.$el.find(Selectors.add_group).remove(); } if (flags.no_delete) { group.$el.find(Selectors.delete_group).remove(); } /** * After group's flags has been applied * @event afterApplyGroupFlags * @memberof QueryBuilder * @param {Group} group */ this.trigger('afterApplyGroupFlags', group); }; /** * Clears all errors markers * @param {Node} [node] default is root Group */ QueryBuilder.prototype.clearErrors = function(node) { node = node || this.model.root; if (!node) { return; } node.error = null; if (node instanceof Group) { node.each(function(rule) { rule.error = null; }, function(group) { this.clearErrors(group); }, this); } }; /** * Adds/Removes error on a Rule or Group * @param {Node} node * @fires QueryBuilder.changer:displayError * @private */ QueryBuilder.prototype.updateError = function(node) { if (this.settings.display_errors) { if (node.error === null) { node.$el.removeClass('has-error'); } else { var errorMessage = this.translate('errors', node.error[0]); errorMessage = Utils.fmt(errorMessage, node.error.slice(1)); /** * Modifies an error message before display * @event changer:displayError * @memberof QueryBuilder * @param {string} errorMessage - the error message (translated and formatted) * @param {array} error - the raw error array (error code and optional arguments) * @param {Node} node * @returns {string} */ errorMessage = this.change('displayError', errorMessage, node.error, node); node.$el.addClass('has-error') .find(QueryBuilder.selectors.error_container).eq(0) .attr('title', errorMessage); } } }; /** * Triggers a validation error event * @param {Node} node * @param {string|array} error * @param {*} value * @fires QueryBuilder.validationError * @private */ QueryBuilder.prototype.triggerValidationError = function(node, error, value) { if (!$.isArray(error)) { error = [error]; } /** * Fired when a validation error occurred, can be prevented * @event validationError * @memberof QueryBuilder * @param {Node} node * @param {string} error * @param {*} value */ var e = this.trigger('validationError', node, error, value); if (!e.isDefaultPrevented()) { node.error = error; } }; /** * Destroys the builder * @fires QueryBuilder.beforeDestroy */ QueryBuilder.prototype.destroy = function() { /** * Before the {@link QueryBuilder#destroy} method * @event beforeDestroy * @memberof QueryBuilder */ this.trigger('beforeDestroy'); if (this.status.generated_id) { this.$el.removeAttr('id'); } this.clear(); this.model = null; this.$el .off('.queryBuilder') .removeClass('query-builder') .removeData('queryBuilder'); delete this.$el[0].queryBuilder; }; /** * Clear all rules and resets the root group * @fires QueryBuilder.beforeReset * @fires QueryBuilder.afterReset */ QueryBuilder.prototype.reset = function() { /** * Before the {@link QueryBuilder#reset} method, can be prevented * @event beforeReset * @memberof QueryBuilder */ var e = this.trigger('beforeReset'); if (e.isDefaultPrevented()) { return; } this.status.group_id = 1; this.status.rule_id = 0; this.model.root.empty(); this.model.root.data = undefined; this.model.root.flags = $.extend({}, this.settings.default_group_flags); this.model.root.condition = this.settings.default_condition; this.addRule(this.model.root); /** * After the {@link QueryBuilder#reset} method * @event afterReset * @memberof QueryBuilder */ this.trigger('afterReset'); this.trigger('rulesChanged'); }; /** * Clears all rules and removes the root group * @fires QueryBuilder.beforeClear * @fires QueryBuilder.afterClear */ QueryBuilder.prototype.clear = function() { /** * Before the {@link QueryBuilder#clear} method, can be prevented * @event beforeClear * @memberof QueryBuilder */ var e = this.trigger('beforeClear'); if (e.isDefaultPrevented()) { return; } this.status.group_id = 0; this.status.rule_id = 0; if (this.model.root) { this.model.root.drop(); this.model.root = null; } /** * After the {@link QueryBuilder#clear} method * @event afterClear * @memberof QueryBuilder */ this.trigger('afterClear'); this.trigger('rulesChanged'); }; /** * Modifies the builder configuration.
* Only options defined in QueryBuilder.modifiable_options are modifiable * @param {object} options */ QueryBuilder.prototype.setOptions = function(options) { $.each(options, function(opt, value) { if (QueryBuilder.modifiable_options.indexOf(opt) !== -1) { this.settings[opt] = value; } }.bind(this)); }; /** * Returns the model associated to a DOM object, or the root model * @param {jQuery} [target] * @returns {Node} */ QueryBuilder.prototype.getModel = function(target) { if (!target) { return this.model.root; } else if (target instanceof Node) { return target; } else { return $(target).data('queryBuilderModel'); } }; /** * Validates the whole builder * @param {object} [options] * @param {boolean} [options.skip_empty=false] - skips validating rules that have no filter selected * @returns {boolean} * @fires QueryBuilder.changer:validate */ QueryBuilder.prototype.validate = function(options) { options = $.extend({ skip_empty: false }, options); this.clearErrors(); var self = this; var valid = (function parse(group) { var done = 0; var errors = 0; group.each(function(rule) { if (!rule.filter && options.skip_empty) { return; } if (!rule.filter) { self.triggerValidationError(rule, 'no_filter', null); errors++; return; } if (!rule.operator) { self.triggerValidationError(rule, 'no_operator', null); errors++; return; } if (rule.operator.nb_inputs !== 0) { var valid = self.validateValue(rule, rule.value); if (valid !== true) { self.triggerValidationError(rule, valid, rule.value); errors++; return; } } done++; }, function(group) { var res = parse(group); if (res === true) { done++; } else if (res === false) { errors++; } }); if (errors > 0) { return false; } else if (done === 0 && !group.isRoot() && options.skip_empty) { return null; } else if (done === 0 && (!self.settings.allow_empty || !group.isRoot())) { self.triggerValidationError(group, 'empty_group', null); return false; } return true; }(this.model.root)); /** * Modifies the result of the {@link QueryBuilder#validate} method * @event changer:validate * @memberof QueryBuilder * @param {boolean} valid * @returns {boolean} */ return this.change('validate', valid); }; /** * Gets an object representing current rules * @param {object} [options] * @param {boolean|string} [options.get_flags=false] - export flags, true: only changes from default flags or 'all' * @param {boolean} [options.allow_invalid=false] - returns rules even if they are invalid * @param {boolean} [options.skip_empty=false] - remove rules that have no filter selected * @returns {object} * @fires QueryBuilder.changer:ruleToJson * @fires QueryBuilder.changer:groupToJson * @fires QueryBuilder.changer:getRules */ QueryBuilder.prototype.getRules = function(options) { options = $.extend({ get_flags: false, allow_invalid: false, skip_empty: false }, options); var valid = this.validate(options); if (!valid && !options.allow_invalid) { return null; } var self = this; var out = (function parse(group) { var groupData = { condition: group.condition, rules: [] }; if (group.data) { groupData.data = $.extendext(true, 'replace', {}, group.data); } if (options.get_flags) { var flags = self.getGroupFlags(group.flags, options.get_flags === 'all'); if (!$.isEmptyObject(flags)) { groupData.flags = flags; } } group.each(function(rule) { if (!rule.filter && options.skip_empty) { return; } var value = null; if (!rule.operator || rule.operator.nb_inputs !== 0) { value = rule.value; } var ruleData = { id: rule.filter ? rule.filter.id : null, field: rule.filter ? rule.filter.field : null, type: rule.filter ? rule.filter.type : null, input: rule.filter ? rule.filter.input : null, operator: rule.operator ? rule.operator.type : null, value: value }; if (rule.filter && rule.filter.data || rule.data) { ruleData.data = $.extendext(true, 'replace', {}, rule.filter ? rule.filter.data : {}, rule.data); } if (options.get_flags) { var flags = self.getRuleFlags(rule.flags, options.get_flags === 'all'); if (!$.isEmptyObject(flags)) { ruleData.flags = flags; } } /** * Modifies the JSON generated from a Rule object * @event changer:ruleToJson * @memberof QueryBuilder * @param {object} json * @param {Rule} rule * @returns {object} */ groupData.rules.push(self.change('ruleToJson', ruleData, rule)); }, function(model) { var data = parse(model); if (data.rules.length !== 0 || !options.skip_empty) { groupData.rules.push(data); } }, this); /** * Modifies the JSON generated from a Group object * @event changer:groupToJson * @memberof QueryBuilder * @param {object} json * @param {Group} group * @returns {object} */ return self.change('groupToJson', groupData, group); }(this.model.root)); out.valid = valid; /** * Modifies the result of the {@link QueryBuilder#getRules} method * @event changer:getRules * @memberof QueryBuilder * @param {object} json * @returns {object} */ return this.change('getRules', out); }; /** * Sets rules from object * @param {object} data * @param {object} [options] * @param {boolean} [options.allow_invalid=false] - silent-fail if the data are invalid * @throws RulesError, UndefinedConditionError * @fires QueryBuilder.changer:setRules * @fires QueryBuilder.changer:jsonToRule * @fires QueryBuilder.changer:jsonToGroup * @fires QueryBuilder.afterSetRules */ QueryBuilder.prototype.setRules = function(data, options) { options = $.extend({ allow_invalid: false }, options); if ($.isArray(data)) { data = { condition: this.settings.default_condition, rules: data }; } if (!data || !data.rules || (data.rules.length === 0 && !this.settings.allow_empty)) { Utils.error('RulesParse', 'Incorrect data object passed'); } this.clear(); this.setRoot(false, data.data, this.parseGroupFlags(data)); /** * Modifies data before the {@link QueryBuilder#setRules} method * @event changer:setRules * @memberof QueryBuilder * @param {object} json * @param {object} options * @returns {object} */ data = this.change('setRules', data, options); var self = this; (function add(data, group) { if (group === null) { return; } if (data.condition === undefined) { data.condition = self.settings.default_condition; } else if (self.settings.conditions.indexOf(data.condition) == -1) { Utils.error(!options.allow_invalid, 'UndefinedCondition', 'Invalid condition "{0}"', data.condition); data.condition = self.settings.default_condition; } group.condition = data.condition; data.rules.forEach(function(item) { var model; if (item.rules !== undefined) { if (self.settings.allow_groups !== -1 && self.settings.allow_groups < group.level) { Utils.error(!options.allow_invalid, 'RulesParse', 'No more than {0} groups are allowed', self.settings.allow_groups); self.reset(); } else { model = self.addGroup(group, false, item.data, self.parseGroupFlags(item)); if (model === null) { return; } add(item, model); } } else { if (!item.empty) { if (item.id === undefined) { Utils.error(!options.allow_invalid, 'RulesParse', 'Missing rule field id'); item.empty = true; } if (item.operator === undefined) { item.operator = 'equal'; } } model = self.addRule(group, item.data, self.parseRuleFlags(item)); if (model === null) { return; } if (!item.empty) { model.filter = self.getFilterById(item.id, !options.allow_invalid); } if (model.filter) { model.operator = self.getOperatorByType(item.operator, !options.allow_invalid); if (!model.operator) { model.operator = self.getOperators(model.filter)[0]; } } if (model.operator && model.operator.nb_inputs !== 0) { if (item.value !== undefined) { model.value = item.value; } else if (model.filter.default_value !== undefined) { model.value = model.filter.default_value; } } /** * Modifies the Rule object generated from the JSON * @event changer:jsonToRule * @memberof QueryBuilder * @param {Rule} rule * @param {object} json * @returns {Rule} the same rule */ if (self.change('jsonToRule', model, item) != model) { Utils.error('RulesParse', 'Plugin tried to change rule reference'); } } }); /** * Modifies the Group object generated from the JSON * @event changer:jsonToGroup * @memberof QueryBuilder * @param {Group} group * @param {object} json * @returns {Group} the same group */ if (self.change('jsonToGroup', group, data) != group) { Utils.error('RulesParse', 'Plugin tried to change group reference'); } }(data, this.model.root)); /** * After the {@link QueryBuilder#setRules} method * @event afterSetRules * @memberof QueryBuilder */ this.trigger('afterSetRules'); }; /** * Performs value validation * @param {Rule} rule * @param {string|string[]} value * @returns {array|boolean} true or error array * @fires QueryBuilder.changer:validateValue */ QueryBuilder.prototype.validateValue = function(rule, value) { var validation = rule.filter.validation || {}; var result = true; if (validation.callback) { result = validation.callback.call(this, value, rule); } else { result = this._validateValue(rule, value); } /** * Modifies the result of the rule validation method * @event changer:validateValue * @memberof QueryBuilder * @param {array|boolean} result - true or an error array * @param {*} value * @param {Rule} rule * @returns {array|boolean} */ return this.change('validateValue', result, value, rule); }; /** * Default validation function * @param {Rule} rule * @param {string|string[]} value * @returns {array|boolean} true or error array * @throws ConfigError * @private */ QueryBuilder.prototype._validateValue = function(rule, value) { var filter = rule.filter; var operator = rule.operator; var validation = filter.validation || {}; var result = true; var tmp, tempValue; if (rule.operator.nb_inputs === 1) { value = [value]; } for (var i = 0; i < operator.nb_inputs; i++) { if (!operator.multiple && $.isArray(value[i]) && value[i].length > 1) { result = ['operator_not_multiple', operator.type, this.translate('operators', operator.type)]; break; } switch (filter.input) { case 'radio': if (value[i] === undefined || value[i].length === 0) { if (!validation.allow_empty_value) { result = ['radio_empty']; } break; } break; case 'checkbox': if (value[i] === undefined || value[i].length === 0) { if (!validation.allow_empty_value) { result = ['checkbox_empty']; } break; } break; case 'select': if (value[i] === undefined || value[i].length === 0 || (filter.placeholder && value[i] == filter.placeholder_value)) { if (!validation.allow_empty_value) { result = ['select_empty']; } break; } break; default: tempValue = $.isArray(value[i]) ? value[i] : [value[i]]; for (var j = 0; j < tempValue.length; j++) { switch (QueryBuilder.types[filter.type]) { case 'string': if (tempValue[j] === undefined || tempValue[j].length === 0) { if (!validation.allow_empty_value) { result = ['string_empty']; } break; } if (validation.min !== undefined) { if (tempValue[j].length < parseInt(validation.min)) { result = [this.getValidationMessage(validation, 'min', 'string_exceed_min_length'), validation.min]; break; } } if (validation.max !== undefined) { if (tempValue[j].length > parseInt(validation.max)) { result = [this.getValidationMessage(validation, 'max', 'string_exceed_max_length'), validation.max]; break; } } if (validation.format) { if (typeof validation.format == 'string') { validation.format = new RegExp(validation.format); } if (!validation.format.test(tempValue[j])) { result = [this.getValidationMessage(validation, 'format', 'string_invalid_format'), validation.format]; break; } } break; case 'number': if (tempValue[j] === undefined || tempValue[j].length === 0) { if (!validation.allow_empty_value) { result = ['number_nan']; } break; } if (isNaN(tempValue[j])) { result = ['number_nan']; break; } if (filter.type == 'integer') { if (parseInt(tempValue[j]) != tempValue[j]) { result = ['number_not_integer']; break; } } else { if (parseFloat(tempValue[j]) != tempValue[j]) { result = ['number_not_double']; break; } } if (validation.min !== undefined) { if (tempValue[j] < parseFloat(validation.min)) { result = [this.getValidationMessage(validation, 'min', 'number_exceed_min'), validation.min]; break; } } if (validation.max !== undefined) { if (tempValue[j] > parseFloat(validation.max)) { result = [this.getValidationMessage(validation, 'max', 'number_exceed_max'), validation.max]; break; } } if (validation.step !== undefined && validation.step !== 'any') { var v = (tempValue[j] / validation.step).toPrecision(14); if (parseInt(v) != v) { result = [this.getValidationMessage(validation, 'step', 'number_wrong_step'), validation.step]; break; } } break; case 'datetime': if (tempValue[j] === undefined || tempValue[j].length === 0) { if (!validation.allow_empty_value) { result = ['datetime_empty']; } break; } // we need MomentJS if (validation.format) { if (!('moment' in window)) { Utils.error('MissingLibrary', 'MomentJS is required for Date/Time validation. Get it here http://momentjs.com'); } var datetime = moment(tempValue[j], validation.format); if (!datetime.isValid()) { result = [this.getValidationMessage(validation, 'format', 'datetime_invalid'), validation.format]; break; } else { if (validation.min) { if (datetime < moment(validation.min, validation.format)) { result = [this.getValidationMessage(validation, 'min', 'datetime_exceed_min'), validation.min]; break; } } if (validation.max) { if (datetime > moment(validation.max, validation.format)) { result = [this.getValidationMessage(validation, 'max', 'datetime_exceed_max'), validation.max]; break; } } } } break; case 'boolean': if (tempValue[j] === undefined || tempValue[j].length === 0) { if (!validation.allow_empty_value) { result = ['boolean_not_valid']; } break; } tmp = ('' + tempValue[j]).trim().toLowerCase(); if (tmp !== 'true' && tmp !== 'false' && tmp !== '1' && tmp !== '0' && tempValue[j] !== 1 && tempValue[j] !== 0) { result = ['boolean_not_valid']; break; } } if (result !== true) { break; } } } if (result !== true) { break; } } if ((rule.operator.type === 'between' || rule.operator.type === 'not_between') && value.length === 2) { switch (QueryBuilder.types[filter.type]) { case 'number': if (value[0] > value[1]) { result = ['number_between_invalid', value[0], value[1]]; } break; case 'datetime': // we need MomentJS if (validation.format) { if (!('moment' in window)) { Utils.error('MissingLibrary', 'MomentJS is required for Date/Time validation. Get it here http://momentjs.com'); } if (moment(value[0], validation.format).isAfter(moment(value[1], validation.format))) { result = ['datetime_between_invalid', value[0], value[1]]; } } break; } } return result; }; /** * Returns an incremented group ID * @returns {string} * @private */ QueryBuilder.prototype.nextGroupId = function() { return this.status.id + '_group_' + (this.status.group_id++); }; /** * Returns an incremented rule ID * @returns {string} * @private */ QueryBuilder.prototype.nextRuleId = function() { return this.status.id + '_rule_' + (this.status.rule_id++); }; /** * Returns the operators for a filter * @param {string|object} filter - filter id or filter object * @returns {object[]} * @fires QueryBuilder.changer:getOperators */ QueryBuilder.prototype.getOperators = function(filter) { if (typeof filter == 'string') { filter = this.getFilterById(filter); } var result = []; for (var i = 0, l = this.operators.length; i < l; i++) { // filter operators check if (filter.operators) { if (filter.operators.indexOf(this.operators[i].type) == -1) { continue; } } // type check else if (this.operators[i].apply_to.indexOf(QueryBuilder.types[filter.type]) == -1) { continue; } result.push(this.operators[i]); } // keep sort order defined for the filter if (filter.operators) { result.sort(function(a, b) { return filter.operators.indexOf(a.type) - filter.operators.indexOf(b.type); }); } /** * Modifies the operators available for a filter * @event changer:getOperators * @memberof QueryBuilder * @param {QueryBuilder.Operator[]} operators * @param {QueryBuilder.Filter} filter * @returns {QueryBuilder.Operator[]} */ return this.change('getOperators', result, filter); }; /** * Returns a particular filter by its id * @param {string} id * @param {boolean} [doThrow=true] * @returns {object|null} * @throws UndefinedFilterError */ QueryBuilder.prototype.getFilterById = function(id, doThrow) { if (id == '-1') { return null; } for (var i = 0, l = this.filters.length; i < l; i++) { if (this.filters[i].id == id) { return this.filters[i]; } } Utils.error(doThrow !== false, 'UndefinedFilter', 'Undefined filter "{0}"', id); return null; }; /** * Returns a particular operator by its type * @param {string} type * @param {boolean} [doThrow=true] * @returns {object|null} * @throws UndefinedOperatorError */ QueryBuilder.prototype.getOperatorByType = function(type, doThrow) { if (type == '-1') { return null; } for (var i = 0, l = this.operators.length; i < l; i++) { if (this.operators[i].type == type) { return this.operators[i]; } } Utils.error(doThrow !== false, 'UndefinedOperator', 'Undefined operator "{0}"', type); return null; }; /** * Returns rule's current input value * @param {Rule} rule * @returns {*} * @fires QueryBuilder.changer:getRuleValue * @private */ QueryBuilder.prototype.getRuleInputValue = function(rule) { var filter = rule.filter; var operator = rule.operator; var value = []; if (filter.valueGetter) { value = filter.valueGetter.call(this, rule); } else { var $value = rule.$el.find(QueryBuilder.selectors.value_container); for (var i = 0; i < operator.nb_inputs; i++) { var name = Utils.escapeElementId(rule.id + '_value_' + i); var tmp; switch (filter.input) { case 'radio': value.push($value.find('[name=' + name + ']:checked').val()); break; case 'checkbox': tmp = []; $value.find('[name=' + name + ']:checked').each(function() { tmp.push($(this).val()); }); value.push(tmp); break; case 'select': if (filter.multiple) { tmp = []; $value.find('[name=' + name + '] option:selected').each(function() { tmp.push($(this).val()); }); value.push(tmp); } else { value.push($value.find('[name=' + name + '] option:selected').val()); } break; default: value.push($value.find('[name=' + name + ']').val()); } } value = value.map(function(val) { if (operator.multiple && filter.value_separator && typeof val == 'string') { val = val.split(filter.value_separator); } if ($.isArray(val)) { return val.map(function(subval) { return Utils.changeType(subval, filter.type); }); } else { return Utils.changeType(val, filter.type); } }); if (operator.nb_inputs === 1) { value = value[0]; } // @deprecated if (filter.valueParser) { value = filter.valueParser.call(this, rule, value); } } /** * Modifies the rule's value grabbed from the DOM * @event changer:getRuleValue * @memberof QueryBuilder * @param {*} value * @param {Rule} rule * @returns {*} */ return this.change('getRuleValue', value, rule); }; /** * Sets the value of a rule's input * @param {Rule} rule * @param {*} value * @private */ QueryBuilder.prototype.setRuleInputValue = function(rule, value) { var filter = rule.filter; var operator = rule.operator; if (!filter || !operator) { return; } rule._updating_input = true; if (filter.valueSetter) { filter.valueSetter.call(this, rule, value); } else { var $value = rule.$el.find(QueryBuilder.selectors.value_container); if (operator.nb_inputs == 1) { value = [value]; } for (var i = 0; i < operator.nb_inputs; i++) { var name = Utils.escapeElementId(rule.id + '_value_' + i); switch (filter.input) { case 'radio': $value.find('[name=' + name + '][value="' + value[i] + '"]').prop('checked', true).trigger('change'); break; case 'checkbox': if (!$.isArray(value[i])) { value[i] = [value[i]]; } value[i].forEach(function(value) { $value.find('[name=' + name + '][value="' + value + '"]').prop('checked', true).trigger('change'); }); break; default: if (operator.multiple && filter.value_separator && $.isArray(value[i])) { value[i] = value[i].join(filter.value_separator); } $value.find('[name=' + name + ']').val(value[i]).trigger('change'); break; } } } rule._updating_input = false; }; /** * Parses rule flags * @param {object} rule * @returns {object} * @fires QueryBuilder.changer:parseRuleFlags * @private */ QueryBuilder.prototype.parseRuleFlags = function(rule) { var flags = $.extend({}, this.settings.default_rule_flags); if (rule.readonly) { $.extend(flags, { filter_readonly: true, operator_readonly: true, value_readonly: true, no_delete: true }); } if (rule.flags) { $.extend(flags, rule.flags); } /** * Modifies the consolidated rule's flags * @event changer:parseRuleFlags * @memberof QueryBuilder * @param {object} flags * @param {object} rule - not a Rule object * @returns {object} */ return this.change('parseRuleFlags', flags, rule); }; /** * Gets a copy of flags of a rule * @param {object} flags * @param {boolean} [all=false] - return all flags or only changes from default flags * @returns {object} * @private */ QueryBuilder.prototype.getRuleFlags = function(flags, all) { if (all) { return $.extend({}, flags); } else { var ret = {}; $.each(this.settings.default_rule_flags, function(key, value) { if (flags[key] !== value) { ret[key] = flags[key]; } }); return ret; } }; /** * Parses group flags * @param {object} group * @returns {object} * @fires QueryBuilder.changer:parseGroupFlags * @private */ QueryBuilder.prototype.parseGroupFlags = function(group) { var flags = $.extend({}, this.settings.default_group_flags); if (group.readonly) { $.extend(flags, { condition_readonly: true, no_add_rule: true, no_add_group: true, no_delete: true }); } if (group.flags) { $.extend(flags, group.flags); } /** * Modifies the consolidated group's flags * @event changer:parseGroupFlags * @memberof QueryBuilder * @param {object} flags * @param {object} group - not a Group object * @returns {object} */ return this.change('parseGroupFlags', flags, group); }; /** * Gets a copy of flags of a group * @param {object} flags * @param {boolean} [all=false] - return all flags or only changes from default flags * @returns {object} * @private */ QueryBuilder.prototype.getGroupFlags = function(flags, all) { if (all) { return $.extend({}, flags); } else { var ret = {}; $.each(this.settings.default_group_flags, function(key, value) { if (flags[key] !== value) { ret[key] = flags[key]; } }); return ret; } }; /** * Translate a label either by looking in the `lang` object or in itself if it's an object where keys are language codes * @param {string} [category] * @param {string|object} key * @returns {string} * @fires QueryBuilder.changer:translate */ QueryBuilder.prototype.translate = function(category, key) { if (!key) { key = category; category = undefined; } var translation; if (typeof key === 'object') { translation = key[this.settings.lang_code] || key['en']; } else { translation = (category ? this.lang[category] : this.lang)[key] || key; } /** * Modifies the translated label * @event changer:translate * @memberof QueryBuilder * @param {string} translation * @param {string|object} key * @param {string} [category] * @returns {string} */ return this.change('translate', translation, key, category); }; /** * Returns a validation message * @param {object} validation * @param {string} type * @param {string} def * @returns {string} * @private */ QueryBuilder.prototype.getValidationMessage = function(validation, type, def) { return validation.messages && validation.messages[type] || def; }; QueryBuilder.templates.group = ({ group_id, level, conditions, icons, settings, translate, builder }) => { return `
${settings.allow_groups === -1 || settings.allow_groups >= level ? ` ` : ''} ${level > 1 ? ` ` : ''}
${conditions.map(condition => ` `).join('\n')}
${settings.display_errors ? `
` : ''}
`; }; QueryBuilder.templates.rule = ({ rule_id, icons, settings, translate, builder }) => { return `
${settings.display_errors ? `
` : ''}
`; }; QueryBuilder.templates.filterSelect = ({ rule, filters, icons, settings, translate, builder }) => { let optgroup = null; return ` `; }; QueryBuilder.templates.operatorSelect = ({ rule, operators, icons, settings, translate, builder }) => { let optgroup = null; return ` ${operators.length === 1 ? ` ${translate("operators", operators[0].type)} ` : ''} `; }; QueryBuilder.templates.ruleValueSelect = ({ name, rule, icons, settings, translate, builder }) => { let optgroup = null; return ` `; }; /** * Returns group's HTML * @param {string} group_id * @param {int} level * @returns {string} * @fires QueryBuilder.changer:getGroupTemplate * @private */ QueryBuilder.prototype.getGroupTemplate = function (group_id, level) { var h = this.templates.group({ builder: this, group_id: group_id, level: level, conditions: this.settings.conditions, icons: this.icons, settings: this.settings, translate: this.translate.bind(this) }).trim(); /** * Modifies the raw HTML of a group * @event changer:getGroupTemplate * @memberof QueryBuilder * @param {string} html * @param {int} level * @returns {string} */ return this.change('getGroupTemplate', h, level); }; /** * Returns rule's HTML * @param {string} rule_id * @returns {string} * @fires QueryBuilder.changer:getRuleTemplate * @private */ QueryBuilder.prototype.getRuleTemplate = function (rule_id) { var h = this.templates.rule({ builder: this, rule_id: rule_id, icons: this.icons, settings: this.settings, translate: this.translate.bind(this) }).trim(); /** * Modifies the raw HTML of a rule * @event changer:getRuleTemplate * @memberof QueryBuilder * @param {string} html * @returns {string} */ return this.change('getRuleTemplate', h); }; /** * Returns rule's filter HTML * @param {Rule} rule * @param {object[]} filters * @returns {string} * @fires QueryBuilder.changer:getRuleFilterTemplate * @private */ QueryBuilder.prototype.getRuleFilterSelect = function (rule, filters) { var h = this.templates.filterSelect({ builder: this, rule: rule, filters: filters, icons: this.icons, settings: this.settings, translate: this.translate.bind(this) }).trim(); /** * Modifies the raw HTML of the rule's filter dropdown * @event changer:getRuleFilterSelect * @memberof QueryBuilder * @param {string} html * @param {Rule} rule * @param {QueryBuilder.Filter[]} filters * @returns {string} */ return this.change('getRuleFilterSelect', h, rule, filters); }; /** * Returns rule's operator HTML * @param {Rule} rule * @param {object[]} operators * @returns {string} * @fires QueryBuilder.changer:getRuleOperatorTemplate * @private */ QueryBuilder.prototype.getRuleOperatorSelect = function (rule, operators) { var h = this.templates.operatorSelect({ builder: this, rule: rule, operators: operators, icons: this.icons, settings: this.settings, translate: this.translate.bind(this) }).trim(); /** * Modifies the raw HTML of the rule's operator dropdown * @event changer:getRuleOperatorSelect * @memberof QueryBuilder * @param {string} html * @param {Rule} rule * @param {QueryBuilder.Operator[]} operators * @returns {string} */ return this.change('getRuleOperatorSelect', h, rule, operators); }; /** * Returns the rule's value select HTML * @param {string} name * @param {Rule} rule * @returns {string} * @fires QueryBuilder.changer:getRuleValueSelect * @private */ QueryBuilder.prototype.getRuleValueSelect = function (name, rule) { var h = this.templates.ruleValueSelect({ builder: this, name: name, rule: rule, icons: this.icons, settings: this.settings, translate: this.translate.bind(this) }).trim(); /** * Modifies the raw HTML of the rule's value dropdown (in case of a "select filter) * @event changer:getRuleValueSelect * @memberof QueryBuilder * @param {string} html * @param [string} name * @param {Rule} rule * @returns {string} */ return this.change('getRuleValueSelect', h, name, rule); }; /** * Returns the rule's value HTML * @param {Rule} rule * @param {int} value_id * @returns {string} * @fires QueryBuilder.changer:getRuleInput * @private */ QueryBuilder.prototype.getRuleInput = function (rule, value_id) { var filter = rule.filter; var validation = rule.filter.validation || {}; var name = rule.id + '_value_' + value_id; var c = filter.vertical ? ' class=block' : ''; var h = ''; var placeholder = Array.isArray(filter.placeholder) ? filter.placeholder[value_id] : filter.placeholder; if (typeof filter.input == 'function') { h = filter.input.call(this, rule, name); } else { switch (filter.input) { case 'radio': case 'checkbox': Utils.iterateOptions(filter.values, function (key, val) { h += ' ' + val + ' '; }); break; case 'select': h = this.getRuleValueSelect(name, rule); break; case 'textarea': h += '