├── .gitignore ├── lib ├── admin │ ├── img │ │ ├── ddown.png │ │ ├── loading.gif │ │ ├── wfg-logo.png │ │ ├── default_cat.png │ │ └── ddown.svg │ ├── js │ │ ├── wfg-admin-scripts.js │ │ └── plugins │ │ │ └── selectize │ │ │ ├── selectize.css │ │ │ └── selectize.min.js │ ├── pages │ │ ├── gift_criteria.php │ │ ├── general_settings.php │ │ └── main_menu_page.php │ ├── WFG_Single_Gift.class.php │ ├── WFG_Admin.class.php │ └── css │ │ └── wfg-admin-styles.css ├── helpers │ ├── WFG_Common_Helper.class.php │ ├── WFG_Settings_Helper.class.php │ ├── WFG_Criteria_Helper.class.php │ └── WFG_Product_Helper.class.php ├── Woocommerce_Multiple_Free_Gift.class.php └── WFG_Frontend.class.php ├── codeclimate.yml ├── languages ├── woocommerce-multiple-free-gift-pt_BR.mo ├── woocommerce-multiple-free-gift.pot └── woocommerce-multiple-free-gift-pt_BR.po ├── composer.json ├── css └── wfg-styles.css ├── bower.json ├── uninstall.php ├── woocommerce-multiple-free-gift.php ├── js └── wfg-scripts.js ├── templates └── default │ ├── wfg-default.css │ └── template-default.php ├── README.md ├── .scrutinizer.yml ├── readme.txt └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | -------------------------------------------------------------------------------- /lib/admin/img/ddown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitpokhrel/WooCommerce-Multiple-Free-Gift/master/lib/admin/img/ddown.png -------------------------------------------------------------------------------- /lib/admin/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitpokhrel/WooCommerce-Multiple-Free-Gift/master/lib/admin/img/loading.gif -------------------------------------------------------------------------------- /lib/admin/img/wfg-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitpokhrel/WooCommerce-Multiple-Free-Gift/master/lib/admin/img/wfg-logo.png -------------------------------------------------------------------------------- /lib/admin/img/default_cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitpokhrel/WooCommerce-Multiple-Free-Gift/master/lib/admin/img/default_cat.png -------------------------------------------------------------------------------- /codeclimate.yml: -------------------------------------------------------------------------------- 1 | # Code climate configuration 2 | languages: 3 | JavaScript: true 4 | PHP: true 5 | exclude_paths: 6 | - "lib/admin/js/plugins/**/*" -------------------------------------------------------------------------------- /languages/woocommerce-multiple-free-gift-pt_BR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitpokhrel/WooCommerce-Multiple-Free-Gift/master/languages/woocommerce-multiple-free-gift-pt_BR.mo -------------------------------------------------------------------------------- /lib/admin/img/ddown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ankitpokhrel/woocommerce-multiple-free-gift", 3 | "description": "WooCommerce giveaway made easy.", 4 | "keywords": [ 5 | "wocommerce free gift", 6 | "woocommerce multiple free gift", 7 | "woocommerce gift manager", 8 | "woocommere gift", 9 | "woocommerce", 10 | "woocommerce plugins", 11 | "wordpress plugins" 12 | ], 13 | "license": "GPL2", 14 | "authors": [ 15 | { 16 | "name": "Ankit Pokhrel", 17 | "email": "info@ankitpokhrel.com.np", 18 | "homepage": "http://ankitpokhrel.com/explore/downloads/woocommerce-multiple-free-gift-plugin-pro/", 19 | "role": "Developer" 20 | } 21 | ], 22 | "require": {} 23 | } 24 | -------------------------------------------------------------------------------- /css/wfg-styles.css: -------------------------------------------------------------------------------- 1 | .wfg-fixed-notice { 2 | background-color: #fff; 3 | position: fixed; 4 | z-index: 999999; 5 | width: 100%; 6 | color: #fff; 7 | text-align: center; 8 | height: 36px; 9 | box-shadow: 0 0 5px 1px #222; 10 | top: 0; 11 | } 12 | 13 | .wfg-fixed-notice > p { 14 | background: red none repeat scroll 0 0; 15 | padding: 5px; 16 | font-family: helvetica, sans-serif; 17 | font-weight: bold; 18 | font-size: 14px; 19 | } 20 | 21 | a.wfg-fixed-notice-remove { 22 | color: #fff; 23 | position: relative; 24 | float: right; 25 | right: 20px; 26 | } 27 | 28 | .wfg-gifts { 29 | padding: 15px; 30 | text-align: center; 31 | max-height: 60vh; 32 | overflow-y: auto; 33 | z-index: 99; 34 | -webkit-overflow-scrolling: touch; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "woocommerce-multiple-free-gift", 3 | "version": "1.1.5", 4 | "homepage": "http://ankitpokhrel.com/explore/downloads/woocommerce-multiple-free-gift-plugin-pro/", 5 | "authors": [ 6 | "Ankit Pokhrel " 7 | ], 8 | "description": "WooCommerce giveaway made easy.", 9 | "main": "woocommerce-multiple-free-gift.php", 10 | "moduleType": [ 11 | "globals" 12 | ], 13 | "keywords": [ 14 | "wocommerce free gift", 15 | "woocommerce multiple free gift", 16 | "woocommerce gift manager", 17 | "woocommere gift", 18 | "woocommerce", 19 | "woocommerce plugins", 20 | "wordpress plugins" 21 | ], 22 | "license": "GPL2", 23 | "ignore": [ 24 | "**/.*", 25 | ".bowerrc", 26 | ".gitignore", 27 | "bower_components", 28 | "tests" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | query( $wpdb->prepare( "DELETE FROM $wpdb->posts WHERE post_title=%s AND post_type=%s", 'wfg_gift_product', 10 | 'product_variation' ) ); 11 | 12 | //delete single gift options 13 | delete_post_meta_by_key( '_wfg_single_gift_allowed' ); 14 | delete_post_meta_by_key( '_wfg_single_gift_products' ); 15 | delete_post_meta_by_key( '_wfg_gift_product' ); 16 | 17 | //delete global options 18 | delete_option( '_wfg_global_enabled' ); 19 | delete_option( '_wfg_global_settings' ); 20 | delete_option( '_wfg_criteria' ); 21 | delete_option( '_wfg_popup_overlay' ); 22 | delete_option( '_wfg_popup_heading' ); 23 | delete_option( '_wfg_invalid_condition_text' ); 24 | delete_option( '_wfg_popup_add_gift_text' ); 25 | delete_option( '_wfg_popup_cancel_text' ); 26 | -------------------------------------------------------------------------------- /lib/admin/js/wfg-admin-scripts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wfg-admin-scripts.js 3 | * 4 | * Script for WooCommerce Free Gift plugin. 5 | * 6 | * Copyright (c) 2015, Ankit Pokhrel 7 | */ 8 | jQuery(document).ready(function ($) { 9 | 10 | //activate chosen 11 | var selectBox = $('.wfg-ajax-select'); 12 | 13 | if (selectBox.length) { 14 | initialize_selectize(selectBox); 15 | } 16 | 17 | }); 18 | 19 | /** 20 | * Initialize selectize js in a element. 21 | * 22 | * @param elm 23 | */ 24 | function initialize_selectize(elm) { 25 | elm.selectize({ 26 | valueField: 'id', 27 | labelField: 'text', 28 | searchField: 'text', 29 | highlight: true, 30 | create: false, 31 | loadingClass: 'wfg-selectize-loading', 32 | load: function (query, callback) { 33 | if (!query.length) return callback(); 34 | jQuery.ajax({ 35 | url: 'admin-ajax.php', 36 | dataType: 'json', 37 | method: 'GET', 38 | data: { 39 | action: 'product_list_callback', 40 | q: query 41 | }, 42 | success: function (res) { 43 | callback(res.options); 44 | } 45 | }); 46 | } 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /woocommerce-multiple-free-gift.php: -------------------------------------------------------------------------------- 1 | . 13 | */ 14 | 15 | //Avoid direct calls to this file 16 | if ( ! defined( 'ABSPATH' ) ) { 17 | header( 'Status: 403 Forbidden' ); 18 | header( 'HTTP/1.1 403 Forbidden' ); 19 | die( 'Access Forbidden' ); 20 | } 21 | 22 | define( 'PLUGIN_BASE', plugin_basename( __FILE__ ) ); 23 | define( 'PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 24 | define( 'PRO_URL', 'http://ankitpokhrel.com.np/blog/downloads/woocommerce-multiple-free-gift-plugin-pro/' ); 25 | 26 | include 'lib/helpers/WFG_Common_Helper.class.php'; 27 | include 'lib/helpers/WFG_Settings_Helper.class.php'; 28 | include 'lib/helpers/WFG_Product_Helper.class.php'; 29 | include 'lib/helpers/WFG_Criteria_Helper.class.php'; 30 | include 'lib/admin/WFG_Admin.class.php'; 31 | include 'lib/admin/WFG_Single_Gift.class.php'; 32 | include 'lib/WFG_Frontend.class.php'; 33 | include 'lib/Woocommerce_Multiple_Free_Gift.class.php'; 34 | 35 | //plugin activation hook 36 | register_activation_hook( __FILE__, array( 'Woocommerce_Multiple_Free_Gift', 'wfg_activate' ) ); 37 | 38 | /** Initialize the awesome */ 39 | new Woocommerce_Multiple_Free_Gift(); 40 | -------------------------------------------------------------------------------- /js/wfg-scripts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wfg-scripts.js 3 | * 4 | * Frontend core script for WooCommerce free gift plugin. 5 | * 6 | * Copyright (c) 2015, Ankit Pokhrel 7 | */ 8 | jQuery(document).ready(function ($) { 9 | if($('.wfg-popup, .wfg-overlay').length) { 10 | setTimeout(function () { 11 | $('.wfg-popup, .wfg-overlay').fadeIn(1300); 12 | }, 700); 13 | 14 | $('.wfg-no-thanks').click(function (e) { 15 | e.preventDefault(); 16 | $('.wfg-popup, .wfg-overlay').fadeOut(500, function () { 17 | $(this).remove(); 18 | }); 19 | }); 20 | 21 | $('.wfg-add-gifts').click(function (e) { 22 | e.preventDefault(); 23 | var form = $(this).closest('form'); 24 | $.ajax({ 25 | type: 'POST', 26 | url: form.attr('action'), 27 | data: form.serialize(), 28 | success: function (response) { 29 | window.location.reload(); 30 | } 31 | }); 32 | }); 33 | 34 | var wfgCheckboxes = $('.wfg-checkbox'); 35 | wfgCheckboxes.click(function () { 36 | if(WFG_SPECIFIC.gifts_allowed <= 0) { 37 | return; 38 | } 39 | 40 | if($('.wfg-checkbox:checked').length >= WFG_SPECIFIC.gifts_allowed) { 41 | wfgCheckboxes.not('.wfg-checkbox:checked').attr('disabled', 'disabled').parent().addClass("opaque"); 42 | } 43 | else { 44 | wfgCheckboxes.removeAttr('disabled').parent().removeClass("opaque"); 45 | } 46 | }) 47 | } 48 | 49 | $('.wfg-fixed-notice-remove').click(function () { 50 | $(this).closest('.wfg-fixed-notice').fadeOut(1000); 51 | }); 52 | }); 53 | 54 | /* use as handler for resize*/ 55 | jQuery(window).resize(wfgAdjustLayout); 56 | /* call function in ready handler*/ 57 | jQuery(document).ready(function () { 58 | wfgAdjustLayout(); 59 | /* Resize ma adjust garnay cod sabai yesma haalnay*/ 60 | }) 61 | 62 | function wfgAdjustLayout() { 63 | jQuery('.wfg-popup').css({ 64 | position: 'fixed', 65 | left: (jQuery(window).width() - jQuery('.wfg-popup').outerWidth()) / 2, 66 | top: (jQuery(window).height() - jQuery('.wfg-popup').outerHeight()) / 2 67 | }); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /lib/helpers/WFG_Common_Helper.class.php: -------------------------------------------------------------------------------- 1 | 10 | * @version 0.0.0 11 | */ 12 | class WFG_Common_Helper 13 | { 14 | /** Current version of the plugin */ 15 | const VERSION = '1.2.3'; 16 | 17 | /** @var string Plugin text domain */ 18 | public static $textDomain = 'woocommerce-multiple-free-gift'; 19 | 20 | /** 21 | * Localize text strings 22 | * 23 | * @since 0.0.0 24 | * @see __() 25 | * 26 | * @return string 27 | */ 28 | public static function translate( $string ) 29 | { 30 | return __( $string, self::$textDomain ); 31 | } 32 | 33 | /** 34 | * Displays error message with WordPress default theme. 35 | * 36 | * @since 0.0.0 37 | * 38 | * @param string $message Message to display 39 | * 40 | * @return void 41 | */ 42 | public static function error_notice( $message ) 43 | { 44 | echo '
'; 45 | echo '

' . $message . '

'; 46 | echo '
'; 47 | } 48 | 49 | /** 50 | * Displays success message with WordPress default theme. 51 | * 52 | * @since 0.0.0 53 | * 54 | * @param string $message Message to display 55 | * 56 | * @return void 57 | */ 58 | public static function success_notice( $message ) 59 | { 60 | echo '
'; 61 | echo '

' . $message . '

'; 62 | echo '
'; 63 | } 64 | 65 | /** 66 | * Displays fixed notice at the top of screen in frontend. 67 | * 68 | * @since 0.0.0 69 | * 70 | * @param string $message Message to display 71 | * 72 | * @return void 73 | */ 74 | public static function fixed_notice( $message ) 75 | { 76 | echo '
'; 77 | echo '

' . $message . 'x

