├── .github └── FUNDING.yml ├── assets ├── screenshot-1.png ├── screenshot-2.gif └── screenshot-3.gif ├── vendor ├── composer │ ├── installed.json │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── autoload_classmap.php │ ├── installed.php │ ├── LICENSE │ ├── autoload_static.php │ ├── autoload_real.php │ ├── InstalledVersions.php │ └── ClassLoader.php └── autoload.php ├── .config └── rome.rjson ├── template └── fields-daterange.html ├── composer.json ├── composer.lock ├── date-range-ninja-forms.php ├── js ├── date-setting-field.js └── date-range.js ├── readme.txt ├── src ├── Field.php └── DateRange.php ├── README.md └── languages └── date-range-ninja-forms.pot /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | custom: https://paypal.me/PerSoderlind 3 | 4 | -------------------------------------------------------------------------------- /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soderlind/date-range-ninja-forms/HEAD/assets/screenshot-1.png -------------------------------------------------------------------------------- /assets/screenshot-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soderlind/date-range-ninja-forms/HEAD/assets/screenshot-2.gif -------------------------------------------------------------------------------- /assets/screenshot-3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soderlind/date-range-ninja-forms/HEAD/assets/screenshot-3.gif -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [], 3 | "dev": true, 4 | "dev-package-names": [] 5 | } 6 | -------------------------------------------------------------------------------- /.config/rome.rjson: -------------------------------------------------------------------------------- 1 | // For configuration documentation see http://romefrontend.dev/#project-configuration 2 | name: "date-range-ninja-forms" 3 | root: true 4 | -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | array($baseDir . '/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | $vendorDir . '/composer/InstalledVersions.php', 10 | ); 11 | -------------------------------------------------------------------------------- /template/fields-daterange.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soderlind/date-range-ninja-forms", 3 | "description": "Date Range field for Ninja Forms", 4 | "type": "wordpress-plugin", 5 | "homepage": "https://github.com/soderlind/date-range-ninja-forms", 6 | "license": [ 7 | "GPL-2.0-only" 8 | ], 9 | "authors": [ 10 | { 11 | "name": "Per Søderlind", 12 | "role": "Developer", 13 | "email": "per@soderlind.no", 14 | "homepage": "https://soderlind.no" 15 | } 16 | ], 17 | "autoload": { 18 | "psr-4": { 19 | "Soderlind\\NinjaForms\\DateRange\\": "src/" 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "f4158eb98d828e207a81c64117adc7e3", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": [], 16 | "platform-dev": [], 17 | "plugin-api-version": "2.0.0" 18 | } 19 | -------------------------------------------------------------------------------- /vendor/composer/installed.php: -------------------------------------------------------------------------------- 1 | 3 | array ( 4 | 'pretty_version' => 'dev-master', 5 | 'version' => 'dev-master', 6 | 'aliases' => 7 | array ( 8 | ), 9 | 'reference' => 'd6ec6ce6c5dd3f6ce168b30a985493f0275e5c56', 10 | 'name' => 'soderlind/date-range-ninja-forms', 11 | ), 12 | 'versions' => 13 | array ( 14 | 'soderlind/date-range-ninja-forms' => 15 | array ( 16 | 'pretty_version' => 'dev-master', 17 | 'version' => 'dev-master', 18 | 'aliases' => 19 | array ( 20 | ), 21 | 'reference' => 'd6ec6ce6c5dd3f6ce168b30a985493f0275e5c56', 22 | ), 23 | ), 24 | ); 25 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'Soderlind\\NinjaForms\\DateRange\\' => 31, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'Soderlind\\NinjaForms\\DateRange\\' => 18 | array ( 19 | 0 => __DIR__ . '/../..' . '/src', 20 | ), 21 | ); 22 | 23 | public static $classMap = array ( 24 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 25 | ); 26 | 27 | public static function getInitializer(ClassLoader $loader) 28 | { 29 | return \Closure::bind(function () use ($loader) { 30 | $loader->prefixLengthsPsr4 = ComposerStaticInitc0a54d18667d05a61f5a4415561bb213::$prefixLengthsPsr4; 31 | $loader->prefixDirsPsr4 = ComposerStaticInitc0a54d18667d05a61f5a4415561bb213::$prefixDirsPsr4; 32 | $loader->classMap = ComposerStaticInitc0a54d18667d05a61f5a4415561bb213::$classMap; 33 | 34 | }, null, ClassLoader::class); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /date-range-ninja-forms.php: -------------------------------------------------------------------------------- 1 | { 10 | const nfRadio = Backbone.Radio; // rome-ignore lint/js/noUndeclaredVariables: Backbone is an external object. 11 | 12 | const DateRangeMinDateSettingChannel = nfRadio.channel("setting-min_date"); 13 | const DateRangeMaxDateSettingChannel = nfRadio.channel("setting-max_date"); 14 | 15 | const DateField = class extends Marionette.Object { // rome-ignore lint/js/noUndeclaredVariables: Marionett is an external object. 16 | nfTextboxStyle = { 17 | "background": "#f9f9f9", 18 | "border": "0", 19 | "marginTop": "7px", 20 | "padding": "12px 15px", 21 | "width": "100%", 22 | "height": "41px", 23 | }; 24 | /** 25 | * initialize() 26 | * 27 | */ 28 | initialize() { 29 | this.listenTo( 30 | DateRangeMinDateSettingChannel, 31 | "render:setting", 32 | this.renderDateField, 33 | ); 34 | this.listenTo( 35 | DateRangeMaxDateSettingChannel, 36 | "render:setting", 37 | this.renderDateField, 38 | ); 39 | } 40 | 41 | /** 42 | * Convert the textbox (input type="text") to date field (input type="date"). 43 | * 44 | * - If empty, set the date to "today", in the format YYYY-MM-DD. 45 | * - Set the style of the field to the same as a textbox. 46 | * 47 | * @see https://caniuse.com/input-datetime 48 | * 49 | * @param {*} settingModel 50 | * @param {*} dataModel 51 | * @param {*} view 52 | */ 53 | renderDateField(settingModel, dataModel, view) { 54 | const element = view.el.getElementsByClassName("setting")[0]; 55 | element.attributes["type"].value = "date"; 56 | element.attributes["pattern"] = "d{4}-d{2}-d{2}"; 57 | Object.assign(element.style, this.nfTextboxStyle); 58 | } 59 | }; 60 | 61 | new DateField(); 62 | }, 63 | ); 64 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 30 | if ($useStaticLoader) { 31 | require __DIR__ . '/autoload_static.php'; 32 | 33 | call_user_func(\Composer\Autoload\ComposerStaticInitc0a54d18667d05a61f5a4415561bb213::getInitializer($loader)); 34 | } else { 35 | $map = require __DIR__ . '/autoload_namespaces.php'; 36 | foreach ($map as $namespace => $path) { 37 | $loader->set($namespace, $path); 38 | } 39 | 40 | $map = require __DIR__ . '/autoload_psr4.php'; 41 | foreach ($map as $namespace => $path) { 42 | $loader->setPsr4($namespace, $path); 43 | } 44 | 45 | $classMap = require __DIR__ . '/autoload_classmap.php'; 46 | if ($classMap) { 47 | $loader->addClassMap($classMap); 48 | } 49 | } 50 | 51 | $loader->register(true); 52 | 53 | return $loader; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Date Range for Ninja Forms === 2 | Contributors: PerS 3 | Donate link: https://soderlind.no/donate/ 4 | Tags: date 5 | Requires at least: 5.4 6 | Tested up to: 5.7 7 | Stable tag: trunk 8 | Requires PHP: 7.2 9 | License: GPLv2 or later 10 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 | 12 | Add a Date Range field to Ninja Forms. 13 | 14 | == Description == 15 | 16 | Add a Date Range field to your Ninja Forms. 17 | 18 | == Filters == 19 | 20 | Add the filters to your child theme functions.php 21 | 22 | = `date_range_lang` = 23 | 24 | Override the value returned from get_locale(). 25 | 26 | E.g. if using Polylang, add: 27 | 28 | ` 29 | add_filter( 'date_range_lang', function( $locale ) { 30 | if ( function_exists( 'pll_current_language' ) ) { 31 | $locale = pll_current_language( 'locale' ); 32 | } 33 | return $locale; 34 | } ); 35 | ` 36 | 37 | = `date_range_dropdowns` = 38 | 39 | Enable dropdowns for months, years. 40 | 41 | If `maxYear` is `null` then `maxYear` will be equal to `(new Date()).getFullYear()`. 42 | 43 | ` 44 | add_filter( 'date_range_dropdowns', function( $dropdowns ) { 45 | 46 | $dropdowns = [ 47 | 'minYear' => 2020, 48 | 'maxYear' => 2030, 49 | 'months' => false, 50 | 'years' => true, // show dropdown for years. 51 | ]; 52 | 53 | return $dropdowns; 54 | } ); 55 | ` 56 | 57 | = `date_range_buttontext` = 58 | 59 | Text for buttons. 60 | 61 | ` 62 | add_filter( 'date_range_buttontext', function( $buttontext ) { 63 | 64 | $buttontext = [ 65 | 'apply' => 'Apply', 66 | 'cancel' => 'Cancel', 67 | 'previousMonth' => '', 68 | 'nextMonth' => '', 69 | ]; 70 | 71 | return $buttontext; 72 | } ); 73 | ` 74 | 75 | 76 | == Installation == 77 | 78 | 1. Upload the plugin files to the `/wp-content/plugins/data-range-ninja-forms` directory, or install the plugin through the WordPress plugins screen directly. 79 | 1. Activate the plugin through the 'Plugins' screen in WordPress 80 | 1. Use Ninja Forms to add the Date Range field. 81 | 82 | == Screenshots == 83 | 84 | 1. Settings. 85 | 2. Using Ninja Forms to add the Date Range field. 86 | 3. Date Range at the front-end. 87 | 88 | == Changelog == 89 | 90 | = 1.1.0 = 91 | 92 | * Update to latest version of [Litepicker](https://github.com/wakirin/Litepicker) 93 | 94 | = 1.0.2 = 95 | 96 | * Lint source code using PHP CodeSniffer and [Rome](https://rome.tools/). 97 | 98 | = 1.0.1 = 99 | 100 | * Add date setting field. 101 | 102 | = 1.0.0 = 103 | 104 | * Refactor 105 | 106 | = 0.0.7 = 107 | 108 | * Refactor JavaScript to ES6. 109 | 110 | = 0.0.6 = 111 | 112 | * Fix breaking bug 113 | 114 | = 0.0.5 = 115 | 116 | * Add more settings. 117 | * Add [filters](#filters): `date_range_lang`, `date_range_dropdowns` and `date_range_buttontext`. 118 | * Add `languages/date-range-ninja-forms.pot` 119 | 120 | = 0.0.4 = 121 | 122 | * Replace Lightpick, no longer maintained, with [Litepicker](https://github.com/wakirin/Litepicker) 123 | 124 | = 0.0.3 = 125 | 126 | * In Ninja Forms builder, select WP Settings date. 127 | 128 | = 0.0.2 = 129 | 130 | * Set date format in Ninja Form builder 131 | 132 | = 0.0.1 = 133 | 134 | * Initial release. 135 | 136 | 137 | -------------------------------------------------------------------------------- /src/Field.php: -------------------------------------------------------------------------------- 1 | _nicename = __( 'Date Range', 'date-range-ninja-forms' ); 112 | $this->init(); 113 | } 114 | 115 | 116 | /** 117 | * Process the field. 118 | * 119 | * @param array $field Fields. 120 | * @param array $data Data. 121 | * @return array 122 | */ 123 | public function process( $field, $data ) { 124 | return $data; 125 | } 126 | 127 | /** 128 | * Init. 129 | * 130 | * @return void 131 | */ 132 | public function init() { 133 | add_filter( 'ninja_forms_field_template_file_paths', [ $this, 'register_template_path' ] ); 134 | add_action( 'ninja_forms_enqueue_scripts', [ $this, 'scripts' ] ); 135 | add_action( 'init', [ $this, 'load_textdomain' ] ); 136 | } 137 | 138 | /** 139 | * Register the template path for the plugin 140 | * 141 | * @param array $file_paths Template paths. 142 | * 143 | * @return array 144 | */ 145 | public function register_template_path( $file_paths ) { 146 | $file_paths[] = plugin_dir_path( DATERANGE_FILE ) . 'template/'; 147 | return $file_paths; 148 | } 149 | 150 | /** 151 | * Enqueue scripts 152 | * 153 | * The js/date-range.js file connects the Litepicker script with ninja forms. 154 | * 155 | * @return void 156 | */ 157 | public function scripts() { 158 | wp_enqueue_script( 'dayjs', '//cdnjs.cloudflare.com/ajax/libs/dayjs/1.8.35/dayjs.min.js', [], DATERANGE_VERSION, true ); 159 | wp_enqueue_script( 'lightpicker', '//cdn.jsdelivr.net/npm/litepicker/dist/litepicker.js', [ 'dayjs' ], DATERANGE_VERSION, true ); 160 | wp_enqueue_script( 'date-range', plugin_dir_url( DATERANGE_FILE ) . 'js/date-range.js', [ 'lightpicker' ], DATERANGE_VERSION, true ); 161 | wp_localize_script( 162 | 'date-range', 163 | 'drDateRange', 164 | [ 165 | 'dateFormat' => get_option( 'date_format' ), 166 | 'lang' => apply_filters( 'date_range_lang', get_locale() ), 167 | 'dropdowns' => apply_filters( 'date_range_dropdowns', wp_json_encode( [] ) ), 168 | 'buttontext' => apply_filters( 'date_range_buttontext', wp_json_encode( [] ) ), 169 | ] 170 | ); 171 | } 172 | 173 | /** 174 | * Load translation. 175 | * 176 | * @return void 177 | */ 178 | public function load_textdomain() { 179 | load_plugin_textdomain( 'date-range-ninja-forms', false, dirname( plugin_basename( DATERANGE_FILE ) ) . '/languages' ); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Date Range field for Ninja Forms 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/soderlind/date-range-ninja-forms/v)](//packagist.org/packages/soderlind/date-range-ninja-forms) 4 | 5 | 6 | [Description](#description) | [Installation](#installation) | [Screenshots](#screenshots) | [Filters](#filters) | [Changelog](#changelog) | [Credits](#credits) | [Copyright and License](#copyright-and-license) 7 | 8 | 9 | ## Description 10 | 11 | Add a Date Range field to your Ninja Forms. 12 | 13 | 14 | ## Installation 15 | 16 | 1. Upload the plugin files to the `/wp-content/plugins/data-range-ninja-forms` directory, or install the plugin through the WordPress plugins screen directly. 17 | 1. Activate the plugin through the 'Plugins' screen in WordPress 18 | 1. Use Ninja Forms to add the Date Range field. 19 | 20 | 21 | ## Screenshots 22 | 23 | ### 1. Settings 24 | 25 | 26 | ### 2. Using Ninja Forms to add the Date Range field. 27 | 28 | 29 | ### 3. Date Range at the front-end. 30 | 31 | 32 | ## Filters 33 | 34 | Add the filters to your child theme functions.php 35 | 36 | ### `date_range_lang` 37 | 38 | Override the value returned from get_locale(). 39 | 40 | E.g. if using Polylang, add: 41 | 42 | ```php 43 | add_filter( 'date_range_lang', function( $locale ) { 44 | if ( function_exists( 'pll_current_language' ) ) { 45 | $locale = pll_current_language( 'locale' ); 46 | } 47 | return $locale; 48 | } ); 49 | ``` 50 | 51 | ### `date_range_dropdowns` 52 | 53 | Enable dropdowns for months, years. 54 | 55 | If `maxYear` is `null` then `maxYear` will be equal to `(new Date()).getFullYear()`. 56 | 57 | ```php 58 | add_filter( 'date_range_dropdowns', function( $dropdowns ) { 59 | 60 | $dropdowns = [ 61 | 'minYear' => 2020, 62 | 'maxYear' => 2030, 63 | 'months' => false, 64 | 'years' => true, // show dropdown for years. 65 | ]; 66 | 67 | return $dropdowns; 68 | } ); 69 | ``` 70 | 71 | ### `date_range_buttontext` 72 | 73 | Text for buttons. 74 | 75 | ```php 76 | add_filter( 'date_range_buttontext', function( $buttontext ) { 77 | 78 | $buttontext = [ 79 | 'apply' => 'Apply', 80 | 'cancel' => 'Cancel', 81 | 'previousMonth' => '', 82 | 'nextMonth' => '', 83 | ]; 84 | 85 | return $buttontext; 86 | } ); 87 | ``` 88 | 89 | ## See also 90 | 91 | I've created an [iCalendar](https://github.com/soderlind/icalendar-ninja-form) add-on for Ninja Forms. 92 | 93 | ## Changelog 94 | 95 | ### 1.1.0 96 | 97 | - Update to latest version of [Litepicker](https://github.com/wakirin/Litepicker) 98 | 99 | ### 1.0.2 100 | 101 | - Lint source code using PHP CodeSniffer and [Rome](https://rome.tools/). 102 | 103 | ### 1.0.1 104 | 105 | - Add date setting field 106 | 107 | ### 1.0.0 108 | 109 | - Refactor 110 | 111 | ### 0.1.0 112 | 113 | - [Custom validation](https://github.com/soderlind/date-range-ninja-forms/blob/master/js/date-range.js#L29-L106) for required fields 114 | - Add moment.js 115 | - Refactor code, using ES2019 Class Fields. 116 | 117 | ### 0.0.7 118 | 119 | - Refactor JavaScript to ES6. 120 | 121 | ### 0.0.6 122 | 123 | - Fix breaking bug 124 | 125 | ### 0.0.5 126 | 127 | - Add more settings. 128 | - Add [filters](#filters): `date_range_lang`, `date_range_dropdowns` and `date_range_buttontext`. 129 | - Add `languages/date-range-ninja-forms.pot` 130 | 131 | 132 | ### 0.0.4 133 | 134 | - Replace Lightpick, no longer maintained, with [Litepicker](https://github.com/wakirin/Litepicker) 135 | 136 | 137 | ### 0.0.3 138 | 139 | - In Ninja Forms builder, select WP Settings date. 140 | 141 | ### 0.0.2 142 | 143 | - Set date format in Ninja Form builder 144 | 145 | ### 0.0.1 146 | 147 | - Initial release. 148 | 149 | 150 | ## Credits 151 | 152 | Date Range field for Ninja Forms uses [Litepicker](https://github.com/wakirin/Litepicker). The Litepicker has a MIT licence and is Copyright 2019 [Rinat G](https://github.com/wakirin/). 153 | 154 | ## Copyright and License 155 | 156 | Date Range field for Ninja Forms is copyright 2020 Per Soderlind 157 | 158 | Date Range field for Ninja Forms is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. 159 | 160 | Date Range field for Ninja Forms is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 161 | 162 | You should have received a copy of the GNU Lesser General Public License along with the Extension. If not, see http://www.gnu.org/licenses/. 163 | -------------------------------------------------------------------------------- /languages/date-range-ninja-forms.pot: -------------------------------------------------------------------------------- 1 | #, fuzzy 2 | msgid "" 3 | msgstr "" 4 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" 5 | "Project-Id-Version: Date Range field for Ninja Forms\n" 6 | "POT-Creation-Date: 2020-03-05 00:13+0100\n" 7 | "PO-Revision-Date: 2020-03-04 10:46+0100\n" 8 | "Last-Translator: \n" 9 | "Language-Team: \n" 10 | "MIME-Version: 1.0\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: 8bit\n" 13 | "X-Generator: Poedit 2.3\n" 14 | "X-Poedit-Basepath: ..\n" 15 | "X-Poedit-Flags-xgettext: --add-comments=translators:\n" 16 | "X-Poedit-WPHeader: date-range-ninja-forms.php\n" 17 | "X-Poedit-SourceCharset: UTF-8\n" 18 | "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;" 19 | "esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;" 20 | "_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n" 21 | "X-Poedit-SearchPath-0: .\n" 22 | "X-Poedit-SearchPathExcluded-0: *.min.js\n" 23 | 24 | #: date-range-ninja-forms.php:75 25 | msgid "Date Range" 26 | msgstr "" 27 | 28 | #: date-range-ninja-forms.php:143 29 | msgid "Date Format" 30 | msgstr "" 31 | 32 | #: date-range-ninja-forms.php:148 33 | #, php-format 34 | msgid "WP Settings (%s)" 35 | msgstr "" 36 | 37 | #: date-range-ninja-forms.php:198 38 | msgid "Start of Week" 39 | msgstr "" 40 | 41 | #: date-range-ninja-forms.php:203 42 | msgid "Sunday" 43 | msgstr "" 44 | 45 | #: date-range-ninja-forms.php:207 46 | msgid "Monday" 47 | msgstr "" 48 | 49 | #: date-range-ninja-forms.php:211 50 | msgid "Tuesday" 51 | msgstr "" 52 | 53 | #: date-range-ninja-forms.php:215 54 | msgid "Wednesday" 55 | msgstr "" 56 | 57 | #: date-range-ninja-forms.php:219 58 | msgid "Thursday" 59 | msgstr "" 60 | 61 | #: date-range-ninja-forms.php:223 62 | msgid "Friday" 63 | msgstr "" 64 | 65 | #: date-range-ninja-forms.php:227 66 | msgid "Saturday" 67 | msgstr "" 68 | 69 | #: date-range-ninja-forms.php:245 70 | msgid "Show Tool Tip" 71 | msgstr "" 72 | 73 | #: date-range-ninja-forms.php:254 74 | msgid "Singular" 75 | msgstr "" 76 | 77 | #: date-range-ninja-forms.php:266 78 | msgid "Plural" 79 | msgstr "" 80 | 81 | #: date-range-ninja-forms.php:279 82 | msgid "Tooltip" 83 | msgstr "" 84 | 85 | #: date-range-ninja-forms.php:288 86 | msgid "Limit Range" 87 | msgstr "" 88 | 89 | #: date-range-ninja-forms.php:297 90 | msgid "Start Date" 91 | msgstr "" 92 | 93 | #: date-range-ninja-forms.php:298 94 | msgid "The minimum/earliest date that can be selected." 95 | msgstr "" 96 | 97 | #: date-range-ninja-forms.php:310 98 | msgid "End Date" 99 | msgstr "" 100 | 101 | #: date-range-ninja-forms.php:311 102 | msgid "" 103 | "The maximum/latest date that can be selected. Leave blank if indefinite." 104 | msgstr "" 105 | 106 | #: date-range-ninja-forms.php:324 107 | msgid "Set Min / Max Days" 108 | msgstr "" 109 | 110 | #: date-range-ninja-forms.php:334 111 | msgid "Minimum days" 112 | msgstr "" 113 | 114 | #: date-range-ninja-forms.php:335 115 | msgid "The minimum days of the selected range." 116 | msgstr "" 117 | 118 | #: date-range-ninja-forms.php:346 119 | msgid "Maximum Days" 120 | msgstr "" 121 | 122 | #: date-range-ninja-forms.php:347 123 | msgid "The maximum days of the selected range." 124 | msgstr "" 125 | 126 | #: date-range-ninja-forms.php:359 127 | msgid "Control date range" 128 | msgstr "" 129 | 130 | #: date-range-ninja-forms.php:368 131 | msgid "Number of days" 132 | msgstr "" 133 | 134 | #: date-range-ninja-forms.php:377 135 | msgid "Show Week Numbers" 136 | msgstr "" 137 | 138 | #: date-range-ninja-forms.php:386 139 | msgid "Disable Weekends" 140 | msgstr "" 141 | 142 | #: date-range-ninja-forms.php:395 143 | msgid "Select Backward" 144 | msgstr "" 145 | 146 | #: date-range-ninja-forms.php:396 147 | msgid "Select second date before the first selected date." 148 | msgstr "" 149 | 150 | #: date-range-ninja-forms.php:405 151 | msgid "Select Forward" 152 | msgstr "" 153 | 154 | #: date-range-ninja-forms.php:406 155 | msgid "Select second date after the first selected date." 156 | msgstr "" 157 | 158 | #: date-range-ninja-forms.php:415 159 | msgid "Auto Apply" 160 | msgstr "" 161 | 162 | #: date-range-ninja-forms.php:416 163 | msgid "" 164 | "When enabled, hide the apply and cancel buttons, and automatically apply a " 165 | "new date range as soon as two dates are clicked." 166 | msgstr "" 167 | 168 | #. Plugin Name of the plugin/theme 169 | msgid "Date Range field for Ninja Forms" 170 | msgstr "" 171 | 172 | #. Plugin URI of the plugin/theme 173 | msgid "https://github.com/soderlind/date-range-ninja-forms" 174 | msgstr "" 175 | 176 | #. Description of the plugin/theme 177 | msgid "Add a Date Range field to your Ninja Forms." 178 | msgstr "" 179 | 180 | #. Author of the plugin/theme 181 | msgid "Per Soderlind" 182 | msgstr "" 183 | 184 | #. Author URI of the plugin/theme 185 | msgid "https://soderlind.no" 186 | msgstr "" 187 | -------------------------------------------------------------------------------- /vendor/composer/InstalledVersions.php: -------------------------------------------------------------------------------- 1 | 27 | array ( 28 | 'pretty_version' => 'dev-master', 29 | 'version' => 'dev-master', 30 | 'aliases' => 31 | array ( 32 | ), 33 | 'reference' => 'd6ec6ce6c5dd3f6ce168b30a985493f0275e5c56', 34 | 'name' => 'soderlind/date-range-ninja-forms', 35 | ), 36 | 'versions' => 37 | array ( 38 | 'soderlind/date-range-ninja-forms' => 39 | array ( 40 | 'pretty_version' => 'dev-master', 41 | 'version' => 'dev-master', 42 | 'aliases' => 43 | array ( 44 | ), 45 | 'reference' => 'd6ec6ce6c5dd3f6ce168b30a985493f0275e5c56', 46 | ), 47 | ), 48 | ); 49 | private static $canGetVendors; 50 | private static $installedByVendor = array(); 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | public static function getInstalledPackages() 59 | { 60 | $packages = array(); 61 | foreach (self::getInstalled() as $installed) { 62 | $packages[] = array_keys($installed['versions']); 63 | } 64 | 65 | 66 | if (1 === \count($packages)) { 67 | return $packages[0]; 68 | } 69 | 70 | return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); 71 | } 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | public static function isInstalled($packageName) 82 | { 83 | foreach (self::getInstalled() as $installed) { 84 | if (isset($installed['versions'][$packageName])) { 85 | return true; 86 | } 87 | } 88 | 89 | return false; 90 | } 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | public static function satisfies(VersionParser $parser, $packageName, $constraint) 106 | { 107 | $constraint = $parser->parseConstraints($constraint); 108 | $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); 109 | 110 | return $provided->matches($constraint); 111 | } 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | public static function getVersionRanges($packageName) 123 | { 124 | foreach (self::getInstalled() as $installed) { 125 | if (!isset($installed['versions'][$packageName])) { 126 | continue; 127 | } 128 | 129 | $ranges = array(); 130 | if (isset($installed['versions'][$packageName]['pretty_version'])) { 131 | $ranges[] = $installed['versions'][$packageName]['pretty_version']; 132 | } 133 | if (array_key_exists('aliases', $installed['versions'][$packageName])) { 134 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); 135 | } 136 | if (array_key_exists('replaced', $installed['versions'][$packageName])) { 137 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); 138 | } 139 | if (array_key_exists('provided', $installed['versions'][$packageName])) { 140 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); 141 | } 142 | 143 | return implode(' || ', $ranges); 144 | } 145 | 146 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 147 | } 148 | 149 | 150 | 151 | 152 | 153 | public static function getVersion($packageName) 154 | { 155 | foreach (self::getInstalled() as $installed) { 156 | if (!isset($installed['versions'][$packageName])) { 157 | continue; 158 | } 159 | 160 | if (!isset($installed['versions'][$packageName]['version'])) { 161 | return null; 162 | } 163 | 164 | return $installed['versions'][$packageName]['version']; 165 | } 166 | 167 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 168 | } 169 | 170 | 171 | 172 | 173 | 174 | public static function getPrettyVersion($packageName) 175 | { 176 | foreach (self::getInstalled() as $installed) { 177 | if (!isset($installed['versions'][$packageName])) { 178 | continue; 179 | } 180 | 181 | if (!isset($installed['versions'][$packageName]['pretty_version'])) { 182 | return null; 183 | } 184 | 185 | return $installed['versions'][$packageName]['pretty_version']; 186 | } 187 | 188 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 189 | } 190 | 191 | 192 | 193 | 194 | 195 | public static function getReference($packageName) 196 | { 197 | foreach (self::getInstalled() as $installed) { 198 | if (!isset($installed['versions'][$packageName])) { 199 | continue; 200 | } 201 | 202 | if (!isset($installed['versions'][$packageName]['reference'])) { 203 | return null; 204 | } 205 | 206 | return $installed['versions'][$packageName]['reference']; 207 | } 208 | 209 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 210 | } 211 | 212 | 213 | 214 | 215 | 216 | public static function getRootPackage() 217 | { 218 | $installed = self::getInstalled(); 219 | 220 | return $installed[0]['root']; 221 | } 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | public static function getRawData() 230 | { 231 | return self::$installed; 232 | } 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | public static function reload($data) 253 | { 254 | self::$installed = $data; 255 | self::$installedByVendor = array(); 256 | } 257 | 258 | 259 | 260 | 261 | private static function getInstalled() 262 | { 263 | if (null === self::$canGetVendors) { 264 | self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); 265 | } 266 | 267 | $installed = array(); 268 | 269 | if (self::$canGetVendors) { 270 | foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { 271 | if (isset(self::$installedByVendor[$vendorDir])) { 272 | $installed[] = self::$installedByVendor[$vendorDir]; 273 | } elseif (is_file($vendorDir.'/composer/installed.php')) { 274 | $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; 275 | } 276 | } 277 | } 278 | 279 | $installed[] = self::$installed; 280 | 281 | return $installed; 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/DateRange.php: -------------------------------------------------------------------------------- 1 | init(); 36 | } 37 | return self::$instance; 38 | } 39 | 40 | /** 41 | * Add hooks. 42 | * 43 | * @return void 44 | */ 45 | public function init() { 46 | add_filter( 'ninja_forms_register_fields', [ $this, 'register_fields' ] ); 47 | add_filter( 'ninja_forms_field_settings', [ $this, 'field_settings' ] ); 48 | add_action( 'nf_admin_enqueue_scripts', [ $this, 'admin_scripts' ] ); 49 | } 50 | 51 | /** 52 | * Register Date Range field 53 | * 54 | * @param array $fields All fields. 55 | * 56 | * @return array 57 | */ 58 | public function register_fields( $fields ) { 59 | $fields['daterange'] = new Field(); 60 | 61 | return $fields; 62 | } 63 | 64 | /** 65 | * Add field settings. 66 | * 67 | * @param array $settings All setings. 68 | * 69 | * @return array 70 | */ 71 | public function field_settings( $settings ) { 72 | 73 | $settings['date_format'] = [ 74 | 'name' => 'date_format', 75 | 'type' => 'select', 76 | 'label' => __( 'Date Format', 'date-range-ninja-forms' ), 77 | 'width' => 'one-half', 78 | 'group' => 'primary', 79 | 'options' => [ 80 | [ 81 | /* translators: Get the date from WordPress settings. */ 82 | 'label' => sprintf( __( 'WP Settings (%s)', 'date-range-ninja-forms' ), get_option( 'date_format' ) ), 83 | 'value' => 'default', 84 | ], 85 | [ 86 | 'label' => 'm/d/Y', 87 | 'value' => 'MM/DD/YYYY', 88 | ], 89 | [ 90 | 'label' => 'm-d-Y', 91 | 'value' => 'MM-DD-YYYY', 92 | ], 93 | [ 94 | 'label' => 'm.d.Y', 95 | 'value' => 'MM.DD.YYYY', 96 | ], 97 | [ 98 | 'label' => 'd/m/Y', 99 | 'value' => 'DD/MM/YYYY', 100 | ], 101 | [ 102 | 'label' => 'd-m-Y', 103 | 'value' => 'DD-MM-YYYY', 104 | ], 105 | [ 106 | 'label' => 'd.m.Y', 107 | 'value' => 'DD.MM.YYYY', 108 | ], 109 | [ 110 | 'label' => 'Y-m-d', 111 | 'value' => 'YYYY-MM-DD', 112 | ], 113 | [ 114 | 'label' => 'Y/m/d', 115 | 'value' => 'YYYY/MM/DD', 116 | ], 117 | [ 118 | 'label' => 'Y.m.d', 119 | 'value' => 'YYYY.MM.DD', 120 | ], 121 | [ 122 | 'label' => 'l, F d Y', 123 | 'value' => 'dddd, MMMM D YYYY', 124 | ], 125 | ], 126 | 'value' => 'default', // the initial selected value. 127 | ]; 128 | 129 | $settings['start_of_week'] = [ 130 | 'name' => 'start_of_week', 131 | 'type' => 'select', 132 | 'label' => __( 'Start of Week', 'date-range-ninja-forms' ), 133 | 'width' => 'one-half', 134 | 'group' => 'primary', 135 | 'options' => [ 136 | [ 137 | 'label' => __( 'Sunday', 'date-range-ninja-forms' ), 138 | 'value' => '0', 139 | ], 140 | [ 141 | 'label' => __( 'Monday', 'date-range-ninja-forms' ), 142 | 'value' => '1', 143 | ], 144 | [ 145 | 'label' => __( 'Tuesday', 'date-range-ninja-forms' ), 146 | 'value' => '2', 147 | ], 148 | [ 149 | 'label' => __( 'Wednesday', 'date-range-ninja-forms' ), 150 | 'value' => '3', 151 | ], 152 | [ 153 | 'label' => __( 'Thursday', 'date-range-ninja-forms' ), 154 | 'value' => '4', 155 | ], 156 | [ 157 | 'label' => __( 'Friday', 'date-range-ninja-forms' ), 158 | 'value' => '5', 159 | ], 160 | [ 161 | 'label' => __( 'Saturday', 'date-range-ninja-forms' ), 162 | 'value' => '6', 163 | ], 164 | ], 165 | 'value' => get_option( 'start_of_week' ), // the initial selected value. 166 | ]; 167 | 168 | /* 169 | |-------------------------------------------------------------------------- 170 | | Advanced Settings 171 | |-------------------------------------------------------------------------- 172 | | 173 | | The least commonly used settings for a field. 174 | */ 175 | 176 | $tooltip['tooltip'] = [ 177 | 'name' => 'tooltip', 178 | 'type' => 'toggle', 179 | 'label' => esc_html__( 'Show Tool Tip', 'date-range-ninja-forms' ), 180 | 'width' => 'one-third', 181 | 'group' => 'advanced', 182 | 'value' => false, 183 | ]; 184 | 185 | $tooltip['tooltip_singular'] = [ 186 | 'name' => 'tooltip_singular', 187 | 'type' => 'textbox', 188 | 'label' => esc_html__( 'Singular', 'date-range-ninja-forms' ), 189 | 'width' => 'one-third', 190 | 'group' => 'advanced', 191 | 'value' => 'day', 192 | 'deps' => [ 193 | 'tooltip' => 1, 194 | ], 195 | ]; 196 | 197 | $tooltip['tooltip_plural'] = [ 198 | 'name' => 'tooltip_plural', 199 | 'type' => 'textbox', 200 | 'label' => esc_html__( 'Plural', 'date-range-ninja-forms' ), 201 | 'width' => 'one-third', 202 | 'group' => 'advanced', 203 | 'value' => 'days', 204 | 'deps' => [ 205 | 'tooltip' => 1, 206 | ], 207 | ]; 208 | 209 | $settings['tooltip_fieldset'] = [ 210 | 'name' => 'tooltip_fieldset', 211 | 'type' => 'fieldset', 212 | 'label' => esc_html__( 'Tooltip', 'date-range-ninja-forms' ), 213 | 'width' => 'full', 214 | 'group' => 'advanced', 215 | 'settings' => $tooltip, 216 | ]; 217 | 218 | $start_end['max_min_date'] = [ 219 | 'name' => 'max_min_date', 220 | 'type' => 'toggle', 221 | 'label' => esc_html__( 'Limit Range', 'date-range-ninja-forms' ), 222 | 'width' => 'one-third', 223 | 'group' => 'advanced', 224 | 'value' => 0, 225 | ]; 226 | 227 | $start_end['min_date'] = [ 228 | 'name' => 'min_date', 229 | 'type' => 'textbox', 230 | 'label' => esc_html__( 'Start Date', 'date-range-ninja-forms' ), 231 | 'help' => esc_html__( 'The minimum/earliest date that can be selected.', 'date-range-ninja-forms' ), 232 | 'placeholder' => 'YYYY-MM-DD', 233 | 'width' => 'one-third', 234 | 'group' => 'advanced', 235 | 'value' => '', 236 | 'deps' => [ 237 | 'max_min_date' => 1, 238 | ], 239 | ]; 240 | $start_end['max_date'] = [ 241 | 'name' => 'max_date', 242 | 'type' => 'textbox', 243 | 'label' => esc_html__( 'End Date', 'date-range-ninja-forms' ), 244 | 'help' => esc_html__( 'The maximum/latest date that can be selected. Leave blank if indefinite.', 'date-range-ninja-forms' ), 245 | 'placeholder' => 'YYYY-MM-DD', 246 | 'width' => 'one-third', 247 | 'group' => 'advanced', 248 | 'value' => '', 249 | 'deps' => [ 250 | 'max_min_date' => 1, 251 | ], 252 | ]; 253 | 254 | $min_max['min_max_days'] = [ 255 | 'name' => 'min_max_days', 256 | 'type' => 'toggle', 257 | 'label' => esc_html__( 'Set Min / Max Days', 'date-range-ninja-forms' ), 258 | 'width' => 'one-third', 259 | 'group' => 'advanced', 260 | 'value' => 0, 261 | ]; 262 | 263 | $min_max['min_days'] = [ 264 | 'name' => 'min_days', 265 | 'type' => 'number', 266 | 'label' => esc_html__( 'Minimum days', 'date-range-ninja-forms' ), 267 | 'help' => esc_html__( 'The minimum days of the selected range.', 'date-range-ninja-forms' ), 268 | 'width' => 'one-third', 269 | 'group' => 'advanced', 270 | 'value' => '0', 271 | 'deps' => [ 272 | 'min_max_days' => 1, 273 | ], 274 | ]; 275 | $min_max['max_days'] = [ 276 | 'name' => 'max_days', 277 | 'type' => 'number', 278 | 'label' => esc_html__( 'Maximum Days', 'date-range-ninja-forms' ), 279 | 'help' => esc_html__( 'The maximum days of the selected range.', 'date-range-ninja-forms' ), 280 | 'width' => 'one-third', 281 | 'group' => 'advanced', 282 | 'value' => '0', 283 | 'deps' => [ 284 | 'min_max_days' => 1, 285 | ], 286 | ]; 287 | 288 | $settings['max_min_date_fieldset'] = [ 289 | 'name' => 'max_min_date_fieldset', 290 | 'type' => 'fieldset', 291 | 'label' => esc_html__( 'Control date range', 'date-range-ninja-forms' ), 292 | 'width' => 'full', 293 | 'group' => 'advanced', 294 | 'settings' => $start_end, 295 | ]; 296 | 297 | $settings['min_max_days_fieldset'] = [ 298 | 'name' => 'min_max_days_fieldset', 299 | 'type' => 'fieldset', 300 | 'label' => esc_html__( 'Number of days', 'date-range-ninja-forms' ), 301 | 'width' => 'full', 302 | 'group' => 'advanced', 303 | 'settings' => $min_max, 304 | ]; 305 | 306 | $settings['show_week_numbers'] = [ 307 | 'name' => 'show_week_numbers', 308 | 'type' => 'toggle', 309 | 'label' => esc_html__( 'Show Week Numbers', 'date-range-ninja-forms' ), 310 | 'width' => 'one-third', 311 | 'group' => 'advanced', 312 | 'value' => 0, 313 | ]; 314 | 315 | $settings['disable_weekends'] = [ 316 | 'name' => 'disable_weekends', 317 | 'type' => 'toggle', 318 | 'label' => esc_html__( 'Disable Weekends', 'date-range-ninja-forms' ), 319 | 'width' => 'one-third', 320 | 'group' => 'advanced', 321 | 'value' => 0, 322 | ]; 323 | 324 | $settings['select_backward'] = [ 325 | 'name' => 'select_backward', 326 | 'type' => 'toggle', 327 | 'label' => esc_html__( 'Select Backward', 'date-range-ninja-forms' ), 328 | 'help' => esc_html__( 'Select second date before the first selected date.', 'date-range-ninja-forms' ), 329 | 'width' => 'one-third', 330 | 'group' => 'advanced', 331 | 'value' => 0, 332 | ]; 333 | 334 | $settings['select_forward'] = [ 335 | 'name' => 'select_forward', 336 | 'type' => 'toggle', 337 | 'label' => esc_html__( 'Select Forward', 'date-range-ninja-forms' ), 338 | 'help' => esc_html__( 'Select second date after the first selected date.', 'date-range-ninja-forms' ), 339 | 'width' => 'one-third', 340 | 'group' => 'advanced', 341 | 'value' => 0, 342 | ]; 343 | 344 | $settings['auto_apply'] = [ 345 | 'name' => 'auto_apply', 346 | 'type' => 'toggle', 347 | 'label' => esc_html__( 'Auto Apply', 'date-range-ninja-forms' ), 348 | 'help' => esc_html__( 'When enabled, hide the apply and cancel buttons, and automatically apply a new date range as soon as two dates are clicked.', 'date-range-ninja-forms' ), 349 | 'width' => 'one-third', 350 | 'group' => 'advanced', 351 | 'value' => 1, 352 | ]; 353 | 354 | return $settings; 355 | } 356 | 357 | /** 358 | * Enqueue setting field script. 359 | * 360 | * @return void 361 | */ 362 | public function admin_scripts() { 363 | wp_enqueue_script( 'date-setting-field', plugin_dir_url( DATERANGE_FILE ) . 'js/date-setting-field.js', [], DATERANGE_VERSION, true ); 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /js/date-range.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Thanks to https://ninjaformsdev.slack.com/archives/C0EFXJF0D/p1524614990000175 3 | */ 4 | // @ts-check 5 | document.addEventListener( 6 | "DOMContentLoaded", 7 | () => { 8 | const nfRadio = Backbone.Radio; 9 | const radioChannel = nfRadio.channel("daterange"); // 'daterange', the $_type value, defined in date-range-ninja-forms.php 10 | const submitChannel = nfRadio.channel("submit"); 11 | const fieldsChannel = nfRadio.channel("fields"); 12 | 13 | const DateRange = class extends Marionette.Object { 14 | fieldType = "daterange"; 15 | fieldID = 0; 16 | picker = {}; 17 | daterangeRequired = 0; 18 | 19 | /** 20 | * initialize() 21 | * 22 | * When initialize the form, listen to the radio chanel to see if there's a 'daterange' chanel. 23 | */ 24 | initialize() { 25 | this.listenTo(radioChannel, "render:view", this.renderView); 26 | 27 | this.listenTo( 28 | submitChannel, 29 | "validate:field", 30 | this.validateRequiredField, 31 | ); 32 | this.listenTo(submitChannel, "validate:field", this.saveField); // when a field isn't required 33 | this.listenTo( 34 | fieldsChannel, 35 | "change:model", 36 | this.useCustomRequiredField, 37 | ); 38 | } 39 | 40 | /** 41 | * Use custom required field 42 | * 43 | * @param {*} model 44 | */ 45 | useCustomRequiredField(model) { 46 | if (this.fieldType !== model.get("type")) { 47 | return true; 48 | } 49 | if (0 === model.get("required")) { 50 | return; 51 | } 52 | if ({} === this.picker) { 53 | return true; 54 | } 55 | 56 | if ( 57 | typeof this.picker.getDate === "function" && 58 | null == this.picker.getDate() 59 | ) { 60 | this.daterangeRequired = 1; 61 | this.fieldID = model.get("id"); 62 | model.set("required", 0); 63 | } 64 | return true; 65 | } 66 | 67 | /** 68 | * validateRequireField() 69 | * 70 | * For required data rage field, check that it has a value. 71 | * Saves validated dates. 72 | * 73 | * @param {*} model 74 | */ 75 | validateRequiredField(model) { 76 | if (this.fieldType !== model.get("type")) { 77 | return true; 78 | } 79 | if (0 === this.daterangeRequired) { 80 | return; 81 | } 82 | if (!this.picker) { 83 | return true; 84 | } 85 | 86 | if ( 87 | true === 88 | dayjs(this.picker.getDate(), this.getDateFormat(model), true).isValid() 89 | ) { 90 | this.addDates(model); 91 | // Remove Error from Model 92 | fieldsChannel.request( 93 | "remove:error", 94 | model.get("id"), 95 | "required-error", 96 | ); 97 | } else { 98 | // Add Error to Model 99 | fieldsChannel.request( 100 | "add:error", 101 | model.get("id"), 102 | "required-error", 103 | nfi18n.validateRequiredField, 104 | ); 105 | } 106 | } 107 | 108 | /** 109 | * saveField() 110 | * 111 | * Used when the field is not required. 112 | * 113 | * @param {*} model 114 | */ 115 | saveField(model) { 116 | if (this.fieldType !== model.get("type")) { 117 | return true; 118 | } 119 | if (1 === this.daterangeRequired) { 120 | return true; 121 | } 122 | if (!this.picker) { 123 | return true; 124 | } 125 | 126 | if ( 127 | typeof this.picker.getDate === "function" && 128 | null != this.picker.getDate() && 129 | this.picker.getDate() > 0 130 | ) { 131 | this.addDates(model); 132 | } 133 | } 134 | 135 | /** 136 | * addDates() 137 | * 138 | * Add dates to the model. 139 | * 140 | * @param {*} model 141 | */ 142 | addDates(model) { 143 | const dateFormat = this.getDateFormat(model); 144 | const startDate = dayjs(this.picker.getStartDate()).format(dateFormat); 145 | const endDate = dayjs(this.picker.getEndDate()).format(dateFormat); 146 | model.set("value", `${startDate} - ${endDate}`); 147 | } 148 | 149 | /** 150 | * renderView() 151 | * 152 | * When rendering the form (i.e. the view), attach custom javascript code and events. 153 | */ 154 | renderView(view) { 155 | const litepickerConfig = { 156 | element: view.el.getElementsByClassName("daterange")[0], 157 | firstDay: view.model.get("start_of_week"), 158 | format: this.getDateFormat(view.model), 159 | lang: () => { 160 | let lang = drDateRange.lang.replace("_", "-"); 161 | try { 162 | Intl.getCanonicalLocales(lang); 163 | } catch (error) { 164 | console.error( 165 | "Invalid date format: %s. Should look something like this: en-US", 166 | lang, 167 | ); 168 | let lang = "en-US"; 169 | } 170 | return lang; 171 | }, 172 | singleMode: 0, 173 | disableWeekends: view.model.get("disable_weekends"), 174 | numberOfMonths: 2, 175 | numberOfColumns: 2, 176 | showWeekNumbers: view.model.get("show_week_numbers"), 177 | selectBackward: view.model.get("select_backward"), 178 | selectForward: view.model.get("select_forward"), 179 | showTooltip: view.model.get("tooltip"), 180 | autoApply: view.model.get("auto_apply"), 181 | onShow: () => { 182 | fieldsChannel.request( 183 | "remove:error", 184 | this.fieldID, 185 | "required-error", 186 | ); 187 | }, 188 | }; 189 | 190 | if (0 !== litepickerConfig.showTooltip) { 191 | litepickerConfig.tooltipText = { 192 | one: view.model.get("tooltip_singular"), 193 | other: view.model.get("tooltip_plural"), 194 | }; 195 | } 196 | 197 | if (0 !== view.model.get("max_min_date")) { 198 | const minMaxDateStart = view.model.get("min_date"); 199 | const minMaxDateEnd = view.model.get("max_date"); 200 | 201 | if (typeof minMaxDateStart !== "undefined" && minMaxDateStart !== "") { 202 | if (this.isValidDate(minMaxDateStart)) { 203 | litepickerConfig.minDate = minMaxDateStart; 204 | } else { 205 | console.error( 206 | "Invalid date format: %s. Valid is YYYY-MM-YY. E.g.: 2020-02-29", 207 | minMaxDateStart, 208 | ); 209 | } 210 | } 211 | 212 | if (typeof minMaxDateEnd !== "undefined" && minMaxDateEnd !== "") { 213 | if (this.isValidDate(minMaxDateEnd)) { 214 | litepickerConfig.maxDate = minMaxDateEnd; 215 | } else { 216 | console.error( 217 | "Invalid date format: %s. Valid is YYYY-MM-YY. E.g.: 2020-02-29", 218 | minMaxDateEnd, 219 | ); 220 | } 221 | } 222 | } 223 | 224 | if (0 !== view.model.get("min_max_days")) { 225 | const minMaxDaysMin = view.model.get("min_days"); 226 | const minMaxDaysMax = view.model.get("max_days"); 227 | 228 | if ( 229 | typeof minMaxDaysMin !== "undefined" && 230 | minMaxDaysMin !== "" && 231 | minMaxDaysMin > 0 232 | ) { 233 | litepickerConfig.minDays = minMaxDaysMin - 1; 234 | } 235 | 236 | if ( 237 | typeof minMaxDaysMax !== "undefined" && 238 | minMaxDaysMax !== "" && 239 | minMaxDaysMax > 0 240 | ) { 241 | litepickerConfig.maxDays = minMaxDaysMax - 1; 242 | } 243 | } 244 | 245 | if ( 246 | typeof drDateRange.dropdowns !== "undefined" && 247 | drDateRange.dropdowns !== "[]" 248 | ) { 249 | litepickerConfig.dropdowns = drDateRange.dropdowns; 250 | } 251 | 252 | if ( 253 | typeof drDateRange.buttontext !== "undefined" && 254 | drDateRange.buttontext !== "[]" 255 | ) { 256 | litepickerConfig.buttonText = drDateRange.buttontext; 257 | } 258 | 259 | // https://wakirin.github.io/Litepicker/ 260 | this.picker = new Litepicker(litepickerConfig); 261 | } 262 | 263 | /** 264 | * 265 | * @param {*} model 266 | */ 267 | getDateFormat(model) { 268 | let dateFormat = model.get("date_format"); 269 | if ("" === dateFormat || "default" === dateFormat) { 270 | dateFormat = this.convertDateFormat(drDateRange.dateFormat); // 'DateRange' from wp_localize in date-range-ninja-forms.php 271 | } 272 | return dateFormat; 273 | } 274 | 275 | /** 276 | * from https://github.com/wpninjas/ninja-forms/blob/83cccc6815c98a7ef50ca62704b2661eb53dd3cc/assets/js/front-end/controllers/fieldDate.js#L77-L136 277 | * @param {*} dateFormat 278 | */ 279 | convertDateFormat(dateFormat) { 280 | // http://php.net/manual/en/function.date.php 281 | // https://github.com/dbushell/Pikaday/blob/master/README.md#formatting 282 | // Note: Be careful not to add overriding replacements. Order is important here. 283 | /** Day*/ 284 | dateFormat = dateFormat.replace("D", "ddd"); // @todo Ordering issue? 285 | dateFormat = dateFormat.replace("d", "DD"); 286 | dateFormat = dateFormat.replace("l", "dddd"); 287 | dateFormat = dateFormat.replace("j", "D"); 288 | dateFormat = dateFormat.replace("N", ""); // Not Supported 289 | dateFormat = dateFormat.replace("S", ""); // Not Supported 290 | dateFormat = dateFormat.replace("w", "d"); 291 | dateFormat = dateFormat.replace("z", ""); // Not Supported 292 | 293 | /** Week*/ 294 | dateFormat = dateFormat.replace("W", "W"); 295 | 296 | /** Month*/ 297 | dateFormat = dateFormat.replace("M", "MMM"); // "M" before "F" or "m" to avoid overriding. 298 | dateFormat = dateFormat.replace("F", "MMMM"); 299 | dateFormat = dateFormat.replace("m", "MM"); 300 | dateFormat = dateFormat.replace("n", "M"); 301 | dateFormat = dateFormat.replace("t", ""); // Not Supported 302 | 303 | // Year 304 | dateFormat = dateFormat.replace("L", ""); // Not Supported 305 | dateFormat = dateFormat.replace("o", "YYYY"); 306 | dateFormat = dateFormat.replace("Y", "YYYY"); 307 | dateFormat = dateFormat.replace("y", "YY"); 308 | 309 | // Time - Not supported 310 | dateFormat = dateFormat.replace("a", ""); 311 | dateFormat = dateFormat.replace("A", ""); 312 | dateFormat = dateFormat.replace("B", ""); 313 | dateFormat = dateFormat.replace("g", ""); 314 | dateFormat = dateFormat.replace("G", ""); 315 | dateFormat = dateFormat.replace("h", ""); 316 | dateFormat = dateFormat.replace("H", ""); 317 | dateFormat = dateFormat.replace("i", ""); 318 | dateFormat = dateFormat.replace("s", ""); 319 | dateFormat = dateFormat.replace("u", ""); 320 | dateFormat = dateFormat.replace("v", ""); 321 | 322 | // Timezone - Not supported 323 | dateFormat = dateFormat.replace("e", ""); 324 | dateFormat = dateFormat.replace("I", ""); 325 | dateFormat = dateFormat.replace("O", ""); 326 | dateFormat = dateFormat.replace("P", ""); 327 | dateFormat = dateFormat.replace("T", ""); 328 | dateFormat = dateFormat.replace("Z", ""); 329 | 330 | // Full Date/Time - Not Supported 331 | dateFormat = dateFormat.replace("c", ""); 332 | dateFormat = dateFormat.replace("r", ""); 333 | dateFormat = dateFormat.replace("u", ""); 334 | 335 | return dateFormat; 336 | } 337 | 338 | /** 339 | * From: https://stackoverflow.com/a/35413963 340 | * @param {*} dateString 341 | */ 342 | isValidDate(dateString) { 343 | const regEx = /^\d{4}-\d{2}-\d{2}$/; 344 | if (!dateString.match(regEx)) { 345 | return false; 346 | } // Invalid format 347 | const d = new Date(dateString); 348 | const dNum = d.getTime(); 349 | if (!dNum && dNum !== 0) { 350 | return false; 351 | } // NaN value, Invalid date 352 | return d.toISOString().slice(0, 10) === dateString; 353 | } 354 | }; 355 | 356 | new DateRange(); 357 | }, 358 | ); 359 | -------------------------------------------------------------------------------- /vendor/composer/ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see https://www.php-fig.org/psr/psr-0/ 41 | * @see https://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | private $vendorDir; 46 | 47 | // PSR-4 48 | private $prefixLengthsPsr4 = array(); 49 | private $prefixDirsPsr4 = array(); 50 | private $fallbackDirsPsr4 = array(); 51 | 52 | // PSR-0 53 | private $prefixesPsr0 = array(); 54 | private $fallbackDirsPsr0 = array(); 55 | 56 | private $useIncludePath = false; 57 | private $classMap = array(); 58 | private $classMapAuthoritative = false; 59 | private $missingClasses = array(); 60 | private $apcuPrefix; 61 | 62 | private static $registeredLoaders = array(); 63 | 64 | public function __construct($vendorDir = null) 65 | { 66 | $this->vendorDir = $vendorDir; 67 | } 68 | 69 | public function getPrefixes() 70 | { 71 | if (!empty($this->prefixesPsr0)) { 72 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 73 | } 74 | 75 | return array(); 76 | } 77 | 78 | public function getPrefixesPsr4() 79 | { 80 | return $this->prefixDirsPsr4; 81 | } 82 | 83 | public function getFallbackDirs() 84 | { 85 | return $this->fallbackDirsPsr0; 86 | } 87 | 88 | public function getFallbackDirsPsr4() 89 | { 90 | return $this->fallbackDirsPsr4; 91 | } 92 | 93 | public function getClassMap() 94 | { 95 | return $this->classMap; 96 | } 97 | 98 | /** 99 | * @param array $classMap Class to filename map 100 | */ 101 | public function addClassMap(array $classMap) 102 | { 103 | if ($this->classMap) { 104 | $this->classMap = array_merge($this->classMap, $classMap); 105 | } else { 106 | $this->classMap = $classMap; 107 | } 108 | } 109 | 110 | /** 111 | * Registers a set of PSR-0 directories for a given prefix, either 112 | * appending or prepending to the ones previously set for this prefix. 113 | * 114 | * @param string $prefix The prefix 115 | * @param array|string $paths The PSR-0 root directories 116 | * @param bool $prepend Whether to prepend the directories 117 | */ 118 | public function add($prefix, $paths, $prepend = false) 119 | { 120 | if (!$prefix) { 121 | if ($prepend) { 122 | $this->fallbackDirsPsr0 = array_merge( 123 | (array) $paths, 124 | $this->fallbackDirsPsr0 125 | ); 126 | } else { 127 | $this->fallbackDirsPsr0 = array_merge( 128 | $this->fallbackDirsPsr0, 129 | (array) $paths 130 | ); 131 | } 132 | 133 | return; 134 | } 135 | 136 | $first = $prefix[0]; 137 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 138 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 139 | 140 | return; 141 | } 142 | if ($prepend) { 143 | $this->prefixesPsr0[$first][$prefix] = array_merge( 144 | (array) $paths, 145 | $this->prefixesPsr0[$first][$prefix] 146 | ); 147 | } else { 148 | $this->prefixesPsr0[$first][$prefix] = array_merge( 149 | $this->prefixesPsr0[$first][$prefix], 150 | (array) $paths 151 | ); 152 | } 153 | } 154 | 155 | /** 156 | * Registers a set of PSR-4 directories for a given namespace, either 157 | * appending or prepending to the ones previously set for this namespace. 158 | * 159 | * @param string $prefix The prefix/namespace, with trailing '\\' 160 | * @param array|string $paths The PSR-4 base directories 161 | * @param bool $prepend Whether to prepend the directories 162 | * 163 | * @throws \InvalidArgumentException 164 | */ 165 | public function addPsr4($prefix, $paths, $prepend = false) 166 | { 167 | if (!$prefix) { 168 | // Register directories for the root namespace. 169 | if ($prepend) { 170 | $this->fallbackDirsPsr4 = array_merge( 171 | (array) $paths, 172 | $this->fallbackDirsPsr4 173 | ); 174 | } else { 175 | $this->fallbackDirsPsr4 = array_merge( 176 | $this->fallbackDirsPsr4, 177 | (array) $paths 178 | ); 179 | } 180 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 181 | // Register directories for a new namespace. 182 | $length = strlen($prefix); 183 | if ('\\' !== $prefix[$length - 1]) { 184 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 185 | } 186 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 187 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 188 | } elseif ($prepend) { 189 | // Prepend directories for an already registered namespace. 190 | $this->prefixDirsPsr4[$prefix] = array_merge( 191 | (array) $paths, 192 | $this->prefixDirsPsr4[$prefix] 193 | ); 194 | } else { 195 | // Append directories for an already registered namespace. 196 | $this->prefixDirsPsr4[$prefix] = array_merge( 197 | $this->prefixDirsPsr4[$prefix], 198 | (array) $paths 199 | ); 200 | } 201 | } 202 | 203 | /** 204 | * Registers a set of PSR-0 directories for a given prefix, 205 | * replacing any others previously set for this prefix. 206 | * 207 | * @param string $prefix The prefix 208 | * @param array|string $paths The PSR-0 base directories 209 | */ 210 | public function set($prefix, $paths) 211 | { 212 | if (!$prefix) { 213 | $this->fallbackDirsPsr0 = (array) $paths; 214 | } else { 215 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 216 | } 217 | } 218 | 219 | /** 220 | * Registers a set of PSR-4 directories for a given namespace, 221 | * replacing any others previously set for this namespace. 222 | * 223 | * @param string $prefix The prefix/namespace, with trailing '\\' 224 | * @param array|string $paths The PSR-4 base directories 225 | * 226 | * @throws \InvalidArgumentException 227 | */ 228 | public function setPsr4($prefix, $paths) 229 | { 230 | if (!$prefix) { 231 | $this->fallbackDirsPsr4 = (array) $paths; 232 | } else { 233 | $length = strlen($prefix); 234 | if ('\\' !== $prefix[$length - 1]) { 235 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 236 | } 237 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 238 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 239 | } 240 | } 241 | 242 | /** 243 | * Turns on searching the include path for class files. 244 | * 245 | * @param bool $useIncludePath 246 | */ 247 | public function setUseIncludePath($useIncludePath) 248 | { 249 | $this->useIncludePath = $useIncludePath; 250 | } 251 | 252 | /** 253 | * Can be used to check if the autoloader uses the include path to check 254 | * for classes. 255 | * 256 | * @return bool 257 | */ 258 | public function getUseIncludePath() 259 | { 260 | return $this->useIncludePath; 261 | } 262 | 263 | /** 264 | * Turns off searching the prefix and fallback directories for classes 265 | * that have not been registered with the class map. 266 | * 267 | * @param bool $classMapAuthoritative 268 | */ 269 | public function setClassMapAuthoritative($classMapAuthoritative) 270 | { 271 | $this->classMapAuthoritative = $classMapAuthoritative; 272 | } 273 | 274 | /** 275 | * Should class lookup fail if not found in the current class map? 276 | * 277 | * @return bool 278 | */ 279 | public function isClassMapAuthoritative() 280 | { 281 | return $this->classMapAuthoritative; 282 | } 283 | 284 | /** 285 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 286 | * 287 | * @param string|null $apcuPrefix 288 | */ 289 | public function setApcuPrefix($apcuPrefix) 290 | { 291 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 292 | } 293 | 294 | /** 295 | * The APCu prefix in use, or null if APCu caching is not enabled. 296 | * 297 | * @return string|null 298 | */ 299 | public function getApcuPrefix() 300 | { 301 | return $this->apcuPrefix; 302 | } 303 | 304 | /** 305 | * Registers this instance as an autoloader. 306 | * 307 | * @param bool $prepend Whether to prepend the autoloader or not 308 | */ 309 | public function register($prepend = false) 310 | { 311 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 312 | 313 | if (null === $this->vendorDir) { 314 | return; 315 | } 316 | 317 | if ($prepend) { 318 | self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 319 | } else { 320 | unset(self::$registeredLoaders[$this->vendorDir]); 321 | self::$registeredLoaders[$this->vendorDir] = $this; 322 | } 323 | } 324 | 325 | /** 326 | * Unregisters this instance as an autoloader. 327 | */ 328 | public function unregister() 329 | { 330 | spl_autoload_unregister(array($this, 'loadClass')); 331 | 332 | if (null !== $this->vendorDir) { 333 | unset(self::$registeredLoaders[$this->vendorDir]); 334 | } 335 | } 336 | 337 | /** 338 | * Loads the given class or interface. 339 | * 340 | * @param string $class The name of the class 341 | * @return bool|null True if loaded, null otherwise 342 | */ 343 | public function loadClass($class) 344 | { 345 | if ($file = $this->findFile($class)) { 346 | includeFile($file); 347 | 348 | return true; 349 | } 350 | } 351 | 352 | /** 353 | * Finds the path to the file where the class is defined. 354 | * 355 | * @param string $class The name of the class 356 | * 357 | * @return string|false The path if found, false otherwise 358 | */ 359 | public function findFile($class) 360 | { 361 | // class map lookup 362 | if (isset($this->classMap[$class])) { 363 | return $this->classMap[$class]; 364 | } 365 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 366 | return false; 367 | } 368 | if (null !== $this->apcuPrefix) { 369 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 370 | if ($hit) { 371 | return $file; 372 | } 373 | } 374 | 375 | $file = $this->findFileWithExtension($class, '.php'); 376 | 377 | // Search for Hack files if we are running on HHVM 378 | if (false === $file && defined('HHVM_VERSION')) { 379 | $file = $this->findFileWithExtension($class, '.hh'); 380 | } 381 | 382 | if (null !== $this->apcuPrefix) { 383 | apcu_add($this->apcuPrefix.$class, $file); 384 | } 385 | 386 | if (false === $file) { 387 | // Remember that this class does not exist. 388 | $this->missingClasses[$class] = true; 389 | } 390 | 391 | return $file; 392 | } 393 | 394 | /** 395 | * Returns the currently registered loaders indexed by their corresponding vendor directories. 396 | * 397 | * @return self[] 398 | */ 399 | public static function getRegisteredLoaders() 400 | { 401 | return self::$registeredLoaders; 402 | } 403 | 404 | private function findFileWithExtension($class, $ext) 405 | { 406 | // PSR-4 lookup 407 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 408 | 409 | $first = $class[0]; 410 | if (isset($this->prefixLengthsPsr4[$first])) { 411 | $subPath = $class; 412 | while (false !== $lastPos = strrpos($subPath, '\\')) { 413 | $subPath = substr($subPath, 0, $lastPos); 414 | $search = $subPath . '\\'; 415 | if (isset($this->prefixDirsPsr4[$search])) { 416 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 417 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 418 | if (file_exists($file = $dir . $pathEnd)) { 419 | return $file; 420 | } 421 | } 422 | } 423 | } 424 | } 425 | 426 | // PSR-4 fallback dirs 427 | foreach ($this->fallbackDirsPsr4 as $dir) { 428 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 429 | return $file; 430 | } 431 | } 432 | 433 | // PSR-0 lookup 434 | if (false !== $pos = strrpos($class, '\\')) { 435 | // namespaced class name 436 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 437 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 438 | } else { 439 | // PEAR-like class name 440 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 441 | } 442 | 443 | if (isset($this->prefixesPsr0[$first])) { 444 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 445 | if (0 === strpos($class, $prefix)) { 446 | foreach ($dirs as $dir) { 447 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 448 | return $file; 449 | } 450 | } 451 | } 452 | } 453 | } 454 | 455 | // PSR-0 fallback dirs 456 | foreach ($this->fallbackDirsPsr0 as $dir) { 457 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 458 | return $file; 459 | } 460 | } 461 | 462 | // PSR-0 include paths. 463 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 464 | return $file; 465 | } 466 | 467 | return false; 468 | } 469 | } 470 | 471 | /** 472 | * Scope isolated include. 473 | * 474 | * Prevents access to $this/self from included files. 475 | */ 476 | function includeFile($file) 477 | { 478 | include $file; 479 | } 480 | --------------------------------------------------------------------------------