├── .github └── stale.yml ├── .gitignore ├── .idea └── .gitignore ├── LICENSE ├── README.md └── popover_ex ├── assets ├── jquery.formchimp.js ├── jquery.formchimp.min.js ├── jquery.wait.js ├── jquery.wait.min.js ├── marked.min.js ├── replacer.js ├── replacer.min.js ├── underscore-min.js ├── underscore-min.map ├── versioncompare.js └── versioncompare.min.js ├── css ├── popoverex.edit.css └── style.css ├── languages ├── de_DE.json ├── en_GB.json └── ru_RU.json ├── plugin.php ├── views ├── WidgetkitExPlugin.php ├── _content.php ├── edit.php └── widget.php └── widget.svg /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 14 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows thumbnail db 2 | Thumbs.db 3 | 4 | # OSX files 5 | .DS_Store 6 | 7 | #log files 8 | *.log 9 | 10 | #bak files 11 | ~* 12 | *.bak 13 | 14 | #archives 15 | *.zip 16 | *.rar -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /../../../../:\git\widgetkit-popover-ex\.idea/dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |        2 | 3 |  4 | 5 | # Overview 6 | **PopoverEx** is an advanced Popover widget for [Yootheme Widgetkit2](https://yootheme.com/widgetkit). After installation it becomes available in the Widgets list as a "native" widget and can be used as any other widget. The widget allows to position each item through coordinates on top of a main image and display content inside a dropdown. 7 | 8 | __Notice about Widgetkit 3 and status of this project__: Yootheme [announced](https://yootheme.com/blog/2021/01/26/widgetkit-3.0-completely-rebuilt-with-uikit-3) a third version of Widgetkit on January 26th, 2021. 9 | I received reports that after upgrade to Widgetkit 3 my widgets do not work properly. 10 | I'm very sorry that I currently not able to devote my time to this project to develop it further. 11 | In its current state it's "bug free" and works with Widgetkit 2 and Joomla 3. 12 | Currently, I don't have plans to add support for Widgetkit 3 or add any other serious improvements. 13 | This project has been my pet project that I was doing in my free time, and I've been happy to share it for free with the community. 14 | As I stopped using Widgetkit in my own websites, I lost motivation in further updates to this project. 15 | I'm happy if my work and widgets were of any help or use in your websites! 16 | 17 | ## Features 18 | ### Basic Features 19 | * **Collection of toggle icons** - you can choose a toggle icon from the predefined list. 20 | * **Hover/Click behavior** - you can select how the widget will respond: the dropdown can be displayed when the user moves their mouse over the icon or when they click it. 21 | * **Panel styles** - different panel styles for the dropdown. 22 | * **Compatible with Yootheme Pro** - you can use this widget with [Yootheme Pro](https://yootheme.com/pro/). 23 | * **Backward compatibility** - all other behavior, styling and features of the original [Popover widget](http://yootheme.com/demo/widgetkit/joomla/index.php/home/popover) are preserved. 24 | 25 | ### Unique Features 26 | The new features that the PopoverEx has and that are not available in the original Popover widget: 27 | 28 | * **Custom Default Toggle Icon** - a custom icon can be set very easy from the control panel. 29 | * **Responsive Toggle Icon** - you can setup the size and resize behavior of the icon. 30 | * **Unique Icons for Every Item** - you can setup individual unique icons for any element. 31 | * **Opacity control** - you can define the transparency level of the icons. 32 | * **Tooltips for all options** - it's much easier to use the widget, because tooltips are available for all settings. 33 | * **Update notifications** - you will be notified if new versions of the widget are available. 34 | * **Multilingual interface**, translated into languages: 35 | * **English** (en_GB) 36 | * **Русский** (ru_RU) 37 | * **Deutsch** (de_DE) 38 | * Your language not listed? You can help with translation, [read more](https://github.com/rvalitov/widgetkit-popover-ex/wiki/Translation-issues). 39 | 40 | # Supported platforms 41 | * The core code is based on Widgetkit 2.5.2 and was updated since accordingly. This widget should work with any Widgetkit 2.4.x and later. Tested on Widgetkit 2.5.x, 2.6.x, 2.7.x, 2.8.x, 2.9.x. 42 | * PHP5 and PHP7 compatible 43 | * Joomla 3.4.x or later required 44 | * Wordpress 4.4.x or later 45 | 46 | **Read full system requirements [here](https://github.com/rvalitov/widgetkit-popover-ex/wiki/System-requirements).** 47 | 48 | # How to install? 49 | The installation procedure is described [here](https://github.com/rvalitov/widgetkit-popover-ex/wiki/How-to-install). 50 | 51 | # The manual 52 | Some issues about using the widget are available in the [Wiki area](https://github.com/rvalitov/widgetkit-popover-ex/wiki). 53 | 54 | # Authors, Contributors and Acknowledgment 55 | * This widget is created by [Ramil Valitov](http://www.valitov.me). 56 | * The code is based on the original [Popover widget](http://yootheme.com/demo/widgetkit/joomla/index.php/home/popover) by [Yootheme](http://yootheme.com/). 57 | * Logo is based on [Tango Icon Library](https://www.iconfinder.com/iconsets/tango-icon-library) 58 | * The default toggle icon is created by [Icons Land](https://www.iconfinder.com/iconsets/softwaredemo) 59 | * Thanks to [Marco Rensch](https://github.com/marcorensch) for translation to German language. 60 | 61 | ## Disclaimer 62 | This project is NOT affiliated with, endorsed, or sponsored by the Yootheme. Widgetkit, its name, trademark, and other aspects of the app are trademarked and owned by their respective owners. 63 | 64 | # Feedback 65 | Your feedback is very appreciated. If you want to see new features in this module, please, post your ideas and feature requests in the [issue tracker](https://github.com/rvalitov/widgetkit-popover-ex/issues). 66 | 67 | # Donations 68 | This is a free project that I do in my spare time. If you find it useful, then you can support it by donating some amount of money. This will help to keep the project alive and making it better: develop new features, make new releases, fix bugs, update the [manuals](https://github.com/rvalitov/widgetkit-popover-ex/wiki), and provide at least some minimal technical support (there's an [issue tracker here](https://github.com/rvalitov/widgetkit-popover-ex/issues)). 69 | 70 | You can choose any payment method you prefer: 71 | 72 | Your Currency | Payment Method 73 | ------------ | ------------- 74 | Euro € | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BJJF3E6DBRYHA) [](https://www.paypal.me/valitov/0eur) 75 | USD $ | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B8VMNU7SEAU8J) [](https://www.paypal.me/valitov/0usd) 76 | Russian Ruble ₽ | [](https://money.yandex.ru/to/410011424143476) [](https://www.paypal.me/valitov/0rub) [](https://money.yandex.ru/to/410011424143476) 77 | Other | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BJJF3E6DBRYHA) [](https://www.paypal.me/valitov) 78 | 79 | # Support or Contact 80 | Having trouble with PopoverEx Widget? May be something has already been described in the [Wiki area](https://github.com/rvalitov/widgetkit-popover-ex/wiki) or reported in the [issue tracker](https://github.com/rvalitov/widgetkit-popover-ex/issues). If you don't find your problem there, then, please, add your issue there. 81 | 82 | Being a free project which I do in my spare time, I hope you understand that I can't offer 24/7 support:) You may contact me via e-mail ramilvalitov@gmail.com, I will try to answer to all of them (if such messages imply an answer), however, not immediately, it may take a few days or a week... so, just be patient. 83 | 84 | Note, that I can answer only to questions and problems directly related to this widget. Answers to basic questions about the widgetkit nature and simple help about how to use widgets in general or how to use Joomla you can find in appropriate forums: 85 | 86 | * [Joomla](http://forum.joomla.org/) 87 | * [Widgetkits](https://yootheme.com/support) 88 | -------------------------------------------------------------------------------- /popover_ex/assets/jquery.formchimp.js: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | 3 | jQuery FormChimp - v1.2.1 4 | A customizable MailChimp ajax plugin for jQuery 5 | Fabio Quarantini - @febba 6 | http://www.fabioquarantini.com/formchimp/ 7 | 8 | ========================================================================== */ 9 | 10 | 11 | ;( function( $, window, document, undefined ) { 12 | 13 | $.fn.formchimp = function( settings ) { 14 | 15 | var $form = $(this); 16 | var $body = $('body'); 17 | var actionUrl = $form.attr('action').replace('/post?', '/post-json?').concat('&c=?'); 18 | var $button = $form.find('[type="submit"]'); 19 | var defaults = { 20 | 'appendElement': $form, // Declare where the new element, containing the messages from Mailchimp will be appended to. 21 | 'buttonSelector': $button, // Set the button selector. 22 | 'buttonText': '', // The message to be written on the submit button after a successful subscription. 23 | 'debug': false, // Activate debug message in console. 24 | 'errorMessage': '', // Set custom error message given when return an error. 25 | 'onMailChimpSuccess': function() {}, // Callback that fires on success. 26 | 'onMailChimpError': function() {}, // Callback that fires on errors. 27 | 'responseClass': "mc-response", // Declare custom element in page for message output. (Set different classes for multiple sign-up forms) 28 | 'successMessage': '', // Set a custom success message. 29 | 'url': actionUrl, // The mailchip list subscription url, to get the JSONP address just change `post` to `post-json` and append `&c=?` at the end. 30 | }; 31 | var originalButtonText = defaults.buttonSelector.text(); 32 | 33 | // Merge default whith settings 34 | $.extend( defaults, settings ); 35 | 36 | // On submit 37 | $( $form ).on( 'submit', function( event ) { 38 | 39 | // Disable default action of submit 40 | event.preventDefault(); 41 | 42 | // Remove status class and add the loading 43 | $body.removeClass('mc-success mc-error').addClass('mc-loading'); 44 | 45 | // If the response container does not exists 46 | if ( $("." + defaults.responseClass).length === 0 ) { 47 | 48 | // Add response container to append element 49 | $responseContainer = $('
').addClass( defaults.responseClass ).appendTo( defaults.appendElement ); 50 | 51 | } else { 52 | 53 | // Remove old message 54 | $responseContainer.html(''); 55 | 56 | } 57 | 58 | // Perform an Ajax request 59 | $.ajax({ 60 | 61 | url: defaults.url, 62 | data: $form.serialize(), 63 | dataType: 'jsonp' 64 | 65 | }).done( function( data ) { 66 | 67 | // If debug is active 68 | if ( defaults.debug ) { 69 | 70 | // Log in cosole the Mailchimp data 71 | console.log( JSON.stringify(data) ); 72 | } 73 | 74 | // Save the Mailchimp data 75 | var responseMessage = data.msg; 76 | 77 | // If the message start with a number and contains "-" 78 | if( !isNaN(responseMessage.charAt(0)) && responseMessage.charAt(2) === '-' ) { 79 | 80 | // Remove first 3 characters 81 | responseMessage = responseMessage.substring(3); 82 | 83 | } 84 | 85 | // Add status class and remove the loading class 86 | $body.addClass('mc-' + data.result).removeClass('mc-loading'); 87 | 88 | // If the Mailchimp result is success 89 | if ( data.result === 'success' ) { 90 | 91 | // If success message parameter is not empty 92 | if ( defaults.successMessage !== '' ) { 93 | 94 | // Replace the default success message with parameter 95 | responseMessage = defaults.successMessage; 96 | 97 | } 98 | 99 | // If button text parameter is not empty 100 | if ( defaults.buttonText !== '' ) { 101 | 102 | // Replace the default button text with parameter 103 | defaults.buttonSelector.text(defaults.buttonText); 104 | 105 | } 106 | 107 | // Add event on error 108 | $(document).trigger('mailChimpSuccess'); 109 | 110 | // Run callback 111 | defaults.onMailChimpSuccess.call(); 112 | 113 | } else { // If there is an error 114 | 115 | // If error message parameter is not empty 116 | if ( defaults.errorMessage !== '' ) { 117 | 118 | // Replace the default error message with parameter 119 | responseMessage = defaults.errorMessage; 120 | 121 | } 122 | 123 | // If button text parameter is not empty 124 | if ( defaults.buttonText !== '' ) { 125 | 126 | // Replace the default button text with the original text 127 | defaults.buttonSelector.text(originalButtonText); 128 | 129 | } 130 | 131 | // Add event on error 132 | $(document).trigger('mailChimpError'); 133 | 134 | // Run callback 135 | defaults.onMailChimpError.call(); 136 | 137 | } 138 | 139 | // Show the message 140 | $responseContainer.html(responseMessage); 141 | 142 | }); 143 | 144 | }); 145 | 146 | }; 147 | 148 | })( jQuery, window, document ); -------------------------------------------------------------------------------- /popover_ex/assets/jquery.formchimp.min.js: -------------------------------------------------------------------------------- 1 | /*! FormChimp v1.2.1 | Fabio Quarantini - http://www.fabioquarantini.com */ 2 | !function(a,b,c,d){a.fn.formchimp=function(b){var d=a(this),e=a("body"),f=d.attr("action").replace("/post?","/post-json?").concat("&c=?"),g=d.find('[type="submit"]'),h={appendElement:d,buttonSelector:g,buttonText:"",debug:!1,errorMessage:"",onMailChimpSuccess:function(){},onMailChimpError:function(){},responseClass:"mc-response",successMessage:"",url:f},i=h.buttonSelector.text();a.extend(h,b),a(d).on("submit",function(b){b.preventDefault(),e.removeClass("mc-success mc-error").addClass("mc-loading"),0===a("."+h.responseClass).length?$responseContainer=a("").addClass(h.responseClass).appendTo(h.appendElement):$responseContainer.html(""),a.ajax({url:h.url,data:d.serialize(),dataType:"jsonp"}).done(function(b){h.debug&&console.log(JSON.stringify(b));var d=b.msg;isNaN(d.charAt(0))||"-"!==d.charAt(2)||(d=d.substring(3)),e.addClass("mc-"+b.result).removeClass("mc-loading"),"success"===b.result?(""!==h.successMessage&&(d=h.successMessage),""!==h.buttonText&&h.buttonSelector.text(h.buttonText),a(c).trigger("mailChimpSuccess"),h.onMailChimpSuccess.call()):(""!==h.errorMessage&&(d=h.errorMessage),""!==h.buttonText&&h.buttonSelector.text(i),a(c).trigger("mailChimpError"),h.onMailChimpError.call()),$responseContainer.html(d)})})}}(jQuery,window,document); -------------------------------------------------------------------------------- /popover_ex/assets/jquery.wait.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | /** 3 | * @function 4 | * @property {object} jQuery plugin which runs handler function once specified element is inserted into the DOM 5 | * @param {function} handler A function to execute at the time when the element is inserted 6 | * @param {bool} shouldRunHandlerOnce Optional: if true, handler is unbound after its first invocation 7 | * @example $(selector).waitUntilExists(function); 8 | */ 9 | 10 | $.fn.waitUntilExists = function (handler, shouldRunHandlerOnce, isChild) { 11 | var found = 'found'; 12 | var $this = $(this.selector); 13 | var $elements = $this.not(function () { return $(this).data(found); }).each(handler).data(found, true); 14 | 15 | if (!isChild) 16 | { 17 | (window.waitUntilExists_Intervals = window.waitUntilExists_Intervals || {})[this.selector] = 18 | window.setInterval(function () { $this.waitUntilExists(handler, shouldRunHandlerOnce, true); }, 500); 19 | } 20 | else if (shouldRunHandlerOnce && $elements.length) 21 | { 22 | window.clearInterval(window.waitUntilExists_Intervals[this.selector]); 23 | } 24 | 25 | return $this; 26 | }; 27 | }(jQuery)); -------------------------------------------------------------------------------- /popover_ex/assets/jquery.wait.min.js: -------------------------------------------------------------------------------- 1 | !function(t){t.fn.waitUntilExists=function(n,i,s){var e="found",a=t(this.selector),l=a.not(function(){return t(this).data(e)}).each(n).data(e,!0);return s?i&&l.length&&window.clearInterval(window.waitUntilExists_Intervals[this.selector]):(window.waitUntilExists_Intervals=window.waitUntilExists_Intervals||{})[this.selector]=window.setInterval(function(){a.waitUntilExists(n,i,!0)},500),a}}(jQuery); -------------------------------------------------------------------------------- /popover_ex/assets/marked.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * marked - a markdown parser 3 | * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) 4 | * https://github.com/chjj/marked 5 | */ 6 | (function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/"+(escaped?code:escape(code,true))+"\n
"}return''+(escaped?code:escape(code,true))+"\n
\n"};Renderer.prototype.blockquote=function(quote){return"\n"+quote+"\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"
"+text+"
\n"};Renderer.prototype.table=function(header,body){return""+text+"
"};Renderer.prototype.br=function(){return this.options.xhtml?""+escape(e.message+"",true)+""}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,sanitizer:null,mangle:true,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}()); -------------------------------------------------------------------------------- /popover_ex/assets/replacer.js: -------------------------------------------------------------------------------- 1 | /* 2 | This code is used for replacements of data in string, usually after $translate service 3 | */ 4 | "use strict"; 5 | /* 6 | data - original string 7 | expression - associative array, key => value. 8 | The function replaces all occurances of %key% in data into value. 9 | Returns the string with all replacements done. 10 | */ 11 | function replaceTransAll(data,expression){ 12 | for (var key in expression) 13 | data=data.replace('%'+key+'%',expression[key]); 14 | return data; 15 | } -------------------------------------------------------------------------------- /popover_ex/assets/replacer.min.js: -------------------------------------------------------------------------------- 1 | "use strict";function replaceTransAll(r,e){for(var n in e)r=r.replace("%"+n+"%",e[n]);return r} -------------------------------------------------------------------------------- /popover_ex/assets/underscore-min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.8.3 2 | // http://underscorejs.org 3 | // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | (function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.hasOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}},j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e