");for(var o=0;o'),d=l('"),a=l(''),c=l('').text(_.i18n_bulk_add_heading_line_label),r=l('').val(e[o]),c=(a.append(c).append(r),l('').val(i.amount)),r=l('').val(i.measurement),i=l('').val(i.name);n.append(d).append(a).append(c).append(r).append(i),u.append(n)}}else s.attr("data-bulk-type",""),s.hide(),k.attr("disabled","disabled")}i.length&&(t=l("#cooked-bulk-add-textarea"),s=l("#cooked-bulk-add-preview"),u=l("#cooked-bulk-add-preview-list"),o=l("#cooked-bulk-add-title"),r=l("#cooked-bulk-add-type"),k=i.find(".cooked-bulk-add-submit"),p=i.find(".cooked-bulk-add-spinner"),_=cooked_admin_functions_js_vars,g=null,m="",t.on("input",function(){var e=r.val(),o=(e=>{for(var t=e.split(/[\r\n]+/),o=[],i=0;i'),a=l('"),c=l('').val(i[n]);d.append(a).append(c),u.append(d)}}else s.attr("data-bulk-type",""),s.hide(),k.attr("disabled","disabled")}else t!==m&&(m=t,h(o,null),g&&clearTimeout(g),g=setTimeout(function(){var t;(t=o).length?(p.show().css("visibility","visible"),l.post(_.ajax_url,{action:"cooked_parse_bulk_ingredients",nonce:_.cooked_bulk_add_nonce,lines:t},function(e){p.hide();e=e.success&&e.data&&e.data.parsed?e.data.parsed:null;h(t,e)}).fail(function(){p.hide(),h(t,null)})):h([],null)},400))}),l(document).on("click",".cooked-bulk-add-button",function(e){e.preventDefault();var e=l(this).data("type");e=e,r.val(e),t.val(""),u.empty(),s.attr("data-bulk-type",""),s.hide(),k.attr("disabled","disabled"),p.hide(),m="","ingredients"===e?(o.text(_.i18n_bulk_add_ingredients),t.attr("placeholder",_.i18n_bulk_add_placeholder_ingredients),k.text(_.i18n_bulk_add_submit_ingredients)):(o.text(_.i18n_bulk_add_directions),t.attr("placeholder",_.i18n_bulk_add_placeholder_directions),k.text(_.i18n_bulk_add_submit_directions)),i.show(),t.focus()}),i.on("click",".cooked-bulk-add-close, .cooked-bulk-add-cancel",function(e){e.preventDefault(),n()}),i.on("click",function(e){l(e.target).is(i)&&n()}),l(document).on("keydown",function(e){"Escape"===e.key&&i.is(":visible")&&n()}),u.on("change",'input[type="checkbox"]',function(){var e=l(this).closest(".cooked-bulk-add-preview-row"),t=l(this).is(":checked");e.toggleClass("cooked-bulk-add-is-heading",t)}),k.on("click",function(e){var i;e.preventDefault(),l(this).attr("disabled")||(e=r.val(),i=[],"ingredients"===e?u.find(".cooked-bulk-add-preview-row").each(function(){var e=l(this),t=e.find('input[type="checkbox"]').is(":checked"),o=e.find(".cooked-bulk-add-preview-text").val().trim();o&&i.push(t?{text:o,heading:!0}:{heading:!1,amount:e.find(".cooked-bulk-add-parsed-amount").val().trim(),measurement:e.find(".cooked-bulk-add-parsed-unit").val().trim(),name:e.find(".cooked-bulk-add-parsed-name").val().trim()||o})}):u.find(".cooked-bulk-add-preview-row").each(function(){var e=l(this),t=e.find(".cooked-bulk-add-preview-text").val().trim(),e=e.find('input[type="checkbox"]').is(":checked");t&&i.push({text:t,heading:e})}),i.length&&(("directions"===e?cooked_bulk_add_directions:cooked_bulk_add_ingredients_parsed)(i),n()))}))}function cooked_bulk_add_directions(e){for(var t=jQuery("#cooked-directions-builder"),o=t.parent(),i=0;i child element
- if (self.__instance.content() === null) {
-
- // TODO: when there are several tags (not supported in
- // today's browsers yet though, still an RFC draft), pick the right
- // one based on its "lang" attribute
- var $title = self.__instance._$origin.find('>title');
-
- if ($title[0]) {
-
- var title = $title.text();
-
- self.__hadTitleTag = true;
- self.__instance._$origin.data('tooltipster-initialTitle', title);
- self.__instance.content(title);
-
- $title.remove();
- }
- }
-
- // rectify the geometry if SVG.js and its screenBBox plugin have been included
- self.__instance
- ._on('geometry.'+ self.namespace, function(event) {
-
- var win = $.tooltipster._env.window;
-
- // SVG coordinates may need fixing but we need svg.screenbox.js
- // to provide it. SVGElement is IE8+
- if (win.SVG.svgjs) {
-
- if (!win.SVG.parser) {
- win.SVG.prepare();
- }
-
- var svgEl = win.SVG.adopt(event.origin);
-
- // not all figures need (and have) screenBBox
- if (svgEl && svgEl.screenBBox) {
-
- var bbox = svgEl.screenBBox();
-
- event.edit({
- height: bbox.height,
- left: bbox.x,
- top: bbox.y,
- width: bbox.width
- });
- }
- }
- })
- // if jQuery < v3.0, we have to remove the class ourselves
- ._on('destroy.'+ self.namespace, function() {
- self.__destroy();
- });
- },
-
- __destroy: function() {
-
- var self = this;
-
- if (!self.__instance._$origin.hasClass('tooltipstered')) {
- var c = self.__instance._$origin.attr('class').replace('tooltipstered', '');
- self.__instance._$origin.attr('class', c);
- }
-
- self.__instance._off('.'+ self.namespace);
-
- // if the content was provided as a title tag, we may need to restore it
- if (self.__hadTitleTag) {
-
- // this must happen after Tooltipster restored (or not) the title attr
- self.__instance.one('destroyed', function() {
-
- // if a title attribute was restored, we just need to replace it with a tag
- var title = self.__instance._$origin.attr('title');
-
- if (title) {
-
- // must be namespaced to work
- $(document.createElementNS('http://www.w3.org/2000/svg', 'title'))
- .text(title)
- .appendTo(self.__instance._$origin);
-
- self.__instance._$origin.removeAttr('title');
- }
- });
- }
- }
- }
-});
-
-/* a build task will add "return $;" here */
-return $;
-
-}));
-
-
-}));
diff --git a/assets/admin/js/tooltipster/plugins/tooltipster/SVG/tooltipster-SVG.min.js b/assets/admin/js/tooltipster/plugins/tooltipster/SVG/tooltipster-SVG.min.js
deleted file mode 100644
index df77068..0000000
--- a/assets/admin/js/tooltipster/plugins/tooltipster/SVG/tooltipster-SVG.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(a,b){"function"==typeof define&&define.amd?define(["tooltipster"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("tooltipster")):b(jQuery)}(this,function(a){!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):b(jQuery)}(this,function(a){var b="tooltipster.SVG";return a.tooltipster._plugin({name:b,core:{__init:function(){a.tooltipster._on("init",function(c){var d=a.tooltipster._env.window;d.SVGElement&&c.origin instanceof d.SVGElement&&c.instance._plug(b)})}},instance:{__init:function(b){var c=this;if(c.__hadTitleTag=!1,c.__instance=b,!c.__instance._$origin.hasClass("tooltipstered")){var d=c.__instance._$origin.attr("class")||"";-1==d.indexOf("tooltipstered")&&c.__instance._$origin.attr("class",d+" tooltipstered")}if(null===c.__instance.content()){var e=c.__instance._$origin.find(">title");if(e[0]){var f=e.text();c.__hadTitleTag=!0,c.__instance._$origin.data("tooltipster-initialTitle",f),c.__instance.content(f),e.remove()}}c.__instance._on("geometry."+c.namespace,function(b){var c=a.tooltipster._env.window;if(c.SVG.svgjs){c.SVG.parser||c.SVG.prepare();var d=c.SVG.adopt(b.origin);if(d&&d.screenBBox){var e=d.screenBBox();b.edit({height:e.height,left:e.x,top:e.y,width:e.width})}}})._on("destroy."+c.namespace,function(){c.__destroy()})},__destroy:function(){var b=this;if(!b.__instance._$origin.hasClass("tooltipstered")){var c=b.__instance._$origin.attr("class").replace("tooltipstered","");b.__instance._$origin.attr("class",c)}b.__instance._off("."+b.namespace),b.__hadTitleTag&&b.__instance.one("destroyed",function(){var c=b.__instance._$origin.attr("title");c&&(a(document.createElementNS("http://www.w3.org/2000/svg","title")).text(c).appendTo(b.__instance._$origin),b.__instance._$origin.removeAttr("title"))})}}}),a})});
\ No newline at end of file
diff --git a/assets/admin/js/tooltipster/tooltipster.bundle.js b/assets/admin/js/tooltipster/tooltipster.bundle.js
deleted file mode 100644
index e40be59..0000000
--- a/assets/admin/js/tooltipster/tooltipster.bundle.js
+++ /dev/null
@@ -1,4275 +0,0 @@
-/**
- * tooltipster http://calebjacob.github.io/tooltipster/
- * A rockin' custom tooltip jQuery plugin
- * Developed by Caleb Jacob and Louis Ameline
- * MIT license
- *
- * Fork: data-tooltip-content uses $(document).find() so the attribute is
- * never passed to $() as HTML (CodeQL js/xss-through-dom).
- */
-(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module unless amdModuleId is set
- define(["jquery"], function (a0) {
- return (factory(a0));
- });
- } else if (typeof exports === 'object') {
- // Node. Does not work with strict CommonJS, but
- // only CommonJS-like environments that support module.exports,
- // like Node.
- module.exports = factory(require("jquery"));
- } else {
- factory(jQuery);
- }
-}(this, function ($) {
-
-// This file will be UMDified by a build task.
-
-var defaults = {
- animation: 'fade',
- animationDuration: 350,
- content: null,
- contentAsHTML: false,
- contentCloning: false,
- debug: true,
- delay: 300,
- delayTouch: [300, 500],
- functionInit: null,
- functionBefore: null,
- functionReady: null,
- functionAfter: null,
- functionFormat: null,
- IEmin: 6,
- interactive: false,
- multiple: false,
- // will default to document.body, or must be an element positioned at (0, 0)
- // in the document, typically like the very top views of an app.
- parent: null,
- plugins: ['sideTip'],
- repositionOnScroll: false,
- restoration: 'none',
- selfDestruction: true,
- theme: [],
- timer: 0,
- trackerInterval: 500,
- trackOrigin: false,
- trackTooltip: false,
- trigger: 'hover',
- triggerClose: {
- click: false,
- mouseleave: false,
- originClick: false,
- scroll: false,
- tap: false,
- touchleave: false
- },
- triggerOpen: {
- click: false,
- mouseenter: false,
- tap: false,
- touchstart: false
- },
- updateAnimation: 'rotate',
- zIndex: 9999999
- },
- // we'll avoid using the 'window' global as a good practice but npm's
- // jquery@<2.1.0 package actually requires a 'window' global, so not sure
- // it's useful at all
- win = (typeof window != 'undefined') ? window : null,
- // env will be proxied by the core for plugins to have access its properties
- env = {
- // detect if this device can trigger touch events. Better have a false
- // positive (unused listeners, that's ok) than a false negative.
- // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
- // http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
- hasTouchCapability: !!(
- win
- && ( 'ontouchstart' in win
- || (win.DocumentTouch && win.document instanceof win.DocumentTouch)
- || win.navigator.maxTouchPoints
- )
- ),
- hasTransitions: transitionSupport(),
- IE: false,
- // don't set manually, it will be updated by a build task after the manifest
- semVer: '4.2.8',
- window: win
- },
- core = function() {
-
- // core variables
-
- // the core emitters
- this.__$emitterPrivate = $({});
- this.__$emitterPublic = $({});
- this.__instancesLatestArr = [];
- // collects plugin constructors
- this.__plugins = {};
- // proxy env variables for plugins who might use them
- this._env = env;
- };
-
-// core methods
-core.prototype = {
-
- /**
- * A function to proxy the public methods of an object onto another
- *
- * @param {object} constructor The constructor to bridge
- * @param {object} obj The object that will get new methods (an instance or the core)
- * @param {string} pluginName A plugin name for the console log message
- * @return {core}
- * @private
- */
- __bridge: function(constructor, obj, pluginName) {
-
- // if it's not already bridged
- if (!obj[pluginName]) {
-
- var fn = function() {};
- fn.prototype = constructor;
-
- var pluginInstance = new fn();
-
- // the _init method has to exist in instance constructors but might be missing
- // in core constructors
- if (pluginInstance.__init) {
- pluginInstance.__init(obj);
- }
-
- $.each(constructor, function(methodName, fn) {
-
- // don't proxy "private" methods, only "protected" and public ones
- if (methodName.indexOf('__') != 0) {
-
- // if the method does not exist yet
- if (!obj[methodName]) {
-
- obj[methodName] = function() {
- return pluginInstance[methodName].apply(pluginInstance, Array.prototype.slice.apply(arguments));
- };
-
- // remember to which plugin this method corresponds (several plugins may
- // have methods of the same name, we need to be sure)
- obj[methodName].bridged = pluginInstance;
- }
- else if (defaults.debug) {
-
- console.log('The '+ methodName +' method of the '+ pluginName
- +' plugin conflicts with another plugin or native methods');
- }
- }
- });
-
- obj[pluginName] = pluginInstance;
- }
-
- return this;
- },
-
- /**
- * For mockup in Node env if need be, for testing purposes
- *
- * @return {core}
- * @private
- */
- __setWindow: function(window) {
- env.window = window;
- return this;
- },
-
- /**
- * Returns a ruler, a tool to help measure the size of a tooltip under
- * various settings. Meant for plugins
- *
- * @see Ruler
- * @return {object} A Ruler instance
- * @protected
- */
- _getRuler: function($tooltip) {
- return new Ruler($tooltip);
- },
-
- /**
- * For internal use by plugins, if needed
- *
- * @return {core}
- * @protected
- */
- _off: function() {
- this.__$emitterPrivate.off.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * For internal use by plugins, if needed
- *
- * @return {core}
- * @protected
- */
- _on: function() {
- this.__$emitterPrivate.on.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * For internal use by plugins, if needed
- *
- * @return {core}
- * @protected
- */
- _one: function() {
- this.__$emitterPrivate.one.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * Returns (getter) or adds (setter) a plugin
- *
- * @param {string|object} plugin Provide a string (in the full form
- * "namespace.name") to use as as getter, an object to use as a setter
- * @return {object|core}
- * @protected
- */
- _plugin: function(plugin) {
-
- var self = this;
-
- // getter
- if (typeof plugin == 'string') {
-
- var pluginName = plugin,
- p = null;
-
- // if the namespace is provided, it's easy to search
- if (pluginName.indexOf('.') > 0) {
- p = self.__plugins[pluginName];
- }
- // otherwise, return the first name that matches
- else {
- $.each(self.__plugins, function(i, plugin) {
-
- if (plugin.name.substring(plugin.name.length - pluginName.length - 1) == '.'+ pluginName) {
- p = plugin;
- return false;
- }
- });
- }
-
- return p;
- }
- // setter
- else {
-
- // force namespaces
- if (plugin.name.indexOf('.') < 0) {
- throw new Error('Plugins must be namespaced');
- }
-
- self.__plugins[plugin.name] = plugin;
-
- // if the plugin has core features
- if (plugin.core) {
-
- // bridge non-private methods onto the core to allow new core methods
- self.__bridge(plugin.core, self, plugin.name);
- }
-
- return this;
- }
- },
-
- /**
- * Trigger events on the core emitters
- *
- * @returns {core}
- * @protected
- */
- _trigger: function() {
-
- var args = Array.prototype.slice.apply(arguments);
-
- if (typeof args[0] == 'string') {
- args[0] = { type: args[0] };
- }
-
- // note: the order of emitters matters
- this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate, args);
- this.__$emitterPublic.trigger.apply(this.__$emitterPublic, args);
-
- return this;
- },
-
- /**
- * Returns instances of all tooltips in the page or an a given element
- *
- * @param {string|HTML object collection} selector optional Use this
- * parameter to restrict the set of objects that will be inspected
- * for the retrieval of instances. By default, all instances in the
- * page are returned.
- * @return {array} An array of instance objects
- * @public
- */
- instances: function(selector) {
-
- var instances = [],
- sel = selector || '.tooltipstered';
-
- $(sel).each(function() {
-
- var $this = $(this),
- ns = $this.data('tooltipster-ns');
-
- if (ns) {
-
- $.each(ns, function(i, namespace) {
- instances.push($this.data(namespace));
- });
- }
- });
-
- return instances;
- },
-
- /**
- * Returns the Tooltipster objects generated by the last initializing call
- *
- * @return {array} An array of instance objects
- * @public
- */
- instancesLatest: function() {
- return this.__instancesLatestArr;
- },
-
- /**
- * For public use only, not to be used by plugins (use ::_off() instead)
- *
- * @return {core}
- * @public
- */
- off: function() {
- this.__$emitterPublic.off.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * For public use only, not to be used by plugins (use ::_on() instead)
- *
- * @return {core}
- * @public
- */
- on: function() {
- this.__$emitterPublic.on.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * For public use only, not to be used by plugins (use ::_one() instead)
- *
- * @return {core}
- * @public
- */
- one: function() {
- this.__$emitterPublic.one.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * Returns all HTML elements which have one or more tooltips
- *
- * @param {string} selector optional Use this to restrict the results
- * to the descendants of an element
- * @return {array} An array of HTML elements
- * @public
- */
- origins: function(selector) {
-
- var sel = selector ?
- selector +' ' :
- '';
-
- return $(sel +'.tooltipstered').toArray();
- },
-
- /**
- * Change default options for all future instances
- *
- * @param {object} d The options that should be made defaults
- * @return {core}
- * @public
- */
- setDefaults: function(d) {
- $.extend(defaults, d);
- return this;
- },
-
- /**
- * For users to trigger their handlers on the public emitter
- *
- * @returns {core}
- * @public
- */
- triggerHandler: function() {
- this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
- return this;
- }
-};
-
-// $.tooltipster will be used to call core methods
-$.tooltipster = new core();
-
-// the Tooltipster instance class (mind the capital T)
-$.Tooltipster = function(element, options) {
-
- // list of instance variables
-
- // stack of custom callbacks provided as parameters to API methods
- this.__callbacks = {
- close: [],
- open: []
- };
- // the schedule time of DOM removal
- this.__closingTime;
- // this will be the user content shown in the tooltip. A capital "C" is used
- // because there is also a method called content()
- this.__Content;
- // for the size tracker
- this.__contentBcr;
- // to disable the tooltip after destruction
- this.__destroyed = false;
- // we can't emit directly on the instance because if a method with the same
- // name as the event exists, it will be called by jQuery. Se we use a plain
- // object as emitter. This emitter is for internal use by plugins,
- // if needed.
- this.__$emitterPrivate = $({});
- // this emitter is for the user to listen to events without risking to mess
- // with our internal listeners
- this.__$emitterPublic = $({});
- this.__enabled = true;
- // the reference to the gc interval
- this.__garbageCollector;
- // various position and size data recomputed before each repositioning
- this.__Geometry;
- // the tooltip position, saved after each repositioning by a plugin
- this.__lastPosition;
- // a unique namespace per instance
- this.__namespace = 'tooltipster-'+ Math.round(Math.random()*1000000);
- this.__options;
- // will be used to support origins in scrollable areas
- this.__$originParents;
- this.__pointerIsOverOrigin = false;
- // to remove themes if needed
- this.__previousThemes = [];
- // the state can be either: appearing, stable, disappearing, closed
- this.__state = 'closed';
- // timeout references
- this.__timeouts = {
- close: [],
- open: null
- };
- // store touch events to be able to detect emulated mouse events
- this.__touchEvents = [];
- // the reference to the tracker interval
- this.__tracker = null;
- // the element to which this tooltip is associated
- this._$origin;
- // this will be the tooltip element (jQuery wrapped HTML element).
- // It's the job of a plugin to create it and append it to the DOM
- this._$tooltip;
-
- // launch
- this.__init(element, options);
-};
-
-$.Tooltipster.prototype = {
-
- /**
- * @param origin
- * @param options
- * @private
- */
- __init: function(origin, options) {
-
- var self = this;
-
- self._$origin = $(origin);
- self.__options = $.extend(true, {}, defaults, options);
-
- // some options may need to be reformatted
- self.__optionsFormat();
-
- // don't run on old IE if asked no to
- if ( !env.IE
- || env.IE >= self.__options.IEmin
- ) {
-
- // note: the content is null (empty) by default and can stay that
- // way if the plugin remains initialized but not fed any content. The
- // tooltip will just not appear.
-
- // let's save the initial value of the title attribute for later
- // restoration if need be.
- var initialTitle = null;
-
- // it will already have been saved in case of multiple tooltips
- if (self._$origin.data('tooltipster-initialTitle') === undefined) {
-
- initialTitle = self._$origin.attr('title');
-
- // we do not want initialTitle to be "undefined" because
- // of how jQuery's .data() method works
- if (initialTitle === undefined) initialTitle = null;
-
- self._$origin.data('tooltipster-initialTitle', initialTitle);
- }
-
- // If content is provided in the options, it has precedence over the
- // title attribute.
- // Note: an empty string is considered content, only 'null' represents
- // the absence of content.
- // Also, an existing title="" attribute will result in an empty string
- // content
- if (self.__options.content !== null) {
- self.__contentSet(self.__options.content);
- }
- else {
-
- var selector = self._$origin.attr('data-tooltip-content'),
- $el;
-
- if (selector){
- $el = $(document).find(selector);
- }
-
- if ($el && $el[0]) {
- self.__contentSet($el.first());
- }
- else {
- self.__contentSet(initialTitle);
- }
- }
-
- self._$origin
- // strip the title off of the element to prevent the default tooltips
- // from popping up
- .removeAttr('title')
- // to be able to find all instances on the page later (upon window
- // events in particular)
- .addClass('tooltipstered');
-
- // set listeners on the origin
- self.__prepareOrigin();
-
- // set the garbage collector
- self.__prepareGC();
-
- // init plugins
- $.each(self.__options.plugins, function(i, pluginName) {
- self._plug(pluginName);
- });
-
- // to detect swiping
- if (env.hasTouchCapability) {
- $(env.window.document.body).on('touchmove.'+ self.__namespace +'-triggerOpen', function(event) {
- self._touchRecordEvent(event);
- });
- }
-
- self
- // prepare the tooltip when it gets created. This event must
- // be fired by a plugin
- ._on('created', function() {
- self.__prepareTooltip();
- })
- // save position information when it's sent by a plugin
- ._on('repositioned', function(e) {
- self.__lastPosition = e.position;
- });
- }
- else {
- self.__options.disabled = true;
- }
- },
-
- /**
- * Insert the content into the appropriate HTML element of the tooltip
- *
- * @returns {self}
- * @private
- */
- __contentInsert: function() {
-
- var self = this,
- $el = self._$tooltip.find('.tooltipster-content'),
- formattedContent = self.__Content,
- format = function(content) {
- formattedContent = content;
- };
-
- self._trigger({
- type: 'format',
- content: self.__Content,
- format: format
- });
-
- if (self.__options.functionFormat) {
-
- formattedContent = self.__options.functionFormat.call(
- self,
- self,
- { origin: self._$origin[0] },
- self.__Content
- );
- }
-
- if (typeof formattedContent === 'string' && !self.__options.contentAsHTML) {
- $el.text(formattedContent);
- }
- else {
- $el
- .empty()
- .append(formattedContent);
- }
-
- return self;
- },
-
- /**
- * Save the content, cloning it beforehand if need be
- *
- * @param content
- * @returns {self}
- * @private
- */
- __contentSet: function(content) {
-
- // clone if asked. Cloning the object makes sure that each instance has its
- // own version of the content (in case a same object were provided for several
- // instances)
- // reminder: typeof null === object
- if (content instanceof $ && this.__options.contentCloning) {
- content = content.clone(true);
- }
-
- this.__Content = content;
-
- this._trigger({
- type: 'updated',
- content: content
- });
-
- return this;
- },
-
- /**
- * Error message about a method call made after destruction
- *
- * @private
- */
- __destroyError: function() {
- throw new Error('This tooltip has been destroyed and cannot execute your method call.');
- },
-
- /**
- * Gather all information about dimensions and available space,
- * called before every repositioning
- *
- * @private
- * @returns {object}
- */
- __geometry: function() {
-
- var self = this,
- $target = self._$origin,
- originIsArea = self._$origin.is('area');
-
- // if this._$origin is a map area, the target we'll need
- // the dimensions of is actually the image using the map,
- // not the area itself
- if (originIsArea) {
-
- var mapName = self._$origin.parent().attr('name');
-
- $target = $('img[usemap="#'+ mapName +'"]');
- }
-
- var bcr = $target[0].getBoundingClientRect(),
- $document = $(env.window.document),
- $window = $(env.window),
- $parent = $target,
- // some useful properties of important elements
- geo = {
- // available space for the tooltip, see down below
- available: {
- document: null,
- window: null
- },
- document: {
- size: {
- height: $document.height(),
- width: $document.width()
- }
- },
- window: {
- scroll: {
- // the second ones are for IE compatibility
- left: env.window.scrollX || env.window.document.documentElement.scrollLeft,
- top: env.window.scrollY || env.window.document.documentElement.scrollTop
- },
- size: {
- height: $window.height(),
- width: $window.width()
- }
- },
- origin: {
- // the origin has a fixed lineage if itself or one of its
- // ancestors has a fixed position
- fixedLineage: false,
- // relative to the document
- offset: {},
- size: {
- height: bcr.bottom - bcr.top,
- width: bcr.right - bcr.left
- },
- usemapImage: originIsArea ? $target[0] : null,
- // relative to the window
- windowOffset: {
- bottom: bcr.bottom,
- left: bcr.left,
- right: bcr.right,
- top: bcr.top
- }
- }
- },
- geoFixed = false;
-
- // if the element is a map area, some properties may need
- // to be recalculated
- if (originIsArea) {
-
- var shape = self._$origin.attr('shape'),
- coords = self._$origin.attr('coords');
-
- if (coords) {
-
- coords = coords.split(',');
-
- $.map(coords, function(val, i) {
- coords[i] = parseInt(val);
- });
- }
-
- // if the image itself is the area, nothing more to do
- if (shape != 'default') {
-
- switch(shape) {
-
- case 'circle':
-
- var circleCenterLeft = coords[0],
- circleCenterTop = coords[1],
- circleRadius = coords[2],
- areaTopOffset = circleCenterTop - circleRadius,
- areaLeftOffset = circleCenterLeft - circleRadius;
-
- geo.origin.size.height = circleRadius * 2;
- geo.origin.size.width = geo.origin.size.height;
-
- geo.origin.windowOffset.left += areaLeftOffset;
- geo.origin.windowOffset.top += areaTopOffset;
-
- break;
-
- case 'rect':
-
- var areaLeft = coords[0],
- areaTop = coords[1],
- areaRight = coords[2],
- areaBottom = coords[3];
-
- geo.origin.size.height = areaBottom - areaTop;
- geo.origin.size.width = areaRight - areaLeft;
-
- geo.origin.windowOffset.left += areaLeft;
- geo.origin.windowOffset.top += areaTop;
-
- break;
-
- case 'poly':
-
- var areaSmallestX = 0,
- areaSmallestY = 0,
- areaGreatestX = 0,
- areaGreatestY = 0,
- arrayAlternate = 'even';
-
- for (var i = 0; i < coords.length; i++) {
-
- var areaNumber = coords[i];
-
- if (arrayAlternate == 'even') {
-
- if (areaNumber > areaGreatestX) {
-
- areaGreatestX = areaNumber;
-
- if (i === 0) {
- areaSmallestX = areaGreatestX;
- }
- }
-
- if (areaNumber < areaSmallestX) {
- areaSmallestX = areaNumber;
- }
-
- arrayAlternate = 'odd';
- }
- else {
- if (areaNumber > areaGreatestY) {
-
- areaGreatestY = areaNumber;
-
- if (i == 1) {
- areaSmallestY = areaGreatestY;
- }
- }
-
- if (areaNumber < areaSmallestY) {
- areaSmallestY = areaNumber;
- }
-
- arrayAlternate = 'even';
- }
- }
-
- geo.origin.size.height = areaGreatestY - areaSmallestY;
- geo.origin.size.width = areaGreatestX - areaSmallestX;
-
- geo.origin.windowOffset.left += areaSmallestX;
- geo.origin.windowOffset.top += areaSmallestY;
-
- break;
- }
- }
- }
-
- // user callback through an event
- var edit = function(r) {
- geo.origin.size.height = r.height,
- geo.origin.windowOffset.left = r.left,
- geo.origin.windowOffset.top = r.top,
- geo.origin.size.width = r.width
- };
-
- self._trigger({
- type: 'geometry',
- edit: edit,
- geometry: {
- height: geo.origin.size.height,
- left: geo.origin.windowOffset.left,
- top: geo.origin.windowOffset.top,
- width: geo.origin.size.width
- }
- });
-
- // calculate the remaining properties with what we got
-
- geo.origin.windowOffset.right = geo.origin.windowOffset.left + geo.origin.size.width;
- geo.origin.windowOffset.bottom = geo.origin.windowOffset.top + geo.origin.size.height;
-
- geo.origin.offset.left = geo.origin.windowOffset.left + geo.window.scroll.left;
- geo.origin.offset.top = geo.origin.windowOffset.top + geo.window.scroll.top;
- geo.origin.offset.bottom = geo.origin.offset.top + geo.origin.size.height;
- geo.origin.offset.right = geo.origin.offset.left + geo.origin.size.width;
-
- // the space that is available to display the tooltip relatively to the document
- geo.available.document = {
- bottom: {
- height: geo.document.size.height - geo.origin.offset.bottom,
- width: geo.document.size.width
- },
- left: {
- height: geo.document.size.height,
- width: geo.origin.offset.left
- },
- right: {
- height: geo.document.size.height,
- width: geo.document.size.width - geo.origin.offset.right
- },
- top: {
- height: geo.origin.offset.top,
- width: geo.document.size.width
- }
- };
-
- // the space that is available to display the tooltip relatively to the viewport
- // (the resulting values may be negative if the origin overflows the viewport)
- geo.available.window = {
- bottom: {
- // the inner max is here to make sure the available height is no bigger
- // than the viewport height (when the origin is off screen at the top).
- // The outer max just makes sure that the height is not negative (when
- // the origin overflows at the bottom).
- height: Math.max(geo.window.size.height - Math.max(geo.origin.windowOffset.bottom, 0), 0),
- width: geo.window.size.width
- },
- left: {
- height: geo.window.size.height,
- width: Math.max(geo.origin.windowOffset.left, 0)
- },
- right: {
- height: geo.window.size.height,
- width: Math.max(geo.window.size.width - Math.max(geo.origin.windowOffset.right, 0), 0)
- },
- top: {
- height: Math.max(geo.origin.windowOffset.top, 0),
- width: geo.window.size.width
- }
- };
-
- while ($parent[0].tagName.toLowerCase() != 'html') {
-
- if ($parent.css('position') == 'fixed') {
- geo.origin.fixedLineage = true;
- break;
- }
-
- $parent = $parent.parent();
- }
-
- return geo;
- },
-
- /**
- * Some options may need to be formated before being used
- *
- * @returns {self}
- * @private
- */
- __optionsFormat: function() {
-
- if (typeof this.__options.animationDuration == 'number') {
- this.__options.animationDuration = [this.__options.animationDuration, this.__options.animationDuration];
- }
-
- if (typeof this.__options.delay == 'number') {
- this.__options.delay = [this.__options.delay, this.__options.delay];
- }
-
- if (typeof this.__options.delayTouch == 'number') {
- this.__options.delayTouch = [this.__options.delayTouch, this.__options.delayTouch];
- }
-
- if (typeof this.__options.theme == 'string') {
- this.__options.theme = [this.__options.theme];
- }
-
- // determine the future parent
- if (this.__options.parent === null) {
- this.__options.parent = $(env.window.document.body);
- }
- else if (typeof this.__options.parent == 'string') {
- this.__options.parent = $(this.__options.parent);
- }
-
- if (this.__options.trigger == 'hover') {
-
- this.__options.triggerOpen = {
- mouseenter: true,
- touchstart: true
- };
-
- this.__options.triggerClose = {
- mouseleave: true,
- originClick: true,
- touchleave: true
- };
- }
- else if (this.__options.trigger == 'click') {
-
- this.__options.triggerOpen = {
- click: true,
- tap: true
- };
-
- this.__options.triggerClose = {
- click: true,
- tap: true
- };
- }
-
- // for the plugins
- this._trigger('options');
-
- return this;
- },
-
- /**
- * Schedules or cancels the garbage collector task
- *
- * @returns {self}
- * @private
- */
- __prepareGC: function() {
-
- var self = this;
-
- // in case the selfDestruction option has been changed by a method call
- if (self.__options.selfDestruction) {
-
- // the GC task
- self.__garbageCollector = setInterval(function() {
-
- var now = new Date().getTime();
-
- // forget the old events
- self.__touchEvents = $.grep(self.__touchEvents, function(event, i) {
- // 1 minute
- return now - event.time > 60000;
- });
-
- // auto-destruct if the origin is gone
- if (!bodyContains(self._$origin)) {
-
- self.close(function(){
- self.destroy();
- });
- }
- }, 20000);
- }
- else {
- clearInterval(self.__garbageCollector);
- }
-
- return self;
- },
-
- /**
- * Sets listeners on the origin if the open triggers require them.
- * Unlike the listeners set at opening time, these ones
- * remain even when the tooltip is closed. It has been made a
- * separate method so it can be called when the triggers are
- * changed in the options. Closing is handled in _open()
- * because of the bindings that may be needed on the tooltip
- * itself
- *
- * @returns {self}
- * @private
- */
- __prepareOrigin: function() {
-
- var self = this;
-
- // in case we're resetting the triggers
- self._$origin.off('.'+ self.__namespace +'-triggerOpen');
-
- // if the device is touch capable, even if only mouse triggers
- // are asked, we need to listen to touch events to know if the mouse
- // events are actually emulated (so we can ignore them)
- if (env.hasTouchCapability) {
-
- self._$origin.on(
- 'touchstart.'+ self.__namespace +'-triggerOpen ' +
- 'touchend.'+ self.__namespace +'-triggerOpen ' +
- 'touchcancel.'+ self.__namespace +'-triggerOpen',
- function(event){
- self._touchRecordEvent(event);
- }
- );
- }
-
- // mouse click and touch tap work the same way
- if ( self.__options.triggerOpen.click
- || (self.__options.triggerOpen.tap && env.hasTouchCapability)
- ) {
-
- var eventNames = '';
- if (self.__options.triggerOpen.click) {
- eventNames += 'click.'+ self.__namespace +'-triggerOpen ';
- }
- if (self.__options.triggerOpen.tap && env.hasTouchCapability) {
- eventNames += 'touchend.'+ self.__namespace +'-triggerOpen';
- }
-
- self._$origin.on(eventNames, function(event) {
- if (self._touchIsMeaningfulEvent(event)) {
- self._open(event);
- }
- });
- }
-
- // mouseenter and touch start work the same way
- if ( self.__options.triggerOpen.mouseenter
- || (self.__options.triggerOpen.touchstart && env.hasTouchCapability)
- ) {
-
- var eventNames = '';
- if (self.__options.triggerOpen.mouseenter) {
- eventNames += 'mouseenter.'+ self.__namespace +'-triggerOpen ';
- }
- if (self.__options.triggerOpen.touchstart && env.hasTouchCapability) {
- eventNames += 'touchstart.'+ self.__namespace +'-triggerOpen';
- }
-
- self._$origin.on(eventNames, function(event) {
- if ( self._touchIsTouchEvent(event)
- || !self._touchIsEmulatedEvent(event)
- ) {
- self.__pointerIsOverOrigin = true;
- self._openShortly(event);
- }
- });
- }
-
- // info for the mouseleave/touchleave close triggers when they use a delay
- if ( self.__options.triggerClose.mouseleave
- || (self.__options.triggerClose.touchleave && env.hasTouchCapability)
- ) {
-
- var eventNames = '';
- if (self.__options.triggerClose.mouseleave) {
- eventNames += 'mouseleave.'+ self.__namespace +'-triggerOpen ';
- }
- if (self.__options.triggerClose.touchleave && env.hasTouchCapability) {
- eventNames += 'touchend.'+ self.__namespace +'-triggerOpen touchcancel.'+ self.__namespace +'-triggerOpen';
- }
-
- self._$origin.on(eventNames, function(event) {
-
- if (self._touchIsMeaningfulEvent(event)) {
- self.__pointerIsOverOrigin = false;
- }
- });
- }
-
- return self;
- },
-
- /**
- * Do the things that need to be done only once after the tooltip
- * HTML element it has been created. It has been made a separate
- * method so it can be called when options are changed. Remember
- * that the tooltip may actually exist in the DOM before it is
- * opened, and present after it has been closed: it's the display
- * plugin that takes care of handling it.
- *
- * @returns {self}
- * @private
- */
- __prepareTooltip: function() {
-
- var self = this,
- p = self.__options.interactive ? 'auto' : '';
-
- // this will be useful to know quickly if the tooltip is in
- // the DOM or not
- self._$tooltip
- .attr('id', self.__namespace)
- .css({
- // pointer events
- 'pointer-events': p,
- zIndex: self.__options.zIndex
- });
-
- // themes
- // remove the old ones and add the new ones
- $.each(self.__previousThemes, function(i, theme) {
- self._$tooltip.removeClass(theme);
- });
- $.each(self.__options.theme, function(i, theme) {
- self._$tooltip.addClass(theme);
- });
-
- self.__previousThemes = $.merge([], self.__options.theme);
-
- return self;
- },
-
- /**
- * Handles the scroll on any of the parents of the origin (when the
- * tooltip is open)
- *
- * @param {object} event
- * @returns {self}
- * @private
- */
- __scrollHandler: function(event) {
-
- var self = this;
-
- if (self.__options.triggerClose.scroll) {
- self._close(event);
- }
- else {
-
- // if the origin or tooltip have been removed: do nothing, the tracker will
- // take care of it later
- if (bodyContains(self._$origin) && bodyContains(self._$tooltip)) {
-
- var geo = null;
-
- // if the scroll happened on the window
- if (event.target === env.window.document) {
-
- // if the origin has a fixed lineage, window scroll will have no
- // effect on its position nor on the position of the tooltip
- if (!self.__Geometry.origin.fixedLineage) {
-
- // we don't need to do anything unless repositionOnScroll is true
- // because the tooltip will already have moved with the window
- // (and of course with the origin)
- if (self.__options.repositionOnScroll) {
- self.reposition(event);
- }
- }
- }
- // if the scroll happened on another parent of the tooltip, it means
- // that it's in a scrollable area and now needs to have its position
- // adjusted or recomputed, depending ont the repositionOnScroll
- // option. Also, if the origin is partly hidden due to a parent that
- // hides its overflow, we'll just hide (not close) the tooltip.
- else {
-
- geo = self.__geometry();
-
- var overflows = false;
-
- // a fixed position origin is not affected by the overflow hiding
- // of a parent
- if (self._$origin.css('position') != 'fixed') {
-
- self.__$originParents.each(function(i, el) {
-
- var $el = $(el),
- overflowX = $el.css('overflow-x'),
- overflowY = $el.css('overflow-y');
-
- if (overflowX != 'visible' || overflowY != 'visible') {
-
- var bcr = el.getBoundingClientRect();
-
- if (overflowX != 'visible') {
-
- if ( geo.origin.windowOffset.left < bcr.left
- || geo.origin.windowOffset.right > bcr.right
- ) {
- overflows = true;
- return false;
- }
- }
-
- if (overflowY != 'visible') {
-
- if ( geo.origin.windowOffset.top < bcr.top
- || geo.origin.windowOffset.bottom > bcr.bottom
- ) {
- overflows = true;
- return false;
- }
- }
- }
-
- // no need to go further if fixed, for the same reason as above
- if ($el.css('position') == 'fixed') {
- return false;
- }
- });
- }
-
- if (overflows) {
- self._$tooltip.css('visibility', 'hidden');
- }
- else {
-
- self._$tooltip.css('visibility', 'visible');
-
- // reposition
- if (self.__options.repositionOnScroll) {
- self.reposition(event);
- }
- // or just adjust offset
- else {
-
- // we have to use offset and not windowOffset because this way,
- // only the scroll distance of the scrollable areas are taken into
- // account (the scrolltop value of the main window must be
- // ignored since the tooltip already moves with it)
- var offsetLeft = geo.origin.offset.left - self.__Geometry.origin.offset.left,
- offsetTop = geo.origin.offset.top - self.__Geometry.origin.offset.top;
-
- // add the offset to the position initially computed by the display plugin
- self._$tooltip.css({
- left: self.__lastPosition.coord.left + offsetLeft,
- top: self.__lastPosition.coord.top + offsetTop
- });
- }
- }
- }
-
- self._trigger({
- type: 'scroll',
- event: event,
- geo: geo
- });
- }
- }
-
- return self;
- },
-
- /**
- * Changes the state of the tooltip
- *
- * @param {string} state
- * @returns {self}
- * @private
- */
- __stateSet: function(state) {
-
- this.__state = state;
-
- this._trigger({
- type: 'state',
- state: state
- });
-
- return this;
- },
-
- /**
- * Clear appearance timeouts
- *
- * @returns {self}
- * @private
- */
- __timeoutsClear: function() {
-
- // there is only one possible open timeout: the delayed opening
- // when the mouseenter/touchstart open triggers are used
- clearTimeout(this.__timeouts.open);
- this.__timeouts.open = null;
-
- // ... but several close timeouts: the delayed closing when the
- // mouseleave close trigger is used and the timer option
- $.each(this.__timeouts.close, function(i, timeout) {
- clearTimeout(timeout);
- });
- this.__timeouts.close = [];
-
- return this;
- },
-
- /**
- * Start the tracker that will make checks at regular intervals
- *
- * @returns {self}
- * @private
- */
- __trackerStart: function() {
-
- var self = this,
- $content = self._$tooltip.find('.tooltipster-content');
-
- // get the initial content size
- if (self.__options.trackTooltip) {
- self.__contentBcr = $content[0].getBoundingClientRect();
- }
-
- self.__tracker = setInterval(function() {
-
- // if the origin or tooltip elements have been removed.
- // Note: we could destroy the instance now if the origin has
- // been removed but we'll leave that task to our garbage collector
- if (!bodyContains(self._$origin) || !bodyContains(self._$tooltip)) {
- self._close();
- }
- // if everything is alright
- else {
-
- // compare the former and current positions of the origin to reposition
- // the tooltip if need be
- if (self.__options.trackOrigin) {
-
- var g = self.__geometry(),
- identical = false;
-
- // compare size first (a change requires repositioning too)
- if (areEqual(g.origin.size, self.__Geometry.origin.size)) {
-
- // for elements that have a fixed lineage (see __geometry()), we track the
- // top and left properties (relative to window)
- if (self.__Geometry.origin.fixedLineage) {
- if (areEqual(g.origin.windowOffset, self.__Geometry.origin.windowOffset)) {
- identical = true;
- }
- }
- // otherwise, track total offset (relative to document)
- else {
- if (areEqual(g.origin.offset, self.__Geometry.origin.offset)) {
- identical = true;
- }
- }
- }
-
- if (!identical) {
-
- // close the tooltip when using the mouseleave close trigger
- // (see https://github.com/calebjacob/tooltipster/pull/253)
- if (self.__options.triggerClose.mouseleave) {
- self._close();
- }
- else {
- self.reposition();
- }
- }
- }
-
- if (self.__options.trackTooltip) {
-
- var currentBcr = $content[0].getBoundingClientRect();
-
- if ( currentBcr.height !== self.__contentBcr.height
- || currentBcr.width !== self.__contentBcr.width
- ) {
- self.reposition();
- self.__contentBcr = currentBcr;
- }
- }
- }
- }, self.__options.trackerInterval);
-
- return self;
- },
-
- /**
- * Closes the tooltip (after the closing delay)
- *
- * @param event
- * @param callback
- * @param force Set to true to override a potential refusal of the user's function
- * @returns {self}
- * @protected
- */
- _close: function(event, callback, force) {
-
- var self = this,
- ok = true;
-
- self._trigger({
- type: 'close',
- event: event,
- stop: function() {
- ok = false;
- }
- });
-
- // a destroying tooltip (force == true) may not refuse to close
- if (ok || force) {
-
- // save the method custom callback and cancel any open method custom callbacks
- if (callback) self.__callbacks.close.push(callback);
- self.__callbacks.open = [];
-
- // clear open/close timeouts
- self.__timeoutsClear();
-
- var finishCallbacks = function() {
-
- // trigger any close method custom callbacks and reset them
- $.each(self.__callbacks.close, function(i,c) {
- c.call(self, self, {
- event: event,
- origin: self._$origin[0]
- });
- });
-
- self.__callbacks.close = [];
- };
-
- if (self.__state != 'closed') {
-
- var necessary = true,
- d = new Date(),
- now = d.getTime(),
- newClosingTime = now + self.__options.animationDuration[1];
-
- // the tooltip may already already be disappearing, but if a new
- // call to close() is made after the animationDuration was changed
- // to 0 (for example), we ought to actually close it sooner than
- // previously scheduled. In that case it should be noted that the
- // browser will not adapt the animation duration to the new
- // animationDuration that was set after the start of the closing
- // animation.
- // Note: the same thing could be considered at opening, but is not
- // really useful since the tooltip is actually opened immediately
- // upon a call to _open(). Since it would not make the opening
- // animation finish sooner, its sole impact would be to trigger the
- // state event and the open callbacks sooner than the actual end of
- // the opening animation, which is not great.
- if (self.__state == 'disappearing') {
-
- if ( newClosingTime > self.__closingTime
- // in case closing is actually overdue because the script
- // execution was suspended. See #679
- && self.__options.animationDuration[1] > 0
- ) {
- necessary = false;
- }
- }
-
- if (necessary) {
-
- self.__closingTime = newClosingTime;
-
- if (self.__state != 'disappearing') {
- self.__stateSet('disappearing');
- }
-
- var finish = function() {
-
- // stop the tracker
- clearInterval(self.__tracker);
-
- // a "beforeClose" option has been asked several times but would
- // probably useless since the content element is still accessible
- // via ::content(), and because people can always use listeners
- // inside their content to track what's going on. For the sake of
- // simplicity, this has been denied. Bur for the rare people who
- // really need the option (for old browsers or for the case where
- // detaching the content is actually destructive, for file or
- // password inputs for example), this event will do the work.
- self._trigger({
- type: 'closing',
- event: event
- });
-
- // unbind listeners which are no longer needed
-
- self._$tooltip
- .off('.'+ self.__namespace +'-triggerClose')
- .removeClass('tooltipster-dying');
-
- // orientationchange, scroll and resize listeners
- $(env.window).off('.'+ self.__namespace +'-triggerClose');
-
- // scroll listeners
- self.__$originParents.each(function(i, el) {
- $(el).off('scroll.'+ self.__namespace +'-triggerClose');
- });
- // clear the array to prevent memory leaks
- self.__$originParents = null;
-
- $(env.window.document.body).off('.'+ self.__namespace +'-triggerClose');
-
- self._$origin.off('.'+ self.__namespace +'-triggerClose');
-
- self._off('dismissable');
-
- // a plugin that would like to remove the tooltip from the
- // DOM when closed should bind on this
- self.__stateSet('closed');
-
- // trigger event
- self._trigger({
- type: 'after',
- event: event
- });
-
- // call our constructor custom callback function
- if (self.__options.functionAfter) {
- self.__options.functionAfter.call(self, self, {
- event: event,
- origin: self._$origin[0]
- });
- }
-
- // call our method custom callbacks functions
- finishCallbacks();
- };
-
- if (env.hasTransitions) {
-
- self._$tooltip.css({
- '-moz-animation-duration': self.__options.animationDuration[1] + 'ms',
- '-ms-animation-duration': self.__options.animationDuration[1] + 'ms',
- '-o-animation-duration': self.__options.animationDuration[1] + 'ms',
- '-webkit-animation-duration': self.__options.animationDuration[1] + 'ms',
- 'animation-duration': self.__options.animationDuration[1] + 'ms',
- 'transition-duration': self.__options.animationDuration[1] + 'ms'
- });
-
- self._$tooltip
- // clear both potential open and close tasks
- .clearQueue()
- .removeClass('tooltipster-show')
- // for transitions only
- .addClass('tooltipster-dying');
-
- if (self.__options.animationDuration[1] > 0) {
- self._$tooltip.delay(self.__options.animationDuration[1]);
- }
-
- self._$tooltip.queue(finish);
- }
- else {
-
- self._$tooltip
- .stop()
- .fadeOut(self.__options.animationDuration[1], finish);
- }
- }
- }
- // if the tooltip is already closed, we still need to trigger
- // the method custom callbacks
- else {
- finishCallbacks();
- }
- }
-
- return self;
- },
-
- /**
- * For internal use by plugins, if needed
- *
- * @returns {self}
- * @protected
- */
- _off: function() {
- this.__$emitterPrivate.off.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * For internal use by plugins, if needed
- *
- * @returns {self}
- * @protected
- */
- _on: function() {
- this.__$emitterPrivate.on.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * For internal use by plugins, if needed
- *
- * @returns {self}
- * @protected
- */
- _one: function() {
- this.__$emitterPrivate.one.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
- return this;
- },
-
- /**
- * Opens the tooltip right away.
- *
- * @param event
- * @param callback Will be called when the opening animation is over
- * @returns {self}
- * @protected
- */
- _open: function(event, callback) {
-
- var self = this;
-
- // if the destruction process has not begun and if this was not
- // triggered by an unwanted emulated click event
- if (!self.__destroying) {
-
- // check that the origin is still in the DOM
- if ( bodyContains(self._$origin)
- // if the tooltip is enabled
- && self.__enabled
- ) {
-
- var ok = true;
-
- // if the tooltip is not open yet, we need to call functionBefore.
- // otherwise we can jst go on
- if (self.__state == 'closed') {
-
- // trigger an event. The event.stop function allows the callback
- // to prevent the opening of the tooltip
- self._trigger({
- type: 'before',
- event: event,
- stop: function() {
- ok = false;
- }
- });
-
- if (ok && self.__options.functionBefore) {
-
- // call our custom function before continuing
- ok = self.__options.functionBefore.call(self, self, {
- event: event,
- origin: self._$origin[0]
- });
- }
- }
-
- if (ok !== false) {
-
- // if there is some content
- if (self.__Content !== null) {
-
- // save the method callback and cancel close method callbacks
- if (callback) {
- self.__callbacks.open.push(callback);
- }
- self.__callbacks.close = [];
-
- // get rid of any appearance timeouts
- self.__timeoutsClear();
-
- var extraTime,
- finish = function() {
-
- if (self.__state != 'stable') {
- self.__stateSet('stable');
- }
-
- // trigger any open method custom callbacks and reset them
- $.each(self.__callbacks.open, function(i,c) {
- c.call(self, self, {
- origin: self._$origin[0],
- tooltip: self._$tooltip[0]
- });
- });
-
- self.__callbacks.open = [];
- };
-
- // if the tooltip is already open
- if (self.__state !== 'closed') {
-
- // the timer (if any) will start (or restart) right now
- extraTime = 0;
-
- // if it was disappearing, cancel that
- if (self.__state === 'disappearing') {
-
- self.__stateSet('appearing');
-
- if (env.hasTransitions) {
-
- self._$tooltip
- .clearQueue()
- .removeClass('tooltipster-dying')
- .addClass('tooltipster-show');
-
- if (self.__options.animationDuration[0] > 0) {
- self._$tooltip.delay(self.__options.animationDuration[0]);
- }
-
- self._$tooltip.queue(finish);
- }
- else {
- // in case the tooltip was currently fading out, bring it back
- // to life
- self._$tooltip
- .stop()
- .fadeIn(finish);
- }
- }
- // if the tooltip is already open, we still need to trigger the method
- // custom callback
- else if (self.__state == 'stable') {
- finish();
- }
- }
- // if the tooltip isn't already open, open it
- else {
-
- // a plugin must bind on this and store the tooltip in this._$tooltip
- self.__stateSet('appearing');
-
- // the timer (if any) will start when the tooltip has fully appeared
- // after its transition
- extraTime = self.__options.animationDuration[0];
-
- // insert the content inside the tooltip
- self.__contentInsert();
-
- // reposition the tooltip and attach to the DOM
- self.reposition(event, true);
-
- // animate in the tooltip. If the display plugin wants no css
- // animations, it may override the animation option with a
- // dummy value that will produce no effect
- if (env.hasTransitions) {
-
- // note: there seems to be an issue with start animations which
- // are randomly not played on fast devices in both Chrome and FF,
- // couldn't find a way to solve it yet. It seems that applying
- // the classes before appending to the DOM helps a little, but
- // it messes up some CSS transitions. The issue almost never
- // happens when delay[0]==0 though
- self._$tooltip
- .addClass('tooltipster-'+ self.__options.animation)
- .addClass('tooltipster-initial')
- .css({
- '-moz-animation-duration': self.__options.animationDuration[0] + 'ms',
- '-ms-animation-duration': self.__options.animationDuration[0] + 'ms',
- '-o-animation-duration': self.__options.animationDuration[0] + 'ms',
- '-webkit-animation-duration': self.__options.animationDuration[0] + 'ms',
- 'animation-duration': self.__options.animationDuration[0] + 'ms',
- 'transition-duration': self.__options.animationDuration[0] + 'ms'
- });
-
- setTimeout(
- function() {
-
- // a quick hover may have already triggered a mouseleave
- if (self.__state != 'closed') {
-
- self._$tooltip
- .addClass('tooltipster-show')
- .removeClass('tooltipster-initial');
-
- if (self.__options.animationDuration[0] > 0) {
- self._$tooltip.delay(self.__options.animationDuration[0]);
- }
-
- self._$tooltip.queue(finish);
- }
- },
- 0
- );
- }
- else {
-
- // old browsers will have to live with this
- self._$tooltip
- .css('display', 'none')
- .fadeIn(self.__options.animationDuration[0], finish);
- }
-
- // checks if the origin is removed while the tooltip is open
- self.__trackerStart();
-
- // NOTE: the listeners below have a '-triggerClose' namespace
- // because we'll remove them when the tooltip closes (unlike
- // the '-triggerOpen' listeners). So some of them are actually
- // not about close triggers, rather about positioning.
-
- $(env.window)
- // reposition on resize
- .on('resize.'+ self.__namespace +'-triggerClose', function(e) {
-
- var $ae = $(document.activeElement);
-
- // reposition only if the resize event was not triggered upon the opening
- // of a virtual keyboard due to an input field being focused within the tooltip
- // (otherwise the repositioning would lose the focus)
- if ( (!$ae.is('input') && !$ae.is('textarea'))
- || !$.contains(self._$tooltip[0], $ae[0])
- ) {
- self.reposition(e);
- }
- })
- // same as below for parents
- .on('scroll.'+ self.__namespace +'-triggerClose', function(e) {
- self.__scrollHandler(e);
- });
-
- self.__$originParents = self._$origin.parents();
-
- // scrolling may require the tooltip to be moved or even
- // repositioned in some cases
- self.__$originParents.each(function(i, parent) {
-
- $(parent).on('scroll.'+ self.__namespace +'-triggerClose', function(e) {
- self.__scrollHandler(e);
- });
- });
-
- if ( self.__options.triggerClose.mouseleave
- || (self.__options.triggerClose.touchleave && env.hasTouchCapability)
- ) {
-
- // we use an event to allow users/plugins to control when the mouseleave/touchleave
- // close triggers will come to action. It allows to have more triggering elements
- // than just the origin and the tooltip for example, or to cancel/delay the closing,
- // or to make the tooltip interactive even if it wasn't when it was open, etc.
- self._on('dismissable', function(event) {
-
- if (event.dismissable) {
-
- if (event.delay) {
-
- timeout = setTimeout(function() {
- // event.event may be undefined
- self._close(event.event);
- }, event.delay);
-
- self.__timeouts.close.push(timeout);
- }
- else {
- self._close(event);
- }
- }
- else {
- clearTimeout(timeout);
- }
- });
-
- // now set the listeners that will trigger 'dismissable' events
- var $elements = self._$origin,
- eventNamesIn = '',
- eventNamesOut = '',
- timeout = null;
-
- // if we have to allow interaction, bind on the tooltip too
- if (self.__options.interactive) {
- $elements = $elements.add(self._$tooltip);
- }
-
- if (self.__options.triggerClose.mouseleave) {
- eventNamesIn += 'mouseenter.'+ self.__namespace +'-triggerClose ';
- eventNamesOut += 'mouseleave.'+ self.__namespace +'-triggerClose ';
- }
- if (self.__options.triggerClose.touchleave && env.hasTouchCapability) {
- eventNamesIn += 'touchstart.'+ self.__namespace +'-triggerClose';
- eventNamesOut += 'touchend.'+ self.__namespace +'-triggerClose touchcancel.'+ self.__namespace +'-triggerClose';
- }
-
- $elements
- // close after some time spent outside of the elements
- .on(eventNamesOut, function(event) {
-
- // it's ok if the touch gesture ended up to be a swipe,
- // it's still a "touch leave" situation
- if ( self._touchIsTouchEvent(event)
- || !self._touchIsEmulatedEvent(event)
- ) {
-
- var delay = (event.type == 'mouseleave') ?
- self.__options.delay :
- self.__options.delayTouch;
-
- self._trigger({
- delay: delay[1],
- dismissable: true,
- event: event,
- type: 'dismissable'
- });
- }
- })
- // suspend the mouseleave timeout when the pointer comes back
- // over the elements
- .on(eventNamesIn, function(event) {
-
- // it's also ok if the touch event is a swipe gesture
- if ( self._touchIsTouchEvent(event)
- || !self._touchIsEmulatedEvent(event)
- ) {
- self._trigger({
- dismissable: false,
- event: event,
- type: 'dismissable'
- });
- }
- });
- }
-
- // close the tooltip when the origin gets a mouse click (common behavior of
- // native tooltips)
- if (self.__options.triggerClose.originClick) {
-
- self._$origin.on('click.'+ self.__namespace + '-triggerClose', function(event) {
-
- // we could actually let a tap trigger this but this feature just
- // does not make sense on touch devices
- if ( !self._touchIsTouchEvent(event)
- && !self._touchIsEmulatedEvent(event)
- ) {
- self._close(event);
- }
- });
- }
-
- // set the same bindings for click and touch on the body to close the tooltip
- if ( self.__options.triggerClose.click
- || (self.__options.triggerClose.tap && env.hasTouchCapability)
- ) {
-
- // don't set right away since the click/tap event which triggered this method
- // (if it was a click/tap) is going to bubble up to the body, we don't want it
- // to close the tooltip immediately after it opened
- setTimeout(function() {
-
- if (self.__state != 'closed') {
-
- var eventNames = '',
- $body = $(env.window.document.body);
-
- if (self.__options.triggerClose.click) {
- eventNames += 'click.'+ self.__namespace +'-triggerClose ';
- }
- if (self.__options.triggerClose.tap && env.hasTouchCapability) {
- eventNames += 'touchend.'+ self.__namespace +'-triggerClose';
- }
-
- $body.on(eventNames, function(event) {
-
- if (self._touchIsMeaningfulEvent(event)) {
-
- self._touchRecordEvent(event);
-
- if (!self.__options.interactive || !$.contains(self._$tooltip[0], event.target)) {
- self._close(event);
- }
- }
- });
-
- // needed to detect and ignore swiping
- if (self.__options.triggerClose.tap && env.hasTouchCapability) {
-
- $body.on('touchstart.'+ self.__namespace +'-triggerClose', function(event) {
- self._touchRecordEvent(event);
- });
- }
- }
- }, 0);
- }
-
- self._trigger('ready');
-
- // call our custom callback
- if (self.__options.functionReady) {
- self.__options.functionReady.call(self, self, {
- origin: self._$origin[0],
- tooltip: self._$tooltip[0]
- });
- }
- }
-
- // if we have a timer set, let the countdown begin
- if (self.__options.timer > 0) {
-
- var timeout = setTimeout(function() {
- self._close();
- }, self.__options.timer + extraTime);
-
- self.__timeouts.close.push(timeout);
- }
- }
- }
- }
- }
-
- return self;
- },
-
- /**
- * When using the mouseenter/touchstart open triggers, this function will
- * schedule the opening of the tooltip after the delay, if there is one
- *
- * @param event
- * @returns {self}
- * @protected
- */
- _openShortly: function(event) {
-
- var self = this,
- ok = true;
-
- if (self.__state != 'stable' && self.__state != 'appearing') {
-
- // if a timeout is not already running
- if (!self.__timeouts.open) {
-
- self._trigger({
- type: 'start',
- event: event,
- stop: function() {
- ok = false;
- }
- });
-
- if (ok) {
-
- var delay = (event.type.indexOf('touch') == 0) ?
- self.__options.delayTouch :
- self.__options.delay;
-
- if (delay[0]) {
-
- self.__timeouts.open = setTimeout(function() {
-
- self.__timeouts.open = null;
-
- // open only if the pointer (mouse or touch) is still over the origin.
- // The check on the "meaningful event" can only be made here, after some
- // time has passed (to know if the touch was a swipe or not)
- if (self.__pointerIsOverOrigin && self._touchIsMeaningfulEvent(event)) {
-
- // signal that we go on
- self._trigger('startend');
-
- self._open(event);
- }
- else {
- // signal that we cancel
- self._trigger('startcancel');
- }
- }, delay[0]);
- }
- else {
- // signal that we go on
- self._trigger('startend');
-
- self._open(event);
- }
- }
- }
- }
-
- return self;
- },
-
- /**
- * Meant for plugins to get their options
- *
- * @param {string} pluginName The name of the plugin that asks for its options
- * @param {object} defaultOptions The default options of the plugin
- * @returns {object} The options
- * @protected
- */
- _optionsExtract: function(pluginName, defaultOptions) {
-
- var self = this,
- options = $.extend(true, {}, defaultOptions);
-
- // if the plugin options were isolated in a property named after the
- // plugin, use them (prevents conflicts with other plugins)
- var pluginOptions = self.__options[pluginName];
-
- // if not, try to get them as regular options
- if (!pluginOptions){
-
- pluginOptions = {};
-
- $.each(defaultOptions, function(optionName, value) {
-
- var o = self.__options[optionName];
-
- if (o !== undefined) {
- pluginOptions[optionName] = o;
- }
- });
- }
-
- // let's merge the default options and the ones that were provided. We'd want
- // to do a deep copy but not let jQuery merge arrays, so we'll do a shallow
- // extend on two levels, that will be enough if options are not more than 1
- // level deep
- $.each(options, function(optionName, value) {
-
- if (pluginOptions[optionName] !== undefined) {
-
- if (( typeof value == 'object'
- && !(value instanceof Array)
- && value != null
- )
- &&
- ( typeof pluginOptions[optionName] == 'object'
- && !(pluginOptions[optionName] instanceof Array)
- && pluginOptions[optionName] != null
- )
- ) {
- $.extend(options[optionName], pluginOptions[optionName]);
- }
- else {
- options[optionName] = pluginOptions[optionName];
- }
- }
- });
-
- return options;
- },
-
- /**
- * Used at instantiation of the plugin, or afterwards by plugins that activate themselves
- * on existing instances
- *
- * @param {object} pluginName
- * @returns {self}
- * @protected
- */
- _plug: function(pluginName) {
-
- var plugin = $.tooltipster._plugin(pluginName);
-
- if (plugin) {
-
- // if there is a constructor for instances
- if (plugin.instance) {
-
- // proxy non-private methods on the instance to allow new instance methods
- $.tooltipster.__bridge(plugin.instance, this, plugin.name);
- }
- }
- else {
- throw new Error('The "'+ pluginName +'" plugin is not defined');
- }
-
- return this;
- },
-
- /**
- * This will return true if the event is a mouse event which was
- * emulated by the browser after a touch event. This allows us to
- * really dissociate mouse and touch triggers.
- *
- * There is a margin of error if a real mouse event is fired right
- * after (within the delay shown below) a touch event on the same
- * element, but hopefully it should not happen often.
- *
- * @returns {boolean}
- * @protected
- */
- _touchIsEmulatedEvent: function(event) {
-
- var isEmulated = false,
- now = new Date().getTime();
-
- for (var i = this.__touchEvents.length - 1; i >= 0; i--) {
-
- var e = this.__touchEvents[i];
-
- // delay, in milliseconds. It's supposed to be 300ms in
- // most browsers (350ms on iOS) to allow a double tap but
- // can be less (check out FastClick for more info)
- if (now - e.time < 500) {
-
- if (e.target === event.target) {
- isEmulated = true;
- }
- }
- else {
- break;
- }
- }
-
- return isEmulated;
- },
-
- /**
- * Returns false if the event was an emulated mouse event or
- * a touch event involved in a swipe gesture.
- *
- * @param {object} event
- * @returns {boolean}
- * @protected
- */
- _touchIsMeaningfulEvent: function(event) {
- return (
- (this._touchIsTouchEvent(event) && !this._touchSwiped(event.target))
- || (!this._touchIsTouchEvent(event) && !this._touchIsEmulatedEvent(event))
- );
- },
-
- /**
- * Checks if an event is a touch event
- *
- * @param {object} event
- * @returns {boolean}
- * @protected
- */
- _touchIsTouchEvent: function(event){
- return event.type.indexOf('touch') == 0;
- },
-
- /**
- * Store touch events for a while to detect swiping and emulated mouse events
- *
- * @param {object} event
- * @returns {self}
- * @protected
- */
- _touchRecordEvent: function(event) {
-
- if (this._touchIsTouchEvent(event)) {
- event.time = new Date().getTime();
- this.__touchEvents.push(event);
- }
-
- return this;
- },
-
- /**
- * Returns true if a swipe happened after the last touchstart event fired on
- * event.target.
- *
- * We need to differentiate a swipe from a tap before we let the event open
- * or close the tooltip. A swipe is when a touchmove (scroll) event happens
- * on the body between the touchstart and the touchend events of an element.
- *
- * @param {object} target The HTML element that may have triggered the swipe
- * @returns {boolean}
- * @protected
- */
- _touchSwiped: function(target) {
-
- var swiped = false;
-
- for (var i = this.__touchEvents.length - 1; i >= 0; i--) {
-
- var e = this.__touchEvents[i];
-
- if (e.type == 'touchmove') {
- swiped = true;
- break;
- }
- else if (
- e.type == 'touchstart'
- && target === e.target
- ) {
- break;
- }
- }
-
- return swiped;
- },
-
- /**
- * Triggers an event on the instance emitters
- *
- * @returns {self}
- * @protected
- */
- _trigger: function() {
-
- var args = Array.prototype.slice.apply(arguments);
-
- if (typeof args[0] == 'string') {
- args[0] = { type: args[0] };
- }
-
- // add properties to the event
- args[0].instance = this;
- args[0].origin = this._$origin ? this._$origin[0] : null;
- args[0].tooltip = this._$tooltip ? this._$tooltip[0] : null;
-
- // note: the order of emitters matters
- this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate, args);
- $.tooltipster._trigger.apply($.tooltipster, args);
- this.__$emitterPublic.trigger.apply(this.__$emitterPublic, args);
-
- return this;
- },
-
- /**
- * Deactivate a plugin on this instance
- *
- * @returns {self}
- * @protected
- */
- _unplug: function(pluginName) {
-
- var self = this;
-
- // if the plugin has been activated on this instance
- if (self[pluginName]) {
-
- var plugin = $.tooltipster._plugin(pluginName);
-
- // if there is a constructor for instances
- if (plugin.instance) {
-
- // unbridge
- $.each(plugin.instance, function(methodName, fn) {
-
- // if the method exists (privates methods do not) and comes indeed from
- // this plugin (may be missing or come from a conflicting plugin).
- if ( self[methodName]
- && self[methodName].bridged === self[pluginName]
- ) {
- delete self[methodName];
- }
- });
- }
-
- // destroy the plugin
- if (self[pluginName].__destroy) {
- self[pluginName].__destroy();
- }
-
- // remove the reference to the plugin instance
- delete self[pluginName];
- }
-
- return self;
- },
-
- /**
- * @see self::_close
- * @returns {self}
- * @public
- */
- close: function(callback) {
-
- if (!this.__destroyed) {
- this._close(null, callback);
- }
- else {
- this.__destroyError();
- }
-
- return this;
- },
-
- /**
- * Sets or gets the content of the tooltip
- *
- * @returns {mixed|self}
- * @public
- */
- content: function(content) {
-
- var self = this;
-
- // getter method
- if (content === undefined) {
- return self.__Content;
- }
- // setter method
- else {
-
- if (!self.__destroyed) {
-
- // change the content
- self.__contentSet(content);
-
- if (self.__Content !== null) {
-
- // update the tooltip if it is open
- if (self.__state !== 'closed') {
-
- // reset the content in the tooltip
- self.__contentInsert();
-
- // reposition and resize the tooltip
- self.reposition();
-
- // if we want to play a little animation showing the content changed
- if (self.__options.updateAnimation) {
-
- if (env.hasTransitions) {
-
- // keep the reference in the local scope
- var animation = self.__options.updateAnimation;
-
- self._$tooltip.addClass('tooltipster-update-'+ animation);
-
- // remove the class after a while. The actual duration of the
- // update animation may be shorter, it's set in the CSS rules
- setTimeout(function() {
-
- if (self.__state != 'closed') {
-
- self._$tooltip.removeClass('tooltipster-update-'+ animation);
- }
- }, 1000);
- }
- else {
- self._$tooltip.fadeTo(200, 0.5, function() {
- if (self.__state != 'closed') {
- self._$tooltip.fadeTo(200, 1);
- }
- });
- }
- }
- }
- }
- else {
- self._close();
- }
- }
- else {
- self.__destroyError();
- }
-
- return self;
- }
- },
-
- /**
- * Destroys the tooltip
- *
- * @returns {self}
- * @public
- */
- destroy: function() {
-
- var self = this;
-
- if (!self.__destroyed) {
-
- if(self.__state != 'closed'){
-
- // no closing delay
- self.option('animationDuration', 0)
- // force closing
- ._close(null, null, true);
- }
- else {
- // there might be an open timeout still running
- self.__timeoutsClear();
- }
-
- // send event
- self._trigger('destroy');
-
- self.__destroyed = true;
-
- self._$origin
- .removeData(self.__namespace)
- // remove the open trigger listeners
- .off('.'+ self.__namespace +'-triggerOpen');
-
- // remove the touch listener
- $(env.window.document.body).off('.' + self.__namespace +'-triggerOpen');
-
- var ns = self._$origin.data('tooltipster-ns');
-
- // if the origin has been removed from DOM, its data may
- // well have been destroyed in the process and there would
- // be nothing to clean up or restore
- if (ns) {
-
- // if there are no more tooltips on this element
- if (ns.length === 1) {
-
- // optional restoration of a title attribute
- var title = null;
- if (self.__options.restoration == 'previous') {
- title = self._$origin.data('tooltipster-initialTitle');
- }
- else if (self.__options.restoration == 'current') {
-
- // old school technique to stringify when outerHTML is not supported
- title = (typeof self.__Content == 'string') ?
- self.__Content :
- $('').append(self.__Content).html();
- }
-
- if (title) {
- self._$origin.attr('title', title);
- }
-
- // final cleaning
-
- self._$origin.removeClass('tooltipstered');
-
- self._$origin
- .removeData('tooltipster-ns')
- .removeData('tooltipster-initialTitle');
- }
- else {
- // remove the instance namespace from the list of namespaces of
- // tooltips present on the element
- ns = $.grep(ns, function(el, i) {
- return el !== self.__namespace;
- });
- self._$origin.data('tooltipster-ns', ns);
- }
- }
-
- // last event
- self._trigger('destroyed');
-
- // unbind private and public event listeners
- self._off();
- self.off();
-
- // remove external references, just in case
- self.__Content = null;
- self.__$emitterPrivate = null;
- self.__$emitterPublic = null;
- self.__options.parent = null;
- self._$origin = null;
- self._$tooltip = null;
-
- // make sure the object is no longer referenced in there to prevent
- // memory leaks
- $.tooltipster.__instancesLatestArr = $.grep($.tooltipster.__instancesLatestArr, function(el, i) {
- return self !== el;
- });
-
- clearInterval(self.__garbageCollector);
- }
- else {
- self.__destroyError();
- }
-
- // we return the scope rather than true so that the call to
- // .tooltipster('destroy') actually returns the matched elements
- // and applies to all of them
- return self;
- },
-
- /**
- * Disables the tooltip
- *
- * @returns {self}
- * @public
- */
- disable: function() {
-
- if (!this.__destroyed) {
-
- // close first, in case the tooltip would not disappear on
- // its own (no close trigger)
- this._close();
- this.__enabled = false;
-
- return this;
- }
- else {
- this.__destroyError();
- }
-
- return this;
- },
-
- /**
- * Returns the HTML element of the origin
- *
- * @returns {self}
- * @public
- */
- elementOrigin: function() {
-
- if (!this.__destroyed) {
- return this._$origin[0];
- }
- else {
- this.__destroyError();
- }
- },
-
- /**
- * Returns the HTML element of the tooltip
- *
- * @returns {self}
- * @public
- */
- elementTooltip: function() {
- return this._$tooltip ? this._$tooltip[0] : null;
- },
-
- /**
- * Enables the tooltip
- *
- * @returns {self}
- * @public
- */
- enable: function() {
- this.__enabled = true;
- return this;
- },
-
- /**
- * Alias, deprecated in 4.0.0
- *
- * @param {function} callback
- * @returns {self}
- * @public
- */
- hide: function(callback) {
- return this.close(callback);
- },
-
- /**
- * Returns the instance
- *
- * @returns {self}
- * @public
- */
- instance: function() {
- return this;
- },
-
- /**
- * For public use only, not to be used by plugins (use ::_off() instead)
- *
- * @returns {self}
- * @public
- */
- off: function() {
-
- if (!this.__destroyed) {
- this.__$emitterPublic.off.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
- }
-
- return this;
- },
-
- /**
- * For public use only, not to be used by plugins (use ::_on() instead)
- *
- * @returns {self}
- * @public
- */
- on: function() {
-
- if (!this.__destroyed) {
- this.__$emitterPublic.on.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
- }
- else {
- this.__destroyError();
- }
-
- return this;
- },
-
- /**
- * For public use only, not to be used by plugins
- *
- * @returns {self}
- * @public
- */
- one: function() {
-
- if (!this.__destroyed) {
- this.__$emitterPublic.one.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
- }
- else {
- this.__destroyError();
- }
-
- return this;
- },
-
- /**
- * @see self::_open
- * @returns {self}
- * @public
- */
- open: function(callback) {
-
- if (!this.__destroyed) {
- this._open(null, callback);
- }
- else {
- this.__destroyError();
- }
-
- return this;
- },
-
- /**
- * Get or set options. For internal use and advanced users only.
- *
- * @param {string} o Option name
- * @param {mixed} val optional A new value for the option
- * @return {mixed|self} If val is omitted, the value of the option
- * is returned, otherwise the instance itself is returned
- * @public
- */
- option: function(o, val) {
-
- // getter
- if (val === undefined) {
- return this.__options[o];
- }
- // setter
- else {
-
- if (!this.__destroyed) {
-
- // change value
- this.__options[o] = val;
-
- // format
- this.__optionsFormat();
-
- // re-prepare the triggers if needed
- if ($.inArray(o, ['trigger', 'triggerClose', 'triggerOpen']) >= 0) {
- this.__prepareOrigin();
- }
-
- if (o === 'selfDestruction') {
- this.__prepareGC();
- }
- }
- else {
- this.__destroyError();
- }
-
- return this;
- }
- },
-
- /**
- * This method is in charge of setting the position and size properties of the tooltip.
- * All the hard work is delegated to the display plugin.
- * Note: The tooltip may be detached from the DOM at the moment the method is called
- * but must be attached by the end of the method call.
- *
- * @param {object} event For internal use only. Defined if an event such as
- * window resizing triggered the repositioning
- * @param {boolean} tooltipIsDetached For internal use only. Set this to true if you
- * know that the tooltip not being in the DOM is not an issue (typically when the
- * tooltip element has just been created but has not been added to the DOM yet).
- * @returns {self}
- * @public
- */
- reposition: function(event, tooltipIsDetached) {
-
- var self = this;
-
- if (!self.__destroyed) {
-
- // if the tooltip is still open and the origin is still in the DOM
- if (self.__state != 'closed' && bodyContains(self._$origin)) {
-
- // if the tooltip has not been removed from DOM manually (or if it
- // has been detached on purpose)
- if (tooltipIsDetached || bodyContains(self._$tooltip)) {
-
- if (!tooltipIsDetached) {
- // detach in case the tooltip overflows the window and adds
- // scrollbars to it, so __geometry can be accurate
- self._$tooltip.detach();
- }
-
- // refresh the geometry object before passing it as a helper
- self.__Geometry = self.__geometry();
-
- // let a plugin fo the rest
- self._trigger({
- type: 'reposition',
- event: event,
- helper: {
- geo: self.__Geometry
- }
- });
- }
- }
- }
- else {
- self.__destroyError();
- }
-
- return self;
- },
-
- /**
- * Alias, deprecated in 4.0.0
- *
- * @param callback
- * @returns {self}
- * @public
- */
- show: function(callback) {
- return this.open(callback);
- },
-
- /**
- * Returns some properties about the instance
- *
- * @returns {object}
- * @public
- */
- status: function() {
-
- return {
- destroyed: this.__destroyed,
- enabled: this.__enabled,
- open: this.__state !== 'closed',
- state: this.__state
- };
- },
-
- /**
- * For public use only, not to be used by plugins
- *
- * @returns {self}
- * @public
- */
- triggerHandler: function() {
-
- if (!this.__destroyed) {
- this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
- }
- else {
- this.__destroyError();
- }
-
- return this;
- }
-};
-
-$.fn.tooltipster = function() {
-
- // for using in closures
- var args = Array.prototype.slice.apply(arguments),
- // common mistake: an HTML element can't be in several tooltips at the same time
- contentCloningWarning = 'You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.';
-
- // this happens with $(sel).tooltipster(...) when $(sel) does not match anything
- if (this.length === 0) {
-
- // still chainable
- return this;
- }
- // this happens when calling $(sel).tooltipster('methodName or options')
- // where $(sel) matches one or more elements
- else {
-
- // method calls
- if (typeof args[0] === 'string') {
-
- var v = '#*$~&';
-
- this.each(function() {
-
- // retrieve the namepaces of the tooltip(s) that exist on that element.
- // We will interact with the first tooltip only.
- var ns = $(this).data('tooltipster-ns'),
- // self represents the instance of the first tooltipster plugin
- // associated to the current HTML object of the loop
- self = ns ? $(this).data(ns[0]) : null;
-
- // if the current element holds a tooltipster instance
- if (self) {
-
- if (typeof self[args[0]] === 'function') {
-
- if ( this.length > 1
- && args[0] == 'content'
- && ( args[1] instanceof $
- || (typeof args[1] == 'object' && args[1] != null && args[1].tagName)
- )
- && !self.__options.contentCloning
- && self.__options.debug
- ) {
- console.log(contentCloningWarning);
- }
-
- // note : args[1] and args[2] may not be defined
- var resp = self[args[0]](args[1], args[2]);
- }
- else {
- throw new Error('Unknown method "'+ args[0] +'"');
- }
-
- // if the function returned anything other than the instance
- // itself (which implies chaining, except for the `instance` method)
- if (resp !== self || args[0] === 'instance') {
-
- v = resp;
-
- // return false to stop .each iteration on the first element
- // matched by the selector
- return false;
- }
- }
- else {
- throw new Error('You called Tooltipster\'s "'+ args[0] +'" method on an uninitialized element');
- }
- });
-
- return (v !== '#*$~&') ? v : this;
- }
- // first argument is undefined or an object: the tooltip is initializing
- else {
-
- // reset the array of last initialized objects
- $.tooltipster.__instancesLatestArr = [];
-
- // is there a defined value for the multiple option in the options object ?
- var multipleIsSet = args[0] && args[0].multiple !== undefined,
- // if the multiple option is set to true, or if it's not defined but
- // set to true in the defaults
- multiple = (multipleIsSet && args[0].multiple) || (!multipleIsSet && defaults.multiple),
- // same for content
- contentIsSet = args[0] && args[0].content !== undefined,
- content = (contentIsSet && args[0].content) || (!contentIsSet && defaults.content),
- // same for contentCloning
- contentCloningIsSet = args[0] && args[0].contentCloning !== undefined,
- contentCloning =
- (contentCloningIsSet && args[0].contentCloning)
- || (!contentCloningIsSet && defaults.contentCloning),
- // same for debug
- debugIsSet = args[0] && args[0].debug !== undefined,
- debug = (debugIsSet && args[0].debug) || (!debugIsSet && defaults.debug);
-
- if ( this.length > 1
- && ( content instanceof $
- || (typeof content == 'object' && content != null && content.tagName)
- )
- && !contentCloning
- && debug
- ) {
- console.log(contentCloningWarning);
- }
-
- // create a tooltipster instance for each element if it doesn't
- // already have one or if the multiple option is set, and attach the
- // object to it
- this.each(function() {
-
- var go = false,
- $this = $(this),
- ns = $this.data('tooltipster-ns'),
- obj = null;
-
- if (!ns) {
- go = true;
- }
- else if (multiple) {
- go = true;
- }
- else if (debug) {
- console.log('Tooltipster: one or more tooltips are already attached to the element below. Ignoring.');
- console.log(this);
- }
-
- if (go) {
- obj = new $.Tooltipster(this, args[0]);
-
- // save the reference of the new instance
- if (!ns) ns = [];
- ns.push(obj.__namespace);
- $this.data('tooltipster-ns', ns);
-
- // save the instance itself
- $this.data(obj.__namespace, obj);
-
- // call our constructor custom function.
- // we do this here and not in ::init() because we wanted
- // the object to be saved in $this.data before triggering
- // it
- if (obj.__options.functionInit) {
- obj.__options.functionInit.call(obj, obj, {
- origin: this
- });
- }
-
- // and now the event, for the plugins and core emitter
- obj._trigger('init');
- }
-
- $.tooltipster.__instancesLatestArr.push(obj);
- });
-
- return this;
- }
- }
-};
-
-// Utilities
-
-/**
- * A class to check if a tooltip can fit in given dimensions
- *
- * @param {object} $tooltip The jQuery wrapped tooltip element, or a clone of it
- */
-function Ruler($tooltip) {
-
- // list of instance variables
-
- this.$container;
- this.constraints = null;
- this.__$tooltip;
-
- this.__init($tooltip);
-}
-
-Ruler.prototype = {
-
- /**
- * Move the tooltip into an invisible div that does not allow overflow to make
- * size tests. Note: the tooltip may or may not be attached to the DOM at the
- * moment this method is called, it does not matter.
- *
- * @param {object} $tooltip The object to test. May be just a clone of the
- * actual tooltip.
- * @private
- */
- __init: function($tooltip) {
-
- this.__$tooltip = $tooltip;
-
- this.__$tooltip
- .css({
- // for some reason we have to specify top and left 0
- left: 0,
- // any overflow will be ignored while measuring
- overflow: 'hidden',
- // positions at (0,0) without the div using 100% of the available width
- position: 'absolute',
- top: 0
- })
- // overflow must be auto during the test. We re-set this in case
- // it were modified by the user
- .find('.tooltipster-content')
- .css('overflow', 'auto');
-
- this.$container = $('')
- .append(this.__$tooltip)
- .appendTo(env.window.document.body);
- },
-
- /**
- * Force the browser to redraw (re-render) the tooltip immediately. This is required
- * when you changed some CSS properties and need to make something with it
- * immediately, without waiting for the browser to redraw at the end of instructions.
- *
- * @see http://stackoverflow.com/questions/3485365/how-can-i-force-webkit-to-redraw-repaint-to-propagate-style-changes
- * @private
- */
- __forceRedraw: function() {
-
- // note: this would work but for Webkit only
- //this.__$tooltip.close();
- //this.__$tooltip[0].offsetHeight;
- //this.__$tooltip.open();
-
- // works in FF too
- var $p = this.__$tooltip.parent();
- this.__$tooltip.detach();
- this.__$tooltip.appendTo($p);
- },
-
- /**
- * Set maximum dimensions for the tooltip. A call to ::measure afterwards
- * will tell us if the content overflows or if it's ok
- *
- * @param {int} width
- * @param {int} height
- * @return {Ruler}
- * @public
- */
- constrain: function(width, height) {
-
- this.constraints = {
- width: width,
- height: height
- };
-
- this.__$tooltip.css({
- // we disable display:flex, otherwise the content would overflow without
- // creating horizontal scrolling (which we need to detect).
- display: 'block',
- // reset any previous height
- height: '',
- // we'll check if horizontal scrolling occurs
- overflow: 'auto',
- // we'll set the width and see what height is generated and if there
- // is horizontal overflow
- width: width
- });
-
- return this;
- },
-
- /**
- * Reset the tooltip content overflow and remove the test container
- *
- * @returns {Ruler}
- * @public
- */
- destroy: function() {
-
- // in case the element was not a clone
- this.__$tooltip
- .detach()
- .find('.tooltipster-content')
- .css({
- // reset to CSS value
- display: '',
- overflow: ''
- });
-
- this.$container.remove();
- },
-
- /**
- * Removes any constraints
- *
- * @returns {Ruler}
- * @public
- */
- free: function() {
-
- this.constraints = null;
-
- // reset to natural size
- this.__$tooltip.css({
- display: '',
- height: '',
- overflow: 'visible',
- width: ''
- });
-
- return this;
- },
-
- /**
- * Returns the size of the tooltip. When constraints are applied, also returns
- * whether the tooltip fits in the provided dimensions.
- * The idea is to see if the new height is small enough and if the content does
- * not overflow horizontally.
- *
- * @param {int} width
- * @param {int} height
- * @returns {object} An object with a bool `fits` property and a `size` property
- * @public
- */
- measure: function() {
-
- this.__forceRedraw();
-
- var tooltipBcr = this.__$tooltip[0].getBoundingClientRect(),
- result = { size: {
- // bcr.width/height are not defined in IE8- but in this
- // case, bcr.right/bottom will have the same value
- // except in iOS 8+ where tooltipBcr.bottom/right are wrong
- // after scrolling for reasons yet to be determined.
- // tooltipBcr.top/left might not be 0, see issue #514
- height: tooltipBcr.height || (tooltipBcr.bottom - tooltipBcr.top),
- width: tooltipBcr.width || (tooltipBcr.right - tooltipBcr.left)
- }};
-
- if (this.constraints) {
-
- // note: we used to use offsetWidth instead of boundingRectClient but
- // it returned rounded values, causing issues with sub-pixel layouts.
-
- // note2: noticed that the bcrWidth of text content of a div was once
- // greater than the bcrWidth of its container by 1px, causing the final
- // tooltip box to be too small for its content. However, evaluating
- // their widths one against the other (below) surprisingly returned
- // equality. Happened only once in Chrome 48, was not able to reproduce
- // => just having fun with float position values...
-
- var $content = this.__$tooltip.find('.tooltipster-content'),
- height = this.__$tooltip.outerHeight(),
- contentBcr = $content[0].getBoundingClientRect(),
- fits = {
- height: height <= this.constraints.height,
- width: (
- // this condition accounts for min-width property that
- // may apply
- tooltipBcr.width <= this.constraints.width
- // the -1 is here because scrollWidth actually returns
- // a rounded value, and may be greater than bcr.width if
- // it was rounded up. This may cause an issue for contents
- // which actually really overflow by 1px or so, but that
- // should be rare. Not sure how to solve this efficiently.
- // See http://blogs.msdn.com/b/ie/archive/2012/02/17/sub-pixel-rendering-and-the-css-object-model.aspx
- && contentBcr.width >= $content[0].scrollWidth - 1
- )
- };
-
- result.fits = fits.height && fits.width;
- }
-
- // old versions of IE get the width wrong for some reason and it causes
- // the text to be broken to a new line, so we round it up. If the width
- // is the width of the screen though, we can assume it is accurate.
- if ( env.IE
- && env.IE <= 11
- && result.size.width !== env.window.document.documentElement.clientWidth
- ) {
- result.size.width = Math.ceil(result.size.width) + 1;
- }
-
- return result;
- }
-};
-
-// quick & dirty compare function, not bijective nor multidimensional
-function areEqual(a,b) {
- var same = true;
- $.each(a, function(i, _) {
- if (b[i] === undefined || a[i] !== b[i]) {
- same = false;
- return false;
- }
- });
- return same;
-}
-
-/**
- * A fast function to check if an element is still in the DOM. It
- * tries to use an id as ids are indexed by the browser, or falls
- * back to jQuery's `contains` method. May fail if two elements
- * have the same id, but so be it
- *
- * @param {object} $obj A jQuery-wrapped HTML element
- * @return {boolean}
- */
-function bodyContains($obj) {
- var id = $obj.attr('id'),
- el = id ? env.window.document.getElementById(id) : null;
- // must also check that the element with the id is the one we want
- return el ? el === $obj[0] : $.contains(env.window.document.body, $obj[0]);
-}
-
-// detect IE versions for dirty fixes
-var uA = navigator.userAgent.toLowerCase();
-if (uA.indexOf('msie') != -1) env.IE = parseInt(uA.split('msie')[1]);
-else if (uA.toLowerCase().indexOf('trident') !== -1 && uA.indexOf(' rv:11') !== -1) env.IE = 11;
-else if (uA.toLowerCase().indexOf('edge/') != -1) env.IE = parseInt(uA.toLowerCase().split('edge/')[1]);
-
-// detecting support for CSS transitions
-function transitionSupport() {
-
- // env.window is not defined yet when this is called
- if (!win) return false;
-
- var b = win.document.body || win.document.documentElement,
- s = b.style,
- p = 'transition',
- v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];
-
- if (typeof s[p] == 'string') { return true; }
-
- p = p.charAt(0).toUpperCase() + p.substr(1);
- for (var i=0; i' +
- '
' +
- '' +
- '
' +
- '
' +
- '
' +
- '' +
- '' +
- '
' +
- '
' +
- ''
- );
-
- // hide arrow if asked
- if (!this.__options.arrow) {
- $html
- .find('.tooltipster-box')
- .css('margin', 0)
- .end()
- .find('.tooltipster-arrow')
- .hide();
- }
-
- // apply min/max width if asked
- if (this.__options.minWidth) {
- $html.css('min-width', this.__options.minWidth + 'px');
- }
- if (this.__options.maxWidth) {
- $html.css('max-width', this.__options.maxWidth + 'px');
- }
-
- this.__instance._$tooltip = $html;
-
- // tell the instance that the tooltip element has been created
- this.__instance._trigger('created');
- },
-
- /**
- * Used when the plugin is to be unplugged
- *
- * @private
- */
- __destroy: function() {
- this.__instance._off('.'+ self.__namespace);
- },
-
- /**
- * (Re)compute this.__options from the options declared to the instance
- *
- * @private
- */
- __optionsFormat: function() {
-
- var self = this;
-
- // get the options
- self.__options = self.__instance._optionsExtract(pluginName, self.__defaults());
-
- // for backward compatibility, deprecated in v4.0.0
- if (self.__options.position) {
- self.__options.side = self.__options.position;
- }
-
- // options formatting
-
- // format distance as a four-cell array if it ain't one yet and then make
- // it an object with top/bottom/left/right properties
- if (typeof self.__options.distance != 'object') {
- self.__options.distance = [self.__options.distance];
- }
- if (self.__options.distance.length < 4) {
- if (self.__options.distance[1] === undefined) self.__options.distance[1] = self.__options.distance[0];
- if (self.__options.distance[2] === undefined) self.__options.distance[2] = self.__options.distance[0];
- if (self.__options.distance[3] === undefined) self.__options.distance[3] = self.__options.distance[1];
- }
-
- self.__options.distance = {
- top: self.__options.distance[0],
- right: self.__options.distance[1],
- bottom: self.__options.distance[2],
- left: self.__options.distance[3]
- };
-
- // let's transform:
- // 'top' into ['top', 'bottom', 'right', 'left']
- // 'right' into ['right', 'left', 'top', 'bottom']
- // 'bottom' into ['bottom', 'top', 'right', 'left']
- // 'left' into ['left', 'right', 'top', 'bottom']
- if (typeof self.__options.side == 'string') {
-
- var opposites = {
- 'top': 'bottom',
- 'right': 'left',
- 'bottom': 'top',
- 'left': 'right'
- };
-
- self.__options.side = [self.__options.side, opposites[self.__options.side]];
-
- if (self.__options.side[0] == 'left' || self.__options.side[0] == 'right') {
- self.__options.side.push('top', 'bottom');
- }
- else {
- self.__options.side.push('right', 'left');
- }
- }
-
- // misc
- // disable the arrow in IE6 unless the arrow option was explicitly set to true
- if ( $.tooltipster._env.IE === 6
- && self.__options.arrow !== true
- ) {
- self.__options.arrow = false;
- }
- },
-
- /**
- * This method must compute and set the positioning properties of the
- * tooltip (left, top, width, height, etc.). It must also make sure the
- * tooltip is eventually appended to its parent (since the element may be
- * detached from the DOM at the moment the method is called).
- *
- * We'll evaluate positioning scenarios to find which side can contain the
- * tooltip in the best way. We'll consider things relatively to the window
- * (unless the user asks not to), then to the document (if need be, or if the
- * user explicitly requires the tests to run on the document). For each
- * scenario, measures are taken, allowing us to know how well the tooltip
- * is going to fit. After that, a sorting function will let us know what
- * the best scenario is (we also allow the user to choose his favorite
- * scenario by using an event).
- *
- * @param {object} helper An object that contains variables that plugin
- * creators may find useful (see below)
- * @param {object} helper.geo An object with many layout properties
- * about objects of interest (window, document, origin). This should help
- * plugin users compute the optimal position of the tooltip
- * @private
- */
- __reposition: function(event, helper) {
-
- var self = this,
- finalResult,
- // to know where to put the tooltip, we need to know on which point
- // of the x or y axis we should center it. That coordinate is the target
- targets = self.__targetFind(helper),
- testResults = [];
-
- // make sure the tooltip is detached while we make tests on a clone
- self.__instance._$tooltip.detach();
-
- // we could actually provide the original element to the Ruler and
- // not a clone, but it just feels right to keep it out of the
- // machinery.
- var $clone = self.__instance._$tooltip.clone(),
- // start position tests session
- ruler = $.tooltipster._getRuler($clone),
- satisfied = false,
- animation = self.__instance.option('animation');
-
- // an animation class could contain properties that distort the size
- if (animation) {
- $clone.removeClass('tooltipster-'+ animation);
- }
-
- // start evaluating scenarios
- $.each(['window', 'document'], function(i, container) {
-
- var takeTest = null;
-
- // let the user decide to keep on testing or not
- self.__instance._trigger({
- container: container,
- helper: helper,
- satisfied: satisfied,
- takeTest: function(bool) {
- takeTest = bool;
- },
- results: testResults,
- type: 'positionTest'
- });
-
- if ( takeTest == true
- || ( takeTest != false
- && satisfied == false
- // skip the window scenarios if asked. If they are reintegrated by
- // the callback of the positionTest event, they will have to be
- // excluded using the callback of positionTested
- && (container != 'window' || self.__options.viewportAware)
- )
- ) {
-
- // for each allowed side
- for (var i=0; i < self.__options.side.length; i++) {
-
- var distance = {
- horizontal: 0,
- vertical: 0
- },
- side = self.__options.side[i];
-
- if (side == 'top' || side == 'bottom') {
- distance.vertical = self.__options.distance[side];
- }
- else {
- distance.horizontal = self.__options.distance[side];
- }
-
- // this may have an effect on the size of the tooltip if there are css
- // rules for the arrow or something else
- self.__sideChange($clone, side);
-
- $.each(['natural', 'constrained'], function(i, mode) {
-
- takeTest = null;
-
- // emit an event on the instance
- self.__instance._trigger({
- container: container,
- event: event,
- helper: helper,
- mode: mode,
- results: testResults,
- satisfied: satisfied,
- side: side,
- takeTest: function(bool) {
- takeTest = bool;
- },
- type: 'positionTest'
- });
-
- if ( takeTest == true
- || ( takeTest != false
- && satisfied == false
- )
- ) {
-
- var testResult = {
- container: container,
- // we let the distance as an object here, it can make things a little easier
- // during the user's calculations at positionTest/positionTested
- distance: distance,
- // whether the tooltip can fit in the size of the viewport (does not mean
- // that we'll be able to make it initially entirely visible, see 'whole')
- fits: null,
- mode: mode,
- outerSize: null,
- side: side,
- size: null,
- target: targets[side],
- // check if the origin has enough surface on screen for the tooltip to
- // aim at it without overflowing the viewport (this is due to the thickness
- // of the arrow represented by the minIntersection length).
- // If not, the tooltip will have to be partly or entirely off screen in
- // order to stay docked to the origin. This value will stay null when the
- // container is the document, as it is not relevant
- whole: null
- };
-
- // get the size of the tooltip with or without size constraints
- var rulerConfigured = (mode == 'natural') ?
- ruler.free() :
- ruler.constrain(
- helper.geo.available[container][side].width - distance.horizontal,
- helper.geo.available[container][side].height - distance.vertical
- ),
- rulerResults = rulerConfigured.measure();
-
- testResult.size = rulerResults.size;
- testResult.outerSize = {
- height: rulerResults.size.height + distance.vertical,
- width: rulerResults.size.width + distance.horizontal
- };
-
- if (mode == 'natural') {
-
- if( helper.geo.available[container][side].width >= testResult.outerSize.width
- && helper.geo.available[container][side].height >= testResult.outerSize.height
- ) {
- testResult.fits = true;
- }
- else {
- testResult.fits = false;
- }
- }
- else {
- testResult.fits = rulerResults.fits;
- }
-
- if (container == 'window') {
-
- if (!testResult.fits) {
- testResult.whole = false;
- }
- else {
- if (side == 'top' || side == 'bottom') {
-
- testResult.whole = (
- helper.geo.origin.windowOffset.right >= self.__options.minIntersection
- && helper.geo.window.size.width - helper.geo.origin.windowOffset.left >= self.__options.minIntersection
- );
- }
- else {
- testResult.whole = (
- helper.geo.origin.windowOffset.bottom >= self.__options.minIntersection
- && helper.geo.window.size.height - helper.geo.origin.windowOffset.top >= self.__options.minIntersection
- );
- }
- }
- }
-
- testResults.push(testResult);
-
- // we don't need to compute more positions if we have one fully on screen
- if (testResult.whole) {
- satisfied = true;
- }
- else {
- // don't run the constrained test unless the natural width was greater
- // than the available width, otherwise it's pointless as we know it
- // wouldn't fit either
- if ( testResult.mode == 'natural'
- && ( testResult.fits
- || testResult.size.width <= helper.geo.available[container][side].width
- )
- ) {
- return false;
- }
- }
- }
- });
- }
- }
- });
-
- // the user may eliminate the unwanted scenarios from testResults, but he's
- // not supposed to alter them at this point. functionPosition and the
- // position event serve that purpose.
- self.__instance._trigger({
- edit: function(r) {
- testResults = r;
- },
- event: event,
- helper: helper,
- results: testResults,
- type: 'positionTested'
- });
-
- /**
- * Sort the scenarios to find the favorite one.
- *
- * The favorite scenario is when we can fully display the tooltip on screen,
- * even if it means that the middle of the tooltip is no longer centered on
- * the middle of the origin (when the origin is near the edge of the screen
- * or even partly off screen). We want the tooltip on the preferred side,
- * even if it means that we have to use a constrained size rather than a
- * natural one (as long as it fits). When the origin is off screen at the top
- * the tooltip will be positioned at the bottom (if allowed), if the origin
- * is off screen on the right, it will be positioned on the left, etc.
- * If there are no scenarios where the tooltip can fit on screen, or if the
- * user does not want the tooltip to fit on screen (viewportAware == false),
- * we fall back to the scenarios relative to the document.
- *
- * When the tooltip is bigger than the viewport in either dimension, we stop
- * looking at the window scenarios and consider the document scenarios only,
- * with the same logic to find on which side it would fit best.
- *
- * If the tooltip cannot fit the document on any side, we force it at the
- * bottom, so at least the user can scroll to see it.
- */
- testResults.sort(function(a, b) {
-
- // best if it's whole (the tooltip fits and adapts to the viewport)
- if (a.whole && !b.whole) {
- return -1;
- }
- else if (!a.whole && b.whole) {
- return 1;
- }
- else if (a.whole && b.whole) {
-
- var ai = self.__options.side.indexOf(a.side),
- bi = self.__options.side.indexOf(b.side);
-
- // use the user's sides fallback array
- if (ai < bi) {
- return -1;
- }
- else if (ai > bi) {
- return 1;
- }
- else {
- // will be used if the user forced the tests to continue
- return a.mode == 'natural' ? -1 : 1;
- }
- }
- else {
-
- // better if it fits
- if (a.fits && !b.fits) {
- return -1;
- }
- else if (!a.fits && b.fits) {
- return 1;
- }
- else if (a.fits && b.fits) {
-
- var ai = self.__options.side.indexOf(a.side),
- bi = self.__options.side.indexOf(b.side);
-
- // use the user's sides fallback array
- if (ai < bi) {
- return -1;
- }
- else if (ai > bi) {
- return 1;
- }
- else {
- // will be used if the user forced the tests to continue
- return a.mode == 'natural' ? -1 : 1;
- }
- }
- else {
-
- // if everything failed, this will give a preference to the case where
- // the tooltip overflows the document at the bottom
- if ( a.container == 'document'
- && a.side == 'bottom'
- && a.mode == 'natural'
- ) {
- return -1;
- }
- else {
- return 1;
- }
- }
- }
- });
-
- finalResult = testResults[0];
-
-
- // now let's find the coordinates of the tooltip relatively to the window
- finalResult.coord = {};
-
- switch (finalResult.side) {
-
- case 'left':
- case 'right':
- finalResult.coord.top = Math.floor(finalResult.target - finalResult.size.height / 2);
- break;
-
- case 'bottom':
- case 'top':
- finalResult.coord.left = Math.floor(finalResult.target - finalResult.size.width / 2);
- break;
- }
-
- switch (finalResult.side) {
-
- case 'left':
- finalResult.coord.left = helper.geo.origin.windowOffset.left - finalResult.outerSize.width;
- break;
-
- case 'right':
- finalResult.coord.left = helper.geo.origin.windowOffset.right + finalResult.distance.horizontal;
- break;
-
- case 'top':
- finalResult.coord.top = helper.geo.origin.windowOffset.top - finalResult.outerSize.height;
- break;
-
- case 'bottom':
- finalResult.coord.top = helper.geo.origin.windowOffset.bottom + finalResult.distance.vertical;
- break;
- }
-
- // if the tooltip can potentially be contained within the viewport dimensions
- // and that we are asked to make it fit on screen
- if (finalResult.container == 'window') {
-
- // if the tooltip overflows the viewport, we'll move it accordingly (then it will
- // not be centered on the middle of the origin anymore). We only move horizontally
- // for top and bottom tooltips and vice versa.
- if (finalResult.side == 'top' || finalResult.side == 'bottom') {
-
- // if there is an overflow on the left
- if (finalResult.coord.left < 0) {
-
- // prevent the overflow unless the origin itself gets off screen (minus the
- // margin needed to keep the arrow pointing at the target)
- if (helper.geo.origin.windowOffset.right - this.__options.minIntersection >= 0) {
- finalResult.coord.left = 0;
- }
- else {
- finalResult.coord.left = helper.geo.origin.windowOffset.right - this.__options.minIntersection - 1;
- }
- }
- // or an overflow on the right
- else if (finalResult.coord.left > helper.geo.window.size.width - finalResult.size.width) {
-
- if (helper.geo.origin.windowOffset.left + this.__options.minIntersection <= helper.geo.window.size.width) {
- finalResult.coord.left = helper.geo.window.size.width - finalResult.size.width;
- }
- else {
- finalResult.coord.left = helper.geo.origin.windowOffset.left + this.__options.minIntersection + 1 - finalResult.size.width;
- }
- }
- }
- else {
-
- // overflow at the top
- if (finalResult.coord.top < 0) {
-
- if (helper.geo.origin.windowOffset.bottom - this.__options.minIntersection >= 0) {
- finalResult.coord.top = 0;
- }
- else {
- finalResult.coord.top = helper.geo.origin.windowOffset.bottom - this.__options.minIntersection - 1;
- }
- }
- // or at the bottom
- else if (finalResult.coord.top > helper.geo.window.size.height - finalResult.size.height) {
-
- if (helper.geo.origin.windowOffset.top + this.__options.minIntersection <= helper.geo.window.size.height) {
- finalResult.coord.top = helper.geo.window.size.height - finalResult.size.height;
- }
- else {
- finalResult.coord.top = helper.geo.origin.windowOffset.top + this.__options.minIntersection + 1 - finalResult.size.height;
- }
- }
- }
- }
- else {
-
- // there might be overflow here too but it's easier to handle. If there has
- // to be an overflow, we'll make sure it's on the right side of the screen
- // (because the browser will extend the document size if there is an overflow
- // on the right, but not on the left). The sort function above has already
- // made sure that a bottom document overflow is preferred to a top overflow,
- // so we don't have to care about it.
-
- // if there is an overflow on the right
- if (finalResult.coord.left > helper.geo.window.size.width - finalResult.size.width) {
-
- // this may actually create on overflow on the left but we'll fix it in a sec
- finalResult.coord.left = helper.geo.window.size.width - finalResult.size.width;
- }
-
- // if there is an overflow on the left
- if (finalResult.coord.left < 0) {
-
- // don't care if it overflows the right after that, we made our best
- finalResult.coord.left = 0;
- }
- }
-
-
- // submit the positioning proposal to the user function which may choose to change
- // the side, size and/or the coordinates
-
- // first, set the rules that corresponds to the proposed side: it may change
- // the size of the tooltip, and the custom functionPosition may want to detect the
- // size of something before making a decision. So let's make things easier for the
- // implementor
- self.__sideChange($clone, finalResult.side);
-
- // add some variables to the helper
- helper.tooltipClone = $clone[0];
- helper.tooltipParent = self.__instance.option('parent').parent[0];
- // move informative values to the helper
- helper.mode = finalResult.mode;
- helper.whole = finalResult.whole;
- // add some variables to the helper for the functionPosition callback (these
- // will also be added to the event fired by self.__instance._trigger but that's
- // ok, we're just being consistent)
- helper.origin = self.__instance._$origin[0];
- helper.tooltip = self.__instance._$tooltip[0];
-
- // leave only the actionable values in there for functionPosition
- delete finalResult.container;
- delete finalResult.fits;
- delete finalResult.mode;
- delete finalResult.outerSize;
- delete finalResult.whole;
-
- // keep only the distance on the relevant side, for clarity
- finalResult.distance = finalResult.distance.horizontal || finalResult.distance.vertical;
-
- // beginners may not be comfortable with the concept of editing the object
- // passed by reference, so we provide an edit function and pass a clone
- var finalResultClone = $.extend(true, {}, finalResult);
-
- // emit an event on the instance
- self.__instance._trigger({
- edit: function(result) {
- finalResult = result;
- },
- event: event,
- helper: helper,
- position: finalResultClone,
- type: 'position'
- });
-
- if (self.__options.functionPosition) {
-
- var result = self.__options.functionPosition.call(self, self.__instance, helper, finalResultClone);
-
- if (result) finalResult = result;
- }
-
- // end the positioning tests session (the user might have had a
- // use for it during the position event, now it's over)
- ruler.destroy();
-
- // compute the position of the target relatively to the tooltip root
- // element so we can place the arrow and make the needed adjustments
- var arrowCoord,
- maxVal;
-
- if (finalResult.side == 'top' || finalResult.side == 'bottom') {
-
- arrowCoord = {
- prop: 'left',
- val: finalResult.target - finalResult.coord.left
- };
- maxVal = finalResult.size.width - this.__options.minIntersection;
- }
- else {
-
- arrowCoord = {
- prop: 'top',
- val: finalResult.target - finalResult.coord.top
- };
- maxVal = finalResult.size.height - this.__options.minIntersection;
- }
-
- // cannot lie beyond the boundaries of the tooltip, minus the
- // arrow margin
- if (arrowCoord.val < this.__options.minIntersection) {
- arrowCoord.val = this.__options.minIntersection;
- }
- else if (arrowCoord.val > maxVal) {
- arrowCoord.val = maxVal;
- }
-
- var originParentOffset;
-
- // let's convert the window-relative coordinates into coordinates relative to the
- // future positioned parent that the tooltip will be appended to
- if (helper.geo.origin.fixedLineage) {
-
- // same as windowOffset when the position is fixed
- originParentOffset = helper.geo.origin.windowOffset;
- }
- else {
-
- // this assumes that the parent of the tooltip is located at
- // (0, 0) in the document, typically like when the parent is
- // .
- // If we ever allow other types of parent, .tooltipster-ruler
- // will have to be appended to the parent to inherit css style
- // values that affect the display of the text and such.
- originParentOffset = {
- left: helper.geo.origin.windowOffset.left + helper.geo.window.scroll.left,
- top: helper.geo.origin.windowOffset.top + helper.geo.window.scroll.top
- };
- }
-
- finalResult.coord = {
- left: originParentOffset.left + (finalResult.coord.left - helper.geo.origin.windowOffset.left),
- top: originParentOffset.top + (finalResult.coord.top - helper.geo.origin.windowOffset.top)
- };
-
- // set position values on the original tooltip element
-
- self.__sideChange(self.__instance._$tooltip, finalResult.side);
-
- if (helper.geo.origin.fixedLineage) {
- self.__instance._$tooltip
- .css('position', 'fixed');
- }
- else {
- // CSS default
- self.__instance._$tooltip
- .css('position', '');
- }
-
- self.__instance._$tooltip
- .css({
- left: finalResult.coord.left,
- top: finalResult.coord.top,
- // we need to set a size even if the tooltip is in its natural size
- // because when the tooltip is positioned beyond the width of the body
- // (which is by default the width of the window; it will happen when
- // you scroll the window horizontally to get to the origin), its text
- // content will otherwise break lines at each word to keep up with the
- // body overflow strategy.
- height: finalResult.size.height,
- width: finalResult.size.width
- })
- .find('.tooltipster-arrow')
- .css({
- 'left': '',
- 'top': ''
- })
- .css(arrowCoord.prop, arrowCoord.val);
-
- // append the tooltip HTML element to its parent
- self.__instance._$tooltip.appendTo(self.__instance.option('parent'));
-
- self.__instance._trigger({
- type: 'repositioned',
- event: event,
- position: finalResult
- });
- },
-
- /**
- * Make whatever modifications are needed when the side is changed. This has
- * been made an independant method for easy inheritance in custom plugins based
- * on this default plugin.
- *
- * @param {object} $obj
- * @param {string} side
- * @private
- */
- __sideChange: function($obj, side) {
-
- $obj
- .removeClass('tooltipster-bottom')
- .removeClass('tooltipster-left')
- .removeClass('tooltipster-right')
- .removeClass('tooltipster-top')
- .addClass('tooltipster-'+ side);
- },
-
- /**
- * Returns the target that the tooltip should aim at for a given side.
- * The calculated value is a distance from the edge of the window
- * (left edge for top/bottom sides, top edge for left/right side). The
- * tooltip will be centered on that position and the arrow will be
- * positioned there (as much as possible).
- *
- * @param {object} helper
- * @return {integer}
- * @private
- */
- __targetFind: function(helper) {
-
- var target = {},
- rects = this.__instance._$origin[0].getClientRects();
-
- // these lines fix a Chrome bug (issue #491)
- if (rects.length > 1) {
- var opacity = this.__instance._$origin.css('opacity');
- if(opacity == 1) {
- this.__instance._$origin.css('opacity', 0.99);
- rects = this.__instance._$origin[0].getClientRects();
- this.__instance._$origin.css('opacity', 1);
- }
- }
-
- // by default, the target will be the middle of the origin
- if (rects.length < 2) {
-
- target.top = Math.floor(helper.geo.origin.windowOffset.left + (helper.geo.origin.size.width / 2));
- target.bottom = target.top;
-
- target.left = Math.floor(helper.geo.origin.windowOffset.top + (helper.geo.origin.size.height / 2));
- target.right = target.left;
- }
- // if multiple client rects exist, the element may be text split
- // up into multiple lines and the middle of the origin may not be
- // best option anymore. We need to choose the best target client rect
- else {
-
- // top: the first
- var targetRect = rects[0];
- target.top = Math.floor(targetRect.left + (targetRect.right - targetRect.left) / 2);
-
- // right: the middle line, rounded down in case there is an even
- // number of lines (looks more centered => check out the
- // demo with 4 split lines)
- if (rects.length > 2) {
- targetRect = rects[Math.ceil(rects.length / 2) - 1];
- }
- else {
- targetRect = rects[0];
- }
- target.right = Math.floor(targetRect.top + (targetRect.bottom - targetRect.top) / 2);
-
- // bottom: the last
- targetRect = rects[rects.length - 1];
- target.bottom = Math.floor(targetRect.left + (targetRect.right - targetRect.left) / 2);
-
- // left: the middle line, rounded up
- if (rects.length > 2) {
- targetRect = rects[Math.ceil((rects.length + 1) / 2) - 1];
- }
- else {
- targetRect = rects[rects.length - 1];
- }
-
- target.left = Math.floor(targetRect.top + (targetRect.bottom - targetRect.top) / 2);
- }
-
- return target;
- }
- }
-});
-
-/* a build task will add "return $;" here */
-return $;
-
-}));
diff --git a/assets/admin/js/tooltipster/tooltipster.bundle.min.js b/assets/admin/js/tooltipster/tooltipster.bundle.min.js
deleted file mode 100644
index e614c7a..0000000
--- a/assets/admin/js/tooltipster/tooltipster.bundle.min.js
+++ /dev/null
@@ -1 +0,0 @@
-(t=>{"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof exports?module.exports=t(require("jquery")):t(jQuery)})(function(y){function t(){this.__$emitterPrivate=y({}),this.__$emitterPublic=y({}),this.__instancesLatestArr=[],this.__plugins={},this._env=b}var a={animation:"fade",animationDuration:350,content:null,contentAsHTML:!1,contentCloning:!1,debug:!0,delay:300,delayTouch:[300,500],functionInit:null,functionBefore:null,functionReady:null,functionAfter:null,functionFormat:null,IEmin:6,interactive:!1,multiple:!1,parent:null,plugins:["sideTip"],repositionOnScroll:!1,restoration:"none",selfDestruction:!0,theme:[],timer:0,trackerInterval:500,trackOrigin:!1,trackTooltip:!1,trigger:"hover",triggerClose:{click:!1,mouseleave:!1,originClick:!1,scroll:!1,tap:!1,touchleave:!1},triggerOpen:{click:!1,mouseenter:!1,tap:!1,touchstart:!1},updateAnimation:"rotate",zIndex:9999999},n="undefined"!=typeof window?window:null,b={hasTouchCapability:!(!n||!("ontouchstart"in n||n.DocumentTouch&&n.document instanceof n.DocumentTouch||n.navigator.maxTouchPoints)),hasTransitions:(()=>{if(n){var t=(n.document.body||n.document.documentElement).style,i="transition",o=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof t[i])return!0;i=i.charAt(0).toUpperCase()+i.substr(1);for(var e=0;e=e.__options.IEmin?(t=null,void 0===e._$origin.data("tooltipster-initialTitle")&&(t=e._$origin.attr("title"),e._$origin.data("tooltipster-initialTitle",t=void 0===t?null:t)),null!==e.__options.content?e.__contentSet(e.__options.content):(o=(i=e._$origin.attr("data-tooltip-content"))?y(document).find(i):o)&&o[0]?e.__contentSet(o.first()):e.__contentSet(t),e._$origin.removeAttr("title").addClass("tooltipstered"),e.__prepareOrigin(),e.__prepareGC(),y.each(e.__options.plugins,function(t,i){e._plug(i)}),b.hasTouchCapability&&y(b.window.document.body).on("touchmove."+e.__namespace+"-triggerOpen",function(t){e._touchRecordEvent(t)}),e._on("created",function(){e.__prepareTooltip()})._on("repositioned",function(t){e.__lastPosition=t.position})):e.__options.disabled=!0},__contentInsert:function(){var t=this,i=t._$tooltip.find(".tooltipster-content"),o=t.__Content;return t._trigger({type:"format",content:t.__Content,format:function(t){o=t}}),"string"!=typeof(o=t.__options.functionFormat?t.__options.functionFormat.call(t,t,{origin:t._$origin[0]},t.__Content):o)||t.__options.contentAsHTML?i.empty().append(o):i.text(o),t},__contentSet:function(t){return t instanceof y&&this.__options.contentCloning&&(t=t.clone(!0)),this.__Content=t,this._trigger({type:"updated",content:t}),this},__destroyError:function(){throw new Error("This tooltip has been destroyed and cannot execute your method call.")},__geometry:function(){var t=this,i=t._$origin,o=t._$origin.is("area"),e=(o&&(e=t._$origin.parent().attr("name"),i=y('img[usemap="#'+e+'"]')),i[0].getBoundingClientRect()),n=y(b.window.document),s=y(b.window),r=i,_={available:{document:null,window:null},document:{size:{height:n.height(),width:n.width()}},window:{scroll:{left:b.window.scrollX||b.window.document.documentElement.scrollLeft,top:b.window.scrollY||b.window.document.documentElement.scrollTop},size:{height:s.height(),width:s.width()}},origin:{fixedLineage:!1,offset:{},size:{height:e.bottom-e.top,width:e.right-e.left},usemapImage:o?i[0]:null,windowOffset:{bottom:e.bottom,left:e.left,right:e.right,top:e.top}}};if(o){var n=t._$origin.attr("shape"),a=t._$origin.attr("coords");if(a&&(a=a.split(","),y.map(a,function(t,i){a[i]=parseInt(t)})),"default"!=n)switch(n){case"circle":var l=a[0],p=a[1],c=a[2],p=p-c,l=l-c;_.origin.size.height=2*c,_.origin.size.width=_.origin.size.height,_.origin.windowOffset.left+=l,_.origin.windowOffset.top+=p;break;case"rect":var c=a[0],l=a[1],p=a[2],h=a[3];_.origin.size.height=h-l,_.origin.size.width=p-c,_.origin.windowOffset.left+=c,_.origin.windowOffset.top+=l;break;case"poly":for(var d=0,u=0,g=0,f=0,m="even",w=0;wi.right))return!(r=!0);if("visible"!=n&&(s.origin.windowOffset.topi.bottom))return!(r=!0)}if("fixed"==o.css("position"))return!1}),r?e._$tooltip.css("visibility","hidden"):(e._$tooltip.css("visibility","visible"),e.__options.repositionOnScroll?e.reposition(t):(i=s.origin.offset.left-e.__Geometry.origin.offset.left,o=s.origin.offset.top-e.__Geometry.origin.offset.top,e._$tooltip.css({left:e.__lastPosition.coord.left+i,top:e.__lastPosition.coord.top+o})))),e._trigger({type:"scroll",event:t,geo:s})),e},__stateSet:function(t){return this.__state=t,this._trigger({type:"state",state:t}),this},__timeoutsClear:function(){return clearTimeout(this.__timeouts.open),this.__timeouts.open=null,y.each(this.__timeouts.close,function(t,i){clearTimeout(i)}),this.__timeouts.close=[],this},__trackerStart:function(){var o=this,e=o._$tooltip.find(".tooltipster-content");return o.__options.trackTooltip&&(o.__contentBcr=e[0].getBoundingClientRect()),o.__tracker=setInterval(function(){var t,i;l(o._$origin)&&l(o._$tooltip)?(o.__options.trackOrigin&&(t=!1,s((i=o.__geometry()).origin.size,o.__Geometry.origin.size)&&(o.__Geometry.origin.fixedLineage?s(i.origin.windowOffset,o.__Geometry.origin.windowOffset)&&(t=!0):s(i.origin.offset,o.__Geometry.origin.offset)&&(t=!0)),t||(o.__options.triggerClose.mouseleave?o._close():o.reposition())),!o.__options.trackTooltip||(i=e[0].getBoundingClientRect()).height===o.__contentBcr.height&&i.width===o.__contentBcr.width||(o.reposition(),o.__contentBcr=i)):o._close()},o.__options.trackerInterval),o},_close:function(o,t,i){var e,n=this,s=!0;return n._trigger({type:"close",event:o,stop:function(){s=!1}}),(s||i)&&(t&&n.__callbacks.close.push(t),n.__callbacks.open=[],n.__timeoutsClear(),e=function(){y.each(n.__callbacks.close,function(t,i){i.call(n,n,{event:o,origin:n._$origin[0]})}),n.__callbacks.close=[]},"closed"!=n.__state?(i=!0,t=(new Date).getTime()+n.__options.animationDuration[1],(i="disappearing"==n.__state&&t>n.__closingTime&&0").append(o.__Content).html()),i&&o._$origin.attr("title",i),o._$origin.removeClass("tooltipstered"),o._$origin.removeData("tooltipster-ns").removeData("tooltipster-initialTitle")):(t=y.grep(t,function(t,i){return t!==o.__namespace}),o._$origin.data("tooltipster-ns",t))),o._trigger("destroyed"),o._off(),o.off(),o.__Content=null,o.__$emitterPrivate=null,o.__$emitterPublic=null,o.__options.parent=null,o._$origin=null,o._$tooltip=null,y.tooltipster.__instancesLatestArr=y.grep(y.tooltipster.__instancesLatestArr,function(t,i){return o!==t}),clearInterval(o.__garbageCollector)),o},disable:function(){return this.__destroyed?this.__destroyError():(this._close(),this.__enabled=!1),this},elementOrigin:function(){if(!this.__destroyed)return this._$origin[0];this.__destroyError()},elementTooltip:function(){return this._$tooltip?this._$tooltip[0]:null},enable:function(){return this.__enabled=!0,this},hide:function(t){return this.close(t)},instance:function(){return this},off:function(){return this.__destroyed||this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},open:function(t){return this.__destroyed?this.__destroyError():this._open(null,t),this},option:function(t,i){return void 0===i?this.__options[t]:(this.__destroyed?this.__destroyError():(this.__options[t]=i,this.__optionsFormat(),0<=y.inArray(t,["trigger","triggerClose","triggerOpen"])&&this.__prepareOrigin(),"selfDestruction"===t&&this.__prepareGC()),this)},reposition:function(t,i){var o=this;return o.__destroyed?o.__destroyError():"closed"!=o.__state&&l(o._$origin)&&(i||l(o._$tooltip))&&(i||o._$tooltip.detach(),o.__Geometry=o.__geometry(),o._trigger({type:"reposition",event:t,helper:{geo:o.__Geometry}})),o},show:function(t){return this.open(t)},status:function(){return{destroyed:this.__destroyed,enabled:this.__enabled,open:"closed"!==this.__state,state:this.__state}},triggerHandler:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},y.fn.tooltipster=function(){var o,n,t,i,e,s,r=Array.prototype.slice.apply(arguments),_="You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.";return 0===this.length?this:"string"==typeof r[0]?(o="#*$~&",this.each(function(){var t=y(this).data("tooltipster-ns"),t=t?y(this).data(t[0]):null;if(!t)throw new Error("You called Tooltipster's \""+r[0]+'" method on an uninitialized element');if("function"!=typeof t[r[0]])throw new Error('Unknown method "'+r[0]+'"');1').append(this.__$tooltip).appendTo(b.window.document.body)},__forceRedraw:function(){var t=this.__$tooltip.parent();this.__$tooltip.detach(),this.__$tooltip.appendTo(t)},constrain:function(t,i){return this.constraints={width:t,height:i},this.__$tooltip.css({display:"block",height:"",overflow:"auto",width:t}),this},destroy:function(){this.__$tooltip.detach().find(".tooltipster-content").css({display:"",overflow:""}),this.$container.remove()},free:function(){return this.constraints=null,this.__$tooltip.css({display:"",height:"",overflow:"visible",width:""}),this},measure:function(){this.__forceRedraw();var t,i,o,e=this.__$tooltip[0].getBoundingClientRect(),n={size:{height:e.height||e.bottom-e.top,width:e.width||e.right-e.left}};return this.constraints&&(t=this.__$tooltip.find(".tooltipster-content"),o=this.__$tooltip.outerHeight(),i=t[0].getBoundingClientRect(),o={height:o<=this.constraints.height,width:e.width<=this.constraints.width&&i.width>=t[0].scrollWidth-1},n.fits=o.height&&o.width),b.IE&&b.IE<=11&&n.size.width!==b.window.document.documentElement.clientWidth&&(n.size.width=Math.ceil(n.size.width)+1),n}};var o=navigator.userAgent.toLowerCase();-1!=o.indexOf("msie")?b.IE=parseInt(o.split("msie")[1]):-1!==o.toLowerCase().indexOf("trident")&&-1!==o.indexOf(" rv:11")?b.IE=11:-1!=o.toLowerCase().indexOf("edge/")&&(b.IE=parseInt(o.toLowerCase().split("edge/")[1]));var e="tooltipster.sideTip";return y.tooltipster._plugin({name:e,instance:{__defaults:function(){return{arrow:!0,distance:6,functionPosition:null,maxWidth:null,minIntersection:16,minWidth:0,position:null,side:"top",viewportAware:!0}},__init:function(t){var i=this;i.__instance=t,i.__namespace="tooltipster-sideTip-"+Math.round(1e6*Math.random()),i.__previousState="closed",i.__options,i.__optionsFormat(),i.__instance._on("state."+i.__namespace,function(t){"closed"==t.state?i.__close():"appearing"==t.state&&"closed"==i.__previousState&&i.__create(),i.__previousState=t.state}),i.__instance._on("options."+i.__namespace,function(){i.__optionsFormat()}),i.__instance._on("reposition."+i.__namespace,function(t){i.__reposition(t.event,t.helper)})},__close:function(){this.__instance.content()instanceof y&&this.__instance.content().detach(),this.__instance._$tooltip.remove(),this.__instance._$tooltip=null},__create:function(){var t=y('
' . esc_html__( 'Default Recipe Template','cooked') . '' . esc_html__( 'Choose from the options below to use this layout as the default for new recipes or for all recipes.', 'cooked') . '' . esc_html__( 'Save as Default','cooked' ) . '' . esc_html__( 'Apply to All','cooked' ) . '0 / 0' ); ?>" class="button cooked-layout-save-default">' . esc_html__( 'Recipe Template','cooked') . '' . esc_html__( 'Using the built-in recipe shortcodes found on the "Shortcodes" tab, you can create the layout of your recipe below. Use the "Save as Default" button to save your template.','cooked') ); ?>">
+
' . esc_html__( 'Recipe Template','cooked') . '' . esc_html__( 'Using the built-in recipe shortcodes found on the "Shortcodes" tab, you can create the layout of your recipe below. Use the "Save as Default" button to save your template.','cooked') ); ?>" data-positions="top">
+
+
+
+
+
+ 0 / 0
+
@@ -550,7 +557,7 @@ function cooked_render_recipe_fields( $post_id ) {
-
+
@@ -573,7 +580,7 @@ function cooked_render_recipe_fields( $post_id ) {
-
+
@@ -611,7 +618,7 @@ function cooked_render_recipe_fields( $post_id ) {
");for(var o=0;o'),d=l('"),a=l(''),c=l('').text(_.i18n_bulk_add_heading_line_label),r=l('').val(e[o]),c=(a.append(c).append(r),l('').val(i.amount)),r=l('').val(i.measurement),i=l('').val(i.name);n.append(d).append(a).append(c).append(r).append(i),u.append(n)}}else s.attr("data-bulk-type",""),s.hide(),p.attr("disabled","disabled")}i.length&&(t=l("#cooked-bulk-add-textarea"),s=l("#cooked-bulk-add-preview"),u=l("#cooked-bulk-add-preview-list"),o=l("#cooked-bulk-add-title"),r=l("#cooked-bulk-add-type"),p=i.find(".cooked-bulk-add-submit"),k=i.find(".cooked-bulk-add-spinner"),_=cooked_admin_functions_js_vars,m=null,g="",t.on("input",function(){var e=r.val(),o=(e=>{for(var t=e.split(/[\r\n]+/),o=[],i=0;i'),a=l('"),c=l('').val(i[n]);d.append(a).append(c),u.append(d)}}else s.attr("data-bulk-type",""),s.hide(),p.attr("disabled","disabled")}else t!==g&&(g=t,h(o,null),m&&clearTimeout(m),m=setTimeout(function(){var t;(t=o).length?(k.show().css("visibility","visible"),l.post(_.ajax_url,{action:"cooked_parse_bulk_ingredients",nonce:_.cooked_bulk_add_nonce,lines:t},function(e){k.hide();e=e.success&&e.data&&e.data.parsed?e.data.parsed:null;h(t,e)}).fail(function(){k.hide(),h(t,null)})):h([],null)},400))}),l(document).on("click",".cooked-bulk-add-button",function(e){e.preventDefault();var e=l(this).data("type");e=e,r.val(e),t.val(""),u.empty(),s.attr("data-bulk-type",""),s.hide(),p.attr("disabled","disabled"),k.hide(),g="","ingredients"===e?(o.text(_.i18n_bulk_add_ingredients),t.attr("placeholder",_.i18n_bulk_add_placeholder_ingredients),p.text(_.i18n_bulk_add_submit_ingredients)):(o.text(_.i18n_bulk_add_directions),t.attr("placeholder",_.i18n_bulk_add_placeholder_directions),p.text(_.i18n_bulk_add_submit_directions)),i.show(),t.focus()}),i.on("click",".cooked-bulk-add-close, .cooked-bulk-add-cancel",function(e){e.preventDefault(),n()}),i.on("click",function(e){l(e.target).is(i)&&n()}),l(document).on("keydown",function(e){"Escape"===e.key&&i.is(":visible")&&n()}),u.on("change",'input[type="checkbox"]',function(){var e=l(this).closest(".cooked-bulk-add-preview-row"),t=l(this).is(":checked");e.toggleClass("cooked-bulk-add-is-heading",t)}),p.on("click",function(e){var i;e.preventDefault(),l(this).attr("disabled")||(e=r.val(),i=[],"ingredients"===e?u.find(".cooked-bulk-add-preview-row").each(function(){var e=l(this),t=e.find('input[type="checkbox"]').is(":checked"),o=e.find(".cooked-bulk-add-preview-text").val().trim();o&&i.push(t?{text:o,heading:!0}:{heading:!1,amount:e.find(".cooked-bulk-add-parsed-amount").val().trim(),measurement:e.find(".cooked-bulk-add-parsed-unit").val().trim(),name:e.find(".cooked-bulk-add-parsed-name").val().trim()||o})}):u.find(".cooked-bulk-add-preview-row").each(function(){var e=l(this),t=e.find(".cooked-bulk-add-preview-text").val().trim(),e=e.find('input[type="checkbox"]').is(":checked");t&&i.push({text:t,heading:e})}),i.length&&(("directions"===e?cooked_bulk_add_directions:cooked_bulk_add_ingredients_parsed)(i),n()))}))}function cooked_bulk_add_directions(e){for(var t=jQuery("#cooked-directions-builder"),o=t.parent(),i=0;it.concat(e,e+"-"+n[0],e+"-"+n[1])),[]),r=Math.min,o=Math.max,l={left:"right",right:"left",bottom:"top",top:"bottom"};function a(t,e,n){return o(t,r(e,n))}function s(t,e){return"function"==typeof t?t(e):t}function f(t){return t.split("-")[0]}function c(t){return t.split("-")[1]}function u(t){return"x"===t?"y":"x"}function m(t){return"y"===t?"height":"width"}function d(t){const e=t[0];return"t"===e||"b"===e?"y":"x"}function g(t){return u(d(t))}function p(t,e,n){void 0===n&&(n=!1);const i=c(t),r=g(t),o=m(r);let l="x"===r?i===(n?"end":"start")?"right":"left":"start"===i?"bottom":"top";return e.reference[o]>e.floating[o]&&(l=A(l)),[l,A(l)]}function h(t){return t.includes("start")?t.replace("start","end"):t.replace("end","start")}const y=["left","right"],w=["right","left"],x=["top","bottom"],v=["bottom","top"];function b(t,e,n,i){const r=c(t);let o=function(t,e,n){switch(t){case"top":case"bottom":return n?e?w:y:e?y:w;case"left":case"right":return e?x:v;default:return[]}}(f(t),"start"===n,i);return r&&(o=o.map((t=>t+"-"+r)),e&&(o=o.concat(o.map(h)))),o}function A(t){const e=f(t);return l[e]+t.slice(e.length)}function R(t){return"number"!=typeof t?function(t){var e,n,i,r;return{top:null!=(e=t.top)?e:0,right:null!=(n=t.right)?n:0,bottom:null!=(i=t.bottom)?i:0,left:null!=(r=t.left)?r:0}}(t):{top:t,right:t,bottom:t,left:t}}function O(t){const{x:e,y:n,width:i,height:r}=t;return{width:i,height:r,top:n,left:e,right:e+i,bottom:n+r,x:e,y:n}}function P(t,e,n){let{reference:i,floating:r}=t;const o=d(e),l=g(e),a=m(l),s=f(e),u="y"===o,p=i.x+i.width/2-r.width/2,h=i.y+i.height/2-r.height/2,y=i[a]/2-r[a]/2;let w;switch(s){case"top":w={x:p,y:i.y-r.height};break;case"bottom":w={x:p,y:i.y+i.height};break;case"right":w={x:i.x+i.width,y:h};break;case"left":w={x:i.x-r.width,y:h};break;default:w={x:i.x,y:i.y}}const x=c(e);return x&&(w[l]+=y*("end"===x?1:-1)*(n&&u?-1:1)),w}async function T(t,e){var n;void 0===e&&(e={});const{x:i,y:r,platform:o,rects:l,elements:a,strategy:f}=t,{boundary:c="clippingAncestors",rootBoundary:u="viewport",elementContext:m="floating",altBoundary:d=!1,padding:g=0}=s(e,t),p=R(g),h=a[d?"floating"===m?"reference":"floating":m],y=O(await o.getClippingRect({element:null==(n=await(null==o.isElement?void 0:o.isElement(h)))||n?h:h.contextElement||await(null==o.getDocumentElement?void 0:o.getDocumentElement(a.floating)),boundary:c,rootBoundary:u,strategy:f})),w="floating"===m?{x:i,y:r,width:l.floating.width,height:l.floating.height}:l.reference,x=await(null==o.getOffsetParent?void 0:o.getOffsetParent(a.floating)),v=await(null==o.isElement?void 0:o.isElement(x))&&await(null==o.getScale?void 0:o.getScale(x))||{x:1,y:1},b=O(o.convertOffsetParentRelativeRectToViewportRelativeRect?await o.convertOffsetParentRelativeRectToViewportRelativeRect({elements:a,rect:w,offsetParent:x,strategy:f}):w);return{top:(y.top-b.top+p.top)/v.y,bottom:(b.bottom-y.bottom+p.bottom)/v.y,left:(y.left-b.left+p.left)/v.x,right:(b.right-y.right+p.right)/v.x}}function D(t,e){return{top:t.top-e.height,right:t.right-e.width,bottom:t.bottom-e.height,left:t.left-e.width}}function E(t){return e.some((e=>t[e]>=0))}function L(t){const e=r(...t.map((t=>t.left))),n=r(...t.map((t=>t.top)));return{x:e,y:n,width:o(...t.map((t=>t.right)))-e,height:o(...t.map((t=>t.bottom)))-n}}const k=new Set(["left","top"]);t.arrow=t=>({name:"arrow",options:t,async fn(e){const{x:n,y:i,placement:o,rects:l,platform:f,elements:u,middlewareData:d}=e,{element:p,padding:h=0}=s(t,e)||{};if(null==p)return{};const y=R(h),w={x:n,y:i},x=g(o),v=m(x),b=await f.getDimensions(p),A="y"===x,O=A?"top":"left",P=A?"bottom":"right",T=A?"clientHeight":"clientWidth",D=l.reference[v]+l.reference[x]-w[x]-l.floating[v],E=w[x]-l.reference[x],L=await(null==f.getOffsetParent?void 0:f.getOffsetParent(p));let k=L?L[T]:0;k&&await(null==f.isElement?void 0:f.isElement(L))||(k=u.floating[T]||l.floating[v]);const C=D/2-E/2,H=k/2-b[v]/2-1,S=r(y[O],H),B=r(y[P],H),F=k-b[v]-B,j=k/2-b[v]/2+C,z=a(S,j,F),M=!d.arrow&&null!=c(o)&&j!==z&&l.reference[v]/2-(jc(e)===t)),...n.filter((e=>c(e)!==t))]:n.filter((t=>f(t)===t))).filter((n=>!t||c(n)===t||!!e&&h(n)!==n))}(y||null,x,w):w,A=(null==(n=a.autoPlacement)?void 0:n.index)||0,R=b[A];if(null==R)return{};if(u!==R)return{reset:{placement:b[0]}};const O=await m.detectOverflow(e,v),P=p(R,l,await(null==m.isRTL?void 0:m.isRTL(d.floating))),T=[O[f(R)],O[P[0]],O[P[1]]],D=[...(null==(r=a.autoPlacement)?void 0:r.overflows)||[],{placement:R,overflows:T}],E=b[A+1];if(E)return{data:{index:A+1,overflows:D},reset:{placement:E}};const L=D.map((t=>{const e=c(t.placement);return[t.placement,e&&g?t.overflows.slice(0,2).reduce(((t,e)=>t+e),0):t.overflows[0],t.overflows]})).sort(((t,e)=>t[1]-e[1])),k=(null==(o=L.filter((t=>t[2].slice(0,c(t[0])?2:3).every((t=>t<=0))))[0])?void 0:o[0])||L[0][0];return k!==u?{data:{index:A+1,overflows:D},reset:{placement:k}}:{}}}},t.computePosition=async(t,e,n)=>{const{placement:i="bottom",strategy:r="absolute",middleware:o=[],platform:l}=n,a=l.detectOverflow?l:{...l,detectOverflow:T},s=await(null==l.isRTL?void 0:l.isRTL(e));let f=await l.getElementRects({reference:t,floating:e,strategy:r}),{x:c,y:u}=P(f,i,s),m=i,d=0;const g={};for(let n=0;nt<=0))){var B,F;const t=((null==(B=o.flip)?void 0:B.index)||0)+1,e=k[t];if(e){if(!("alignment"===g&&P!==d(e))||S.every((t=>d(t.placement)!==P||t.overflows[0]>0)))return{data:{index:t,overflows:S},reset:{placement:e}}}let n=null==(F=S.filter((t=>t.overflows[0]<=0)).sort(((t,e)=>t.overflows[1]-e.overflows[1]))[0])?void 0:F.placement;if(!n)switch(w){case"bestFit":{var j;const t=null==(j=S.filter((t=>{if(L){const e=d(t.placement);return e===P||"y"===e}return!0})).map((t=>[t.placement,t.overflows.filter((t=>t>0)).reduce(((t,e)=>t+e),0)])).sort(((t,e)=>t[1]-e[1]))[0])?void 0:j[0];t&&(n=t);break}case"initialPlacement":n=a}if(r!==n)return{reset:{placement:n}}}return{}}}},t.hide=function(t){return void 0===t&&(t={}),{name:"hide",options:t,async fn(e){const{rects:n,platform:i}=e,{strategy:r="referenceHidden",...o}=s(t,e);switch(r){case"referenceHidden":{const t=D(await i.detectOverflow(e,{...o,elementContext:"reference"}),n.reference);return{data:{referenceHiddenOffsets:t,referenceHidden:E(t)}}}case"escaped":{const t=D(await i.detectOverflow(e,{...o,altBoundary:!0}),n.floating);return{data:{escapedOffsets:t,escaped:E(t)}}}default:return{}}}}},t.inline=function(t){return void 0===t&&(t={}),{name:"inline",options:t,async fn(e){const{placement:n,elements:i,rects:l,platform:a,strategy:c}=e,{padding:u=2,x:m,y:g}=s(t,e),p=Array.from(await(null==a.getClientRects?void 0:a.getClientRects(i.reference))||[]);if(!p.length)return{};const h=function(t){const e=t.slice().sort(((t,e)=>t.y-e.y)),n=[];let i=null;for(let t=0;ti.height/2?n.push([r]):n[n.length-1].push(r),i=r}return n.map((t=>O(L(t))))}(p),y=O(L(p)),w=R(u);const x=await a.getElementRects({reference:{getBoundingClientRect:function(){if(2===h.length&&(h[0].left>h[1].right||h[1].left>h[0].right)&&null!=m&&null!=g)return h.find((t=>m>t.left-w.left&&mt.top-w.top&&g=2){if("y"===d(n)){const t=h[0],e=h[h.length-1],i="top"===f(n),r=t.top,o=e.bottom,l=i?t.left:e.left;return O({x:l,y:r,width:(i?t.right:e.right)-l,height:o-r})}const t="left"===f(n),e=o(...h.map((t=>t.right))),i=r(...h.map((t=>t.left))),l=h.filter((n=>t?n.left===i:n.right===e)),a=l[0].top,s=l[l.length-1].bottom;return O({x:i,y:a,width:e-i,height:s-a})}return y}},floating:i.floating,strategy:c});return l.reference.x!==x.reference.x||l.reference.y!==x.reference.y||l.reference.width!==x.reference.width||l.reference.height!==x.reference.height?{reset:{rects:x}}:{}}}},t.limitShift=function(t){return void 0===t&&(t={}),{options:t,fn(e){var n,i;const{x:r,y:o,placement:l,rects:a,middlewareData:c}=e,{offset:m=0,mainAxis:g=!0,crossAxis:p=!0}=s(t,e),h={x:r,y:o},y=d(l),w=u(y);let x=h[w],v=h[y];const b=s(m,e),A="number"==typeof b?{mainAxis:b,crossAxis:0}:{mainAxis:null!=(n=b.mainAxis)?n:0,crossAxis:null!=(i=b.crossAxis)?i:0};if(g){const t="y"===w?"height":"width",e=a.reference[w]-a.floating[t]+A.mainAxis,n=a.reference[w]+a.reference[t]-A.mainAxis;xn&&(x=n)}if(p){var R,O;const t="y"===w?"width":"height",e=k.has(f(l)),n=a.reference[y]-a.floating[t]+(e&&(null==(R=c.offset)?void 0:R[y])||0)+(e?0:A.crossAxis),i=a.reference[y]+a.reference[t]+(e?0:(null==(O=c.offset)?void 0:O[y])||0)-(e?A.crossAxis:0);vi&&(v=i)}return{[w]:x,[y]:v}}}},t.offset=function(t){return void 0===t&&(t=0),{name:"offset",options:t,async fn(e){var n,i;const{x:r,y:o,placement:l,middlewareData:a}=e,u=await async function(t,e){const{placement:n,platform:i,elements:r}=t,o=await(null==i.isRTL?void 0:i.isRTL(r.floating)),l=f(n),a=c(n),u="y"===d(n),m=k.has(l)?-1:1,g=o&&u?-1:1,p=s(e,t);let{mainAxis:h,crossAxis:y,alignmentAxis:w}="number"==typeof p?{mainAxis:p,crossAxis:0,alignmentAxis:null}:{mainAxis:p.mainAxis||0,crossAxis:p.crossAxis||0,alignmentAxis:p.alignmentAxis};return a&&"number"==typeof w&&(y="end"===a?-1*w:w),u?{x:y*g,y:h*m}:{x:h*m,y:y*g}}(e,t);return l===(null==(n=a.offset)?void 0:n.placement)&&null!=(i=a.arrow)&&i.alignmentOffset?{}:{x:r+u.x,y:o+u.y,data:{...u,placement:l}}}}},t.rectToClientRect=O,t.shift=function(t){return void 0===t&&(t={}),{name:"shift",options:t,async fn(e){const{x:n,y:i,placement:r,platform:o}=e,{mainAxis:l=!0,crossAxis:f=!1,limiter:c={fn:t=>{let{x:e,y:n}=t;return{x:e,y:n}}},...m}=s(t,e),g={x:n,y:i},p=await o.detectOverflow(e,m),h=d(r),y=u(h);let w=g[y],x=g[h];const v=(t,e)=>a(e+p["y"===t?"top":"left"],e,e-p["y"===t?"bottom":"right"]);l&&(w=v(y,w)),f&&(x=v(h,x));const b=c.fn({...e,[y]:w,[h]:x});return{...b,data:{x:b.x-n,y:b.y-i,enabled:{[y]:l,[h]:f}}}}}},t.size=function(t){return void 0===t&&(t={}),{name:"size",options:t,async fn(e){const{placement:n,rects:i,platform:l,elements:a}=e,{apply:u=()=>{},...m}=s(t,e),g=await l.detectOverflow(e,m),p=f(n),h=c(n),y="y"===d(n),{width:w,height:x}=i.floating;let v,b;"top"===p||"bottom"===p?(v=p,b=h===(await(null==l.isRTL?void 0:l.isRTL(a.floating))?"start":"end")?"left":"right"):(b=p,v="end"===h?"top":"bottom");const A=x-g.top-g.bottom,R=w-g.left-g.right,O=r(x-g[v],A),P=r(w-g[b],R),T=e.middlewareData.shift,D=!T;let E=O,L=P;null!=T&&T.enabled.x&&(L=R),null!=T&&T.enabled.y&&(E=A),D&&!h&&(y?L=w-2*o(g.left,g.right):E=x-2*o(g.top,g.bottom)),await u({...e,availableWidth:L,availableHeight:E});const k=await l.getDimensions(a.floating);return w!==k.width||x!==k.height?{reset:{rects:!0}}:{}}}}}));
diff --git a/assets/vendor/floating-ui/floating-ui.dom.umd.min.js b/assets/vendor/floating-ui/floating-ui.dom.umd.min.js
new file mode 100644
index 0000000..cd3dea3
--- /dev/null
+++ b/assets/vendor/floating-ui/floating-ui.dom.umd.min.js
@@ -0,0 +1 @@
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@floating-ui/core")):"function"==typeof define&&define.amd?define(["exports","@floating-ui/core"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).FloatingUIDOM={},t.FloatingUICore)}(this,(function(t,e){"use strict";const n=Math.min,o=Math.max,i=Math.round,r=Math.floor,l=t=>({x:t,y:t});function c(){return"undefined"!=typeof window}function s(t){return a(t)?(t.nodeName||"").toLowerCase():"#document"}function f(t){var e;return(null==t||null==(e=t.ownerDocument)?void 0:e.defaultView)||window}function u(t){var e;return null==(e=(a(t)?t.ownerDocument:t.document)||window.document)?void 0:e.documentElement}function a(t){return!!c()&&(t instanceof Node||t instanceof f(t).Node)}function d(t){return!!c()&&(t instanceof Element||t instanceof f(t).Element)}function h(t){return!!c()&&(t instanceof HTMLElement||t instanceof f(t).HTMLElement)}function p(t){return!(!c()||"undefined"==typeof ShadowRoot)&&(t instanceof ShadowRoot||t instanceof f(t).ShadowRoot)}function g(t){const{overflow:e,overflowX:n,overflowY:o,display:i}=C(t);return/auto|scroll|overlay|hidden|clip/.test(e+o+n)&&"inline"!==i&&"contents"!==i}function m(t){return/^(table|td|th)$/.test(s(t))}function y(t){try{if(t.matches(":popover-open"))return!0}catch(t){}try{return t.matches(":modal")}catch(t){return!1}}const w=/transform|translate|scale|rotate|perspective|filter/,v=/paint|layout|strict|content/,x=t=>!!t&&"none"!==t;let b;function L(t){const e=d(t)?C(t):t;return x(e.transform)||x(e.translate)||x(e.scale)||x(e.rotate)||x(e.perspective)||!R()&&(x(e.backdropFilter)||x(e.filter))||w.test(e.willChange||"")||v.test(e.contain||"")}function R(){return null==b&&(b="undefined"!=typeof CSS&&CSS.supports&&CSS.supports("-webkit-backdrop-filter","none")),b}function T(t){return/^(html|body|#document)$/.test(s(t))}function C(t){return f(t).getComputedStyle(t)}function S(t){return d(t)?{scrollLeft:t.scrollLeft,scrollTop:t.scrollTop}:{scrollLeft:t.scrollX,scrollTop:t.scrollY}}function E(t){if("html"===s(t))return t;const e=t.assignedSlot||t.parentNode||p(t)&&t.host||u(t);return p(e)?e.host:e}function F(t){const e=E(t);return T(e)?(t.ownerDocument||t).body:h(e)&&g(e)?e:F(e)}function O(t,e,n){var o;void 0===e&&(e=[]),void 0===n&&(n=!0);const i=F(t),r=i===(null==(o=t.ownerDocument)?void 0:o.body),l=f(i);if(r){const t=D(l);return e.concat(l,l.visualViewport||[],g(i)?i:[],t&&n?O(t):[])}return e.concat(i,O(i,[],n))}function D(t){return t.parent&&Object.getPrototypeOf(t.parent)?t.frameElement:null}function W(t){const e=C(t);let n=parseFloat(e.width)||0,o=parseFloat(e.height)||0;const r=h(t),l=r?t.offsetWidth:n,c=r?t.offsetHeight:o,s=i(n)!==l||i(o)!==c;return s&&(n=l,o=c),{width:n,height:o,$:s}}function M(t){return d(t)?t:t.contextElement}function z(t){const e=M(t);if(!h(e))return l(1);const n=e.getBoundingClientRect(),{width:o,height:r,$:c}=W(e);let s=(c?i(n.width):n.width)/o,f=(c?i(n.height):n.height)/r;return s&&Number.isFinite(s)||(s=1),f&&Number.isFinite(f)||(f=1),{x:s,y:f}}const H=l(0);function P(t){const e=f(t);return R()&&e.visualViewport?{x:e.visualViewport.offsetLeft,y:e.visualViewport.offsetTop}:H}function V(t,n,o,i){void 0===n&&(n=!1),void 0===o&&(o=!1);const r=t.getBoundingClientRect(),c=M(t);let s=l(1);n&&(i?d(i)&&(s=z(i)):s=z(t));const u=function(t,e,n){return void 0===e&&(e=!1),!!n&&e&&n===f(t)}(c,o,i)?P(c):l(0);let a=(r.left+u.x)/s.x,h=(r.top+u.y)/s.y,p=r.width/s.x,g=r.height/s.y;if(c&&i){const t=f(c),e=d(i)?f(i):i;let n=t,o=D(n);for(;o&&e!==n;){const t=z(o),e=o.getBoundingClientRect(),i=C(o),r=e.left+(o.clientLeft+parseFloat(i.paddingLeft))*t.x,l=e.top+(o.clientTop+parseFloat(i.paddingTop))*t.y;a*=t.x,h*=t.y,p*=t.x,g*=t.y,a+=r,h+=l,n=f(o),o=D(n)}}return e.rectToClientRect({width:p,height:g,x:a,y:h})}function A(t,e){const n=S(t).scrollLeft;return e?e.left+n:V(u(t)).left+n}function B(t,e){const n=t.getBoundingClientRect();return{x:n.left+e.scrollLeft-A(t,n),y:n.top+e.scrollTop}}function N(t,n,i){let r;if("viewport"===n||"layoutViewport"===n)r=function(t,e,n){void 0===n&&(n="viewport");const o="layoutViewport"===n,i=f(t),r=u(t),l=i.visualViewport;let c=r.clientWidth,s=r.clientHeight,a=0,d=0;if(l){const t=!R()||"fixed"===e;o?t||(a=-l.offsetLeft,d=-l.offsetTop):(c=l.width,s=l.height,t&&(a=l.offsetLeft,d=l.offsetTop))}if(A(r)<=0){const t=r.ownerDocument,e=t.body,n=getComputedStyle(e),o="CSS1Compat"===t.compatMode&&parseFloat(n.marginLeft)+parseFloat(n.marginRight)||0,i=Math.abs(r.clientWidth-e.clientWidth-o),l="stable both-edges"===getComputedStyle(r).scrollbarGutter?i/2:i;l<=25&&(c-=l)}return{width:c,height:s,x:a,y:d}}(t,i,n);else if("document"===n)r=function(t){const e=S(t),n=t.ownerDocument.body,i=o(t.scrollWidth,t.clientWidth,n.scrollWidth,n.clientWidth),r=o(t.scrollHeight,t.clientHeight,n.scrollHeight,n.clientHeight);let l=-e.scrollLeft+A(t);const c=-e.scrollTop;return"rtl"===C(n).direction&&(l+=o(t.clientWidth,n.clientWidth)-i),{width:i,height:r,x:l,y:c}}(u(t));else if(d(n))r=function(t,e){const n=V(t,!0,"fixed"===e),o=n.top+t.clientTop,i=n.left+t.clientLeft,r=z(t);return{width:t.clientWidth*r.x,height:t.clientHeight*r.y,x:i*r.x,y:o*r.y}}(n,i);else{const e=P(t);r={x:n.x-e.x,y:n.y-e.y,width:n.width,height:n.height}}return e.rectToClientRect(r)}function I(t,e,n){const o=h(e),i=u(e),r="fixed"===n,c=V(t,!0,r,e);let f={scrollLeft:0,scrollTop:0};const a=l(0);if((o||!r)&&(("body"!==s(e)||g(i))&&(f=S(e)),o)){const t=V(e,!0,r,e);a.x=t.x+e.clientLeft,a.y=t.y+e.clientTop}!o&&i&&(a.x=A(i));const d=!i||o||r?l(0):B(i,f);return{x:c.left+f.scrollLeft-a.x-d.x,y:c.top+f.scrollTop-a.y-d.y,width:c.width,height:c.height}}function $(t){return"static"===C(t).position}function k(t,e){if(!h(t)||"fixed"===C(t).position)return null;if(e)return e(t);let n=t.offsetParent;return u(t)===n&&(n=n.ownerDocument.body),n}function q(t,e){const n=f(t);if(y(t))return n;if(!h(t)){let e=E(t);for(;e&&!T(e);){if(d(e)&&!$(e))return e;e=E(e)}return n}let o=k(t,e);for(;o&&m(o)&&$(o);)o=k(o,e);return o&&T(o)&&$(o)&&!L(o)?n:o||function(t){let e=E(t);for(;h(e)&&!T(e);){if(L(e))return e;if(y(e))return null;e=E(e)}return null}(t)||n}const U={convertOffsetParentRelativeRectToViewportRelativeRect:function(t){let{elements:e,rect:n,offsetParent:o,strategy:i}=t;const r="fixed"===i,c=u(o),f=!!e&&y(e.floating);if(o===c||f&&r)return n;let a={scrollLeft:0,scrollTop:0},d=l(1);const p=l(0),m=h(o);if((m||!r)&&(("body"!==s(o)||g(c))&&(a=S(o)),m)){const t=V(o);d=z(o),p.x=t.x+o.clientLeft,p.y=t.y+o.clientTop}const w=!c||m||r?l(0):B(c,a);return{width:n.width*d.x,height:n.height*d.y,x:n.x*d.x-a.scrollLeft*d.x+p.x+w.x,y:n.y*d.y-a.scrollTop*d.y+p.y+w.y}},getDocumentElement:u,getClippingRect:function(t){let{element:e,boundary:i,rootBoundary:r,strategy:l}=t;const c=[..."clippingAncestors"===i?y(e)?[]:function(t,e){const n=e.get(t);if(n)return n;let o=O(t,[],!1).filter((t=>d(t)&&"body"!==s(t))),i=null;const r="fixed"===C(t).position;let l=r?E(t):t;for(;d(l)&&!T(l);){const t=C(l),e=L(l),n=i?i.position:r?"fixed":"";e||"fixed"!==n&&("absolute"!==n||"static"!==t.position)?i=t:o=o.filter((t=>t!==l)),l=E(l)}return e.set(t,o),o}(e,this._c):[].concat(i),r],f=N(e,c[0],l);let u=f.top,a=f.right,h=f.bottom,p=f.left;for(let t=1;t{c&&t.addEventListener("scroll",i),s&&t.addEventListener("resize",i)}));const m=p&&d?function(t,e,i){let l,c=null;const s=u(t);function a(){var t;clearTimeout(l),null==(t=c)||t.disconnect(),c=null}function d(i,f){void 0===i&&(i=!1),void 0===f&&(f=1),a();const u=t.getBoundingClientRect(),{left:h,top:p,width:g,height:m}=u;if(i||e(),!g||!m)return;const y={rootMargin:-r(p)+"px "+-r(s.clientWidth-(h+g))+"px "+-r(s.clientHeight-(p+m))+"px "+-r(h)+"px",threshold:o(0,n(1,f))||1};let w=!0;function v(e){const n=e[0].intersectionRatio;if(!j(u,t.getBoundingClientRect()))return d();if(n!==f){if(!w)return d();n?d(!1,n):l=setTimeout((()=>{d(!1,1e-7)}),1e3)}w=!1}try{c=new IntersectionObserver(v,{...y,root:s.ownerDocument})}catch(t){c=new IntersectionObserver(v,y)}c.observe(t)}const h=f(t),p=()=>d(i);return h.addEventListener("resize",p),d(!0),()=>{h.removeEventListener("resize",p),a()}}(p,i,s):null;let y,w=-1,v=null;a&&(v=new ResizeObserver((t=>{let[n]=t;n&&n.target===p&&v&&e&&(v.unobserve(e),cancelAnimationFrame(w),w=requestAnimationFrame((()=>{var t;null==(t=v)||t.observe(e)}))),i()})),p&&!h&&v.observe(p),e&&v.observe(e));let x=h?V(t):null;return h&&function e(){const n=V(t);x&&!j(x,n)&&i();x=n,y=requestAnimationFrame(e)}(),i(),()=>{var t;g.forEach((t=>{c&&t.removeEventListener("scroll",i),s&&t.removeEventListener("resize",i)})),null==m||m(),null==(t=v)||t.disconnect(),v=null,h&&cancelAnimationFrame(y)}},t.computePosition=(t,n,o)=>{const i=new Map,r=null!=o?o:{},l={...U,...r.platform,_c:i};return e.computePosition(t,n,{...r,platform:l})},t.detectOverflow=X,t.flip=J,t.getOverflowAncestors=O,t.hide=Q,t.inline=tt,t.limitShift=et,t.offset=Y,t.platform=U,t.shift=G,t.size=K}));
diff --git a/assets/vendor/tooltip/LICENSE b/assets/vendor/tooltip/LICENSE
deleted file mode 100644
index 650b770..0000000
--- a/assets/vendor/tooltip/LICENSE
+++ /dev/null
@@ -1,15 +0,0 @@
-ISC License
-
-Copyright (c) 2024-present, Eqbal Sohrabi
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-PERFORMANCE OF THIS SOFTWARE.
diff --git a/assets/vendor/tooltip/README.md b/assets/vendor/tooltip/README.md
deleted file mode 100644
index ae1a959..0000000
--- a/assets/vendor/tooltip/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# @sohrabi/tooltip (vendored)
-
-- Version: 2.0.0
-- Source: https://github.com/sohrabi/tooltip
-- Upstream file: `dist/tooltip.min.js` (copied verbatim as `tooltip.min.js`)
-- License: ISC (see `LICENSE`)
-- Usage: `initTooltip()` global (`window.initTooltip` / `window.SohrabiTooltip.initTooltip`).
- Add `data-tooltip="..."` to any element; see https://github.com/XjSv/Cooked/issues/182.
-
-Do not edit `tooltip.min.js` directly. To update, copy the new
-`dist/tooltip.min.js` from upstream and bump the version in
-`includes/class.cooked-admin-enqueues.php` (`cooked-tooltip` handle).
diff --git a/assets/vendor/tooltip/tooltip.min.js b/assets/vendor/tooltip/tooltip.min.js
deleted file mode 100644
index 342f0eb..0000000
--- a/assets/vendor/tooltip/tooltip.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-"use strict";var SohrabiTooltip=(()=>{var T=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var q=(t,e)=>{for(var o in e)T(t,o,{get:e[o],enumerable:!0})},V=(t,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of D(e))!B.call(t,r)&&r!==o&&T(t,r,{get:()=>e[r],enumerable:!(i=$(e,r))||i.enumerable});return t};var K=t=>V(T({},"__esModule",{value:!0}),t);var ot={};q(ot,{initTooltip:()=>et});var j="[data-tooltip]",U=["bottom","top","right","left","bottomleft","bottomright","topleft","topright"],d={allowHTML:!1,backgroundColor:"#000",borderRadius:"4px",className:"",color:"#fff",disableOnMobile:!1,maxWidth:"320px",offset:6,viewportPadding:4,zIndex:2001},G=0,f=null,J=t=>t==="bottom"||t==="top"||t==="right"||t==="left"||t==="bottomleft"||t==="bottomright"||t==="topleft"||t==="topright",g=(t,e)=>typeof t=="number"&&Number.isFinite(t)&&t>=0?t:e,Q=t=>({allowHTML:t.allowHTML??d.allowHTML,backgroundColor:t.backgroundColor??d.backgroundColor,borderRadius:t.borderRadius??d.borderRadius,className:t.className??d.className,color:t.color??d.color,disableOnMobile:t.disableOnMobile??d.disableOnMobile,maxWidth:t.maxWidth??d.maxWidth,offset:g(t.offset,d.offset),viewportPadding:g(t.viewportPadding,d.viewportPadding),zIndex:g(t.zIndex,d.zIndex)}),F=t=>{if(!(t instanceof Element))return null;let e=t.closest(j);return e instanceof HTMLElement?e:null},R=t=>{let e=[],o=(t.dataset.positions??"").split(",").map(i=>i.trim().toLowerCase()).filter(J);for(let i of[...o,...U])e.includes(i)||e.push(i);return e},S=(t,e,o,i,r)=>{let s=e.left+(e.width-o)/2,a=e.top+(e.height-i)/2;switch(t){case"top":return{left:s,top:e.top-i-r};case"right":return{left:e.right+r,top:a};case"left":return{left:e.left-o-r,top:a};case"bottomleft":return{left:e.left-o-r,top:e.bottom+r};case"bottomright":return{left:e.right+r,top:e.bottom+r};case"topleft":return{left:e.left-o-r,top:e.top-i-r};case"topright":return{left:e.right+r,top:e.top-i-r};case"bottom":return{left:s,top:e.bottom+r}}},X=(t,e,o,i)=>t.left>=i&&t.top>=i&&t.left+e<=window.innerWidth-i&&t.top+o<=window.innerHeight-i,Y=(t,e,o,i)=>{let r=Math.max(i,window.innerWidth-e-i),s=Math.max(i,window.innerHeight-o-i);return{left:Math.min(Math.max(t.left,i),r),top:Math.min(Math.max(t.top,i),s)}},Z=t=>t.bottom>=0&&t.right>=0&&t.top<=window.innerHeight&&t.left<=window.innerWidth,_=()=>typeof window.matchMedia=="function"&&window.matchMedia("(hover: none)").matches,tt=t=>{let e=document.createElement("span");return e.id=`sohrabi-tooltip-${++G}`,e.className=`sohrabi-tooltip ${t.className}`.trim(),e.setAttribute("aria-hidden","true"),e.setAttribute("role","tooltip"),Object.assign(e.style,{backgroundColor:t.backgroundColor,borderRadius:t.borderRadius,boxSizing:"border-box",color:t.color,display:"none",left:"0",maxWidth:t.maxWidth,padding:"5px 8px",pointerEvents:"none",position:"fixed",textAlign:"center",top:"0",visibility:"hidden",whiteSpace:"normal",zIndex:String(t.zIndex)}),e},et=(t={})=>{if(f)return f;let e=Q(t),o=tt(e),i=document.body??document.documentElement;if(!i)throw new Error("Tooltip requires a document element.");i.appendChild(o);let r=new Set,s=null,a=null,h=!1,u=null,y=()=>{s&&(u===null?s.removeAttribute("aria-describedby"):s.setAttribute("aria-describedby",u))},c=()=>{a!==null&&(window.cancelAnimationFrame(a),a=null),y(),r.clear(),s=null,u=null,o.style.display="none",o.style.visibility="hidden",o.setAttribute("aria-hidden","true")},v=()=>{if(!s||!s.isConnected){c();return}let n=s.getBoundingClientRect();if(!Z(n)){c();return}let l=o.getBoundingClientRect(),m=l.width||o.offsetWidth,p=l.height||o.offsetHeight,I=R(s),w=I[0]??"bottom",k=S(w,n,m,p,e.offset);for(let z of I){let W=S(z,n,m,p,e.offset);if(X(W,m,p,e.viewportPadding)){w=z,k=W;break}}let N=Y(k,m,p,e.viewportPadding);o.dataset.position=w,o.style.left=`${N.left}px`,o.style.top=`${N.top}px`,o.style.visibility="visible",o.setAttribute("aria-hidden","false")},x=n=>{let l=n.getAttribute("data-tooltip");return l?(e.allowHTML?o.innerHTML=l:o.textContent=l,!0):!1},M=(n,l)=>{if(!(h||e.disableOnMobile&&_())){if(s!==n){y(),r.clear(),s=n,u=n.getAttribute("aria-describedby");let m=u?`${u} ${o.id}`:o.id;n.setAttribute("aria-describedby",m)}if(r.add(l),!x(n)){c();return}o.style.display="block",o.style.visibility="hidden",v()}},E=n=>{r.delete(n),r.size===0&&c()},b=()=>{!s||a!==null||(a=window.requestAnimationFrame(()=>{a=null,v()}))},L=n=>{let l=F(n.target);l&&M(l,"hover")},O=n=>{s&&(!(n.target instanceof Node)||!s.contains(n.target)||n.relatedTarget instanceof Node&&s.contains(n.relatedTarget)||E("hover"))},C=n=>{let l=F(n.target);l&&M(l,"focus")},P=n=>{s&&(!(n.target instanceof Node)||!s.contains(n.target)||n.relatedTarget instanceof Node&&s.contains(n.relatedTarget)||E("focus"))},A=n=>{n.key==="Escape"&&c()};document.addEventListener("mouseover",L),document.addEventListener("mouseout",O),document.addEventListener("focusin",C),document.addEventListener("focusout",P),document.addEventListener("keydown",A),window.addEventListener("resize",b),window.addEventListener("scroll",b,!0);let H={destroy:()=>{h||(h=!0,c(),document.removeEventListener("mouseover",L),document.removeEventListener("mouseout",O),document.removeEventListener("focusin",C),document.removeEventListener("focusout",P),document.removeEventListener("keydown",A),window.removeEventListener("resize",b),window.removeEventListener("scroll",b,!0),o.remove(),f=null)},hide:c,refresh:()=>{if(!s||!x(s)){c();return}v()}};return f=H,H};return K(ot);})();
-typeof window!=="undefined"&&(window.initTooltip=SohrabiTooltip.initTooltip);
diff --git a/includes/class.cooked-admin-enqueues.php b/includes/class.cooked-admin-enqueues.php
index 9da6464..fa9d337 100644
--- a/includes/class.cooked-admin-enqueues.php
+++ b/includes/class.cooked-admin-enqueues.php
@@ -188,14 +188,15 @@ public function admin_enqueues( $hook ) {
];
// Cooked Admin Style Assets
- wp_register_script( 'cooked-admin-functions', COOKED_URL . 'assets/admin/js/cooked-functions' . $min . '.js', ['jquery'], COOKED_VERSION, true );
+ // Tooltips (Floating UI, vendored — see assets/vendor/floating-ui/README.md).
+ wp_register_script( 'cooked-floating-ui-core', COOKED_URL . 'assets/vendor/floating-ui/floating-ui.core.umd.min.js', [], '1.8.0', true );
+ wp_register_script( 'cooked-floating-ui-dom', COOKED_URL . 'assets/vendor/floating-ui/floating-ui.dom.umd.min.js', ['cooked-floating-ui-core'], '1.8.0', true );
+
+ wp_register_script( 'cooked-admin-functions', COOKED_URL . 'assets/admin/js/cooked-functions' . $min . '.js', ['jquery', 'cooked-floating-ui-dom'], COOKED_VERSION, true );
wp_register_script( 'cooked-migration', COOKED_URL . 'assets/admin/js/cooked-migration' . $min . '.js', ['jquery'], COOKED_VERSION, true );
wp_enqueue_style( 'cooked-admin', COOKED_URL . 'assets/admin/css/style' . $min . '.css', [], COOKED_VERSION );
wp_enqueue_style( 'wp-color-picker' );
- // Tooltips (@sohrabi/tooltip, vendored — see assets/vendor/tooltip/README.md).
- wp_enqueue_script( 'cooked-tooltip', COOKED_URL . 'assets/vendor/tooltip/tooltip.min.js', [], '2.0.0', true );
-
// Cooked Admin Script
wp_localize_script('cooked-admin-functions', 'cooked_admin_functions_js_vars', $cooked_js_vars );
wp_localize_script('cooked-migration', 'cooked_migration_js_vars', $cooked_js_vars );
diff --git a/includes/class.cooked-recipe-meta.php b/includes/class.cooked-recipe-meta.php
index 5fa346b..0686e2e 100644
--- a/includes/class.cooked-recipe-meta.php
+++ b/includes/class.cooked-recipe-meta.php
@@ -560,7 +560,7 @@ function cooked_render_recipe_fields( $post_id ) {
-
+
' . esc_html__( 'Recipe Excerpt', 'cooked' ) . '' . esc_html__( 'The excerpt is used on recipe listing templates, where the full recipe should not be displayed.','cooked') ); ?>" data-positions="top">
@@ -583,7 +583,7 @@ function cooked_render_recipe_fields( $post_id ) {
-
+
' . esc_html__( 'SEO Description', 'cooked' ) . '' . esc_html__( 'This description is used for SEO purposes and is optional. By default, Cooked will use the Recipe Excerpt above if available or the Recipe Title if not.','cooked') ); ?>" data-positions="top">
@@ -621,7 +621,7 @@ function cooked_render_recipe_fields( $post_id ) {
-
+
' . esc_html__( 'Recipe Notes', 'cooked' ) . '' . esc_html__( 'The notes are displayed in the recipe.','cooked') ); ?>" data-positions="top">