'; 78 | echo '
'; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /templates/default/wfg-default.css: -------------------------------------------------------------------------------- 1 | .wfg-overlay { 2 | width: 100%; 3 | height: 100%; 4 | position: fixed; 5 | background-color: rgba(0, 0, 0, 0.8); 6 | z-index: 9999; 7 | } 8 | 9 | .wfg-popup { 10 | border: 1px solid #ccc; 11 | max-width: 648px; 12 | margin: 0 auto; 13 | position: fixed; 14 | z-index: 110000; 15 | background: #fff; 16 | box-shadow: 0 0 10px 0 #222; 17 | } 18 | 19 | .wfg-popup h2 { 20 | font: 18px sans-serif; 21 | text-align: center; 22 | text-transform: uppercase; 23 | line-height: 24px; 24 | letter-spacing: 0.04em; 25 | padding: 7px 15px; 26 | margin-top: 0; 27 | color: #5d6b6c; 28 | text-shadow: 0 1px rgba(255, 255, 255, 0.7); 29 | background: #f0f1f2; 30 | border-bottom: 1px solid #d1d1d1; 31 | border-radius: 3px 3px 0 0; 32 | background-image: -webkit-linear-gradient(top, #f5f7fd, #e6eaec); 33 | background-image: -moz-linear-gradient(top, #f5f7fd, #e6eaec); 34 | background-image: -o-linear-gradient(top, #f5f7fd, #e6eaec); 35 | background-image: linear-gradient(to bottom, #f5f7fd, #e6eaec); 36 | -webkit-box-shadow: inset 0 1px rgba(255, 255, 255, 0.5), 0 1px rgba(0, 0, 0, 0.03); 37 | box-shadow: inset 0 1px rgba(255, 255, 255, 0.5), 0 1px rgba(0, 0, 0, 0.03); 38 | } 39 | 40 | .wfg-gifts { 41 | padding: 15px; 42 | text-align: center; 43 | } 44 | 45 | .wfg-heading { 46 | cursor: pointer; 47 | } 48 | 49 | .wfg-gift-item { 50 | display: inline-block; 51 | text-align: center; 52 | position: relative; 53 | border: 1px solid #ccc; 54 | cursor: pointer; 55 | width: 150px; 56 | } 57 | 58 | .wfg-gift-item h3 { 59 | background: rgb(233, 237, 239); 60 | padding: 4px 8px; 61 | color: #494848; 62 | font-size: 12px; 63 | white-space: nowrap; 64 | text-overflow: ellipsis; 65 | margin: 0; 66 | overflow: hidden; 67 | } 68 | 69 | .wfg-gift-item .wfg-checkbox { 70 | position: absolute; 71 | top: 2px; 72 | left: 5px; 73 | } 74 | 75 | .wfg-gifts .wfg-actions { 76 | text-align: center; 77 | margin-top: 35px; 78 | } 79 | 80 | .wfg-title > img { 81 | display: inline-block; 82 | } 83 | 84 | .wfg-button:hover { 85 | cursor: pointer; 86 | opacity: 0.9; 87 | } 88 | 89 | .wfg-button:focus { 90 | opacity: 0.9; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /languages/woocommerce-multiple-free-gift.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: \n" 4 | "POT-Creation-Date: \n" 5 | "PO-Revision-Date: \n" 6 | "Last-Translator: Ankit Pokhrel \n" 7 | "Language-Team: \n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=iso-8859-1\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "X-Generator: Poedit 1.5.4\n" 12 | "X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n" 13 | "X-Poedit-Basepath: .\n" 14 | "Language: English\n" 15 | 16 | msgid "WooCommerce Multiple Free Gift" 17 | msgstr "" 18 | 19 | msgid "Woo Free Gift" 20 | msgstr "" 21 | 22 | msgid "Gift Criteria" 23 | msgstr "" 24 | 25 | msgid "General Settings" 26 | msgstr "" 27 | 28 | msgid "Enable/Disable free gift" 29 | msgstr "" 30 | 31 | msgid "Number of gifts allowed" 32 | msgstr "" 33 | 34 | msgid "Select Gift criteria" 35 | msgstr "" 36 | 37 | msgid "None" 38 | msgstr "" 39 | 40 | msgid "Select Gift Products" 41 | msgstr "" 42 | 43 | msgid "Add new gift" 44 | msgstr "" 45 | 46 | msgid "Save" 47 | msgstr "" 48 | 49 | msgid "Create Criteria" 50 | msgstr "" 51 | 52 | msgid "Name this criteria" 53 | msgstr "" 54 | 55 | msgid "Total number of item/s in cart" 56 | msgstr "" 57 | 58 | msgid "Cart total price" 59 | msgstr "" 60 | 61 | msgid "is greater than" 62 | msgstr "" 63 | 64 | msgid "is less than" 65 | msgstr "" 66 | 67 | msgid "is equal to" 68 | msgstr "" 69 | 70 | msgid "is not equal to" 71 | msgstr "" 72 | 73 | msgid "You can add multiple gift criteria in premium version." 74 | msgstr "" 75 | 76 | msgid "Learn more..." 77 | msgstr "" 78 | 79 | msgid "Popup Overlay" 80 | msgstr "" 81 | 82 | msgid "Popup Heading Text" 83 | msgstr "" 84 | 85 | msgid "Add Gift Text" 86 | msgstr "" 87 | 88 | msgid "Cancel Text" 89 | msgstr "" 90 | 91 | msgid "Invalid Gift Condition Text" 92 | msgstr "" 93 | 94 | msgid "Save Changes" 95 | msgstr "" 96 | 97 | msgid "Free Gift Options" 98 | msgstr "" 99 | 100 | msgid "Enable free gift for this product." 101 | msgstr "" 102 | 103 | msgid "Enabling single gift settings will overwrite global settings." 104 | msgstr "" 105 | 106 | msgid "Select single/multiple gift items you want to giveaway for free." 107 | msgstr "" 108 | 109 | msgid "Note that duplicate items are saved only once." 110 | msgstr "" 111 | 112 | msgid "Number of items user are allowed to select as a gift" 113 | msgstr "" 114 | 115 | msgid "Type" 116 | msgstr "" 117 | 118 | msgid "Free Item" 119 | msgstr "" 120 | 121 | msgstr "" 122 | 123 | msgid "The cart contains gift items that are going to be removed, as gift criteria isn't fulfilled. Please reload the page." 124 | msgstr "" 125 | 126 | -------------------------------------------------------------------------------- /templates/default/template-default.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 68 | -------------------------------------------------------------------------------- /languages/woocommerce-multiple-free-gift-pt_BR.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: \n" 4 | "POT-Creation-Date: \n" 5 | "PO-Revision-Date: \n" 6 | "Last-Translator: Paula Bambino \n" 7 | "Language-Team: \n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "X-Generator: Poedit 1.5.4\n" 12 | "X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n" 13 | "X-Poedit-Basepath: .\n" 14 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 15 | "Language: pt_BR\n" 16 | 17 | msgid "WooCommerce Multiple Free Gift" 18 | msgstr "WooCommerce Multiple Free Gift" 19 | 20 | msgid "Woo Free Gift" 21 | msgstr "Presente Grátis Woo" 22 | 23 | msgid "Gift Criteria" 24 | msgstr "Critérios de Presente" 25 | 26 | msgid "General Settings" 27 | msgstr "Configurações Gerais" 28 | 29 | msgid "Enable/Disable free gift" 30 | msgstr "Habilitar/Desabilitar presente grátis" 31 | 32 | msgid "Number of gifts allowed" 33 | msgstr "Número de presentes permitidos" 34 | 35 | msgid "Select Gift criteria" 36 | msgstr "Selecione critério de Presente" 37 | 38 | msgid "None" 39 | msgstr "Nenhum" 40 | 41 | msgid "Select Gift Products" 42 | msgstr "Selecione Produtos de Presente" 43 | 44 | msgid "Add new gift" 45 | msgstr "Adicionar novo presente" 46 | 47 | msgid "Save" 48 | msgstr "Salvar" 49 | 50 | msgid "Create Criteria" 51 | msgstr "Criar Critério" 52 | 53 | msgid "Name this criteria" 54 | msgstr "Nomeie este critério" 55 | 56 | msgid "Total number of item/s in cart" 57 | msgstr "Número total de itens no carrinho" 58 | 59 | msgid "Cart total price" 60 | msgstr "Preço total do carrinho" 61 | 62 | msgid "is greater than" 63 | msgstr "é maior do que" 64 | 65 | msgid "is less than" 66 | msgstr "é menor do que" 67 | 68 | msgid "is equal to" 69 | msgstr "é igual a" 70 | 71 | msgid "is not equal to" 72 | msgstr "não é igual a" 73 | 74 | msgid "You can add multiple gift criteria in premium version." 75 | msgstr "Você pode adicionar múltiplos critérios de presente na versão premium." 76 | 77 | msgid "Learn more..." 78 | msgstr "Saiba mais..." 79 | 80 | msgid "Popup Overlay" 81 | msgstr "Sobreposição Popup" 82 | 83 | msgid "Popup Heading Text" 84 | msgstr "Texto de Cabeçalho do Popup" 85 | 86 | msgid "Add Gift Text" 87 | msgstr "Texto para Adicionar Presente" 88 | 89 | msgid "Cancel Text" 90 | msgstr "Texto para Cancelar" 91 | 92 | msgid "Invalid Gift Condition Text" 93 | msgstr "Texto para Condição Inválida de Presente" 94 | 95 | msgid "Save Changes" 96 | msgstr "Salvar Alterações" 97 | 98 | msgid "Free Gift Options" 99 | msgstr "Opções de Presente Grátis" 100 | 101 | msgid "Enable free gift for this product." 102 | msgstr "Habilitar presente grátis para este produto." 103 | 104 | msgid "Enabling single gift settings will overwrite global settings." 105 | msgstr "" 106 | "Ativar configurações individuais do presente irá sobrescrever as " 107 | "configurações globais." 108 | 109 | msgid "Select single/multiple gift items you want to giveaway for free." 110 | msgstr "" 111 | "Selecione itens de presente único/múltiplo que você quer distribuir " 112 | "gratuitamente." 113 | 114 | msgid "Note that duplicate items are saved only once." 115 | msgstr "Note que itens duplicados serão salvos apenas uma vez." 116 | 117 | msgid "Number of items user are allowed to select as a gift" 118 | msgstr "Número de itens que o usuário pode selecionar como presente" 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WooCommerce Multiple Free Gift 2 | 3 | > This open-source version of WooCommerce Multiple Free Gift project is no longer maintained. 4 | 5 | [![Latest Stable Version](https://img.shields.io/wordpress/plugin/v/woocommerce-multiple-free-gift.svg?style=flat-square)](https://wordpress.org/plugins/woocommerce-multiple-free-gift/) 6 | [![WordPress](https://img.shields.io/wordpress/v/woocommerce-multiple-free-gift.svg?style=flat-square)](https://wordpress.org/plugins/woocommerce-multiple-free-gift/) 7 | [![WordPress Rating](https://img.shields.io/wordpress/plugin/r/woocommerce-multiple-free-gift.svg?style=flat-square)](https://wordpress.org/plugins/woocommerce-multiple-free-gift/) 8 | [![Download](https://img.shields.io/wordpress/plugin/dt/woocommerce-multiple-free-gift.svg?style=flat-square)](https://wordpress.org/plugins/woocommerce-multiple-free-gift) 9 | 10 | _WooCommerce giveaway made easy._ 11 | 12 | ### Overview 13 | Gift giving is one of the best ways for marketing. This way creates good vibes and your customers will come back more often. 14 | 15 | WooCommerce Multiple Free Gift is a WordPress WooCommerce plugin that makes gift management easy for your WooCommerce site. The plugin helps you offer free products or gifts to your customer when they purchase products at your store. WooCommerce Multiple Free Gift plugin - gives you an edge by allowing you to write your own gift conditions which gives you great control on how you want to provide gifts to your customer. 16 | 17 | ### Installation 18 | 1. Unzip and upload the `woocommerce-multiple-free-gift-plugin` directory to the plugin directory (`/wp-content/plugins/`) or install it from `Plugins->Add New->Upload`. 19 | 2. Activate the plugin through the `Plugins` menu in WordPress. 20 | 3. That's all you need to do. You will notice Woo Free Gift settings page in the admin. 21 | 22 | ### Bower 23 | ```shell 24 | bower install woocommerce-multiple-free-gift 25 | ``` 26 | 27 | ## Features 28 | 1. Gift based on single or particular product 29 | - Provide gift when only one item is added to the cart. 30 | - BOGO (Buy one get one) 31 | - Buy X get Y free 32 | - Buy X get many free gifts (Y,Z...free) 33 | 2. Choose number of gifts allowed 34 | - Allow 1 or N number of gifts without any problem. 35 | 3. Gift Criteria 36 | - Allow gifts based on total items in cart and total cart price. 37 | - Purchase over X USD get X free 38 | - Purchase over X USD get many free gifts (X, Y... free) 39 | - Purchase N number of items get X free 40 | - Purchase N number of items get many free gifts (X, Y... free) 41 | 4. Global gifts 42 | - Give gift for anything added in the cart. 43 | 5. Custom promotion message 44 | 6. Customization and translation ready 45 | 7. [Loco Translate](https://wordpress.org/plugins/loco-translate/) compatible 46 | 8. [Aelia Currency Switcher](https://aelia.co/shop/currency-switcher-woocommerce/) compatible 47 | 48 | ### Compatiblity 49 | PHP v5.4.0+ 50 | WordPress v3.8+ 51 | WooCommerce v2.5+ 52 | All modern browsers and IE9+ 53 | 54 | ### Resources 55 | * [Wiki](https://github.com/ankitpokhrel/WooCommerce-Multiple-Free-Gift/wiki) 56 | 57 | ### WooCommerce Multiple Free Gift PRO 58 | A premium version of this plugin is also available. Users looking for more timely/in-depth support and extended features are encouraged to check out [WooCommerce Multiple Free Gift PRO](https://lilmonkee.com/product/woocommerce-multiple-free-gift-plugin-pro/). 59 | 60 | ### Questions about this project? 61 | Please feel free to report any bug found. Pull requests, issues, and plugin recommendations are more than welcome! 62 | -------------------------------------------------------------------------------- /lib/helpers/WFG_Settings_Helper.class.php: -------------------------------------------------------------------------------- 1 | 9 | * @version 0.0.0 10 | * @static 11 | */ 12 | class WFG_Settings_Helper 13 | { 14 | /* Option key prefix */ 15 | const PREFIX = '_wfg_'; 16 | 17 | /* Flag to check if the setting is already fetched */ 18 | private static $__initialized = false; 19 | 20 | /* Hold all settings */ 21 | protected static $_settings = []; 22 | 23 | /** 24 | * Prevent the instantiation of class using 25 | * private constructor 26 | */ 27 | private function __construct() 28 | { 29 | } 30 | 31 | /** 32 | * Fetch settings from database if not already fetched. 33 | * 34 | * @access protected 35 | * @static 36 | * @see get_option() 37 | * 38 | * @return void 39 | */ 40 | protected static function __init() 41 | { 42 | if ( self::$__initialized ) { 43 | return; 44 | } 45 | 46 | //fetch settings 47 | $settings['global_settings'] = get_option( self::PREFIX . 'global_settings' ); 48 | $settings['global_options'][ self::PREFIX . 'global_enabled' ] = get_option( self::PREFIX . 'global_enabled' ); 49 | $settings['global_options'][ self::PREFIX . 'popup_overlay' ] = get_option( self::PREFIX . 'popup_overlay' ); 50 | $settings['global_options'][ self::PREFIX . 'popup_heading' ] = get_option( self::PREFIX . 'popup_heading' ); 51 | $settings['global_options'][ self::PREFIX . 'invalid_condition_text' ] = get_option( self::PREFIX . 'invalid_condition_text' ); 52 | $settings['global_options'][ self::PREFIX . 'popup_add_gift_text' ] = get_option( self::PREFIX . 'popup_add_gift_text' ); 53 | $settings['global_options'][ self::PREFIX . 'popup_cancel_text' ] = get_option( self::PREFIX . 'popup_cancel_text' ); 54 | $settings['criteria'] = get_option( self::PREFIX . 'criteria' ); 55 | 56 | if ( ! empty( $settings ) ) { 57 | self::$_settings = $settings; 58 | } 59 | 60 | self::$__initialized = true; 61 | } 62 | 63 | /** 64 | * Forcefully reinitialze settings 65 | * 66 | * @access public 67 | * @static 68 | * 69 | * @return void 70 | */ 71 | public static function force_init() 72 | { 73 | self::$__initialized = false; 74 | } 75 | 76 | /** 77 | * Check if setting is available. 78 | * 79 | * @access public 80 | * @static 81 | * 82 | * @return boolean 83 | */ 84 | public static function has_settings() 85 | { 86 | self::__init(); 87 | 88 | return ! empty( self::$_settings ); 89 | } 90 | 91 | /** 92 | * Check settings array and return setting if available. 93 | * 94 | * @access public 95 | * @static 96 | * 97 | * @return string|boolean 98 | */ 99 | public static function get( $key, $bool = false, $type = 'global_settings', $prefix = true ) 100 | { 101 | self::__init(); 102 | 103 | if ( $prefix ) { 104 | $key = self::PREFIX . $key; 105 | } 106 | 107 | if ( empty( $key ) && isset( self::$_settings[ $type ] ) ) { 108 | return self::$_settings[ $type ]; 109 | } 110 | 111 | if ( isset( self::$_settings[ $type ][ $key ] ) ) { 112 | return $bool ? (bool) self::$_settings[ $type ][ $key ] : self::$_settings[ $type ][ $key ]; 113 | } 114 | 115 | return false; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /lib/helpers/WFG_Criteria_Helper.class.php: -------------------------------------------------------------------------------- 1 | 10 | * @version 0.0.0 11 | */ 12 | class WFG_Criteria_Helper 13 | { 14 | /** 15 | * Parse user defined expression to valid boolean 16 | * 17 | * @since 0.0.0 18 | * @access public 19 | * @static 20 | * 21 | * @param string $slug Slug of the criteria 22 | * 23 | * @return boolean 24 | */ 25 | public static function parse_criteria( $slug ) 26 | { 27 | // If the slug is empty then, it satisfies every condition 28 | if ( empty( $slug ) ) { 29 | return true; 30 | } 31 | 32 | $conditions = self::arrange_criteria( $slug ); 33 | if ( empty( $conditions ) ) { 34 | return false; 35 | } 36 | 37 | $flag = false; 38 | foreach ( $conditions as $condition ) { 39 | $real_value = self::get_real_value( $condition[0] ); 40 | switch ( $condition[1] ) { 41 | case '<': 42 | $flag = $real_value < $condition[2]; 43 | break; 44 | 45 | case '>': 46 | $flag = $real_value > $condition[2]; 47 | break; 48 | 49 | case '==': 50 | $flag = $real_value == $condition[2]; 51 | break; 52 | 53 | case '!=': 54 | $flag = $real_value != $condition[2]; 55 | break; 56 | } 57 | 58 | return $flag; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | /** 65 | * Get real values from data available in cart 66 | * 67 | * @since 0.0.0 68 | * @access public 69 | * @static 70 | * 71 | * @param string $param Key 72 | * 73 | * @return integer|boolean 74 | */ 75 | public static function get_real_value( $param ) 76 | { 77 | switch ( $param ) { 78 | case 'num_products': 79 | return WFG_Product_Helper::get_main_product_quantity_count(); 80 | 81 | case 'total_price': 82 | return WC()->cart->cart_contents_total; 83 | } 84 | } 85 | 86 | /** 87 | * Filters and returns condition array 88 | * 89 | * @since 0.0.0 90 | * @access public 91 | * @static 92 | * 93 | * @param string $slug Slug of the criteria 94 | * 95 | * @return array 96 | */ 97 | public static function arrange_criteria( $slug ) 98 | { 99 | $criteria = self::get_criteria( $slug ); 100 | 101 | $filtered_conditions = []; 102 | if ( ! empty( $criteria ) ) { 103 | /** @var array $conditions */ 104 | $conditions = $criteria; 105 | 106 | unset( $conditions['name'] ); 107 | unset( $conditions['slug'] ); 108 | 109 | foreach ( $conditions as $condition ) { 110 | $filtered_conditions[] = $condition; 111 | } 112 | } 113 | 114 | return $filtered_conditions; 115 | } 116 | 117 | /** 118 | * Get criteria from slug 119 | * 120 | * @since 0.0.0 121 | * @access public 122 | * @static 123 | * 124 | * @param string $slug Slug of the criteria 125 | * 126 | * @return array|boolean 127 | */ 128 | public static function get_criteria( $slug ) 129 | { 130 | /** @var array $all_criteria */ 131 | $all_criteria = WFG_Settings_Helper::get( '', false, 'criteria', false ); 132 | if ( empty( $all_criteria ) ) { 133 | return false; 134 | } 135 | 136 | foreach ( $all_criteria as $criteria ) { 137 | if ( $criteria['slug'] === $slug ) { 138 | return $criteria; 139 | } 140 | } 141 | 142 | return false; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/Woocommerce_Multiple_Free_Gift.class.php: -------------------------------------------------------------------------------- 1 | 9 | * @version 1.2.0 10 | */ 11 | class Woocommerce_Multiple_Free_Gift 12 | { 13 | /** 14 | * Constructor 15 | * 16 | * @see add_action() 17 | * @since 0.0.0 18 | */ 19 | public function __construct() 20 | { 21 | //check if woocommerce plugin is installed and activated 22 | add_action( 'plugins_loaded', [ $this, 'wfg_validate_installation' ] ); 23 | 24 | //load plugin textdomain 25 | add_action( 'plugins_loaded', [ $this, 'load_plugin_textdomain' ] ); 26 | 27 | //enqueue necessary scripts and styles 28 | add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_global_scripts' ] ); 29 | 30 | //add action links 31 | add_filter( 'plugin_action_links_' . PLUGIN_BASE, [ $this, 'wfg_action_links' ] ); 32 | } 33 | 34 | /** 35 | * Plugin activation hook 36 | * 37 | * @access public 38 | * @since 0.0.0 39 | * 40 | * @see add_option() 41 | * 42 | * @return void 43 | */ 44 | public static function wfg_activate() 45 | { 46 | update_option( '_wfg_popup_overlay', 1 ); 47 | update_option( '_wfg_popup_heading', WFG_Common_Helper::translate( 'Choose your free gift' ) ); 48 | update_option( '_wfg_invalid_condition_text', 49 | WFG_Common_Helper::translate( 'Gift items removed as gift criteria isn\'t fulfilled' ) ); 50 | update_option( '_wfg_popup_add_gift_text', WFG_Common_Helper::translate( 'Add Gifts' ) ); 51 | update_option( '_wfg_popup_cancel_text', WFG_Common_Helper::translate( 'No Thanks' ) ); 52 | } 53 | 54 | /** 55 | * Enqueue required global styles and scirpts 56 | * 57 | * @access public 58 | * @since 0.0.0 59 | * 60 | * @see wp_enqueue_style() 61 | * 62 | * @return void 63 | */ 64 | public function enqueue_global_scripts() 65 | { 66 | //enqueue styles 67 | wp_enqueue_style( 'wfg-styles', plugins_url( '/css/wfg-styles.css', dirname( __FILE__ ) ) ); 68 | 69 | //enqueue scripts 70 | wp_enqueue_script( 'wfg-scripts', plugins_url( '/js/wfg-scripts.js', dirname( __FILE__ ) ), [ 'jquery' ] ); 71 | } 72 | 73 | /** 74 | * Add notice if WooCommerce plugin is not activated 75 | * 76 | * @since 0.0.0 77 | * @access public 78 | * 79 | * @see add_action() 80 | * 81 | * @return void 82 | */ 83 | public function wfg_validate_installation() 84 | { 85 | if ( ! class_exists( 'WooCommerce' ) ) { 86 | add_action( 'admin_notices', [ $this, 'wfg_plugin_required_notice' ] ); 87 | } 88 | } 89 | 90 | /** 91 | * Error notice: WooCommerce Plugin is required for this plugin to work 92 | * 93 | * @access public 94 | * @since 0.0.0 95 | * 96 | * @return void 97 | */ 98 | public function wfg_plugin_required_notice() 99 | { 100 | WFG_Common_Helper::error_notice( 101 | WFG_Common_Helper::translate( 102 | 'WooCommerce Free Gift plugin requires 103 | WooCommerce 104 | plugin to work. Please make sure that WooCommerce is installed and activated.' 105 | ) 106 | ); 107 | } 108 | 109 | /** 110 | * Add premium version link 111 | * 112 | * @access public 113 | * @since 1.0.0 114 | * @action plugin_action_links 115 | * 116 | * @param array $links Action links 117 | * 118 | * @return array 119 | */ 120 | public function wfg_action_links( $links ) 121 | { 122 | $wfg_links = [ 123 | 'Upgrade to Premium', 124 | ]; 125 | 126 | return array_merge( $links, $wfg_links ); 127 | } 128 | 129 | /** 130 | * Load the plugin's textdomain hooked to 'plugins_loaded'. 131 | * 132 | * @since 0.0.0 133 | * @access public 134 | * 135 | * @see load_plugin_textdomain() 136 | * @see plugin_basename() 137 | * @action plugins_loaded 138 | * 139 | * @return void 140 | */ 141 | public function load_plugin_textdomain() 142 | { 143 | load_plugin_textdomain( 144 | WFG_Common_Helper::$textDomain, 145 | false, 146 | dirname( plugin_basename( __FILE__ ) ) . '/../languages/' 147 | ); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | # .scrutinizer.yml 2 | filter: 3 | excluded_paths: 4 | - 'tests/*' 5 | - 'examples/*' 6 | tools: 7 | php_mess_detector: 8 | config: 9 | code_size_rules: { cyclomatic_complexity: true, npath_complexity: true, excessive_method_length: true, excessive_class_length: true, excessive_parameter_list: true, excessive_public_count: true, too_many_fields: true, too_many_methods: true, excessive_class_complexity: true } 10 | design_rules: { number_of_class_children: true, depth_of_inheritance: true, coupling_between_objects: true } 11 | unused_code_rules: { unused_local_variable: true, unused_private_method: true, unused_formal_parameter: true } 12 | naming_rules: { short_variable: true, long_variable: true, short_method: true, boolean_method_name: true } 13 | controversial_rules: { camel_case_class_name: false, camel_case_property_name: false, camel_case_method_name: false, camel_case_parameter_name: false, camel_case_variable_name: false } 14 | php_cs_fixer: 15 | config: 16 | level: all 17 | fixers: { unused_use: true, phpdoc_params: true, braces: true, php_closing_tag: true } 18 | php_analyzer: 19 | config: 20 | suspicious_code: { enabled: true, overriding_parameter: true, overriding_closure_use: true, parameter_closure_use_conflict: true, parameter_multiple_times: true, non_existent_class_in_instanceof_check: true, non_existent_class_in_catch_clause: true, assignment_of_null_return: true, non_commented_switch_fallthrough: true, non_commented_empty_catch_block: true, overriding_private_members: true, use_statement_alias_conflict: true, precedence_in_condition_assignment: true } 21 | verify_php_doc_comments: { enabled: true, parameters: true, return: true, suggest_more_specific_types: true, ask_for_return_if_not_inferrable: true, ask_for_param_type_annotation: true } 22 | loops_must_use_braces: { enabled: true } 23 | simplify_boolean_return: { enabled: true } 24 | phpunit_checks: { enabled: true } 25 | reflection_fixes: { enabled: true } 26 | use_statement_fixes: { enabled: true, order_alphabetically: true, remove_unused: true, preserve_multiple: false, preserve_blanklines: false } 27 | parameter_reference_check: { enabled: false } 28 | checkstyle: { enabled: false, no_trailing_whitespace: true, naming: { enabled: true, local_variable: '^[a-z][a-zA-Z0-9]*$', abstract_class_name: ^Abstract|Factory$, utility_class_name: 'Utils?$', constant_name: '^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$', property_name: '^[a-z][a-zA-Z0-9]*$', method_name: '^(?:[a-z]|__)[a-zA-Z0-9]*$', parameter_name: '^[a-z][a-zA-Z0-9]*$', interface_name: '^[A-Z][a-zA-Z0-9]*Interface$', type_name: '^[A-Z][a-zA-Z0-9]*$', exception_name: '^[A-Z][a-zA-Z0-9]*Exception$', isser_method_name: '^(?:is|has|should|may|supports)' } } 29 | unreachable_code: { enabled: false } 30 | check_access_control: { enabled: false } 31 | typo_checks: { enabled: false } 32 | check_variables: { enabled: false } 33 | check_calls: { enabled: true, too_many_arguments: true, missing_argument: true, argument_type_checks: lenient } 34 | dead_assignments: { enabled: false } 35 | check_usage_context: { enabled: true, foreach: { value_as_reference: true, traversable: true } } 36 | reflection_checks: { enabled: false } 37 | precedence_checks: { enabled: true, assignment_in_condition: true, comparison_of_bit_result: true } 38 | basic_semantic_checks: { enabled: false } 39 | unused_code: { enabled: false } 40 | deprecation_checks: { enabled: false } 41 | useless_function_calls: { enabled: false } 42 | metrics_lack_of_cohesion_methods: { enabled: false } 43 | metrics_coupling: { enabled: true, stable_code: { namespace_prefixes: { }, classes: { } } } 44 | doctrine_parameter_binding: { enabled: false } 45 | doctrine_entity_manager_injection: { enabled: false } 46 | symfony_request_injection: { enabled: false } 47 | doc_comment_fixes: { enabled: false } 48 | php_code_sniffer: 49 | config: 50 | standard: WordPress 51 | sniffs: { wordpress: { arrays: { array_declaration_sniff: true }, classes: { valid_class_name_sniff: true }, files: { file_name_sniff: true }, formatting: { multiple_statement_alignment_sniff: true }, functions: { function_call_signature_sniff: true, function_declaration_argument_spacing_sniff: true }, naming_conventions: { valid_function_name_sniff: true }, objects: { object_instantiation_sniff: true }, php: { discouraged_functions_sniff: true }, strings: { double_quote_usage_sniff: true }, white_space: { control_structure_spacing_sniff: true, operator_spacing_sniff: true, php_indent_sniff: true }, xss: { escape_output_sniff: true } } } 52 | sensiolabs_security_checker: true 53 | php_loc: true 54 | php_pdepend: true 55 | php_sim: true 56 | php_changetracking: true -------------------------------------------------------------------------------- /lib/admin/pages/gift_criteria.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | '; 6 | ?> 7 |
8 |
9 |

