| Server IP : 80.74.154.100 / Your IP : 216.73.217.0 Web Server : Apache System : Linux marissa.metanet.ch 4.18.0-553.141.2.lve.el7h.x86_64 #1 SMP Wed Jul 8 17:20:31 UTC 2026 x86_64 User : onlineadmin ( 10487) PHP Version : 8.5.7 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/httpd/vhosts/comeonline.ch/httpdocs/wp-content/themes/travelify/library/js/ |
Upload File : |
/**
* CloneYa!: Plugin to clone form elements in a nested manner
* @author Saurabh Shukla <saurabh@yapapaya.com>
* http://hookrefineandtinker.com
* License GNU/GPL & MIT
*/
(function ($) {
"use strict";
var name = "cloneya", defaults = {
cloneThis: '.toclone',
cloneButton: '.clone',
deleteButton: '.delete',
clonePosition: 'after',
minimum: 1,
// renaming limit
maximum: 999, //setting it to a high number, by default
//limit: 999,
valueClone: false,
dataClone: false,
deepClone: false,
serializeID: true,
ignore: 'label.error',
preserveChildCount: false
};
/**
* Create the class CloneYa
*
* @class CloneYa
* @classdesc Adds cloning functionality to element
*
* @param {String | Object} element - the clone wrapper
*
* @param {Object} options - options to initialise with
*
* @param {String} options.cloneThis - Selector for the clone element
* @param {String} options.cloneButton - Selector for the clone button
* @param {String} options.deleteButton - Selector for the delete button
*
* @param {String} options.clonePosition - Where should the clone be added 'before' or 'after'
*
* @param {Number} options.limit - The maximum number of clones
*
* @param {Boolean} options.valueClone - Clone the input values as well?
* @param {Boolean} options.dataClone - Clone the data attributes?
* @param {Boolean} options.deepClone - Clone other data added to the jQuery object
*
* @param {Boolean} options.serializeID - Whether to serialize the IDs, automatically
* @param {String} options.ignore - Selectors for clonables' elements that should not be cloned
* @param {Boolean} options.defaultRender - Start with this number of clones, by default
* @param {Boolean} options.preserveChildCount - whether to preserve the initial number of clone's child clones, works with nesting as well.
*
* @returns {_L13.CloneYa}
*/
function CloneYa(element, options) {
/**
* regex for recalculating the ids
*
* @type RegExp
*/
this.regex = /^(.*)(\d)+$/i;
this.elem = element;
this.$elem = $(element);
this.elemClass = name + '-wrap';
/**
* creating a jQuery object, just in case
*
* @type @call;$
*/
//var elem = $(element);
/**
* Support deprecated parameters
*/
if (typeof options !== 'undefined') {
if (typeof options.limit !== 'undefined' && options.limit > 0) {
options.maximum = options.limit;
}
}
/**
* merge the passed options object with defaults
*
* @type @exp;$@call;extend
*/
this.config = $.extend({}, defaults, options);
/**
*
* @type @exp;elem@call;closestChild
*/
this.clones = this.$elem.closestChild(this.config.cloneThis);
this.init();
}
CloneYa.prototype = {
init: function () {
var $this = this;
// add our classes
$this.$elem.addClass($this.elemClass);
$this.clones.addClass(name);
// save the sibling count into data attr
$this.clones.data('initialCount', $this.clones.length);
//Now, what if the clone button and delete button are not contained in
//the clonable?
// add a click handler for the clone buttons
$this.$elem.on('click.' + name, $this.config.cloneThis + '>' + $this.config.cloneButton, function (event) {
event.preventDefault();
event.stopPropagation();
var toClone = $(this).closest($this.config.cloneThis);
// this is just a wrapper for the custom clone event
$this.$elem.triggerAll('clone_clone clone.' + name, [toClone]);
});
// the custom clone event
$this.$elem.on('clone.' + name, function (event, toClone) {
if (event.namespace === name) {
$this._cloneAndAppend(toClone);
}
});
// click handler for delete button
$this.$elem.on('click.' + name, $this.config.cloneThis + '>' + $this.config.deleteButton, function (event) {
event.preventDefault();
event.stopPropagation();
var toDelete = $(this).closest($this.config.cloneThis);
// just a wrapper for delclone event
$this.$elem.triggerAll('clone_delete delete.' + name, [toDelete]);
});
// the delete clone event
$this.$elem.on('delete.' + name, function (event, toDelete) {
// get the count of all the sibling clones
/**
*
* @type @exp;$todelete@call;closest@call;closestChild@pro;length
*/
var cloneCount = toDelete.closest('.' + $this.elemClass).closestChild($this.config.cloneThis).length;
if (cloneCount > $this.config.minimum) {
// trigger hook
$this.$elem.triggerAll('clone_before_delete before_delete.' + name, [toDelete, cloneCount]);
$this.$elem.triggerHandler('remove.' + name, [toDelete]);
$this.$elem.triggerAll('clone_after_delete after_delete.' + name);
}
else {
$this.$elem.triggerHandler('minimum.' + name, $this.config.minimum, [toDelete]);
// First clone form can't be deleted, but the values should be removed from first form
// is this expected behaviour? especially since we use minimum?
toDelete.find('input, textarea, select').each(function () {
$this._clearForm($(this));
});
}
});
$this.$elem.on('remove.' + name, function (event, toDelete) {
$(toDelete).remove();
});
},
_clean: function () {
var $this = this;
$this.$elem.removeClass(name + '-wrap');
$this.clones.removeClass(name);
$this.$elem.off('click.' + name, $this.config.cloneThis + '>' + $this.config.cloneButton);
$this.$elem.off('click.' + name, $this.config.cloneThis + '>' + $this.config.deleteButton);
$this.$elem.off('clone_clone clone_delete clone_before_delete clone.' + name + ' delete.' + name + ' before_delete.' + name);
},
destroy: function () {
this._clean();
this.$elem.removeData(name);
},
getOption: function () {
return this.config;
},
setOption: function (lateOptions) {
$.extend(this.config, lateOptions || {});
this._clean();
this.init();
},
_cloneAndAppend: function (toClone) {
// get the count of all the sibling clones
/**
*
* @type @exp;$toclone@call;closest@call;closestChild@pro;length
*/
var cloneCount = toClone.closest('.' + this.elemClass).closestChild(this.config.cloneThis).length;
// check if we've reached the maximum limit
if (cloneCount < this.config.maximum) {
// trigger a custom event for hooking in
this.$elem.triggerAll('clone_before_clone before_clone.' + name, [toClone]);
var newClone = this._cloneItem(toClone);
// trigger custom event on the original element
this.$elem.triggerAll('clone_after_clone after_clone.' + name, [toClone, newClone]);
// add to our clones object
this.clones.add(newClone);
// trigger custom event on the new clone
this.$elem.triggerAll('clone_before_append before_append.' + name, [toClone, newClone]);
// get the position where the clone has to be added
// and add the newclone
if (this.config.clonePosition !== 'after') {
toClone.before(newClone);
} else {
toClone.after(newClone);
}
if (this.config.ignore) {
newClone.find(this.config.ignore).remove();
}
// reformat the id attributes
this._redoIDs();
// trigger custom event for hooking
this.$elem.triggerAll('clone_after_append after_append.' + name, [toClone, newClone]);
} else {
// trigger a custom event for hooking
this.$elem.triggerAll('clone_limit maximum.' + name, this.config.maximum, [toClone]);
}
},
_cloneItem: function (toClone) {
var $this = this;
// clone it
/**
*
* @type @exp;$toclone@call;clone
*/
var newClone = toClone.clone($this.config.dataClone, $this.config.deepClone);
// we want to preserve the initial child count
if ($this.config.preserveChildCount !== false) {
// the child count only needs preservation if they are clonable.
var originalChildren = toClone.find('.' + name + '-wrap');
// for each wrapper
newClone.find('.' + name + '-wrap').each(function (index) {
/**
*
* @type @call;jquery-cloneya_L8.$@call;closestChild
*/
var inNewClone = $(this).closestChild('.' + name);
var inOriginal = $(originalChildren[index]).closestChild('.' + name);
/**
*
* @type @exp;inOriginal@call;data
*/
var originalCount = inOriginal.data('initialCount');
/**
*
* @type @exp;inNewClone@call;slice
*/
var $extra = inNewClone.slice(originalCount, inNewClone.length);
$extra.remove();
inNewClone.data('initial-count', originalCount);
});
}
// get the form input
newClone.find('input, textarea, select').each(function () {
// check if the values need to be copied, if not empty them
$this._clearForm($(this));
// removed the portion taking care of the index
// each case is specific and I'd rather leave it to the developer
// custom event hook for index handling
$this.$elem.triggerAll('clone_form_input form_input.' + name, [$(this), toClone, newClone]);
});
return newClone;
},
/*
* Clear Form will used to clear the values of the form
*/
/**
*
* @param {type} $el
* @returns {undefined}
*/
_clearForm: function ($el) {
if (!this.config.valueClone && !$el.hasClass('noEmpty')) {
if ($el.is(':checkbox') || $el.is(':radio')) {
$el.prop('checked', false);
}
else {
$el.val('');
}
}
},
/**
* Redo the id attribute, serially
*/
/**
*
* @returns {undefined}
*/
_redoIDs: function () {
var $this = this;
// check if this even needs to be done
if ($this.config.serializeID !== true) {
return;
}
// get the id of the first clone (hoping to increment the ids)
/**
*
* @type @exp;elem@call;find@call;first@call;attr
*/
var mainid = $this.$elem.find($this.config.cloneThis).first().attr('id');
$this.$elem.find($this.config.cloneThis).each(function (i) {
var j;
// assign the index to a string var for appending to the ids
// 0 index will have no number at the end
if (i !== 0) {
j = i;
} else {
j = '';
}
// first modify the clone id
if ($(this).attr('id')) {
$(this).attr('id', mainid + j);
}
var id, nId;
// take all the elements inside the clone
$(this).find('*').each(function () {
id = $(this).attr('id');
if (id) {
// match the id with the regex to get the string part
// separate from the number part
var match = id.match($this.regex);
// if there was a number
if (match && match.length === 3) {
// just take the string part
// add the new number to it
nId = id.replace(/\d+$/, "") + j;
$(this).attr('id', nId);
} else {
// else there was no number,
// this was earlier the first element
// just add the number to its id
nId = id + j;
$(this).attr('id', nId);
}
}
//update label
$(this).closest($this.config.cloneThis).find("label[for='" + id + "']").attr('for', nId);
if ($this.config.serializeIndex) {
var name = $(this).attr('name');
// This will increment the numeric array index for cloned field names
if (name) {
var matches = name.match(/\[([0-9}]+)\]/);
if (matches && matches.length >= 1) {
var st = name;
var name = st.replace(matches[0], "["+i+"]");
$(this).attr('name', name);
}
}
}
});
});
}
};
// add the cloneya to the global object
/**
*
* @param {type} options
* @returns {jquery-cloneya_L8.$.fn@call;each}
*/
$.fn[name] = function (options)
{
var args = arguments;
if (options === undefined || typeof options === 'object') {
// Creates a new plugin instance, for each selected element, and
// stores a reference withint the element's data
return this.each(function () {
if (!$.data(this, name)) {
$.data(this, name, new CloneYa(this, options));
}
});
} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
// Call a public pluguin method (not starting with an underscore) for each
// selected element.
if (Array.prototype.slice.call(args, 1).length === 0 && $.inArray(options, $.fn[name].getters) !== -1) {
// If the user does not pass any arguments and the method allows to
// work as a getter then break the chainability so we can return a value
// instead the element reference.
var instance = $.data(this[0], name);
return instance[options].apply(instance, Array.prototype.slice.call(args, 1));
} else {
// Invoke the speficied method on each selected element
return this.each(function () {
var instance = $.data(this, name);
if (instance instanceof CloneYa && typeof instance[options] === 'function') {
instance[options].apply(instance, Array.prototype.slice.call(args, 1));
}
});
}
}
};
$.fn[name].getters = ['getOption'];
/*
* jquery.closestchild 0.1.1
*
* Author: Andrey Mikhaylov aka lolmaus
* Email: lolmaus@gmail.com
*
*/
/**
*
* @param {type} selector
* @returns {$}
*/
$.fn.closestChild = function (selector) {
var $children, $results;
$children = this.children();
if ($children.length === 0) {
return $();
}
$results = $children.filter(selector);
if ($results.length > 0) {
return $results;
} else {
return $children.closestChild(selector);
}
};
/*
* TriggerAll, modified from stackoverflow
* http://stackoverflow.com/questions/11850625/jquery-trigger-multiple-events
*/
$.fn.extend({
triggerAll: function (events, params) {
var el = this, i, evts = events.split(' ');
for (i = 0; i < evts.length; i += 1) {
el.triggerHandler(evts[i], params);
}
return el;
}
});
})(jQuery);