10 |
11 |
12 |
13 |

14 | 15 |

16 |
17 |
18 |
19 |

20 | 21 |
22 |
23 | 32 |
33 | 37 |
38 | 46 | 60 | 63 |
64 |
65 |
66 |
67 | 68 |
69 |

70 | 71 | 73 |

74 |
75 |
76 |
77 |
78 |   79 | 81 | -------------------------------------------------------------------------------- /lib/admin/pages/general_settings.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | '; 6 | ?> 7 |
8 |
9 |

10 |
11 |
12 |
13 |

14 | 15 |

16 |
17 |
18 | 19 | 20 | 21 | 22 | 25 | 40 | 41 | 42 | 45 | 55 | 56 | 57 | 60 | 70 | 71 | 72 | 75 | 85 | 86 | 87 | 90 | 100 | 101 | 102 |
23 | 24 | 26 | 33 | 39 |
43 | 44 | 46 | 52 | 54 |
58 | 59 | 61 | 67 | 69 |
73 | 74 | 76 | 82 | 84 |
88 | 89 | 91 | 97 | 99 |
103 |

104 | 105 | 107 |

108 |
109 |
110 | -------------------------------------------------------------------------------- /lib/admin/WFG_Single_Gift.class.php: -------------------------------------------------------------------------------- 1 | 9 | * @version 0.0.0 10 | */ 11 | class WFG_Single_Gift 12 | { 13 | /** 14 | * Constructor 15 | * 16 | * @see add_action() 17 | * @since 0.0.0 18 | */ 19 | public function __construct() 20 | { 21 | /* WooCommerce panel tab hooks */ 22 | add_action( 'woocommerce_product_write_panel_tabs', [ $this, 'create_admin_free_gift_tab' ] ); 23 | add_action( 'woocommerce_product_write_panels', [ $this, 'wfg_tab_contents' ] ); 24 | add_action( 'woocommerce_process_product_meta', [ $this, 'process_wfg_tab' ] ); 25 | } 26 | 27 | /** 28 | * Free gift option tab in product add/edit 29 | * 30 | * @access public 31 | * @since 0.0.0 32 | * 33 | * @return void 34 | */ 35 | public function create_admin_free_gift_tab() 36 | { 37 | ?> 38 |
  • 39 | 40 | 41 | 42 |
  • 43 | 61 |
    62 |
    63 |

    64 | > 66 | 69 | 76 |

    77 |
    78 |

    79 | 80 | 87 |

    88 |
    89 | 90 |
    91 | 92 |

    93 | 96 | 98 | 105 |

    106 |
    107 | "; 124 | if ( ! empty( $wfg_products ) ) { 125 | $product_list = WFG_Product_Helper::get_products( [ 126 | 'post__in' => $wfg_products, 127 | 'post__not_in' => [ $post_id ], 128 | ], - 1 ); 129 | $products = $product_list->get_posts(); 130 | if ( ! empty( $products ) ) { 131 | foreach ( $products as $product ) { 132 | $product_id = $product->ID; 133 | $selected = in_array( $product_id, $wfg_products ); 134 | $html .= ""; 135 | } 136 | } 137 | } 138 | $html .= ''; 139 | 140 | return $html; 141 | } 142 | 143 | /** 144 | * Save free gift tab contents 145 | * 146 | * @since 0.0.0 147 | * @access public 148 | * 149 | * @param integer $post_id Current post id 150 | * 151 | * @return void 152 | */ 153 | public function process_wfg_tab( $post_id ) 154 | { 155 | $wfg_enabled = ( isset( $_POST['wfg_single_gift_enabled'] ) && $_POST['wfg_single_gift_enabled'] ) ? 1 : 0; 156 | $wfg_gifts_allowed = ( isset( $_POST['wfg_single_gift_allowed'] ) && $_POST['wfg_single_gift_allowed'] >= 0 ) ? $_POST['wfg_single_gift_allowed'] : 1; 157 | if ( ! (bool) $wfg_enabled ) { 158 | delete_post_meta( $post_id, '_wfg_single_gift_enabled' ); 159 | } else { 160 | update_post_meta( $post_id, '_wfg_single_gift_enabled', $wfg_enabled ); 161 | } 162 | 163 | update_post_meta( $post_id, '_wfg_single_gift_allowed', $wfg_gifts_allowed ); 164 | if ( ! empty( $_POST['_wfg_single_gift_products'] ) ) { 165 | $products = array_unique( $_POST['_wfg_single_gift_products'] ); 166 | update_post_meta( $post_id, '_wfg_single_gift_products', $products ); 167 | } else { 168 | delete_post_meta( $post_id, '_wfg_single_gift_products' ); 169 | } 170 | } 171 | 172 | } 173 | 174 | /* initialize */ 175 | new WFG_Single_Gift(); 176 | -------------------------------------------------------------------------------- /lib/admin/pages/main_menu_page.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | '; 6 | ?> 7 |
    8 |
    9 |

    10 | 11 |

    12 |
    13 |
    14 | 15 |
    16 |
    17 |

    18 | 19 | have_posts() ): ?> 20 |
    21 |

    22 | 28 | 29 | 35 |

    36 |
    37 | 38 |
    39 | 43 |
    44 |
    45 |

    46 | 49 | 52 |

    53 |

    54 | 57 | 58 | 73 |

    74 |
    75 |
    76 |

    77 | 78 |

    79 |
    80 | 105 |
    106 |
    107 |
    108 | 109 | 110 | 112 | 113 |
    114 |

    115 | ' . WFG_Common_Helper::translate( 'products' ) . ''; 121 | $message .= ' '; 122 | $message .= WFG_Common_Helper::translate( 'first.' ); 123 | echo $message; 124 | ?> 125 |

    126 |
    127 | 128 |
    129 |
    130 |
    131 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === WooCommerce Multiple Free Gift === 2 | Contributors: ankitpokhrel, ncreep 3 | Tags: woocommerce, woocommerce free gift, woocommerce gift management, woocommerce multiple gifts, woocommerce multiple free gifts, woocommerce gift manager, woocommerce plugin, woocommerce freebies, woocommerce prizes 4 | Requires at least: 3.8 5 | Tested up to: 4.7.5 6 | WC requires at least: 2.3 7 | WC tested up to: 2.4.6 8 | Stable tag: 1.2.3 9 | License: GPLv2 or later 10 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 11 | 12 | WooCommerce giveaway made easy. Best way to offer freebies, gifts or prizes. 13 | 14 | == Description == 15 | Gift giving is one of the best ways for marketing. This way creates good vibes and your customers will come back more often. 16 | 17 | WooCommerce Multiple Free Gift Plugin is a WordPress WooCommerce plugin that makes gift management easy for your woocommerce site. The plugin helps you offer free products or gifts to your customer when they purchase products at your store. WooCommerce Multiple Free Gift plugin - gives you an edge by allowing you to write your own gift conditions which gives you great control on how you want to provide gifts to your customer. 18 | 19 | > **A NOTE ABOUT SUPPORT:** We’re here to help troubleshoot bugs, but please don't set expectations early as the support forums at WordPress.org are only checked every two days. 20 | 21 | > Users looking for more timely/in-depth support and extended features are encouraged to check out [WooCommerce Multiple Free Gift Plugin PRO](http://ankitpokhrel.com/explore/woocommerce-multiple-free-gift-plugin-pro/ "WooCommerce Multiple Free Gift PRO"). 22 | 23 | > **FEATURES OF PRO VERSION** 24 | – All features of basic version. 25 | – Write your own rules. 26 | – Rule can be from very simple to highly complex. 27 | – You can write multiple fallback condition for gifts. 28 | – If condition A isn’t fulfilled the plugin will check for next condition and so on. 29 | - Automatic gift addition. 30 | - Multiple themes. 31 | - Option to exclude sales and tax amount from calculation. 32 | – Priority support. 33 | – Access to more advance features and future updates for free. 34 | 35 | > [Learn more »](http://ankitpokhrel.com/explore/woocommerce-multiple-free-gift-plugin-pro/ "WooCommerce Multiple Free Gift PRO") 36 | 37 | **Overview** 38 | WooCommerce Multiple Free Gift Plugin enables the option to provide gifts to your customer. Gift Giving is one of the best ways for marketing. This way creates good vibes and your Customers will come back more often. With WooCommerce Multiple Free Gift Plugin you can provide gifts to your customer based on single or multiple products. 39 | 40 | **Features** 41 | 42 | 1. Gift based on single or particular product 43 | - Provide gift when only one item is added to the cart. 44 | - BOGO (Buy one get one) 45 | - Buy X get Y free 46 | - Buy X get many free gifts (Y,Z...free) 47 | 2. Choose number of gifts allowed 48 | - Allow 1 or N number of gifts without any problem. 49 | 3. Gift Criteria 50 | - Allow gifts based on total items in cart and total cart price. 51 | - Purchase over X USD get X free 52 | - Purchase over X USD get many free gifts (X, Y... free) 53 | - Purchase N number of items get X free 54 | - Purchase N number of items get many free gifts (X, Y... free) 55 | 4. Global gifts 56 | - Give gift for anything added in the cart. 57 | 5. Custom promotion message 58 | 6. Customization and translation ready 59 | 7. Loco translate compatible 60 | 8. Aelia Currency Converter compatible 61 | 9. Tested with over half million products 62 | 63 | **How it works?** 64 | 1. After successful plugin activation go to `Woo Free Gift` settings page or product edit page. 65 | 2. If you are in `Woo Free Gift` page, go to gift criteria first to add your condition, in main settings enable free gift and choose number of gifts allowed and gifts to show. 66 | 3. If you are in product edit page, scroll down to products data section and go to `Free Gift Options` tab. 67 | 4. You can enable free gift for a particular product from this page. Enable free gift and choose number of gifts allowed and gifts to show. 68 | 5. Save. 69 | 6. At frontend, add some products and go to cart page. You will notice a popup with gift products to add. 70 | 71 | **Contribute** 72 | If you'd like to check out the code and contribute, join us on [Github](https://github.com/ankitpokhrel/WooCommerce-Multiple-Free-Gift/ "View this plugin in github"). Pull requests, issues, and plugin recommendations are more than welcome! 73 | 74 | **Want more? Watch the demo of PRO version** 75 | https://www.youtube.com/watch?v=z5ZnYj1TH_8 76 | 77 | == Installation == 78 | 79 | 1. Unzip and upload the `woocommerce-multiple-free-gift-plugin` directory to the plugin directory (`/wp-content/plugins/`) or install it from `Plugins->Add New->Upload`. 80 | 2. Activate the plugin through the `Plugins` menu in WordPress. 81 | 3. That's all you need to do. You will notice Woo Free Gift settings page in the admin. 82 | 83 | == Frequently Asked Questions == 84 | = 1. How many gifts can I add? = 85 | You can add as many gifts as you want. There is no limit to this. 86 | 87 | = 2. How many gifts are allowed to select? = 88 | You can define how many gifts you want to allow users to select from admin settings page. Users are allowed to select 1 gift by default. 89 | 90 | = 3. Can I provide gifts based on total quantity or price? = 91 | Yes, you can provide gifts based on total quantity or price. PRO version allows you to write your own rules. Rules can be very simple to highly complex. 92 | 93 | = 4. Other problems or questions? = 94 | Other problems? Don't forget to check the [blog](http://ankitpokhrel.com/explore/) or contact through support forum. 95 | 96 | Please use [support forum](http://wordpress.org/support/plugin/woocommerce-free-gift-plugin) first if you have any question or queries about the project. 97 | If you don't receive any help in support forum then you can directly contact me at `info [at] ankitpokhrel.com.np`. Please at least wait for 48hrs before sending another request. 98 | 99 | Please feel free to report any bug found to `info [at] ankitpokhrel.com.np`. 100 | 101 | == Screenshots == 102 | 1. Gift criteria 103 | 2. Global gift page. 104 | 3. Single gift page. 105 | 4. General settings page. 106 | 5. Gifts shown in frontend. 107 | 108 | == Changelog == 109 | 110 | = 1.2.3 = 111 | * Fix wcv3 gift loop issue (Fixes #22) 112 | * Fix downloadable product metadata issue 113 | * Some code formatting and minor fixes 114 | 115 | = 1.2.0 = 116 | * Remove one-item restriction. (PR #21, thanks to David Marín) 117 | * Support bundles plugin. (PR #21, thanks to David Marín) 118 | * Checks cart items, emitting an error notice if gift criteria is not met. (PR #20, thanks to David Marín) 119 | * Various cleanups and refactoring 120 | 121 | = 1.1.5 = 122 | * Fix virtual product issue. 123 | 124 | = 1.1.4 = 125 | * Fix pop up design issue in mobile device. 126 | 127 | = 1.1.3 = 128 | * Respect existing products marked with "Sold individually", so it is not possible to add multiple items of those products. PR #16 129 | * Remove deprecated add_object_page. 130 | * Fix message box design. 131 | 132 | = 1.1.0 = 133 | * Now works with large number of products (fixes issue #1). 134 | * Tested with over half million products. 135 | * Compatible with Aelia Currency Switcher plugin (thanks to Diego Zanella). 136 | * Remove the evil eval. 137 | * Brazilian Portugese translation (thanks to Paula Bambino). 138 | * Design improvements. 139 | * Special thanks to Cory Jeffries for his contributions. 140 | 141 | = 1.0.1 = 142 | * Fix issue with remove gift button on first use 143 | * Fix plugin text domain issue 144 | * Enhancement: Loco translate compatible 145 | * Fix info message design when no product is present 146 | 147 | = 1.0.0 = 148 | * Gift criteria based on total quantity and price. 149 | 150 | = 0.0.0 = 151 | * Initial release. 152 | 153 | == Upgrade Notice == 154 | = 1.1.4 = 155 | * This version has some minor design fixes. 156 | 157 | = 1.1.3 = 158 | * This version has some minor fixes. 159 | 160 | = 1.1.0 = 161 | * This is a major update. Upgrade with caution. 162 | 163 | = 1.0.1 = 164 | * One of the library file name is changed which may cause some error. It would be better to remove previous files completely and add new one. 165 | 166 | = 1.0.0 = 167 | * You can now add a single gift criteria based on total quantity and price. 168 | 169 | = 0.0.0 = 170 | * First release. 171 | -------------------------------------------------------------------------------- /lib/admin/WFG_Admin.class.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class WFG_Admin 12 | { 13 | /** 14 | * Constructor 15 | * 16 | * @access public 17 | * @since 0.0.0 18 | * 19 | * @see add_action() 20 | */ 21 | public function __construct() 22 | { 23 | add_action( 'admin_menu', [ $this, 'main_menu' ] ); 24 | 25 | //enqueue necessary scripts and styles 26 | add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] ); 27 | 28 | //register ajax call to fetch products 29 | add_action( 'wp_ajax_product_list_callback', [ $this, 'ajax_product_list_callback' ] ); 30 | } 31 | 32 | /** 33 | * Add main menu page 34 | * 35 | * @access public 36 | * @see add_menu_page() 37 | * 38 | * @return void 39 | */ 40 | public function main_menu() 41 | { 42 | add_menu_page( 43 | WFG_Common_Helper::translate( 'WooCommerce Multiple Free Gift' ), 44 | WFG_Common_Helper::translate( 'Woo Free Gift' ), 45 | 'manage_options', 46 | 'woocommerce-multiple-free-gift', 47 | [ $this, 'main_menu_template' ], 48 | 'dashicons-cart' 49 | ); 50 | 51 | add_submenu_page( 52 | 'woocommerce-multiple-free-gift', 53 | WFG_Common_Helper::translate( 'Gift Criteria' ) . ' - ' . 54 | WFG_Common_Helper::translate( 'WooCommerce Multiple Free Gift' ), 55 | WFG_Common_Helper::translate( 'Gift Criteria' ), 56 | 'manage_options', 57 | 'woocommerce-multiple-free-gift-criteria', 58 | [ $this, 'wfg_criteria_template' ] 59 | ); 60 | 61 | add_submenu_page( 62 | 'woocommerce-multiple-free-gift', 63 | WFG_Common_Helper::translate( 'General Settings' ) . ' - ' . 64 | WFG_Common_Helper::translate( 'WooCommerce Multiple Free Gift' ), 65 | WFG_Common_Helper::translate( 'General Settings' ), 66 | 'manage_options', 67 | 'woocommerce-multiple-free-gift-settings', 68 | [ $this, 'wfg_general_settings' ] 69 | ); 70 | } 71 | 72 | /** 73 | * Enqueue required styles and scirpts for admin 74 | * 75 | * @access public 76 | * @since 0.0.0 77 | * 78 | * @see wp_enqueue_style() 79 | * 80 | * @return void 81 | */ 82 | public function enqueue_admin_scripts() 83 | { 84 | //enqueue styles 85 | wp_enqueue_style( 'wmfg-admin-styles', plugins_url( '/admin/css/wfg-admin-styles.css', dirname( __FILE__ ) ) ); 86 | wp_enqueue_style( 'wp-jquery-ui-dialog' ); 87 | wp_enqueue_style( 88 | 'wfg-selectize', 89 | plugins_url( '/admin/js/plugins/selectize/selectize.css', dirname( __FILE__ ) ) 90 | ); 91 | 92 | //enqueue scripts 93 | wp_enqueue_script( 94 | 'wmfg-admin-scripts', 95 | plugins_url( '/admin/js/wfg-admin-scripts.js', dirname( __FILE__ ) ), 96 | [ 'jquery', 'jquery-ui-dialog' ] 97 | ); 98 | 99 | wp_enqueue_script( 100 | 'wfg-selectize-lib', 101 | plugins_url( '/admin/js/plugins/selectize/selectize.min.js', dirname( __FILE__ ) ), 102 | [ 'jquery' ] 103 | ); 104 | 105 | wp_enqueue_script( 'jquery-ui-dialog', false, [ 'jquery' ] ); 106 | wp_enqueue_script( 'jquery-ui-sortable', false, [ 'jquery' ] ); 107 | 108 | wp_localize_script( 109 | 'wmfg-admin-scripts', 110 | 'WMFG_SPECIFIC', 111 | [ 112 | 'loading_url' => plugins_url( '/admin/img/loading.gif', dirname( __FILE__ ) ), 113 | ] 114 | ); 115 | } 116 | 117 | /** 118 | * Main settings page template 119 | * 120 | * @access public 121 | * @since 0.0.0 122 | * 123 | * @return void 124 | */ 125 | public function main_menu_template() 126 | { 127 | if ( ( isset( $_POST['_wfg_global_hidden'] ) && 'Y' == $_POST['_wfg_global_hidden'] ) 128 | && wp_verify_nonce( $_POST['_wfg_global_nonce'], 'wfg_global_settings' ) 129 | ) { 130 | 131 | $wfg_globally_enabled = isset( $_POST['wfg_globally_enabled'] ) ? true : false; 132 | $enabled = update_option( '_wfg_global_enabled', $wfg_globally_enabled ); 133 | 134 | if ( isset( $_POST['_wfg_criteria'] ) ) { 135 | $user_criteria = $_POST['_wfg_criteria']; 136 | 137 | //remove extra fields 138 | unset( $user_criteria['_wfg_global_nonce'] ); 139 | unset( $user_criteria['_wp_http_referer'] ); 140 | unset( $user_criteria['_wfg_global_hidden'] ); 141 | 142 | $conditionSaved = update_option( '_wfg_global_settings', $user_criteria ); 143 | if ( $enabled || $conditionSaved ) { 144 | WFG_Common_Helper::success_notice( 145 | WFG_Common_Helper::translate( 146 | 'Gift conditions saved successfully' 147 | ) 148 | ); 149 | 150 | WFG_Settings_Helper::force_init(); 151 | } else { 152 | WFG_Common_Helper::error_notice( 153 | WFG_Common_Helper::translate( 154 | 'There was a problem. Please try again.' 155 | ) 156 | ); 157 | } 158 | } else { 159 | if ( get_option( '_wfg_global_settings' ) !== false ) { 160 | if ( delete_option( '_wfg_global_settings' ) ) { 161 | WFG_Common_Helper::success_notice( 162 | WFG_Common_Helper::translate( 163 | 'Gift conditions emptied successfully' 164 | ) 165 | ); 166 | } 167 | } else { 168 | WFG_Common_Helper::error_notice( 169 | WFG_Common_Helper::translate( 170 | 'No gift conditions to save. You can add conditions by clicking Add new gift condition button' 171 | ) 172 | ); 173 | } 174 | } 175 | 176 | //update settings 177 | WFG_Settings_Helper::force_init(); 178 | } 179 | 180 | include 'pages/main_menu_page.php'; 181 | } 182 | 183 | public function wfg_criteria_template() 184 | { 185 | if ( ( isset( $_POST['_wfg_criteria_hidden'] ) && $_POST['_wfg_criteria_hidden'] == 'Y' ) 186 | && wp_verify_nonce( $_POST['_wfg_criteria_nonce'], 'wfg_criteria_settings' ) 187 | ) { 188 | 189 | if ( isset( $_POST['_wfg_criteria'] ) ) { 190 | $user_criteria = $_POST['_wfg_criteria']; 191 | foreach ( $user_criteria as &$criteria ) { 192 | $criteria['slug'] = sanitize_title( $criteria['name'] ); 193 | } 194 | 195 | if ( update_option( '_wfg_criteria', $user_criteria ) ) { 196 | WFG_Common_Helper::success_notice( 197 | WFG_Common_Helper::translate( 198 | 'Criteria saved successfully' 199 | ) 200 | ); 201 | 202 | WFG_Settings_Helper::force_init(); 203 | } else { 204 | WFG_Common_Helper::error_notice( 205 | WFG_Common_Helper::translate( 206 | 'There was a problem. Please try again.' 207 | ) 208 | ); 209 | } 210 | } else { 211 | if ( get_option( '_wfg_criteria' ) !== false ) { 212 | if ( delete_option( '_wfg_criteria' ) ) { 213 | WFG_Common_Helper::success_notice( 214 | WFG_Common_Helper::translate( 215 | 'Criteria saved successfully' 216 | ) 217 | ); 218 | } 219 | } else { 220 | WFG_Common_Helper::error_notice( 221 | WFG_Common_Helper::translate( 222 | 'No criteria to save. You can add criteria by clicking Add New Criteria button' 223 | ) 224 | ); 225 | } 226 | } 227 | 228 | //update settings 229 | WFG_Settings_Helper::force_init(); 230 | } 231 | 232 | include 'pages/gift_criteria.php'; 233 | } 234 | 235 | public function wfg_general_settings() 236 | { 237 | if ( ( isset( $_POST['_wfg_general_settings_submitted'] ) && $_POST['_wfg_general_settings_submitted'] == 'Y' ) 238 | && wp_verify_nonce( $_POST['_wfg_general_nonce'], 'wfg_general_settings' ) 239 | ) { 240 | 241 | $popup_overlay = isset( $_POST['_wfg_popup_overlay'] ) ? 1 : 0; 242 | $popup_heading = isset( $_POST['_wfg_popup_heading'] ) ? $_POST['_wfg_popup_heading'] : WFG_Common_Helper::translate( 'Choose your free gift' ); 243 | $invalid_text = isset( $_POST['_wfg_invalid_condition_text'] ) ? $_POST['_wfg_invalid_condition_text'] : WFG_Common_Helper::translate( 'Gift items removed as gift criteria isn\'t fulfilled' ); 244 | $add_gift_text = isset( $_POST['_wfg_popup_add_gift_text'] ) ? $_POST['_wfg_popup_add_gift_text'] : WFG_Common_Helper::translate( 'Add Gifts' ); 245 | $cancel_text = isset( $_POST['_wfg_popup_cancel_text'] ) ? $_POST['_wfg_popup_cancel_text'] : WFG_Common_Helper::translate( 'No Thanks' ); 246 | 247 | $overlay = update_option( '_wfg_popup_overlay', $popup_overlay ); 248 | $heading = update_option( '_wfg_popup_heading', $popup_heading ); 249 | $invalid = update_option( '_wfg_invalid_condition_text', $invalid_text ); 250 | $add_gift = update_option( '_wfg_popup_add_gift_text', $add_gift_text ); 251 | $cancel_text = update_option( '_wfg_popup_cancel_text', $cancel_text ); 252 | 253 | if ( $overlay || $heading || $invalid || $add_gift || $cancel_text ) { 254 | WFG_Common_Helper::success_notice( 255 | WFG_Common_Helper::translate( 256 | 'Settings saved successfully' 257 | ) 258 | ); 259 | 260 | //update settings 261 | WFG_Settings_Helper::force_init(); 262 | } else { 263 | WFG_Common_Helper::error_notice( 264 | WFG_Common_Helper::translate( 265 | 'No changes to save.' 266 | ) 267 | ); 268 | } 269 | } 270 | 271 | include 'pages/general_settings.php'; 272 | } 273 | 274 | public function ajax_product_list_callback() 275 | { 276 | $q = isset( $_GET['q'] ) ? $_GET['q'] : ''; 277 | if ( ! $q ) { 278 | return 0; 279 | } 280 | 281 | $products = WFG_Product_Helper::get_products( [ 's' => $q, 'posts_per_page' => 15 ] ); 282 | $list = []; 283 | if ( ! empty( $products ) && ! empty( $products->posts ) ) { 284 | foreach ( $products->posts as $product ) { 285 | $list[] = [ 'id' => $product->ID, 'text' => $product->post_title ]; 286 | } 287 | } 288 | 289 | echo json_encode( [ 'options' => $list ] ); 290 | wp_die(); 291 | } 292 | } 293 | 294 | /** Initialize */ 295 | new WFG_Admin(); 296 | -------------------------------------------------------------------------------- /lib/admin/css/wfg-admin-styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wfg-admin-styles.css 3 | * 4 | * Style for WooCommerce Free Gift plugin 5 | * 6 | * Copyright (c) 2015, Ankit Pokhrel 7 | */ 8 | 9 | img.wfg-logo { 10 | width: 64px; 11 | } 12 | 13 | #wfg_free_gift_tab .search-field input { 14 | height: 25px !important; 15 | } 16 | 17 | #wfg_free_gift_tab ._wfg-repeat { 18 | width: 90%; 19 | margin-left: 10px; 20 | } 21 | 22 | #wfg_free_gift_tab { 23 | padding: 0 5px; 24 | } 25 | 26 | .wfg-input-large { 27 | width: 80%; 28 | } 29 | 30 | .wfg-adjust-form-field-gap { 31 | padding: 5px 20px 0 162px !important; 32 | } 33 | 34 | .woocommerce_options_panel p.form-field.wfg_form_field { 35 | padding: 10px !important; 36 | } 37 | 38 | .woocommerce_options_panel p.form-field.wfg_form_field label.description { 39 | font-style: normal; 40 | position: relative; 41 | top: -4px; 42 | width: auto; 43 | float: none; 44 | font-size: 13px; 45 | margin-left: 0; 46 | display: inline-block; 47 | } 48 | 49 | .wfg-remove-gift-product, 50 | .wfg-remove-option-condition { 51 | margin: 5px 0 0 5px; 52 | } 53 | 54 | .wfg-inputs { 55 | padding: 0 10px !important; 56 | } 57 | 58 | .wfg_form_field input[type=text] { 59 | float: none !important; 60 | } 61 | 62 | .wfg_form_field .input-small { 63 | width: 10% !important; 64 | margin-left: 1%; 65 | text-align: center; 66 | } 67 | 68 | .wfg-settings-repeater { 69 | margin-bottom: 20px; 70 | padding-top: 15px !important; 71 | } 72 | 73 | .wfg-settings-repeater .wfg-inputs { 74 | padding: 0 !important; 75 | } 76 | 77 | .adjust-right-gap { 78 | margin-right: 50px; 79 | } 80 | 81 | .wfg-error, 82 | .wfg-updated { 83 | width: 93%; 84 | } 85 | 86 | .wfg-hr { 87 | left: -30px; 88 | margin-bottom: 20px; 89 | margin-top: 30px; 90 | overflow: hidden; 91 | position: relative; 92 | width: 106%; 93 | border-color: #f5f5f5; 94 | } 95 | 96 | /* Criteria css */ 97 | .wfg-criteria-clone, 98 | .wfg-settings-clone { 99 | display: none; 100 | } 101 | 102 | .wfg-criteria-item, 103 | .wfg-settings-repeater { 104 | padding: 30px; 105 | background-color: #fff; 106 | margin-top: 20px; 107 | width: 90%; 108 | border: 1px solid #dedede; 109 | } 110 | 111 | .wfg-criteria-options-wrap, 112 | .wfg-criteria-name { 113 | margin-bottom: 10px; 114 | } 115 | 116 | .wfg-input-full { 117 | width: 100%; 118 | padding: 7px; 119 | } 120 | 121 | .wfg-input-small { 122 | width: 80px; 123 | } 124 | 125 | .wfg-adjust-position { 126 | position: relative; 127 | top: 3px; 128 | } 129 | 130 | .wfg-remove-option-condition, 131 | .wfg-remove-condition-criteria { 132 | margin-top: 17px; 133 | } 134 | 135 | .wfg-remove-criteria, 136 | .wfg-remove-condition { 137 | float: right; 138 | position: relative; 139 | top: -20px; 140 | left: 20px; 141 | } 142 | 143 | .wfg-remove-condition { 144 | top: 0; 145 | } 146 | 147 | .wfg-criteria input[readOnly] { 148 | cursor: text; 149 | background-color: #fff; 150 | } 151 | 152 | /*------------------------------------------------------------------- 153 | 154 | RESET & STUFF 155 | 156 | -------------------------------------------------------------------*/ 157 | 158 | .clearfix:after { 159 | visibility: hidden; 160 | display: block; 161 | font-size: 0; 162 | content: " "; 163 | clear: both; 164 | height: 0; 165 | } 166 | 167 | * html .clearfix { 168 | zoom: 1; 169 | } 170 | 171 | /* IE6 */ 172 | *:first-child + html .clearfix { 173 | zoom: 1; 174 | } 175 | 176 | /* IE7 */ 177 | 178 | #wfg_setting .left { 179 | float: left; 180 | } 181 | 182 | #wfg_setting .right { 183 | float: left; 184 | } 185 | 186 | /*------------------------------------------------------------------- 187 | 188 | WFG Setting Global 189 | 190 | -------------------------------------------------------------------*/ 191 | #wfg_setting .header { 192 | padding-top: 20px; 193 | } 194 | 195 | #wfg_setting .header p.title { 196 | line-height: 35px; 197 | padding-left: 20px; 198 | font-size: 20px; 199 | } 200 | 201 | #wfg_setting .shadow { 202 | -webkit-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .16), 0 2px 10px 0 rgba(0, 0, 0, .12); 203 | -moz-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .16), 0 2px 10px 0 rgba(0, 0, 0, .12); 204 | box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .16), 0 2px 10px 0 rgba(0, 0, 0, .12); 205 | } 206 | 207 | /*------------------------------------------------------------------- 208 | 209 | WFG form 210 | 211 | -------------------------------------------------------------------*/ 212 | #wfg_free_gift_global_settings input, 213 | #wfg_free_gift_global_settings select, 214 | #wfg_free_gift_global_settings button, 215 | #wfg_free_gift_global_settings textarea { 216 | -webkit-appearance: none; 217 | -moz-appearance: none; 218 | appearance: none; 219 | font: inherit; 220 | color: inherit; 221 | } 222 | 223 | #wfg_free_gift_global_settings select { 224 | background-image: url("../img/ddown.png"); 225 | background-image: url("../img/ddown.svg"), none; 226 | background-repeat: no-repeat; 227 | background-size: 1.5rem 1rem; 228 | background-position: right center; 229 | } 230 | 231 | #wfg_free_gift_global_settings .wfg-inputs select { 232 | border: 1px solid #c0c8c9; 233 | border-radius: 4px; 234 | } 235 | 236 | #wfg_free_gift_global_settings select, 237 | #wfg_free_gift_global_settings .button, 238 | #wfg_free_gift_global_settings .input-text, 239 | #wfg_free_gift_global_settings .wfg-input-small, 240 | #wfg_free_gift_global_settings .wfg-criteria-name, 241 | #wfg_setting .regular-text { 242 | padding: 10px 21px 10px 15px !important; 243 | background-color: transparent; 244 | border-radius: 4px; 245 | height: auto; 246 | line-height: 21px; 247 | } 248 | 249 | #wfg_free_gift_global_settings .button { 250 | border: 2px solid #C0C8C9 !important; 251 | line-height: 1.375; 252 | padding-left: 1.5rem !important; 253 | padding-right: 1.5rem !important; 254 | height: auto !important; 255 | font-weight: 700; 256 | color: #666666; 257 | cursor: pointer; 258 | -webkit-animation: hue 60s infinite linear; 259 | box-shadow: none; 260 | } 261 | 262 | #wfg_free_gift_global_settings .wp-core-ui .button-primary { 263 | background: transparent; 264 | } 265 | 266 | /* Buttons */ 267 | 268 | #wfg_setting button, 269 | #wfg_setting .button, 270 | #wfg_setting input[type="button"], 271 | #wfg_setting input[type="reset"], 272 | #wfg_setting input[type="submit"] { 273 | background-color: #24890d; 274 | border: 0; 275 | border-radius: 2px; 276 | color: #fff; 277 | font-size: 12px; 278 | font-weight: 700; 279 | padding: 10px 30px 11px; 280 | text-transform: uppercase; 281 | vertical-align: middle; 282 | height: auto; 283 | } 284 | 285 | #wfg_setting button:hover, 286 | #wfg_setting button:focus, 287 | #wfg_setting .button:hover, 288 | #wfg_setting .button:focus, 289 | #wfg_setting input[type="button"]:hover, 290 | #wfg_setting input[type="button"]:focus, 291 | #wfg_setting input[type="reset"]:hover, 292 | #wfg_setting input[type="reset"]:focus, 293 | #wfg_setting input[type="submit"]:hover, 294 | #wfg_setting input[type="submit"]:focus { 295 | background-color: #41a62a; 296 | color: #fff; 297 | } 298 | 299 | #wfg_setting button:active, 300 | #wfg_setting .button:active, 301 | #wfg_setting input[type="button"]:active, 302 | #wfg_setting input[type="reset"]:active, 303 | #wfg_setting input[type="submit"]:active { 304 | background-color: #55d737; 305 | } 306 | 307 | /*------------------------------------------------------------------- 308 | 309 | SWITCH 310 | 311 | -------------------------------------------------------------------*/ 312 | 313 | .container > .switch { 314 | display: block; 315 | margin: 12px auto; 316 | } 317 | 318 | .switch { 319 | position: relative; 320 | display: inline-block; 321 | vertical-align: top; 322 | width: 56px; 323 | height: 20px; 324 | padding: 3px; 325 | background-color: white; 326 | border-radius: 18px; 327 | box-shadow: inset 0 -1px white, inset 0 1px 1px rgba(0, 0, 0, 0.05); 328 | cursor: pointer; 329 | background-image: -webkit-linear-gradient(top, #eeeeee, white 25px); 330 | background-image: -moz-linear-gradient(top, #eeeeee, white 25px); 331 | background-image: -o-linear-gradient(top, #eeeeee, white 25px); 332 | background-image: linear-gradient(to bottom, #eeeeee, white 25px); 333 | } 334 | 335 | .switcher { 336 | background-color: #fbc02d; 337 | padding: 14px 32px; 338 | width: 90%; 339 | 340 | } 341 | 342 | .switcher span { 343 | line-height: 25px; 344 | } 345 | 346 | .switch-input { 347 | position: absolute; 348 | top: 0; 349 | left: 0; 350 | opacity: 0; 351 | } 352 | 353 | .switch-label { 354 | position: relative; 355 | display: block; 356 | height: inherit; 357 | font-size: 10px; 358 | text-transform: uppercase; 359 | background: #eceeef; 360 | border-radius: inherit; 361 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.15); 362 | -webkit-transition: 0.15s ease-out; 363 | -moz-transition: 0.15s ease-out; 364 | -o-transition: 0.15s ease-out; 365 | transition: 0.15s ease-out; 366 | -webkit-transition-property: opacity background; 367 | -moz-transition-property: opacity background; 368 | -o-transition-property: opacity background; 369 | transition-property: opacity background; 370 | } 371 | 372 | .switch-label:before, .switch-label:after { 373 | position: absolute; 374 | top: 50%; 375 | margin-top: -.5em; 376 | line-height: 1; 377 | -webkit-transition: inherit; 378 | -moz-transition: inherit; 379 | -o-transition: inherit; 380 | transition: inherit; 381 | } 382 | 383 | .switch-label:before { 384 | content: attr(data-off); 385 | right: 11px; 386 | color: #aaa; 387 | text-shadow: 0 1px rgba(255, 255, 255, 0.5); 388 | } 389 | 390 | .switch-label:after { 391 | content: attr(data-on); 392 | left: 11px; 393 | color: white; 394 | text-shadow: 0 1px rgba(0, 0, 0, 0.2); 395 | opacity: 0; 396 | } 397 | 398 | .switch-input:checked ~ .switch-label { 399 | background: #47a8d8; 400 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 0 3px rgba(0, 0, 0, 0.2); 401 | } 402 | 403 | .switch-input:checked ~ .switch-label:before { 404 | opacity: 0; 405 | } 406 | 407 | .switch-input:checked ~ .switch-label:after { 408 | opacity: 1; 409 | } 410 | 411 | .switch-handle { 412 | position: absolute; 413 | top: 4px; 414 | left: 4px; 415 | width: 18px; 416 | height: 18px; 417 | background: white; 418 | border-radius: 10px; 419 | box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); 420 | background-image: -webkit-linear-gradient(top, white 40%, #f0f0f0); 421 | background-image: -moz-linear-gradient(top, white 40%, #f0f0f0); 422 | background-image: -o-linear-gradient(top, white 40%, #f0f0f0); 423 | background-image: linear-gradient(to bottom, white 40%, #f0f0f0); 424 | -webkit-transition: left 0.15s ease-out; 425 | -moz-transition: left 0.15s ease-out; 426 | -o-transition: left 0.15s ease-out; 427 | transition: left 0.15s ease-out; 428 | } 429 | 430 | .switch-handle:before { 431 | content: ''; 432 | position: absolute; 433 | top: 50%; 434 | left: 50%; 435 | margin: -6px 0 0 -6px; 436 | width: 12px; 437 | height: 12px; 438 | background: #f9f9f9; 439 | border-radius: 6px; 440 | box-shadow: inset 0 1px rgba(0, 0, 0, 0.02); 441 | background-image: -webkit-linear-gradient(top, #eeeeee, white); 442 | background-image: -moz-linear-gradient(top, #eeeeee, white); 443 | background-image: -o-linear-gradient(top, #eeeeee, white); 444 | background-image: linear-gradient(to bottom, #eeeeee, white); 445 | } 446 | 447 | .switch-input:checked ~ .switch-handle { 448 | left: 40px; 449 | box-shadow: -1px 1px 5px rgba(0, 0, 0, 0.2); 450 | } 451 | 452 | .switch-green > .switch-input:checked ~ .switch-label { 453 | background: #4fb845; 454 | } 455 | 456 | .wfg-info-wrapper { 457 | color: #fff; 458 | font-size: 14px; 459 | } 460 | 461 | .wfg-info-wrapper a { 462 | color: #fff; 463 | text-decoration: none; 464 | } 465 | 466 | .wfg-info-wrapper a:hover { 467 | text-decoration: underline; 468 | } 469 | 470 | .margin-top-20 { 471 | margin-top: 20px; 472 | } 473 | 474 | /* Selectize js css fixes */ 475 | 476 | #wfg_free_gift_tab .selectize-control { 477 | width: 90%; 478 | margin-left: 10px; 479 | } 480 | 481 | .selectize-input input { 482 | float: none !important; 483 | } 484 | 485 | .selectize-dropdown.multi.wfg-ajax-select { 486 | position: relative; 487 | top: 0 !important; 488 | } 489 | 490 | .wfg-selectize-loading:after { 491 | content: url("../img/loading.gif"); 492 | float: right; 493 | position: absolute; 494 | top: 10px; 495 | right: 10px; 496 | z-index: 999; 497 | } 498 | -------------------------------------------------------------------------------- /lib/admin/js/plugins/selectize/selectize.css: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.default.css (v0.12.1) - Default Theme 3 | * Copyright (c) 2013–2015 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { 17 | visibility: visible !important; 18 | background: #f2f2f2 !important; 19 | background: rgba(0, 0, 0, 0.06) !important; 20 | border: 0 none !important; 21 | -webkit-box-shadow: inset 0 0 12px 4px #ffffff; 22 | box-shadow: inset 0 0 12px 4px #ffffff; 23 | } 24 | .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { 25 | content: '!'; 26 | visibility: hidden; 27 | } 28 | .selectize-control.plugin-drag_drop .ui-sortable-helper { 29 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 30 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 31 | } 32 | .selectize-dropdown-header { 33 | position: relative; 34 | padding: 5px 8px; 35 | border-bottom: 1px solid #d0d0d0; 36 | background: #f8f8f8; 37 | -webkit-border-radius: 3px 3px 0 0; 38 | -moz-border-radius: 3px 3px 0 0; 39 | border-radius: 3px 3px 0 0; 40 | } 41 | .selectize-dropdown-header-close { 42 | position: absolute; 43 | right: 8px; 44 | top: 50%; 45 | color: #303030; 46 | opacity: 0.4; 47 | margin-top: -12px; 48 | line-height: 20px; 49 | font-size: 20px !important; 50 | } 51 | .selectize-dropdown-header-close:hover { 52 | color: #000000; 53 | } 54 | .selectize-dropdown.plugin-optgroup_columns .optgroup { 55 | border-right: 1px solid #f2f2f2; 56 | border-top: 0 none; 57 | float: left; 58 | -webkit-box-sizing: border-box; 59 | -moz-box-sizing: border-box; 60 | box-sizing: border-box; 61 | } 62 | .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { 63 | border-right: 0 none; 64 | } 65 | .selectize-dropdown.plugin-optgroup_columns .optgroup:before { 66 | display: none; 67 | } 68 | .selectize-dropdown.plugin-optgroup_columns .optgroup-header { 69 | border-top: 0 none; 70 | } 71 | .selectize-control.plugin-remove_button [data-value] { 72 | position: relative; 73 | padding-right: 24px !important; 74 | } 75 | .selectize-control.plugin-remove_button [data-value] .remove { 76 | z-index: 1; 77 | /* fixes ie bug (see #392) */ 78 | position: absolute; 79 | top: 0; 80 | right: 0; 81 | bottom: 0; 82 | width: 17px; 83 | text-align: center; 84 | font-weight: bold; 85 | font-size: 12px; 86 | color: inherit; 87 | text-decoration: none; 88 | vertical-align: middle; 89 | display: inline-block; 90 | padding: 2px 0 0 0; 91 | border-left: 1px solid #0073bb; 92 | -webkit-border-radius: 0 2px 2px 0; 93 | -moz-border-radius: 0 2px 2px 0; 94 | border-radius: 0 2px 2px 0; 95 | -webkit-box-sizing: border-box; 96 | -moz-box-sizing: border-box; 97 | box-sizing: border-box; 98 | } 99 | .selectize-control.plugin-remove_button [data-value] .remove:hover { 100 | background: rgba(0, 0, 0, 0.05); 101 | } 102 | .selectize-control.plugin-remove_button [data-value].active .remove { 103 | border-left-color: #00578d; 104 | } 105 | .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { 106 | background: none; 107 | } 108 | .selectize-control.plugin-remove_button .disabled [data-value] .remove { 109 | border-left-color: #aaaaaa; 110 | } 111 | .selectize-control { 112 | position: relative; 113 | } 114 | .selectize-dropdown, 115 | .selectize-input, 116 | .selectize-input input { 117 | color: #303030; 118 | font-family: inherit; 119 | font-size: 13px; 120 | line-height: 18px; 121 | -webkit-font-smoothing: inherit; 122 | } 123 | .selectize-input, 124 | .selectize-control.single .selectize-input.input-active { 125 | background: #ffffff; 126 | cursor: text; 127 | display: inline-block; 128 | } 129 | .selectize-input { 130 | border: 1px solid #d0d0d0; 131 | padding: 8px 8px; 132 | display: inline-block; 133 | width: 100%; 134 | overflow: hidden; 135 | position: relative; 136 | z-index: 1; 137 | -webkit-box-sizing: border-box; 138 | -moz-box-sizing: border-box; 139 | box-sizing: border-box; 140 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 141 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 142 | -webkit-border-radius: 3px; 143 | -moz-border-radius: 3px; 144 | border-radius: 3px; 145 | } 146 | .selectize-control.multi .selectize-input.has-items { 147 | padding: 5px 8px 2px; 148 | } 149 | .selectize-input.full { 150 | background-color: #ffffff; 151 | } 152 | .selectize-input.disabled, 153 | .selectize-input.disabled * { 154 | cursor: default !important; 155 | } 156 | .selectize-input.focus { 157 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 158 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 159 | } 160 | .selectize-input.dropdown-active { 161 | -webkit-border-radius: 3px 3px 0 0; 162 | -moz-border-radius: 3px 3px 0 0; 163 | border-radius: 3px 3px 0 0; 164 | } 165 | .selectize-input > * { 166 | vertical-align: baseline; 167 | display: -moz-inline-stack; 168 | display: inline-block; 169 | zoom: 1; 170 | *display: inline; 171 | } 172 | .selectize-control.multi .selectize-input > div { 173 | cursor: pointer; 174 | margin: 0 3px 3px 0; 175 | padding: 2px 6px; 176 | background: #1da7ee; 177 | color: #ffffff; 178 | border: 1px solid #0073bb; 179 | } 180 | .selectize-control.multi .selectize-input > div.active { 181 | background: #92c836; 182 | color: #ffffff; 183 | border: 1px solid #00578d; 184 | } 185 | .selectize-control.multi .selectize-input.disabled > div, 186 | .selectize-control.multi .selectize-input.disabled > div.active { 187 | color: #ffffff; 188 | background: #d2d2d2; 189 | border: 1px solid #aaaaaa; 190 | } 191 | .selectize-input > input { 192 | display: inline-block !important; 193 | padding: 0 !important; 194 | min-height: 0 !important; 195 | max-height: none !important; 196 | max-width: 100% !important; 197 | margin: 0 1px !important; 198 | text-indent: 0 !important; 199 | border: 0 none !important; 200 | background: none !important; 201 | line-height: inherit !important; 202 | -webkit-user-select: auto !important; 203 | -webkit-box-shadow: none !important; 204 | box-shadow: none !important; 205 | } 206 | .selectize-input > input::-ms-clear { 207 | display: none; 208 | } 209 | .selectize-input > input:focus { 210 | outline: none !important; 211 | } 212 | .selectize-input::after { 213 | content: ' '; 214 | display: block; 215 | clear: left; 216 | } 217 | .selectize-input.dropdown-active::before { 218 | content: ' '; 219 | display: block; 220 | position: absolute; 221 | background: #f0f0f0; 222 | height: 1px; 223 | bottom: 0; 224 | left: 0; 225 | right: 0; 226 | } 227 | .selectize-dropdown { 228 | position: absolute; 229 | z-index: 10; 230 | border: 1px solid #d0d0d0; 231 | background: #ffffff; 232 | margin: -1px 0 0 0; 233 | border-top: 0 none; 234 | -webkit-box-sizing: border-box; 235 | -moz-box-sizing: border-box; 236 | box-sizing: border-box; 237 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 238 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 239 | -webkit-border-radius: 0 0 3px 3px; 240 | -moz-border-radius: 0 0 3px 3px; 241 | border-radius: 0 0 3px 3px; 242 | } 243 | .selectize-dropdown [data-selectable] { 244 | cursor: pointer; 245 | overflow: hidden; 246 | } 247 | .selectize-dropdown [data-selectable] .highlight { 248 | background: rgba(125, 168, 208, 0.2); 249 | -webkit-border-radius: 1px; 250 | -moz-border-radius: 1px; 251 | border-radius: 1px; 252 | } 253 | .selectize-dropdown [data-selectable], 254 | .selectize-dropdown .optgroup-header { 255 | padding: 5px 8px; 256 | } 257 | .selectize-dropdown .optgroup:first-child .optgroup-header { 258 | border-top: 0 none; 259 | } 260 | .selectize-dropdown .optgroup-header { 261 | color: #303030; 262 | background: #ffffff; 263 | cursor: default; 264 | } 265 | .selectize-dropdown .active { 266 | background-color: #f5fafd; 267 | color: #495c68; 268 | } 269 | .selectize-dropdown .active.create { 270 | color: #495c68; 271 | } 272 | .selectize-dropdown .create { 273 | color: rgba(48, 48, 48, 0.5); 274 | } 275 | .selectize-dropdown-content { 276 | overflow-y: auto; 277 | overflow-x: hidden; 278 | max-height: 200px; 279 | } 280 | .selectize-control.single .selectize-input, 281 | .selectize-control.single .selectize-input input { 282 | cursor: pointer; 283 | } 284 | .selectize-control.single .selectize-input.input-active, 285 | .selectize-control.single .selectize-input.input-active input { 286 | cursor: text; 287 | } 288 | .selectize-control.single .selectize-input:after { 289 | content: ' '; 290 | display: block; 291 | position: absolute; 292 | top: 50%; 293 | right: 15px; 294 | margin-top: -3px; 295 | width: 0; 296 | height: 0; 297 | border-style: solid; 298 | border-width: 5px 5px 0 5px; 299 | border-color: #808080 transparent transparent transparent; 300 | } 301 | .selectize-control.single .selectize-input.dropdown-active:after { 302 | margin-top: -4px; 303 | border-width: 0 5px 5px 5px; 304 | border-color: transparent transparent #808080 transparent; 305 | } 306 | .selectize-control.rtl.single .selectize-input:after { 307 | left: 15px; 308 | right: auto; 309 | } 310 | .selectize-control.rtl .selectize-input > input { 311 | margin: 0 4px 0 -2px !important; 312 | } 313 | .selectize-control .selectize-input.disabled { 314 | opacity: 0.5; 315 | background-color: #fafafa; 316 | } 317 | .selectize-control.multi .selectize-input.has-items { 318 | padding-left: 5px; 319 | padding-right: 5px; 320 | } 321 | .selectize-control.multi .selectize-input.disabled [data-value] { 322 | color: #999; 323 | text-shadow: none; 324 | background: none; 325 | -webkit-box-shadow: none; 326 | box-shadow: none; 327 | } 328 | .selectize-control.multi .selectize-input.disabled [data-value], 329 | .selectize-control.multi .selectize-input.disabled [data-value] .remove { 330 | border-color: #e6e6e6; 331 | } 332 | .selectize-control.multi .selectize-input.disabled [data-value] .remove { 333 | background: none; 334 | } 335 | .selectize-control.multi .selectize-input [data-value] { 336 | text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3); 337 | -webkit-border-radius: 3px; 338 | -moz-border-radius: 3px; 339 | border-radius: 3px; 340 | background-color: #1b9dec; 341 | background-image: -moz-linear-gradient(top, #1da7ee, #178ee9); 342 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9)); 343 | background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9); 344 | background-image: -o-linear-gradient(top, #1da7ee, #178ee9); 345 | background-image: linear-gradient(to bottom, #1da7ee, #178ee9); 346 | background-repeat: repeat-x; 347 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0); 348 | -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03); 349 | box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03); 350 | } 351 | .selectize-control.multi .selectize-input [data-value].active { 352 | background-color: #0085d4; 353 | background-image: -moz-linear-gradient(top, #008fd8, #0075cf); 354 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf)); 355 | background-image: -webkit-linear-gradient(top, #008fd8, #0075cf); 356 | background-image: -o-linear-gradient(top, #008fd8, #0075cf); 357 | background-image: linear-gradient(to bottom, #008fd8, #0075cf); 358 | background-repeat: repeat-x; 359 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0); 360 | } 361 | .selectize-control.single .selectize-input { 362 | -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8); 363 | box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8); 364 | background-color: #f9f9f9; 365 | background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2); 366 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2)); 367 | background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2); 368 | background-image: -o-linear-gradient(top, #fefefe, #f2f2f2); 369 | background-image: linear-gradient(to bottom, #fefefe, #f2f2f2); 370 | background-repeat: repeat-x; 371 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0); 372 | } 373 | .selectize-control.single .selectize-input, 374 | .selectize-dropdown.single { 375 | border-color: #b8b8b8; 376 | } 377 | .selectize-dropdown .optgroup-header { 378 | padding-top: 7px; 379 | font-weight: bold; 380 | font-size: 0.85em; 381 | } 382 | .selectize-dropdown .optgroup { 383 | border-top: 1px solid #f0f0f0; 384 | } 385 | .selectize-dropdown .optgroup:first-child { 386 | border-top: 0 none; 387 | } 388 | -------------------------------------------------------------------------------- /lib/helpers/WFG_Product_Helper.class.php: -------------------------------------------------------------------------------- 1 | 10 | * @version 0.0.0 11 | */ 12 | class WFG_Product_Helper 13 | { 14 | /** 15 | * Fetch products based on given conditions 16 | * 17 | * @since 0.0.0 18 | * @access public 19 | * @static 20 | * 21 | * @param array $options Query params 22 | * @param int $limit 23 | * 24 | * @return null|WP_Query 25 | */ 26 | public static function get_products( $options = [], $limit = 15 ) 27 | { 28 | $args = [ 29 | 'post_type' => 'product', 30 | 'post_status' => 'publish', 31 | 'posts_per_page' => $limit, 32 | 'cache_results' => false, 33 | 'no_found_rows' => true, 34 | ]; 35 | 36 | //merge default and user options 37 | $args = array_merge( $args, $options ); 38 | 39 | $products = new WP_Query( $args ); 40 | wp_reset_postdata(); 41 | 42 | return $products; 43 | } 44 | 45 | /** 46 | * Fetch all product categories 47 | * 48 | * @since 0.0.0 49 | * @access public 50 | * @static 51 | * 52 | * @return array 53 | */ 54 | public static function get_product_categories() 55 | { 56 | $args = [ 57 | 'taxonomy' => 'product_cat', 58 | 'orderby' => 'name', 59 | 'show_count' => 0, 60 | 'pad_counts' => 0, 61 | 'hierarchical' => 1, 62 | 'title_li' => '', 63 | 'hide_empty' => 0, 64 | ]; 65 | 66 | return get_categories( $args ); 67 | } 68 | 69 | /** 70 | * Fetch items added to the cart. 71 | * 72 | * @since 0.0.0 73 | * @access public 74 | * @static 75 | * 76 | * @return array Items in cart 77 | */ 78 | public static function get_cart_products() 79 | { 80 | global $woocommerce; 81 | $cart_items = $woocommerce->cart->get_cart(); 82 | 83 | $added_products = []; 84 | $added_products['count'] = count( $cart_items ); 85 | if ( ! empty( $cart_items ) ) { 86 | foreach ( $cart_items as $cart_item ) { 87 | $added_products['ids'][] = $cart_item['product_id']; 88 | $added_products['objects'][] = $cart_item['data']; 89 | } 90 | } 91 | 92 | return $added_products; 93 | } 94 | 95 | /** 96 | * Fetch gift items added to the cart. 97 | * 98 | * @since 0.0.0 99 | * @access public 100 | * @static 101 | * 102 | * @return array Gift items in cart 103 | */ 104 | public static function get_gift_products_in_cart() 105 | { 106 | $free_items = []; 107 | $cart_items = WC()->cart->cart_contents; 108 | if ( empty( $cart_items ) ) { 109 | return $free_items; 110 | } 111 | 112 | foreach ( $cart_items as $key => $content ) { 113 | $is_gift_product = ! empty( $content['variation_id'] ) && (bool) get_post_meta( $content['variation_id'], 114 | '_wfg_gift_product' ); 115 | if ( $is_gift_product ) { 116 | $free_items[] = $content['product_id']; 117 | } 118 | } 119 | 120 | return $free_items; 121 | } 122 | 123 | /** 124 | * Fetch required product details for given product. 125 | * 126 | * @since 0.0.0 127 | * @access public 128 | * @static 129 | * 130 | * @param integer $product_id Product to get details of 131 | * 132 | * @return object 133 | */ 134 | public static function get_product_details( $product_id ) 135 | { 136 | $options = [ 'p' => $product_id ]; 137 | $product_details = self::get_products( $options ); 138 | 139 | $wfg_product_details = []; 140 | if ( ! empty( $product_details ) && ! empty( $product_details->posts ) ) { 141 | $wfg_product_details['detail'] = $product_details->post; 142 | $product_image = wp_get_attachment_image_src( get_post_thumbnail_id( $product_details->post->ID ), 143 | 'thumbnail' ); 144 | $wfg_product_details['image'] = isset( $product_image[0] ) ? $product_image[0] : false; 145 | } 146 | 147 | return (object) $wfg_product_details; 148 | } 149 | 150 | /** 151 | * Create variation product for given item. 152 | * 153 | * @since 0.0.0 154 | * @access public 155 | * @static 156 | * 157 | * @param integer $product_id Product to create variation of 158 | * 159 | * @return integer Product variation id 160 | */ 161 | public static function create_gift_variation( $product_id ) 162 | { 163 | //check if product variation already exists 164 | $product_variation = get_posts( [ 165 | 'post_parent' => $product_id, 166 | 's' => 'wfg_gift_product', 167 | 'post_type' => 'product_variation', 168 | 'posts_per_page' => 1, 169 | ] 170 | ); 171 | 172 | if ( ! empty( $product_variation ) ) { 173 | //update required meta values 174 | self::update_product_meta( $product_variation[0]->ID, $product_id ); 175 | 176 | return $product_variation[0]->ID; 177 | } 178 | 179 | //if product variation doesn't exist, add one 180 | $admin = get_users( 'orderby=nicename&role=administrator&number=1' ); 181 | $variation = [ 182 | 'post_author' => $admin[0]->ID, 183 | 'post_status' => 'publish', 184 | 'post_name' => 'product-' . $product_id . '-variation', 185 | 'post_parent' => $product_id, 186 | 'post_title' => 'wfg_gift_product', 187 | 'post_type' => 'product_variation', 188 | 'comment_status' => 'closed', 189 | 'ping_status' => 'closed', 190 | ]; 191 | 192 | $post_id = wp_insert_post( $variation ); 193 | 194 | //update meta values 195 | self::update_product_meta( $post_id, $product_id ); 196 | 197 | return $post_id; 198 | } 199 | 200 | /** 201 | * Update product meta values 202 | * 203 | * @since 1.1.5 204 | * @access public 205 | * @static 206 | * 207 | * @param integer $id WMFG product id 208 | * @param integer $product_id Original product id 209 | */ 210 | protected static function update_product_meta( $id, $product_id ) 211 | { 212 | update_post_meta( $id, '_price', 0 ); 213 | update_post_meta( $id, '_regular_price', 0 ); 214 | update_post_meta( $id, '_wfg_gift_product', 1 ); 215 | update_post_meta( $id, '_virtual', get_post_meta( $product_id, '_virtual', true ) ); 216 | 217 | // Fix by Brett Pollett 218 | update_post_meta( $id, '_downloadable', get_post_meta( $product_id, '_downloadable', true ) ); 219 | update_post_meta( $id, '_downloadable_files', get_post_meta( $product_id, '_downloadable_files', true ) ); 220 | update_post_meta( $id, '_download_limit', get_post_meta( $product_id, '_download_limit', true ) ); 221 | update_post_meta( $id, '_download_expiry', get_post_meta( $product_id, '_download_expiry', true ) ); 222 | update_post_meta( $id, '_download_type', get_post_meta( $product_id, '_download_type', true ) ); 223 | } 224 | 225 | /** 226 | * Add free gift item to cart. 227 | * 228 | * @since 0.0.0 229 | * @access public 230 | * @static 231 | * 232 | * @param integer $parent_product_id Main product id 233 | * @param integer $product_id Product variation id 234 | * 235 | * @return void 236 | */ 237 | public static function add_free_product_to_cart( $parent_product_id, $product_id ) 238 | { 239 | $found = false; 240 | //check if product is already in cart 241 | if ( count( WC()->cart->get_cart() ) > 0 ) { 242 | foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) { 243 | $_product = $values['data']; 244 | if ( $_product->id == $product_id ) { 245 | $found = true; 246 | } 247 | } 248 | 249 | // if product not found, add it 250 | if ( ! $found ) { 251 | WC()->cart->add_to_cart( 252 | $product_id, 253 | 1, 254 | $parent_product_id, 255 | [ WFG_Common_Helper::translate( 'Type' ) => WFG_Common_Helper::translate( 'Free Item' ) ] 256 | ); 257 | } 258 | } 259 | } 260 | 261 | /** 262 | * Check if gift item added is valid. 263 | * 264 | * @since 0.0.0 265 | * @access public 266 | * @static 267 | * 268 | * @param array $items_added Items added as free gift 269 | * 270 | * @return boolean 271 | */ 272 | public static function crosscheck_gift_items( $items_added, $gift_items ) 273 | { 274 | foreach ( $items_added as $item ) { 275 | if ( ! in_array( $item, $gift_items ) ) { 276 | return false; 277 | } 278 | } 279 | 280 | return true; 281 | } 282 | 283 | /** 284 | * Count total product excluding gift items 285 | * 286 | * @since 0.0.0 287 | * @access public 288 | * @static 289 | * 290 | * @return integer 291 | */ 292 | public static function get_main_product_count() 293 | { 294 | $count = 0; 295 | foreach ( WC()->cart->cart_contents as $key => $content ) { 296 | $is_gift_product = ! empty( $content['variation_id'] ) && (bool) get_post_meta( $content['variation_id'], 297 | '_wfg_gift_product' ); 298 | if ( ! $is_gift_product ) { 299 | $count ++; 300 | } 301 | } 302 | 303 | return $count; 304 | } 305 | 306 | /** 307 | * Count total quantity excluding gift items 308 | * 309 | * @since 0.0.0 310 | * @access public 311 | * @static 312 | * 313 | * @return integer 314 | */ 315 | public static function get_main_product_quantity_count() 316 | { 317 | $count = 0; 318 | foreach ( WC()->cart->cart_contents as $key => $content ) { 319 | $is_gift_product = ! empty( $content['variation_id'] ) && (bool) get_post_meta( $content['variation_id'], 320 | '_wfg_gift_product' ); 321 | if ( ! $is_gift_product ) { 322 | $count += (int) $content['quantity']; 323 | } 324 | } 325 | 326 | return $count; 327 | } 328 | 329 | /** 330 | * Category wise product count 331 | * 332 | * @since 0.0.0 333 | * @access public 334 | * @static 335 | * 336 | * @return array 337 | */ 338 | public static function get_category_products_count() 339 | { 340 | return self::wfg_counter(); 341 | } 342 | 343 | /** 344 | * Return max from category products count 345 | * 346 | * @since 0.0.0 347 | * @access public 348 | * @static 349 | * 350 | * @return integer 351 | */ 352 | public static function get_max_category_products_count() 353 | { 354 | $products = self::get_category_products_count(); 355 | 356 | return ! empty( $products ) ? max( $products ) : 0; 357 | } 358 | 359 | /** 360 | * Category wise quantity count 361 | * 362 | * @since 0.0.0 363 | * @access public 364 | * @static 365 | * 366 | * @return array 367 | */ 368 | public static function get_category_quantity_count() 369 | { 370 | return self::wfg_counter( 'quantity' ); 371 | } 372 | 373 | /** 374 | * Cart counter 375 | * 376 | * @since 1.1.6 377 | * @access public 378 | * @static 379 | * 380 | * @param string|null $index 381 | * 382 | * @return array 383 | */ 384 | public static function wfg_counter( $index = null ) 385 | { 386 | $products = []; 387 | 388 | foreach ( WC()->cart->cart_contents as $key => $content ) { 389 | $is_gift_product = ! empty( $content['variation_id'] ) && 390 | (bool) get_post_meta( $content['variation_id'], '_wfg_gift_product' ); 391 | 392 | if ( $is_gift_product ) { 393 | continue; 394 | } 395 | 396 | $terms = get_the_terms( $content['product_id'], 'product_cat' ); 397 | if ( empty( $terms ) ) { 398 | continue; 399 | } 400 | 401 | foreach ( $terms as $term ) { 402 | if ( isset( $products[ $term->term_id ] ) ) { 403 | $products[ $term->term_id ] += ( $index ? $content[ $key ] : 1 ); 404 | } else { 405 | $products[ $term->term_id ] = ( $index ? $content[ $key ] : 1 ); 406 | } 407 | } 408 | } 409 | 410 | return $products; 411 | } 412 | 413 | /** 414 | * Return max from category quantity count 415 | * 416 | * @since 0.0.0 417 | * @access public 418 | * @static 419 | * 420 | * @return integer 421 | */ 422 | public static function get_max_category_quantity_count() 423 | { 424 | $products = self::get_category_quantity_count(); 425 | 426 | return ! empty( $products ) ? max( $products ) : 0; 427 | } 428 | 429 | } 430 | -------------------------------------------------------------------------------- /lib/WFG_Frontend.class.php: -------------------------------------------------------------------------------- 1 | 9 | * @version 0.0.0 10 | */ 11 | class WFG_Frontend 12 | { 13 | /** @var boolean Denote if WFG is enabled */ 14 | protected $_wfg_enabled; 15 | 16 | /** @var integer Number of gift items allowed */ 17 | protected $_wfg_gifts_allowed; 18 | 19 | /** @var array Gift products */ 20 | protected $_wfg_products; 21 | 22 | /** @var integer Minimum number of items in cart for gift */ 23 | protected $_minimum_qty; 24 | 25 | /** @var string Free gift type */ 26 | protected $_wfg_type; 27 | 28 | /** @var boolean Denotes if there is a valid criteria */ 29 | protected $_wfg_criteria; 30 | 31 | /** 32 | * Constructor 33 | * 34 | * @see get_option() 35 | * @since 0.0.0 36 | */ 37 | public function __construct() 38 | { 39 | $this->_wfg_type = 'global'; 40 | $this->_minimum_qty = 1; 41 | 42 | $this->_wfg_enabled = WFG_Settings_Helper::get( $this->_wfg_type . '_enabled', true, 'global_options' ); 43 | $this->_wfg_criteria = false; 44 | $this->_wfg_gifts_allowed = 1; 45 | $this->_wfg_products = []; 46 | 47 | //Add hooks and filters 48 | self::__init(); 49 | } 50 | 51 | 52 | /** 53 | * Add require hooks and filters 54 | * 55 | * @see add_action() 56 | * @since 0.0.0 57 | * @access private 58 | */ 59 | private function __init() 60 | { 61 | /* Add free gifts ajax callback */ 62 | add_action( 'wp_ajax_wfg_add_gifts', [ $this, 'wfg_ajax_add_free_gifts' ] ); 63 | add_action( 'wp_ajax_nopriv_wfg_add_gifts', [ $this, 'wfg_ajax_add_free_gifts' ] ); 64 | 65 | /* Display gifts in frontend */ 66 | add_action( 'wp_head', [ $this, 'validate_gifts' ] ); 67 | add_action( 'wp_head', [ $this, 'display_gifts' ] ); 68 | 69 | /* Do not allow user to update quantity of gift items */ 70 | add_filter( 'woocommerce_is_sold_individually', [ $this, 'wfg_disallow_qty_update' ], 10, 2 ); 71 | 72 | /* Remove gifts when main item is removed */ 73 | add_action( 'woocommerce_cart_item_removed', [ $this, 'wfg_item_removed' ], 10, 2 ); 74 | 75 | /* Final cart gift validation as last step when checking cart items ( as other check process could have 76 | * removed products from the cart ) */ 77 | add_action( 'woocommerce_check_cart_items', [ $this, 'check_cart_items' ], 99 ); 78 | 79 | } 80 | 81 | /** 82 | * Overwrite default settings with actual settings 83 | * 84 | * @since 0.0.0 85 | * @access private 86 | * 87 | * @return void 88 | */ 89 | private function __get_actual_settings() 90 | { 91 | //single gift 92 | $post_id = $this->__get_post_id(); 93 | if ( empty( $post_id ) ) { 94 | return; 95 | } 96 | 97 | $wfg_enabled = get_post_meta( $post_id, '_wfg_single_gift_enabled', true ); 98 | if ( (bool) $wfg_enabled ) { 99 | $this->_wfg_type = 'single_gift'; 100 | $this->_wfg_enabled = $wfg_enabled; 101 | $this->_wfg_criteria = true; 102 | $this->_wfg_gifts_allowed = get_post_meta( $post_id, '_wfg_single_gift_allowed', true ); 103 | $this->_wfg_products = get_post_meta( $post_id, '_wfg_single_gift_products', true ); 104 | 105 | return; 106 | } 107 | 108 | return $this->__hook_global_settings(); 109 | } 110 | 111 | /** 112 | * Fetch actual product id 113 | * 114 | * @since 1.1.0 115 | * @access private 116 | * 117 | * @return integer|null 118 | */ 119 | private function __get_post_id() 120 | { 121 | $post_id = null; 122 | foreach ( WC()->cart->cart_contents as $key => $content ) { 123 | 124 | // If there are bundles in the cart, exclude bundled products 125 | if ( isset( $content['bundled_by'] ) ) { 126 | continue; 127 | } 128 | 129 | $is_gift_product = ! empty( $content['variation_id'] ) && (bool) get_post_meta( $content['variation_id'], 130 | '_wfg_gift_product' ); 131 | if ( ! $is_gift_product ) { 132 | return $content['product_id']; 133 | } 134 | } 135 | 136 | return $post_id; 137 | } 138 | 139 | /** 140 | * Hook global settings to actual settings 141 | * 142 | * @since 1.1.0 143 | * @access private 144 | * 145 | * @return void 146 | */ 147 | private function __hook_global_settings() 148 | { 149 | //look for global settings 150 | $wfg_global_settings = WFG_Settings_Helper::get( '', false, 'global_settings', false ); 151 | if ( empty( $wfg_global_settings ) ) { 152 | return; 153 | } 154 | 155 | foreach ( $wfg_global_settings as $setting ) { 156 | $gift_criteria = $setting['condition']; 157 | $criteria = WFG_Criteria_Helper::parse_criteria( $gift_criteria ); 158 | if ( $criteria ) { 159 | $this->__set_actual_values( $setting ); 160 | 161 | return; 162 | } 163 | } 164 | } 165 | 166 | /** 167 | * Set required values 168 | * 169 | * @since 1.1.0 170 | * @access private 171 | * 172 | * @return void 173 | */ 174 | private function __set_actual_values( $setting ) 175 | { 176 | $this->_wfg_criteria = true; 177 | $this->_wfg_gifts_allowed = $setting['num_allowed']; 178 | $this->_wfg_products = ! empty( $setting['items'] ) ? array_unique( $setting['items'] ) : []; 179 | } 180 | 181 | /** 182 | * Add free item to cart. 183 | * 184 | * @since 0.0.0 185 | * @access public 186 | * 187 | * @return void 188 | */ 189 | public function wfg_ajax_add_free_gifts() 190 | { 191 | if ( empty( $_POST ) || ! wp_verify_nonce( $_POST['_wfg_nonce'], 'wfg_add_free_gifts' ) ) { 192 | return; 193 | } 194 | 195 | if ( empty( $_POST['wfg_free_items'] ) ) { 196 | return; 197 | } 198 | 199 | //check if gift item is valid 200 | self::__get_actual_settings(); 201 | if ( ! WFG_Product_Helper::crosscheck_gift_items( $_POST['wfg_free_items'], $this->_wfg_products ) ) { 202 | return; 203 | } 204 | 205 | foreach ( $_POST['wfg_free_items'] as $item ) { 206 | $free_product = WFG_Product_Helper::create_gift_variation( $item ); 207 | WFG_Product_Helper::add_free_product_to_cart( $item, $free_product ); 208 | } 209 | 210 | wp_die(); 211 | } 212 | 213 | /** 214 | * Disallow qty update in gift products. 215 | * 216 | * @since 0.0.0 217 | * @access public 218 | * 219 | * @param boolean $return Is return product 220 | * @param object $product Product object 221 | * 222 | * @return integer|void 223 | */ 224 | public function wfg_disallow_qty_update( $return, $product ) 225 | { 226 | $variation_id = $product->get_id(); 227 | 228 | if ( $variation_id ) { 229 | $is_wfg_variation = get_post_meta( $variation_id, '_wfg_gift_product', true ); 230 | if ( (bool) $is_wfg_variation ) { 231 | return 1; 232 | } 233 | } 234 | 235 | return $return; 236 | } 237 | 238 | /** 239 | * Remove all gifts when main item is removed. 240 | * 241 | * @since 0.0.0 242 | * @access public 243 | * 244 | * @param string $cart_item_key Removed item key 245 | * @param object $cart Cart object 246 | * 247 | * @return void 248 | */ 249 | public function wfg_item_removed( $cart_item_key, $cart ) 250 | { 251 | //no need to process further if qty is zero 252 | if ( empty( $cart->cart_contents ) ) { 253 | return; 254 | } 255 | 256 | //check if removed item is a variation or main product 257 | $removed_item = $cart->removed_cart_contents[ $cart_item_key ]; 258 | if ( ! empty( $removed_item['variation_id'] ) ) { 259 | return; 260 | } 261 | 262 | if ( 'global' == $this->_wfg_type && 0 == WFG_Product_Helper::get_main_product_count() ) { 263 | foreach ( $cart->cart_contents as $key => $content ) { 264 | WC()->cart->remove_cart_item( $key ); 265 | } 266 | } 267 | } 268 | 269 | /** 270 | * Remove gifts if the criteria is invalid. 271 | * 272 | * @since 0.0.0 273 | * @access public 274 | * 275 | * @return null|boolean 276 | */ 277 | public function validate_gifts() 278 | { 279 | if ( ! is_cart() ) { 280 | return false; 281 | } 282 | 283 | if ( ! $this->__gift_item_in_cart() ) { 284 | return false; 285 | } 286 | 287 | self::__get_actual_settings(); 288 | self::_validate_single_gift_condition(); 289 | 290 | $cart_items = WFG_Product_Helper::get_gift_products_in_cart(); 291 | if ( ! $this->_wfg_criteria || ! WFG_Product_Helper::crosscheck_gift_items( $cart_items, 292 | $this->_wfg_products ) 293 | ) { 294 | //remove gift products 295 | if ( $this->__remove_gift_products() ) { 296 | $this->__set_notice_text(); 297 | } 298 | } 299 | 300 | } 301 | 302 | /** 303 | * Checks cart items, emitting an error notice if gift criteria is not met 304 | * 305 | * @since 0.0.0 306 | * @access public 307 | * 308 | * @return void 309 | */ 310 | public function check_cart_items() 311 | { 312 | if ( ! is_cart() && ! is_checkout() ) { 313 | return; 314 | } 315 | 316 | if ( ! $this->__gift_item_in_cart() ) { 317 | return; 318 | } 319 | 320 | self::__get_actual_settings(); 321 | self::_validate_single_gift_condition(); 322 | 323 | $cart_items = WFG_Product_Helper::get_gift_products_in_cart(); 324 | if ( ! $this->_wfg_criteria || ! WFG_Product_Helper::crosscheck_gift_items( $cart_items, 325 | $this->_wfg_products, $this->_wfg_type ) 326 | ) { 327 | // Generate error notice to abort any checkout transaction in process 328 | wc_add_notice( WFG_Common_Helper::translate( 'The cart contains gift items that are going to be removed, as gift criteria isn\'t fulfilled. Please reload the page.' ), 329 | 'error' ); 330 | } 331 | 332 | } 333 | 334 | /** 335 | * Set notice text. 336 | * 337 | * @since 1.1.0 338 | * @access private 339 | * 340 | * @return void 341 | */ 342 | private function __set_notice_text() 343 | { 344 | $noticeText = WFG_Settings_Helper::get( 'invalid_condition_text', false, 'global_options' ); 345 | if ( false === $noticeText ) { 346 | $noticeText = WFG_Common_Helper::translate( 'Gift items removed as gift criteria isn\'t fulfilled' ); 347 | } 348 | 349 | WFG_Common_Helper::fixed_notice( $noticeText ); 350 | } 351 | 352 | /** 353 | * Validate single gift condition. 354 | * 355 | * @since 1.1.0 356 | * @access protected 357 | * 358 | * @return boolean 359 | */ 360 | protected function _validate_single_gift_condition() 361 | { 362 | if ( 'single_gift' !== $this->_wfg_type ) { 363 | return false; 364 | } 365 | 366 | $total_items_in_cart = WFG_Product_Helper::get_main_product_count(); 367 | if ( 1 !== $total_items_in_cart ) { 368 | return false; 369 | } 370 | 371 | return $this->__remove_gift_products(); 372 | } 373 | 374 | /** 375 | * Remove gifts products. 376 | * 377 | * @since 1.1.0 378 | * @access private 379 | * 380 | * @return boolean 381 | */ 382 | private function __remove_gift_products() 383 | { 384 | $removed = false; 385 | foreach ( WC()->cart->cart_contents as $key => $content ) { 386 | $is_gift_product = ! empty( $content['variation_id'] ) && (bool) get_post_meta( $content['variation_id'], 387 | '_wfg_gift_product' ); 388 | if ( $is_gift_product && ! in_array( $content['product_id'], $this->_wfg_products ) ) { 389 | WC()->cart->remove_cart_item( $key ); 390 | $removed = true; 391 | } 392 | } 393 | 394 | return $removed; 395 | } 396 | 397 | /** 398 | * Display gift popup in frontend. 399 | * 400 | * @since 0.0.0 401 | * @access public 402 | * 403 | * @return void 404 | */ 405 | public function display_gifts() 406 | { 407 | if ( ! is_cart() ) { 408 | return; 409 | } 410 | 411 | if ( $this->__gift_item_in_cart() ) { 412 | return; 413 | } 414 | 415 | self::__get_actual_settings(); 416 | 417 | //check gift criteria 418 | if ( ! $this->_check_global_gift_criteria() ) { 419 | return; 420 | } 421 | 422 | //enqueue required styles for this page 423 | wp_enqueue_style( 'wfg-core-styles', plugins_url( '/css/wfg-styles.css', dirname( __FILE__ ) ) ); 424 | wp_enqueue_style( 'wfg-template-styles', 425 | plugins_url( '/templates/default/wfg-default.css', dirname( __FILE__ ) ) ); 426 | 427 | $items = WFG_Product_Helper::get_cart_products(); 428 | if ( $items['count'] >= $this->_minimum_qty ) { 429 | $this->_show_gifts(); 430 | } 431 | } 432 | 433 | /** 434 | * Display gifts. 435 | * 436 | * @since 1.1.0 437 | * @access public 438 | * 439 | * @return void 440 | */ 441 | protected function _show_gifts() 442 | { 443 | if ( ! $this->_wfg_enabled ) { 444 | return; 445 | } 446 | 447 | if ( empty( $this->_wfg_products ) ) { 448 | return; 449 | } 450 | 451 | $wfg_free_products = []; 452 | foreach ( $this->_wfg_products as $product ) { 453 | $wfg_free_products[] = WFG_Product_Helper::get_product_details( $product ); 454 | } 455 | 456 | $localize = [ 457 | 'gifts_allowed' => ( false !== $this->_wfg_gifts_allowed ) ? $this->_wfg_gifts_allowed : 1, 458 | ]; 459 | 460 | echo ''; 465 | 466 | include( PLUGIN_DIR . 'templates/default/template-default.php' ); 467 | } 468 | 469 | /** 470 | * Check if global gift condition is satisfied. 471 | * 472 | * @since 1.1.0 473 | * @access public 474 | * 475 | * @return boolean 476 | */ 477 | protected function _check_global_gift_criteria() 478 | { 479 | if ( 'single_gift' === $this->_wfg_type ) { 480 | return true; 481 | } 482 | 483 | $gift_criteria = WFG_Settings_Helper::get( 'global_gift_criteria' ); 484 | if ( empty( $gift_criteria ) ) { 485 | return true; 486 | } 487 | 488 | return WFG_Criteria_Helper::parse_criteria( $gift_criteria ); 489 | } 490 | 491 | /** 492 | * Check if there is already gift item in the cart 493 | * 494 | * @since 0.0.0 495 | * @access private 496 | * 497 | * @return boolean 498 | */ 499 | private function __gift_item_in_cart() 500 | { 501 | $cart = WC()->cart->get_cart(); 502 | if ( count( $cart ) < 0 ) { 503 | return false; 504 | } 505 | 506 | foreach ( $cart as $cart_item_key => $values ) { 507 | $product = $values['data']; 508 | $variation_id = $product->get_id(); 509 | if ( $variation_id ) { 510 | $is_wfg_variation = get_post_meta( $variation_id, '_wfg_gift_product', true ); 511 | if ( (bool) $is_wfg_variation ) { 512 | return true; 513 | } 514 | } 515 | } 516 | 517 | return false; 518 | } 519 | 520 | } 521 | 522 | /* initialize */ 523 | new WFG_Frontend(); 524 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /lib/admin/js/plugins/selectize/selectize.min.js: -------------------------------------------------------------------------------- 1 | /*! selectize.js - v0.12.1 | https://github.com/brianreavis/selectize.js | Apache License (v2) */ 2 | !function(a,b){"function"==typeof define&&define.amd?define("sifter",b):"object"==typeof exports?module.exports=b():a.Sifter=b()}(this,function(){var a=function(a,b){this.items=a,this.settings=b||{diacritics:!0}};a.prototype.tokenize=function(a){if(a=d(String(a||"").toLowerCase()),!a||!a.length)return[];var b,c,f,h,i=[],j=a.split(/ +/);for(b=0,c=j.length;c>b;b++){if(f=e(j[b]),this.settings.diacritics)for(h in g)g.hasOwnProperty(h)&&(f=f.replace(new RegExp(h,"g"),g[h]));i.push({string:j[b],regex:new RegExp(f,"i")})}return i},a.prototype.iterator=function(a,b){var c;c=f(a)?Array.prototype.forEach||function(a){for(var b=0,c=this.length;c>b;b++)a(this[b],b,this)}:function(a){for(var b in this)this.hasOwnProperty(b)&&a(this[b],b,this)},c.apply(a,[b])},a.prototype.getScoreFunction=function(a,b){var c,d,e,f;c=this,a=c.prepareSearch(a,b),e=a.tokens,d=a.options.fields,f=e.length;var g=function(a,b){var c,d;return a?(a=String(a||""),d=a.search(b.regex),-1===d?0:(c=b.string.length/a.length,0===d&&(c+=.5),c)):0},h=function(){var a=d.length;return a?1===a?function(a,b){return g(b[d[0]],a)}:function(b,c){for(var e=0,f=0;a>e;e++)f+=g(c[d[e]],b);return f/a}:function(){return 0}}();return f?1===f?function(a){return h(e[0],a)}:"and"===a.options.conjunction?function(a){for(var b,c=0,d=0;f>c;c++){if(b=h(e[c],a),0>=b)return 0;d+=b}return d/f}:function(a){for(var b=0,c=0;f>b;b++)c+=h(e[b],a);return c/f}:function(){return 0}},a.prototype.getSortFunction=function(a,c){var d,e,f,g,h,i,j,k,l,m,n;if(f=this,a=f.prepareSearch(a,c),n=!a.query&&c.sort_empty||c.sort,l=function(a,b){return"$score"===a?b.score:f.items[b.id][a]},h=[],n)for(d=0,e=n.length;e>d;d++)(a.query||"$score"!==n[d].field)&&h.push(n[d]);if(a.query){for(m=!0,d=0,e=h.length;e>d;d++)if("$score"===h[d].field){m=!1;break}m&&h.unshift({field:"$score",direction:"desc"})}else for(d=0,e=h.length;e>d;d++)if("$score"===h[d].field){h.splice(d,1);break}for(k=[],d=0,e=h.length;e>d;d++)k.push("desc"===h[d].direction?-1:1);return i=h.length,i?1===i?(g=h[0].field,j=k[0],function(a,c){return j*b(l(g,a),l(g,c))}):function(a,c){var d,e,f;for(d=0;i>d;d++)if(f=h[d].field,e=k[d]*b(l(f,a),l(f,c)))return e;return 0}:null},a.prototype.prepareSearch=function(a,b){if("object"==typeof a)return a;b=c({},b);var d=b.fields,e=b.sort,g=b.sort_empty;return d&&!f(d)&&(b.fields=[d]),e&&!f(e)&&(b.sort=[e]),g&&!f(g)&&(b.sort_empty=[g]),{options:b,query:String(a||"").toLowerCase(),tokens:this.tokenize(a),total:0,items:[]}},a.prototype.search=function(a,b){var c,d,e,f,g=this;return d=this.prepareSearch(a,b),b=d.options,a=d.query,f=b.score||g.getScoreFunction(d),a.length?g.iterator(g.items,function(a,e){c=f(a),(b.filter===!1||c>0)&&d.items.push({score:c,id:e})}):g.iterator(g.items,function(a,b){d.items.push({score:1,id:b})}),e=g.getSortFunction(d,b),e&&d.items.sort(e),d.total=d.items.length,"number"==typeof b.limit&&(d.items=d.items.slice(0,b.limit)),d};var b=function(a,b){return"number"==typeof a&&"number"==typeof b?a>b?1:b>a?-1:0:(a=h(String(a||"")),b=h(String(b||"")),a>b?1:b>a?-1:0)},c=function(a){var b,c,d,e;for(b=1,c=arguments.length;c>b;b++)if(e=arguments[b])for(d in e)e.hasOwnProperty(d)&&(a[d]=e[d]);return a},d=function(a){return(a+"").replace(/^\s+|\s+$|/g,"")},e=function(a){return(a+"").replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")},f=Array.isArray||$&&$.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},g={a:"[aÀÁÂÃÄÅàáâãäåĀāąĄ]",c:"[cÇçćĆčČ]",d:"[dđĐďĎ]",e:"[eÈÉÊËèéêëěĚĒēęĘ]",i:"[iÌÍÎÏìíîïĪī]",l:"[lłŁ]",n:"[nÑñňŇńŃ]",o:"[oÒÓÔÕÕÖØòóôõöøŌō]",r:"[rřŘ]",s:"[sŠšśŚ]",t:"[tťŤ]",u:"[uÙÚÛÜùúûüůŮŪū]",y:"[yŸÿýÝ]",z:"[zŽžżŻźŹ]"},h=function(){var a,b,c,d,e="",f={};for(c in g)if(g.hasOwnProperty(c))for(d=g[c].substring(2,g[c].length-1),e+=d,a=0,b=d.length;b>a;a++)f[d.charAt(a)]=c;var h=new RegExp("["+e+"]","g");return function(a){return a.replace(h,function(a){return f[a]}).toLowerCase()}}();return a}),function(a,b){"function"==typeof define&&define.amd?define("microplugin",b):"object"==typeof exports?module.exports=b():a.MicroPlugin=b()}(this,function(){var a={};a.mixin=function(a){a.plugins={},a.prototype.initializePlugins=function(a){var c,d,e,f=this,g=[];if(f.plugins={names:[],settings:{},requested:{},loaded:{}},b.isArray(a))for(c=0,d=a.length;d>c;c++)"string"==typeof a[c]?g.push(a[c]):(f.plugins.settings[a[c].name]=a[c].options,g.push(a[c].name));else if(a)for(e in a)a.hasOwnProperty(e)&&(f.plugins.settings[e]=a[e],g.push(e));for(;g.length;)f.require(g.shift())},a.prototype.loadPlugin=function(b){var c=this,d=c.plugins,e=a.plugins[b];if(!a.plugins.hasOwnProperty(b))throw new Error('Unable to find "'+b+'" plugin');d.requested[b]=!0,d.loaded[b]=e.fn.apply(c,[c.plugins.settings[b]||{}]),d.names.push(b)},a.prototype.require=function(a){var b=this,c=b.plugins;if(!b.plugins.loaded.hasOwnProperty(a)){if(c.requested[a])throw new Error('Plugin has circular dependency ("'+a+'")');b.loadPlugin(a)}return c.loaded[a]},a.define=function(b,c){a.plugins[b]={name:b,fn:c}}};var b={isArray:Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)}};return a}),function(a,b){"function"==typeof define&&define.amd?define("selectize",["jquery","sifter","microplugin"],b):"object"==typeof exports?module.exports=b(require("jquery"),require("sifter"),require("microplugin")):a.Selectize=b(a.jQuery,a.Sifter,a.MicroPlugin)}(this,function(a,b,c){"use strict";var d=function(a,b){if("string"!=typeof b||b.length){var c="string"==typeof b?new RegExp(b,"i"):b,d=function(a){var b=0;if(3===a.nodeType){var e=a.data.search(c);if(e>=0&&a.data.length>0){var f=a.data.match(c),g=document.createElement("span");g.className="highlight";var h=a.splitText(e),i=(h.splitText(f[0].length),h.cloneNode(!0));g.appendChild(i),h.parentNode.replaceChild(g,h),b=1}}else if(1===a.nodeType&&a.childNodes&&!/(script|style)/i.test(a.tagName))for(var j=0;j/g,">").replace(/"/g,""")},B=function(a){return(a+"").replace(/\$/g,"$$$$")},C={};C.before=function(a,b,c){var d=a[b];a[b]=function(){return c.apply(a,arguments),d.apply(a,arguments)}},C.after=function(a,b,c){var d=a[b];a[b]=function(){var b=d.apply(a,arguments);return c.apply(a,arguments),b}};var D=function(a){var b=!1;return function(){b||(b=!0,a.apply(this,arguments))}},E=function(a,b){var c;return function(){var d=this,e=arguments;window.clearTimeout(c),c=window.setTimeout(function(){a.apply(d,e)},b)}},F=function(a,b,c){var d,e=a.trigger,f={};a.trigger=function(){var c=arguments[0];return-1===b.indexOf(c)?e.apply(a,arguments):void(f[c]=arguments)},c.apply(a,[]),a.trigger=e;for(d in f)f.hasOwnProperty(d)&&e.apply(a,f[d])},G=function(a,b,c,d){a.on(b,c,function(b){for(var c=b.target;c&&c.parentNode!==a[0];)c=c.parentNode;return b.currentTarget=c,d.apply(this,[b])})},H=function(a){var b={};if("selectionStart"in a)b.start=a.selectionStart,b.length=a.selectionEnd-b.start;else if(document.selection){a.focus();var c=document.selection.createRange(),d=document.selection.createRange().text.length;c.moveStart("character",-a.value.length),b.start=c.text.length-d,b.length=d}return b},I=function(a,b,c){var d,e,f={};if(c)for(d=0,e=c.length;e>d;d++)f[c[d]]=a.css(c[d]);else f=a.css();b.css(f)},J=function(b,c){if(!b)return 0;var d=a("").css({position:"absolute",top:-99999,left:-99999,width:"auto",padding:0,whiteSpace:"pre"}).text(b).appendTo("body");I(c,d,["letterSpacing","fontSize","fontFamily","fontWeight","textTransform"]);var e=d.width();return d.remove(),e},K=function(a){var b=null,c=function(c,d){var e,f,g,h,i,j,k,l;c=c||window.event||{},d=d||{},c.metaKey||c.altKey||(d.force||a.data("grow")!==!1)&&(e=a.val(),c.type&&"keydown"===c.type.toLowerCase()&&(f=c.keyCode,g=f>=97&&122>=f||f>=65&&90>=f||f>=48&&57>=f||32===f,f===q||f===p?(l=H(a[0]),l.length?e=e.substring(0,l.start)+e.substring(l.start+l.length):f===p&&l.start?e=e.substring(0,l.start-1)+e.substring(l.start+1):f===q&&"undefined"!=typeof l.start&&(e=e.substring(0,l.start)+e.substring(l.start+1))):g&&(j=c.shiftKey,k=String.fromCharCode(c.keyCode),k=j?k.toUpperCase():k.toLowerCase(),e+=k)),h=a.attr("placeholder"),!e&&h&&(e=h),i=J(e,a)+4,i!==b&&(b=i,a.width(i),a.triggerHandler("resize")))};a.on("keydown keyup update blur",c),c()},L=function(c,d){var e,f,g,h,i=this;h=c[0],h.selectize=i;var j=window.getComputedStyle&&window.getComputedStyle(h,null);if(g=j?j.getPropertyValue("direction"):h.currentStyle&&h.currentStyle.direction,g=g||c.parents("[dir]:first").attr("dir")||"",a.extend(i,{order:0,settings:d,$input:c,tabIndex:c.attr("tabindex")||"",tagType:"select"===h.tagName.toLowerCase()?v:w,rtl:/rtl/i.test(g),eventNS:".selectize"+ ++L.count,highlightedValue:null,isOpen:!1,isDisabled:!1,isRequired:c.is("[required]"),isInvalid:!1,isLocked:!1,isFocused:!1,isInputHidden:!1,isSetup:!1,isShiftDown:!1,isCmdDown:!1,isCtrlDown:!1,ignoreFocus:!1,ignoreBlur:!1,ignoreHover:!1,hasOptions:!1,currentResults:null,lastValue:"",caretPos:0,loading:0,loadedSearches:{},$activeOption:null,$activeItems:[],optgroups:{},options:{},userOptions:{},items:[],renderCache:{},onSearchChange:null===d.loadThrottle?i.onSearchChange:E(i.onSearchChange,d.loadThrottle)}),i.sifter=new b(this.options,{diacritics:d.diacritics}),i.settings.options){for(e=0,f=i.settings.options.length;f>e;e++)i.registerOption(i.settings.options[e]);delete i.settings.options}if(i.settings.optgroups){for(e=0,f=i.settings.optgroups.length;f>e;e++)i.registerOptionGroup(i.settings.optgroups[e]);delete i.settings.optgroups}i.settings.mode=i.settings.mode||(1===i.settings.maxItems?"single":"multi"),"boolean"!=typeof i.settings.hideSelected&&(i.settings.hideSelected="multi"===i.settings.mode),i.initializePlugins(i.settings.plugins),i.setupCallbacks(),i.setupTemplates(),i.setup()};return e.mixin(L),c.mixin(L),a.extend(L.prototype,{setup:function(){var b,c,d,e,g,h,i,j,k,l=this,m=l.settings,n=l.eventNS,o=a(window),p=a(document),q=l.$input;if(i=l.settings.mode,j=q.attr("class")||"",b=a("
    ").addClass(m.wrapperClass).addClass(j).addClass(i),c=a("
    ").addClass(m.inputClass).addClass("items").appendTo(b),d=a('').appendTo(c).attr("tabindex",q.is(":disabled")?"-1":l.tabIndex),h=a(m.dropdownParent||b),e=a("
    ").addClass(m.dropdownClass).addClass(i).hide().appendTo(h),g=a("
    ").addClass(m.dropdownContentClass).appendTo(e),l.settings.copyClassesToDropdown&&e.addClass(j),b.css({width:q[0].style.width}),l.plugins.names.length&&(k="plugin-"+l.plugins.names.join(" plugin-"),b.addClass(k),e.addClass(k)),(null===m.maxItems||m.maxItems>1)&&l.tagType===v&&q.attr("multiple","multiple"),l.settings.placeholder&&d.attr("placeholder",m.placeholder),!l.settings.splitOn&&l.settings.delimiter){var u=l.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&");l.settings.splitOn=new RegExp("\\s*"+u+"+\\s*")}q.attr("autocorrect")&&d.attr("autocorrect",q.attr("autocorrect")),q.attr("autocapitalize")&&d.attr("autocapitalize",q.attr("autocapitalize")),l.$wrapper=b,l.$control=c,l.$control_input=d,l.$dropdown=e,l.$dropdown_content=g,e.on("mouseenter","[data-selectable]",function(){return l.onOptionHover.apply(l,arguments)}),e.on("mousedown click","[data-selectable]",function(){return l.onOptionSelect.apply(l,arguments)}),G(c,"mousedown","*:not(input)",function(){return l.onItemSelect.apply(l,arguments)}),K(d),c.on({mousedown:function(){return l.onMouseDown.apply(l,arguments)},click:function(){return l.onClick.apply(l,arguments)}}),d.on({mousedown:function(a){a.stopPropagation()},keydown:function(){return l.onKeyDown.apply(l,arguments)},keyup:function(){return l.onKeyUp.apply(l,arguments)},keypress:function(){return l.onKeyPress.apply(l,arguments)},resize:function(){l.positionDropdown.apply(l,[])},blur:function(){return l.onBlur.apply(l,arguments)},focus:function(){return l.ignoreBlur=!1,l.onFocus.apply(l,arguments)},paste:function(){return l.onPaste.apply(l,arguments)}}),p.on("keydown"+n,function(a){l.isCmdDown=a[f?"metaKey":"ctrlKey"],l.isCtrlDown=a[f?"altKey":"ctrlKey"],l.isShiftDown=a.shiftKey}),p.on("keyup"+n,function(a){a.keyCode===t&&(l.isCtrlDown=!1),a.keyCode===r&&(l.isShiftDown=!1),a.keyCode===s&&(l.isCmdDown=!1)}),p.on("mousedown"+n,function(a){if(l.isFocused){if(a.target===l.$dropdown[0]||a.target.parentNode===l.$dropdown[0])return!1;l.$control.has(a.target).length||a.target===l.$control[0]||l.blur(a.target)}}),o.on(["scroll"+n,"resize"+n].join(" "),function(){l.isOpen&&l.positionDropdown.apply(l,arguments)}),o.on("mousemove"+n,function(){l.ignoreHover=!1}),this.revertSettings={$children:q.children().detach(),tabindex:q.attr("tabindex")},q.attr("tabindex",-1).hide().after(l.$wrapper),a.isArray(m.items)&&(l.setValue(m.items),delete m.items),x&&q.on("invalid"+n,function(a){a.preventDefault(),l.isInvalid=!0,l.refreshState()}),l.updateOriginalInput(),l.refreshItems(),l.refreshState(),l.updatePlaceholder(),l.isSetup=!0,q.is(":disabled")&&l.disable(),l.on("change",this.onChange),q.data("selectize",l),q.addClass("selectized"),l.trigger("initialize"),m.preload===!0&&l.onSearchChange("")},setupTemplates:function(){var b=this,c=b.settings.labelField,d=b.settings.optgroupLabelField,e={optgroup:function(a){return'
    '+a.html+"
    "},optgroup_header:function(a,b){return'
    '+b(a[d])+"
    "},option:function(a,b){return'
    '+b(a[c])+"
    "},item:function(a,b){return'
    '+b(a[c])+"
    "},option_create:function(a,b){return'
    Add '+b(a.input)+"
    "}};b.settings.render=a.extend({},e,b.settings.render)},setupCallbacks:function(){var a,b,c={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(a in c)c.hasOwnProperty(a)&&(b=this.settings[c[a]],b&&this.on(a,b))},onClick:function(a){var b=this;b.isFocused||(b.focus(),a.preventDefault())},onMouseDown:function(b){{var c=this,d=b.isDefaultPrevented();a(b.target)}if(c.isFocused){if(b.target!==c.$control_input[0])return"single"===c.settings.mode?c.isOpen?c.close():c.open():d||c.setActiveItem(null),!1}else d||window.setTimeout(function(){c.focus()},0)},onChange:function(){this.$input.trigger("change")},onPaste:function(b){var c=this;c.isFull()||c.isInputHidden||c.isLocked?b.preventDefault():c.settings.splitOn&&setTimeout(function(){for(var b=a.trim(c.$control_input.val()||"").split(c.settings.splitOn),d=0,e=b.length;e>d;d++)c.createItem(b[d])},0)},onKeyPress:function(a){if(this.isLocked)return a&&a.preventDefault();var b=String.fromCharCode(a.keyCode||a.which);return this.settings.create&&"multi"===this.settings.mode&&b===this.settings.delimiter?(this.createItem(),a.preventDefault(),!1):void 0},onKeyDown:function(a){var b=(a.target===this.$control_input[0],this);if(b.isLocked)return void(a.keyCode!==u&&a.preventDefault());switch(a.keyCode){case g:if(b.isCmdDown)return void b.selectAll();break;case i:return void(b.isOpen&&(a.preventDefault(),a.stopPropagation(),b.close()));case o:if(!a.ctrlKey||a.altKey)break;case n:if(!b.isOpen&&b.hasOptions)b.open();else if(b.$activeOption){b.ignoreHover=!0;var c=b.getAdjacentOption(b.$activeOption,1);c.length&&b.setActiveOption(c,!0,!0)}return void a.preventDefault();case l:if(!a.ctrlKey||a.altKey)break;case k:if(b.$activeOption){b.ignoreHover=!0;var d=b.getAdjacentOption(b.$activeOption,-1);d.length&&b.setActiveOption(d,!0,!0)}return void a.preventDefault();case h:return void(b.isOpen&&b.$activeOption&&(b.onOptionSelect({currentTarget:b.$activeOption}),a.preventDefault()));case j:return void b.advanceSelection(-1,a);case m:return void b.advanceSelection(1,a);case u:return b.settings.selectOnTab&&b.isOpen&&b.$activeOption&&(b.onOptionSelect({currentTarget:b.$activeOption}),b.isFull()||a.preventDefault()),void(b.settings.create&&b.createItem()&&a.preventDefault());case p:case q:return void b.deleteSelection(a)}return!b.isFull()&&!b.isInputHidden||(f?a.metaKey:a.ctrlKey)?void 0:void a.preventDefault()},onKeyUp:function(a){var b=this;if(b.isLocked)return a&&a.preventDefault();var c=b.$control_input.val()||"";b.lastValue!==c&&(b.lastValue=c,b.onSearchChange(c),b.refreshOptions(),b.trigger("type",c))},onSearchChange:function(a){var b=this,c=b.settings.load;c&&(b.loadedSearches.hasOwnProperty(a)||(b.loadedSearches[a]=!0,b.load(function(d){c.apply(b,[a,d])})))},onFocus:function(a){var b=this,c=b.isFocused;return b.isDisabled?(b.blur(),a&&a.preventDefault(),!1):void(b.ignoreFocus||(b.isFocused=!0,"focus"===b.settings.preload&&b.onSearchChange(""),c||b.trigger("focus"),b.$activeItems.length||(b.showInput(),b.setActiveItem(null),b.refreshOptions(!!b.settings.openOnFocus)),b.refreshState()))},onBlur:function(a,b){var c=this;if(c.isFocused&&(c.isFocused=!1,!c.ignoreFocus)){if(!c.ignoreBlur&&document.activeElement===c.$dropdown_content[0])return c.ignoreBlur=!0,void c.onFocus(a);var d=function(){c.close(),c.setTextboxValue(""),c.setActiveItem(null),c.setActiveOption(null),c.setCaret(c.items.length),c.refreshState(),(b||document.body).focus(),c.ignoreFocus=!1,c.trigger("blur")};c.ignoreFocus=!0,c.settings.create&&c.settings.createOnBlur?c.createItem(null,!1,d):d()}},onOptionHover:function(a){this.ignoreHover||this.setActiveOption(a.currentTarget,!1)},onOptionSelect:function(b){var c,d,e=this;b.preventDefault&&(b.preventDefault(),b.stopPropagation()),d=a(b.currentTarget),d.hasClass("create")?e.createItem(null,function(){e.settings.closeAfterSelect&&e.close()}):(c=d.attr("data-value"),"undefined"!=typeof c&&(e.lastQuery=null,e.setTextboxValue(""),e.addItem(c),e.settings.closeAfterSelect?e.close():!e.settings.hideSelected&&b.type&&/mouse/.test(b.type)&&e.setActiveOption(e.getOption(c))))},onItemSelect:function(a){var b=this;b.isLocked||"multi"===b.settings.mode&&(a.preventDefault(),b.setActiveItem(a.currentTarget,a))},load:function(a){var b=this,c=b.$wrapper.addClass(b.settings.loadingClass);b.loading++,a.apply(b,[function(a){b.loading=Math.max(b.loading-1,0),a&&a.length&&(b.addOption(a),b.refreshOptions(b.isFocused&&!b.isInputHidden)),b.loading||c.removeClass(b.settings.loadingClass),b.trigger("load",a)}])},setTextboxValue:function(a){var b=this.$control_input,c=b.val()!==a;c&&(b.val(a).triggerHandler("update"),this.lastValue=a)},getValue:function(){return this.tagType===v&&this.$input.attr("multiple")?this.items:this.items.join(this.settings.delimiter)},setValue:function(a,b){var c=b?[]:["change"];F(this,c,function(){this.clear(b),this.addItems(a,b)})},setActiveItem:function(b,c){var d,e,f,g,h,i,j,k,l=this;if("single"!==l.settings.mode){if(b=a(b),!b.length)return a(l.$activeItems).removeClass("active"),l.$activeItems=[],void(l.isFocused&&l.showInput());if(d=c&&c.type.toLowerCase(),"mousedown"===d&&l.isShiftDown&&l.$activeItems.length){for(k=l.$control.children(".active:last"),g=Array.prototype.indexOf.apply(l.$control[0].childNodes,[k[0]]),h=Array.prototype.indexOf.apply(l.$control[0].childNodes,[b[0]]),g>h&&(j=g,g=h,h=j),e=g;h>=e;e++)i=l.$control[0].childNodes[e],-1===l.$activeItems.indexOf(i)&&(a(i).addClass("active"),l.$activeItems.push(i));c.preventDefault()}else"mousedown"===d&&l.isCtrlDown||"keydown"===d&&this.isShiftDown?b.hasClass("active")?(f=l.$activeItems.indexOf(b[0]),l.$activeItems.splice(f,1),b.removeClass("active")):l.$activeItems.push(b.addClass("active")[0]):(a(l.$activeItems).removeClass("active"),l.$activeItems=[b.addClass("active")[0]]);l.hideInput(),this.isFocused||l.focus()}},setActiveOption:function(b,c,d){var e,f,g,h,i,j=this;j.$activeOption&&j.$activeOption.removeClass("active"),j.$activeOption=null,b=a(b),b.length&&(j.$activeOption=b.addClass("active"),(c||!y(c))&&(e=j.$dropdown_content.height(),f=j.$activeOption.outerHeight(!0),c=j.$dropdown_content.scrollTop()||0,g=j.$activeOption.offset().top-j.$dropdown_content.offset().top+c,h=g,i=g-e+f,g+f>e+c?j.$dropdown_content.stop().animate({scrollTop:i},d?j.settings.scrollDuration:0):c>g&&j.$dropdown_content.stop().animate({scrollTop:h},d?j.settings.scrollDuration:0)))},selectAll:function(){var a=this;"single"!==a.settings.mode&&(a.$activeItems=Array.prototype.slice.apply(a.$control.children(":not(input)").addClass("active")),a.$activeItems.length&&(a.hideInput(),a.close()),a.focus())},hideInput:function(){var a=this;a.setTextboxValue(""),a.$control_input.css({opacity:0,position:"absolute",left:a.rtl?1e4:-1e4}),a.isInputHidden=!0},showInput:function(){this.$control_input.css({opacity:1,position:"relative",left:0}),this.isInputHidden=!1},focus:function(){var a=this;a.isDisabled||(a.ignoreFocus=!0,a.$control_input[0].focus(),window.setTimeout(function(){a.ignoreFocus=!1,a.onFocus()},0))},blur:function(a){this.$control_input[0].blur(),this.onBlur(null,a)},getScoreFunction:function(a){return this.sifter.getScoreFunction(a,this.getSearchOptions())},getSearchOptions:function(){var a=this.settings,b=a.sortField;return"string"==typeof b&&(b=[{field:b}]),{fields:a.searchField,conjunction:a.searchConjunction,sort:b}},search:function(b){var c,d,e,f=this,g=f.settings,h=this.getSearchOptions();if(g.score&&(e=f.settings.score.apply(this,[b]),"function"!=typeof e))throw new Error('Selectize "score" setting must be a function that returns a function');if(b!==f.lastQuery?(f.lastQuery=b,d=f.sifter.search(b,a.extend(h,{score:e})),f.currentResults=d):d=a.extend(!0,{},f.currentResults),g.hideSelected)for(c=d.items.length-1;c>=0;c--)-1!==f.items.indexOf(z(d.items[c].id))&&d.items.splice(c,1);return d},refreshOptions:function(b){var c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;"undefined"==typeof b&&(b=!0);var t=this,u=a.trim(t.$control_input.val()),v=t.search(u),w=t.$dropdown_content,x=t.$activeOption&&z(t.$activeOption.attr("data-value"));for(g=v.items.length,"number"==typeof t.settings.maxOptions&&(g=Math.min(g,t.settings.maxOptions)),h={},i=[],c=0;g>c;c++)for(j=t.options[v.items[c].id],k=t.render("option",j),l=j[t.settings.optgroupField]||"",m=a.isArray(l)?l:[l],e=0,f=m&&m.length;f>e;e++)l=m[e],t.optgroups.hasOwnProperty(l)||(l=""),h.hasOwnProperty(l)||(h[l]=[],i.push(l)),h[l].push(k);for(this.settings.lockOptgroupOrder&&i.sort(function(a,b){var c=t.optgroups[a].$order||0,d=t.optgroups[b].$order||0;return c-d}),n=[],c=0,g=i.length;g>c;c++)l=i[c],t.optgroups.hasOwnProperty(l)&&h[l].length?(o=t.render("optgroup_header",t.optgroups[l])||"",o+=h[l].join(""),n.push(t.render("optgroup",a.extend({},t.optgroups[l],{html:o})))):n.push(h[l].join(""));if(w.html(n.join("")),t.settings.highlight&&v.query.length&&v.tokens.length)for(c=0,g=v.tokens.length;g>c;c++)d(w,v.tokens[c].regex);if(!t.settings.hideSelected)for(c=0,g=t.items.length;g>c;c++)t.getOption(t.items[c]).addClass("selected");p=t.canCreate(u),p&&(w.prepend(t.render("option_create",{input:u})),s=a(w[0].childNodes[0])),t.hasOptions=v.items.length>0||p,t.hasOptions?(v.items.length>0?(r=x&&t.getOption(x),r&&r.length?q=r:"single"===t.settings.mode&&t.items.length&&(q=t.getOption(t.items[0])),q&&q.length||(q=s&&!t.settings.addPrecedence?t.getAdjacentOption(s,1):w.find("[data-selectable]:first"))):q=s,t.setActiveOption(q),b&&!t.isOpen&&t.open()):(t.setActiveOption(null),b&&t.isOpen&&t.close())},addOption:function(b){var c,d,e,f=this;if(a.isArray(b))for(c=0,d=b.length;d>c;c++)f.addOption(b[c]);else(e=f.registerOption(b))&&(f.userOptions[e]=!0,f.lastQuery=null,f.trigger("option_add",e,b))},registerOption:function(a){var b=z(a[this.settings.valueField]);return!b||this.options.hasOwnProperty(b)?!1:(a.$order=a.$order||++this.order,this.options[b]=a,b)},registerOptionGroup:function(a){var b=z(a[this.settings.optgroupValueField]);return b?(a.$order=a.$order||++this.order,this.optgroups[b]=a,b):!1},addOptionGroup:function(a,b){b[this.settings.optgroupValueField]=a,(a=this.registerOptionGroup(b))&&this.trigger("optgroup_add",a,b)},removeOptionGroup:function(a){this.optgroups.hasOwnProperty(a)&&(delete this.optgroups[a],this.renderCache={},this.trigger("optgroup_remove",a))},clearOptionGroups:function(){this.optgroups={},this.renderCache={},this.trigger("optgroup_clear")},updateOption:function(b,c){var d,e,f,g,h,i,j,k=this;if(b=z(b),f=z(c[k.settings.valueField]),null!==b&&k.options.hasOwnProperty(b)){if("string"!=typeof f)throw new Error("Value must be set in option data");j=k.options[b].$order,f!==b&&(delete k.options[b],g=k.items.indexOf(b),-1!==g&&k.items.splice(g,1,f)),c.$order=c.$order||j,k.options[f]=c,h=k.renderCache.item,i=k.renderCache.option,h&&(delete h[b],delete h[f]),i&&(delete i[b],delete i[f]),-1!==k.items.indexOf(f)&&(d=k.getItem(b),e=a(k.render("item",c)),d.hasClass("active")&&e.addClass("active"),d.replaceWith(e)),k.lastQuery=null,k.isOpen&&k.refreshOptions(!1)}},removeOption:function(a,b){var c=this;a=z(a);var d=c.renderCache.item,e=c.renderCache.option;d&&delete d[a],e&&delete e[a],delete c.userOptions[a],delete c.options[a],c.lastQuery=null,c.trigger("option_remove",a),c.removeItem(a,b)},clearOptions:function(){var a=this;a.loadedSearches={},a.userOptions={},a.renderCache={},a.options=a.sifter.items={},a.lastQuery=null,a.trigger("option_clear"),a.clear()},getOption:function(a){return this.getElementWithValue(a,this.$dropdown_content.find("[data-selectable]"))},getAdjacentOption:function(b,c){var d=this.$dropdown.find("[data-selectable]"),e=d.index(b)+c;return e>=0&&ed;d++)if(c[d].getAttribute("data-value")===b)return a(c[d]);return a()},getItem:function(a){return this.getElementWithValue(a,this.$control.children())},addItems:function(b,c){for(var d=a.isArray(b)?b:[b],e=0,f=d.length;f>e;e++)this.isPending=f-1>e,this.addItem(d[e],c)},addItem:function(b,c){var d=c?[]:["change"];F(this,d,function(){var d,e,f,g,h,i=this,j=i.settings.mode;return b=z(b),-1!==i.items.indexOf(b)?void("single"===j&&i.close()):void(i.options.hasOwnProperty(b)&&("single"===j&&i.clear(c),"multi"===j&&i.isFull()||(d=a(i.render("item",i.options[b])),h=i.isFull(),i.items.splice(i.caretPos,0,b),i.insertAtCaret(d),(!i.isPending||!h&&i.isFull())&&i.refreshState(),i.isSetup&&(f=i.$dropdown_content.find("[data-selectable]"),i.isPending||(e=i.getOption(b),g=i.getAdjacentOption(e,1).attr("data-value"),i.refreshOptions(i.isFocused&&"single"!==j),g&&i.setActiveOption(i.getOption(g))),!f.length||i.isFull()?i.close():i.positionDropdown(),i.updatePlaceholder(),i.trigger("item_add",b,d),i.updateOriginalInput({silent:c})))))})},removeItem:function(a,b){var c,d,e,f=this;c="object"==typeof a?a:f.getItem(a),a=z(c.attr("data-value")),d=f.items.indexOf(a),-1!==d&&(c.remove(),c.hasClass("active")&&(e=f.$activeItems.indexOf(c[0]),f.$activeItems.splice(e,1)),f.items.splice(d,1),f.lastQuery=null,!f.settings.persist&&f.userOptions.hasOwnProperty(a)&&f.removeOption(a,b),d0),b.$control_input.data("grow",!c&&!d)},isFull:function(){return null!==this.settings.maxItems&&this.items.length>=this.settings.maxItems},updateOriginalInput:function(a){var b,c,d,e,f=this;if(a=a||{},f.tagType===v){for(d=[],b=0,c=f.items.length;c>b;b++)e=f.options[f.items[b]][f.settings.labelField]||"",d.push('");d.length||this.$input.attr("multiple")||d.push(''),f.$input.html(d.join(""))}else f.$input.val(f.getValue()),f.$input.attr("value",f.$input.val());f.isSetup&&(a.silent||f.trigger("change",f.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var a=this.$control_input;this.items.length?a.removeAttr("placeholder"):a.attr("placeholder",this.settings.placeholder),a.triggerHandler("update",{force:!0})}},open:function(){var a=this;a.isLocked||a.isOpen||"multi"===a.settings.mode&&a.isFull()||(a.focus(),a.isOpen=!0,a.refreshState(),a.$dropdown.css({visibility:"hidden",display:"block"}),a.positionDropdown(),a.$dropdown.css({visibility:"visible"}),a.trigger("dropdown_open",a.$dropdown))},close:function(){var a=this,b=a.isOpen;"single"===a.settings.mode&&a.items.length&&a.hideInput(),a.isOpen=!1,a.$dropdown.hide(),a.setActiveOption(null),a.refreshState(),b&&a.trigger("dropdown_close",a.$dropdown)},positionDropdown:function(){var a=this.$control,b="body"===this.settings.dropdownParent?a.offset():a.position();b.top+=a.outerHeight(!0),this.$dropdown.css({width:a.outerWidth(),top:b.top,left:b.left})},clear:function(a){var b=this;b.items.length&&(b.$control.children(":not(input)").remove(),b.items=[],b.lastQuery=null,b.setCaret(0),b.setActiveItem(null),b.updatePlaceholder(),b.updateOriginalInput({silent:a}),b.refreshState(),b.showInput(),b.trigger("clear"))},insertAtCaret:function(b){var c=Math.min(this.caretPos,this.items.length);0===c?this.$control.prepend(b):a(this.$control[0].childNodes[c]).before(b),this.setCaret(c+1)},deleteSelection:function(b){var c,d,e,f,g,h,i,j,k,l=this;if(e=b&&b.keyCode===p?-1:1,f=H(l.$control_input[0]),l.$activeOption&&!l.settings.hideSelected&&(i=l.getAdjacentOption(l.$activeOption,-1).attr("data-value")),g=[],l.$activeItems.length){for(k=l.$control.children(".active:"+(e>0?"last":"first")),h=l.$control.children(":not(input)").index(k),e>0&&h++,c=0,d=l.$activeItems.length;d>c;c++)g.push(a(l.$activeItems[c]).attr("data-value")); 3 | b&&(b.preventDefault(),b.stopPropagation())}else(l.isFocused||"single"===l.settings.mode)&&l.items.length&&(0>e&&0===f.start&&0===f.length?g.push(l.items[l.caretPos-1]):e>0&&f.start===l.$control_input.val().length&&g.push(l.items[l.caretPos]));if(!g.length||"function"==typeof l.settings.onDelete&&l.settings.onDelete.apply(l,[g])===!1)return!1;for("undefined"!=typeof h&&l.setCaret(h);g.length;)l.removeItem(g.pop());return l.showInput(),l.positionDropdown(),l.refreshOptions(!0),i&&(j=l.getOption(i),j.length&&l.setActiveOption(j)),!0},advanceSelection:function(a,b){var c,d,e,f,g,h,i=this;0!==a&&(i.rtl&&(a*=-1),c=a>0?"last":"first",d=H(i.$control_input[0]),i.isFocused&&!i.isInputHidden?(f=i.$control_input.val().length,g=0>a?0===d.start&&0===d.length:d.start===f,g&&!f&&i.advanceCaret(a,b)):(h=i.$control.children(".active:"+c),h.length&&(e=i.$control.children(":not(input)").index(h),i.setActiveItem(null),i.setCaret(a>0?e+1:e))))},advanceCaret:function(a,b){var c,d,e=this;0!==a&&(c=a>0?"next":"prev",e.isShiftDown?(d=e.$control_input[c](),d.length&&(e.hideInput(),e.setActiveItem(d),b&&b.preventDefault())):e.setCaret(e.caretPos+a))},setCaret:function(b){var c=this;if(b="single"===c.settings.mode?c.items.length:Math.max(0,Math.min(c.items.length,b)),!c.isPending){var d,e,f,g;for(f=c.$control.children(":not(input)"),d=0,e=f.length;e>d;d++)g=a(f[d]).detach(),b>d?c.$control_input.before(g):c.$control.append(g)}c.caretPos=b},lock:function(){this.close(),this.isLocked=!0,this.refreshState()},unlock:function(){this.isLocked=!1,this.refreshState()},disable:function(){var a=this;a.$input.prop("disabled",!0),a.$control_input.prop("disabled",!0).prop("tabindex",-1),a.isDisabled=!0,a.lock()},enable:function(){var a=this;a.$input.prop("disabled",!1),a.$control_input.prop("disabled",!1).prop("tabindex",a.tabIndex),a.isDisabled=!1,a.unlock()},destroy:function(){var b=this,c=b.eventNS,d=b.revertSettings;b.trigger("destroy"),b.off(),b.$wrapper.remove(),b.$dropdown.remove(),b.$input.html("").append(d.$children).removeAttr("tabindex").removeClass("selectized").attr({tabindex:d.tabindex}).show(),b.$control_input.removeData("grow"),b.$input.removeData("selectize"),a(window).off(c),a(document).off(c),a(document.body).off(c),delete b.$input[0].selectize},render:function(a,b){var c,d,e="",f=!1,g=this,h=/^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;return("option"===a||"item"===a)&&(c=z(b[g.settings.valueField]),f=!!c),f&&(y(g.renderCache[a])||(g.renderCache[a]={}),g.renderCache[a].hasOwnProperty(c))?g.renderCache[a][c]:(e=g.settings.render[a].apply(this,[b,A]),("option"===a||"option_create"===a)&&(e=e.replace(h,"<$1 data-selectable")),"optgroup"===a&&(d=b[g.settings.optgroupValueField]||"",e=e.replace(h,'<$1 data-group="'+B(A(d))+'"')),("option"===a||"item"===a)&&(e=e.replace(h,'<$1 data-value="'+B(A(c||""))+'"')),f&&(g.renderCache[a][c]=e),e)},clearCache:function(a){var b=this;"undefined"==typeof a?b.renderCache={}:delete b.renderCache[a]},canCreate:function(a){var b=this;if(!b.settings.create)return!1;var c=b.settings.createFilter;return!(!a.length||"function"==typeof c&&!c.apply(b,[a])||"string"==typeof c&&!new RegExp(c).test(a)||c instanceof RegExp&&!c.test(a))}}),L.count=0,L.defaults={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:!1,createOnBlur:!1,createFilter:null,highlight:!0,openOnFocus:!0,maxOptions:1e3,maxItems:null,hideSelected:null,addPrecedence:!1,selectOnTab:!1,preload:!1,allowEmptyOption:!1,closeAfterSelect:!1,scrollDuration:60,loadThrottle:300,loadingClass:"loading",dataAttr:"data-data",optgroupField:"optgroup",valueField:"value",labelField:"text",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"selectize-control",inputClass:"selectize-input",dropdownClass:"selectize-dropdown",dropdownContentClass:"selectize-dropdown-content",dropdownParent:null,copyClassesToDropdown:!0,render:{}},a.fn.selectize=function(b){var c=a.fn.selectize.defaults,d=a.extend({},c,b),e=d.dataAttr,f=d.labelField,g=d.valueField,h=d.optgroupField,i=d.optgroupLabelField,j=d.optgroupValueField,k=function(b,c){var h,i,j,k,l=b.attr(e);if(l)for(c.options=JSON.parse(l),h=0,i=c.options.length;i>h;h++)c.items.push(c.options[h][g]);else{var m=a.trim(b.val()||"");if(!d.allowEmptyOption&&!m.length)return;for(j=m.split(d.delimiter),h=0,i=j.length;i>h;h++)k={},k[f]=j[h],k[g]=j[h],c.options.push(k);c.items=j}},l=function(b,c){var k,l,m,n,o=c.options,p={},q=function(a){var b=e&&a.attr(e);return"string"==typeof b&&b.length?JSON.parse(b):null},r=function(b,e){b=a(b);var i=z(b.attr("value"));if(i||d.allowEmptyOption)if(p.hasOwnProperty(i)){if(e){var j=p[i][h];j?a.isArray(j)?j.push(e):p[i][h]=[j,e]:p[i][h]=e}}else{var k=q(b)||{};k[f]=k[f]||b.text(),k[g]=k[g]||i,k[h]=k[h]||e,p[i]=k,o.push(k),b.is(":selected")&&c.items.push(i)}},s=function(b){var d,e,f,g,h;for(b=a(b),f=b.attr("label"),f&&(g=q(b)||{},g[i]=f,g[j]=f,c.optgroups.push(g)),h=a("option",b),d=0,e=h.length;e>d;d++)r(h[d],f)};for(c.maxItems=b.attr("multiple")?null:1,n=b.children(),k=0,l=n.length;l>k;k++)m=n[k].tagName.toLowerCase(),"optgroup"===m?s(n[k]):"option"===m&&r(n[k])};return this.each(function(){if(!this.selectize){var e,f=a(this),g=this.tagName.toLowerCase(),h=f.attr("placeholder")||f.attr("data-placeholder");h||d.allowEmptyOption||(h=f.children('option[value=""]').text());var i={placeholder:h,options:[],optgroups:[],items:[]};"select"===g?l(f,i):k(f,i),e=new L(f,a.extend(!0,{},c,i,b))}})},a.fn.selectize.defaults=L.defaults,a.fn.selectize.support={validity:x},L.define("drag_drop",function(){if(!a.fn.sortable)throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');if("multi"===this.settings.mode){var b=this;b.lock=function(){var a=b.lock;return function(){var c=b.$control.data("sortable");return c&&c.disable(),a.apply(b,arguments)}}(),b.unlock=function(){var a=b.unlock;return function(){var c=b.$control.data("sortable");return c&&c.enable(),a.apply(b,arguments)}}(),b.setup=function(){var c=b.setup;return function(){c.apply(this,arguments);var d=b.$control.sortable({items:"[data-value]",forcePlaceholderSize:!0,disabled:b.isLocked,start:function(a,b){b.placeholder.css("width",b.helper.css("width")),d.css({overflow:"visible"})},stop:function(){d.css({overflow:"hidden"});var c=b.$activeItems?b.$activeItems.slice():null,e=[];d.children("[data-value]").each(function(){e.push(a(this).attr("data-value"))}),b.setValue(e),b.setActiveItem(c)}})}}()}}),L.define("dropdown_header",function(b){var c=this;b=a.extend({title:"Untitled",headerClass:"selectize-dropdown-header",titleRowClass:"selectize-dropdown-header-title",labelClass:"selectize-dropdown-header-label",closeClass:"selectize-dropdown-header-close",html:function(a){return'
    '+a.title+'×
    '}},b),c.setup=function(){var d=c.setup;return function(){d.apply(c,arguments),c.$dropdown_header=a(b.html(b)),c.$dropdown.prepend(c.$dropdown_header)}}()}),L.define("optgroup_columns",function(b){var c=this;b=a.extend({equalizeWidth:!0,equalizeHeight:!0},b),this.getAdjacentOption=function(b,c){var d=b.closest("[data-group]").find("[data-selectable]"),e=d.index(b)+c;return e>=0&&e
    ',a=a.firstChild,c.body.appendChild(a),b=d.width=a.offsetWidth-a.clientWidth,c.body.removeChild(a)),b},e=function(){var e,f,g,h,i,j,k;if(k=a("[data-group]",c.$dropdown_content),f=k.length,f&&c.$dropdown_content.width()){if(b.equalizeHeight){for(g=0,e=0;f>e;e++)g=Math.max(g,k.eq(e).height());k.css({height:g})}b.equalizeWidth&&(j=c.$dropdown_content.innerWidth()-d(),h=Math.round(j/f),k.css({width:h}),f>1&&(i=j-h*(f-1),k.eq(f-1).css({width:i})))}};(b.equalizeHeight||b.equalizeWidth)&&(C.after(this,"positionDropdown",e),C.after(this,"refreshOptions",e))}),L.define("remove_button",function(b){if("single"!==this.settings.mode){b=a.extend({label:"×",title:"Remove",className:"remove",append:!0},b);var c=this,d=''+b.label+"",e=function(a,b){var c=a.search(/(<\/[^>]+>\s*)$/);return a.substring(0,c)+b+a.substring(c)};this.setup=function(){var f=c.setup;return function(){if(b.append){var g=c.settings.render.item;c.settings.render.item=function(){return e(g.apply(this,arguments),d)}}f.apply(this,arguments),this.$control.on("click","."+b.className,function(b){if(b.preventDefault(),!c.isLocked){var d=a(b.currentTarget).parent();c.setActiveItem(d),c.deleteSelection()&&c.setCaret(c.items.length)}})}}()}}),L.define("restore_on_backspace",function(a){var b=this;a.text=a.text||function(a){return a[this.settings.labelField]},this.onKeyDown=function(){var c=b.onKeyDown;return function(b){var d,e;return b.keyCode===p&&""===this.$control_input.val()&&!this.$activeItems.length&&(d=this.caretPos-1,d>=0&&d