diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module index 0811ed99..a7da6b05 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module @@ -9,7 +9,7 @@ * /wire/core/Fieldtype.php * /wire/core/FieldtypeMulti.php * - * ProcessWire 3.x, Copyright 2016 by Ryan Cramer + * ProcessWire 3.x, Copyright 2021 by Ryan Cramer * https://processwire.com * * @todo: automatic sorting. @@ -34,7 +34,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { return array( 'title' => __('Repeater', __FILE__), // Module Title 'summary' => __('Maintains a collection of fields that are repeated for any number of times.', __FILE__), // Module Summary - 'version' => 106, + 'version' => 107, 'autoload' => true, 'installs' => 'InputfieldRepeater' ); diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.js b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.js index fc04f387..8e82c926 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.js +++ b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.js @@ -3,7 +3,7 @@ * * Maintains a collection of fields that are repeated for any number of times. * - * ProcessWire 3.x, Copyright 2016 by Ryan Cramer + * ProcessWire 3.x, Copyright 2021 by Ryan Cramer * https://processwire.com * */ @@ -528,6 +528,8 @@ function InputfieldRepeater($) { } } + /*** DEPTH FUNCTIONS **********************************************************************************/ + /** * Determine the sortable depth of a repeater item and either return it or apply it * @@ -539,7 +541,8 @@ function InputfieldRepeater($) { */ function sortableDepth(ui, maxDepth, updateNow) { - var $depth = ui.item.find('.InputfieldRepeaterDepth'); + var $wrap = ui.item.children('.InputfieldContent').children('.Inputfields').children('.InputfieldRepeaterItemDepth'); + var $depth = $wrap.find('input'); var depth = -1; var prevDepth = parseInt($depth.val()); var left = ui.position.left; @@ -553,25 +556,113 @@ function InputfieldRepeater($) { // console.log('increase depth to: ' + depth); } - if(depth < 1) { - depth = 0; - } else if(depth > maxDepth) { - depth = maxDepth; - } - if(updateNow) { - if(depth) { - ui.item.css('margin-left', (depth * depthSize) + 'px'); - } else { - ui.item.css('margin-left', 0); - } - - $depth.val(depth); + depth = setItemDepth(ui.item, depth, maxDepth); ui.item.children('.InputfieldHeader').removeClass('ui-state-error'); } return depth; } + + /** + * Set repeater item depth + * + * @param $item Repeater item + * @param int depth Depth to set + * @param int maxDepth Max depth (you can optionally omit this if depth is already validated for the max) + * @param bool noValidate Specify true to prevent depth validation, otherwise omit + * @returns int Returns adjusted depth or -1 on fail + * + */ + function setItemDepth($item, depth, maxDepth, noValidate) { + + noValidate = typeof noValidate === "undefined" ? false : noValidate; + + if(depth < 1) depth = 0; + if(typeof maxDepth !== 'undefined' && depth > maxDepth) depth = maxDepth; + if(!$item.hasClass('InputfieldRepeaterItem')) $item = $item.closest('.InputfieldRepeaterItem'); + if(!$item.length) return -1; + + var $depthInput = $item.children('.InputfieldContent').children('.Inputfields') + .children('.InputfieldRepeaterItemDepth').find('input'); + + if(!$depthInput.length) { + console.log('Cannot find depth input for ' + $item.attr('id')); + } + + if(!noValidate && $item.closest('.InputfieldRepeater').hasClass('InputfieldRepeaterFamilyFriendly')) { + var $prevItem = $item.prev('.InputfieldRepeaterItem:not(.InputfieldRepeaterNewItem)'); + if($prevItem.length) { + var prevItemDepth = parseInt($prevItem.attr('data-depth')); + if(depth - prevItemDepth > 1) depth = prevItemDepth + 1; + } else { + depth = 0; + } + } + + $depthInput.val(depth); + $item.attr('data-depth', depth); + + if(depth > 0) { + $item.css('margin-left', (depth * depthSize) + 'px'); + } else { + $item.css('margin-left', 0); + } + + return depth; + } + + /** + * Get repeater item depth + * + * @param $item Repeater item + * @returns int Returns depth or -1 on fail + * + */ + function getItemDepth($item) { + if(!$item.hasClass('InputfieldRepeaterItem')) $item = $item.closest('.InputfieldRepeaterItem'); + if(!$item.length) return -1; + return parseInt($item.attr('data-depth')); + } + + /** + * Get all depth children for given repeater item + * + * @param $item Repeater item + * @returns {Array} + * + */ + function getDepthChildren($item) { + + var children = []; + var n = 0; + var startDepth = parseInt($item.attr('data-depth')); + var pageId = $item.attr('data-page'); + var pageIdClass = 'Inputfield_repeater_item_' + pageId; + + // ui.sortable adds additional copies of $item, so make sure we have the last one + while($item.hasClass(pageIdClass)) { + var $nextItem = $item.next('.InputfieldRepeaterItem:not(.InputfieldRepeaterNewItem)'); + if(!$nextItem.length || !$nextItem.hasClass(pageIdClass)) break; + $item = $nextItem; + } + + do { + // var $child = $item.next('.InputfieldRepeaterItem:not(.' + pageIdClass + '):not(.InputfieldRepeaterNewItem)'); + var $child = $item.next('.InputfieldRepeaterItem:not(.InputfieldRepeaterNewItem)'); + if(!$child.length) break; + + var childDepth = parseInt($child.attr('data-depth')); + if(!childDepth || childDepth <= startDepth) break; + + $item = $child; + children[n] = $child; + n++; + + } while(true); + + return children; + } /*** INIT FUNCTIONS **********************************************************************************/ @@ -579,14 +670,15 @@ function InputfieldRepeater($) { * Initialize repeater item depths * * Applies a left-margin to repeater items consistent with with value in - * each item's input.InputfieldRepeaterDepth hidden input. + * each item's '.InputfieldRepeaterItemDepth input' hidden input. * * @param $inputfieldRepeater * */ function initDepths($inputfieldRepeater) { - $inputfieldRepeater.find('.InputfieldRepeaterDepth').each(function() { - var $depth = $(this); + $inputfieldRepeater.find('.InputfieldRepeaterItemDepth').each(function() { + var $wrap = $(this); + var $depth = $wrap.find('input'); var depth = $depth.val(); var $item = $depth.closest('.InputfieldRepeaterItem'); var currentLeft = $item.css('margin-left'); @@ -610,12 +702,15 @@ function InputfieldRepeater($) { function initSortable($inputfieldRepeater, $inputfields) { var maxDepth = parseInt($inputfieldRepeater.attr('data-depth')); + var depthChildren = []; + var startDepth = 0; + var familyFriendly = $inputfieldRepeater.hasClass('InputfieldRepeaterFamilyFriendly'); var sortableOptions = { items: '> li:not(.InputfieldRepeaterNewItem)', handle: '.InputfieldRepeaterDrag', start: function(e, ui) { ui.item.find('.InputfieldHeader').addClass("ui-state-highlight"); - + // CKEditor doesn't like being sorted, do destroy when sort starts, and reload after sort ui.item.find('textarea.InputfieldCKEditorNormal.InputfieldCKEditorLoaded').each(function() { $(this).removeClass('InputfieldCKEditorLoaded'); @@ -628,12 +723,39 @@ function InputfieldRepeater($) { ui.item.find('.InputfieldTinyMCE textarea').each(function() { tinyMCE.execCommand('mceRemoveControl', false, $(this).attr('id')); }); + + if(familyFriendly && maxDepth > 0) { + // remember and hide depth children + startDepth = parseInt(ui.item.attr('data-depth')); + depthChildren = getDepthChildren(ui.item); + for(var n = 0; n < depthChildren.length; n++) { + depthChildren[n].slideUp('fast'); + } + } }, stop: function(e, ui) { if(maxDepth > 0) { sortableDepth(ui, maxDepth, true); } + + // update/move and show depth children + if(maxDepth > 0 && familyFriendly && depthChildren.length) { + var $item = ui.item; + var stopDepth = parseInt($item.attr('data-depth')); + var diffDepth = stopDepth - startDepth; + for(var n = 0; n < depthChildren.length; n++) { + var $child = depthChildren[n]; + if(diffDepth != 0) { + var itemDepth = getItemDepth($child); + setItemDepth($child, itemDepth + diffDepth, maxDepth, true); + } + $item.after($child); + $child.slideDown('fast'); + $item = $child; + } + depthChildren = []; + } ui.item.find('.InputfieldHeader').removeClass("ui-state-highlight"); $(this).children().each(function(n) { @@ -649,6 +771,7 @@ function InputfieldRepeater($) { ui.item.find('.InputfieldTinyMCE textarea').each(function() { tinyMCE.execCommand('mceAddControl', false, $(this).attr('id')); }); + } }; diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.min.js b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.min.js index 28220b80..c02cee7a 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.min.js +++ b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.min.js @@ -1 +1 @@ -function InputfieldRepeater($){var depthSize=50;var isAdminDefault=$("body").hasClass("AdminThemeDefault");var doubleClickTimer=null;var eventReloaded=function(event,source){if(typeof source!="undefined"){if(source=="InputfieldRepeaterItemEdit"||source=="InputfieldRepeaterItemAdd"){event.stopPropagation();var $r=$(this).find(".InputfieldRepeater");if($r.length)$r.each(function(){initRepeater($(this))});return}}initRepeater($(this))};var eventDeleteClick=function(e){var $this=$(this);var $header=$this.closest(".InputfieldHeader");var $item=$header.parent();if(isActionDisabled($this))return false;if($item.hasClass("InputfieldRepeaterNewItem")){var $numAddInput=$item.children(".InputfieldContent").children(".InputfieldRepeaterAddItem").children("input");$numAddInput.attr("value",parseInt($numAddInput.attr("value")-1));$item.remove()}else{var pageID=$item.attr("data-page");var $checkbox=$item.find("#delete_repeater"+pageID);if($checkbox.is(":checked")){$checkbox.prop("checked",false);$header.removeClass("ui-state-error").addClass("ui-state-default");$item.removeClass("InputfieldRepeaterDeletePending")}else{$checkbox.prop("checked",true);$header.removeClass("ui-state-default").addClass("ui-state-error");if(!$item.hasClass("InputfieldStateCollapsed")){$header.find(".toggle-icon").click()}$item.addClass("InputfieldRepeaterDeletePending");$item.closest(".Inputfield").addClass("InputfieldStateChanged")}$header.find(".InputfieldRepeaterItemControls").css("background-color",$header.css("background-color"))}checkMinMax($item.closest(".InputfieldRepeater"));e.stopPropagation()};var eventDeleteDblClick=function(){var $this=$(this);var $li=$(this).closest("li");var undelete=$li.hasClass("InputfieldRepeaterDeletePending");if(isActionDisabled($this))return false;function selectAll(){$li.parent().children("li").each(function(){var $item=$(this);var $trashLink=$item.children(".InputfieldHeader").find(".InputfieldRepeaterTrash");if($item.hasClass("InputfieldRepeaterDeletePending")){if(undelete)$trashLink.click()}else{if(!undelete)$trashLink.click()}})}if(undelete){selectAll()}else{ProcessWire.confirm(ProcessWire.config.InputfieldRepeater.labels.removeAll,selectAll)}};var eventCloneClick=function(){var $this=$(this);if(isActionDisabled($this))return false;var $item=$this.closest(".InputfieldRepeaterItem");ProcessWire.confirm(ProcessWire.config.InputfieldRepeater.labels.clone,function(){var itemID=$item.attr("data-page");var $addLink=$item.closest(".InputfieldRepeater").children(".InputfieldContent").children(".InputfieldRepeaterAddItem").find(".InputfieldRepeaterAddLink:eq(0)");$addLink.attr("data-clone",itemID).click();$("html, body").animate({scrollTop:$addLink.offset().top-100},250,"swing")});return false};var eventSettingsClick=function(e){var $this=$(this);var $item=$this.closest(".InputfieldRepeaterItem");var $settingsParent=$item.children(".InputfieldContent").children(".Inputfields");var $settings=$settingsParent.children(".InputfieldRepeaterSettings");if(!$settings.length){$settingsParent=$settingsParent.children(".InputfieldWrapper").children(".Inputfields");$settings=$settingsParent.children(".InputfieldRepeaterSettings")}if($item.hasClass("InputfieldStateCollapsed")){$this.closest(".InputfieldHeader").click()}if($settings.is(":visible")){$settings.slideUp("fast");$this.addClass("ui-priority-secondary")}else{$settings.slideDown("fast");$this.removeClass("ui-priority-secondary")}return false};var eventToggleClick=function(e){var $this=$(this);var toggleOn=$this.attr("data-on");var toggleOff=$this.attr("data-off");var $item=$this.closest(".InputfieldRepeaterItem");var $input=$item.find(".InputfieldRepeaterPublish");if(doubleClickTimer)clearTimeout(doubleClickTimer);doubleClickTimer=setTimeout(function(){if(isActionDisabled($this))return false;if($this.hasClass(toggleOn)){$this.removeClass(toggleOn).addClass(toggleOff);$item.addClass("InputfieldRepeaterUnpublished InputfieldRepeaterOff");$input.val("-1")}else{$this.removeClass(toggleOff).addClass(toggleOn);$item.removeClass("InputfieldRepeaterUnpublished InputfieldRepeaterOff").addClass("InputfieldRepeaterWasUnpublished");$input.val("1")}checkMinMax($item.closest(".InputfieldRepeater"))},250);e.stopPropagation()};var eventItemOpenReady=function(){var $item=$(this);var $loaded=$item.find(".InputfieldRepeaterLoaded");if(parseInt($loaded.val())>0)return;$item.addClass("InputfieldRepeaterItemLoading")};var eventItemOpened=function(){var $item=$(this);var $loaded=$item.find(".InputfieldRepeaterLoaded");updateState($item);if(parseInt($loaded.val())>0){updateAccordion($item);return}$loaded.val("1");var $content=$item.find(".InputfieldContent").hide();var $repeater=$item.closest(".InputfieldRepeater");var pageID=$repeater.attr("data-page");var itemID=parseInt($item.attr("data-page"));var repeaterID=$repeater.attr("id");var fieldName=repeaterID.replace("wrap_Inputfield_","").replace("_LPID"+pageID,"");var ajaxURL=ProcessWire.config.InputfieldRepeater.editorUrl+"?id="+pageID+"&field="+fieldName+"&repeater_edit="+itemID;var $spinner=$item.find(".InputfieldRepeaterDrag");var $inputfields=$loaded.closest(".Inputfields");if($repeater.hasClass("InputfieldRenderValueMode"))ajaxURL+="&inrvm=1";if($repeater.hasClass("InputfieldNoDraft"))ajaxURL+="&nodraft=1";$spinner.removeClass("fa-arrows").addClass("fa-spin fa-spinner");repeaterID=repeaterID.replace(/_repeater\d+$/,"").replace("_LPID"+pageID,"");$.get(ajaxURL,function(data){var $inputs=$(data).find("#"+repeaterID+" > "+".InputfieldContent > .Inputfields > "+".InputfieldRepeaterItem > .InputfieldContent > .Inputfields > .InputfieldWrapper > "+".Inputfields > .Inputfield");$inputfields.append($inputs);$item.removeClass("InputfieldRepeaterItemLoading");InputfieldsInit($inputfields);var $repeaters=$inputs.find(".InputfieldRepeater");if($repeaters.length){$repeaters.each(function(){initRepeater($(this))})}else{$item.find(".InputfieldRepeaterSettings").hide()}$content.slideDown("fast",function(){$spinner.removeClass("fa-spin fa-spinner").addClass("fa-arrows");updateAccordion($item)});setTimeout(function(){$inputfields.find(".Inputfield").trigger("reloaded",["InputfieldRepeaterItemEdit"])},50);runScripts(data)})};var eventItemClosed=function(){updateState($(this))};var eventAddLinkClick=function(){var $addLink=$(this);var $inputfields=$addLink.parent("p").prev("ul.Inputfields");var $inputfieldRepeater=$addLink.closest(".InputfieldRepeater");var $numAddInput=$addLink.parent().children("input");var newItemTotal=0;var useAjax=$addLink.attr("data-noajax").length==0;var cloneID=$addLink.attr("data-clone");function addRepeaterItem($addItem){var id=$addItem.attr("id")+"_";while($("#"+id).length>0)id+="_";$addItem.attr("id",id);$inputfields.append($addItem);$addItem.css("display","block");adjustItemLabel($addItem,true);$addLink.trigger("repeateradd",[$addItem])}if(typeof cloneID=="undefined"||!cloneID)cloneID=null;if(cloneID)$addLink.removeAttr("data-clone");if(!useAjax){var $newItem=$inputfields.children(".InputfieldRepeaterNewItem");newItemTotal=$newItem.length;if(newItemTotal>0){if(newItemTotal>1)$newItem=$newItem.slice(0,1);var $addItem=$newItem.clone(true);addRepeaterItem($addItem);$numAddInput.attr("value",newItemTotal);checkMinMax($inputfieldRepeater)}return false}var pageID=$inputfieldRepeater.attr("data-page");var fieldName=$inputfieldRepeater.attr("id").replace("wrap_Inputfield_","");var $spinner=$addLink.parent().find(".InputfieldRepeaterSpinner");var ajaxURL=ProcessWire.config.InputfieldRepeater.editorUrl+"?id="+pageID+"&field="+fieldName;$spinner.removeClass($spinner.attr("data-off")).addClass($spinner.attr("data-on"));if(cloneID){ajaxURL+="&repeater_clone="+cloneID}else{ajaxURL+="&repeater_add="+$addLink.attr("data-type")}var $existingItems=$inputfields.find(".InputfieldRepeaterItem:not(.InputfieldRepeaterNewItem)");if($existingItems.length){ajaxURL+="&repeater_not=";$existingItems.each(function(){ajaxURL+=$(this).attr("data-page")+","})}$.get(ajaxURL,function(data){$spinner.removeClass($spinner.attr("data-on")).addClass($spinner.attr("data-off"));var $addItem=$(data).find(".InputfieldRepeaterItemRequested");if(!$addItem.length){return}addRepeaterItem($addItem);$addItem.wrap("
");InputfieldsInit($addItem.parent());initRepeater($addItem);$addItem.unwrap();$addItem.find(".Inputfield").trigger("reloaded",["InputfieldRepeaterItemAdd"]);if(cloneID){$addItem.find(".Inputfield").trigger("cloned",["InputfieldRepeaterItemAdd"]);$addItem.find(".InputfieldTableRowID").val(0)}$addItem.find(".InputfieldRepeaterSort").val($inputfields.children().length);$("html, body").animate({scrollTop:$addItem.offset().top},500,"swing");updateState($addItem);checkMinMax($inputfieldRepeater);updateAccordion($addItem);var $nestedRepeaters=$addItem.find(".InputfieldRepeater");if($nestedRepeaters.length){$nestedRepeaters.each(function(){initRepeater($(this))})}runScripts(data)});return false};var eventOpenAllClick=function(e){e.stopPropagation();e.preventDefault();if(doubleClickTimer)clearTimeout(doubleClickTimer);if($(this).closest(".InputfieldRepeater").hasClass("InputfieldRepeaterAccordion"))return false;var $repeater=$(this).closest(".InputfieldRepeater");var $items=$repeater.children(".InputfieldContent").children(".Inputfields").children(".InputfieldRepeaterItem");if(!$items.length)return false;var $item=$items.eq(0);var label,selector;if($item.hasClass("InputfieldStateCollapsed")){label=ProcessWire.config.InputfieldRepeater.labels.openAll;selector=".InputfieldStateCollapsed"}else{label=ProcessWire.config.InputfieldRepeater.labels.collapseAll;selector=".InputfieldRepeaterItem:not(.InputfieldStateCollapsed)"}ProcessWire.confirm(label,function(){$items.filter(selector).each(function(){$(this).children(".InputfieldHeader").find(".toggle-icon").click()})});return false};function isActionDisabled($this){if($this.hasClass("pw-icon-disabled")){ProcessWire.alert(ProcessWire.config.InputfieldRepeater.labels.disabledMinMax);return true}return false}function updateAccordion($item){if(!$item.closest(".InputfieldRepeater").hasClass("InputfieldRepeaterAccordion"))return false;var itemID=$item.attr("id");var useScroll=false;var $siblings=$item.parent().children(".InputfieldRepeaterItem");var itemHasPassed=false;var hasOpen=false;$siblings.each(function(){var $sibling=$(this);if($sibling.attr("id")==itemID){itemHasPassed=true;return}if($sibling.hasClass("InputfieldStateCollapsed"))return;if(!$sibling.is(":visible"))return;if(!itemHasPassed)useScroll=true;$sibling.children(".InputfieldHeader").find(".toggle-icon").trigger("click",[{duration:0}]);hasOpen=true});if(useScroll&&hasOpen){$("html, body").animate({scrollTop:$item.offset().top-10},0)}return true}function adjustItemLabel($item,doIncrement){var $label;$label=$item.children(".InputfieldHeader").find(".InputfieldRepeaterItemLabel");if(typeof $label=="undefined")$label=$item.children("label");var labelHTML=$label.html();var _labelHTML=labelHTML;if(typeof labelHTML!="undefined"){if(doIncrement&&labelHTML.indexOf("#")>-1){var num=$item.siblings(".InputfieldRepeaterItem:visible").length+1;labelHTML=labelHTML.replace(/#[0-9]+/,"#"+num)}while(labelHTML.indexOf("}")>-1){labelHTML=labelHTML.replace(/\{/,'');labelHTML=labelHTML.replace(/}/,"")}if(labelHTML!=_labelHTML){$label.html(labelHTML)}}}function sortableDepth(ui,maxDepth,updateNow){var $depth=ui.item.find(".InputfieldRepeaterDepth");var depth=-1;var prevDepth=parseInt($depth.val());var left=ui.position.left;if(left<0){depth=prevDepth-Math.round(Math.abs(left)/depthSize)}else{depth=Math.round(left/depthSize)+prevDepth}if(depth<1){depth=0}else if(depth>maxDepth){depth=maxDepth}if(updateNow){if(depth){ui.item.css("margin-left",depth*depthSize+"px")}else{ui.item.css("margin-left",0)}$depth.val(depth);ui.item.children(".InputfieldHeader").removeClass("ui-state-error")}return depth}function initDepths($inputfieldRepeater){$inputfieldRepeater.find(".InputfieldRepeaterDepth").each(function(){var $depth=$(this);var depth=$depth.val();var $item=$depth.closest(".InputfieldRepeaterItem");var currentLeft=$item.css("margin-left");if(currentLeft=="auto")currentLeft=0;currentLeft=parseInt(currentLeft);var targetLeft=depth*depthSize;if(targetLeft!=currentLeft){$item.css("margin-left",targetLeft+"px")}});$inputfieldRepeater.children(".InputfieldContent").css("position","relative")}function initSortable($inputfieldRepeater,$inputfields){var maxDepth=parseInt($inputfieldRepeater.attr("data-depth"));var sortableOptions={items:"> li:not(.InputfieldRepeaterNewItem)",handle:".InputfieldRepeaterDrag",start:function(e,ui){ui.item.find(".InputfieldHeader").addClass("ui-state-highlight");ui.item.find("textarea.InputfieldCKEditorNormal.InputfieldCKEditorLoaded").each(function(){$(this).removeClass("InputfieldCKEditorLoaded");var editor=CKEDITOR.instances[$(this).attr("id")];editor.destroy();CKEDITOR.remove($(this).attr("id"))});ui.item.find(".InputfieldTinyMCE textarea").each(function(){tinyMCE.execCommand("mceRemoveControl",false,$(this).attr("id"))})},stop:function(e,ui){if(maxDepth>0){sortableDepth(ui,maxDepth,true)}ui.item.find(".InputfieldHeader").removeClass("ui-state-highlight");$(this).children().each(function(n){$(this).find(".InputfieldRepeaterSort").slice(0,1).attr("value",n)});ui.item.find("textarea.InputfieldCKEditorNormal:not(.InputfieldCKEditorLoaded)").each(function(){$(this).closest(".InputfieldCKEditor").trigger("reloaded",["InputfieldRepeaterSort"])});ui.item.find(".InputfieldTinyMCE textarea").each(function(){tinyMCE.execCommand("mceAddControl",false,$(this).attr("id"))})}};if(maxDepth>0){initDepths($inputfieldRepeater);sortableOptions.grid=[depthSize,1];sortableOptions.sort=function(event,ui){var depth=sortableDepth(ui,99,false);var $header=ui.item.children(".InputfieldHeader");if(depth>maxDepth){$header.addClass("ui-state-error")}else if($header.hasClass("ui-state-error")){$header.removeClass("ui-state-error")}}}else{sortableOptions.axis="y"}$(".InputfieldRepeaterDrag",$inputfields).hover(function(){$(this).parent("label").addClass("ui-state-focus")},function(){$(this).parent("label").removeClass("ui-state-focus")});$inputfields.sortable(sortableOptions)}function initHeaders($headers,$inputfieldRepeater,renderValueMode){var $clone=$("").css("display","block");var $delete=$("");var $toggle=$("");var cfg=ProcessWire.config.InputfieldRepeater;var allowClone=!$inputfieldRepeater.hasClass("InputfieldRepeaterNoAjaxAdd");var allowSettings=$inputfieldRepeater.hasClass("InputfieldRepeaterHasSettings");if(cfg){$toggle.attr("title",cfg.labels.toggle);$delete.attr("title",cfg.labels.remove);$clone.attr("title",cfg.labels.clone)}if(allowSettings){$inputfieldRepeater.find(".InputfieldRepeaterSettings").hide()}$headers.each(function(){var $t=$(this);if($t.hasClass("InputfieldRepeaterHeaderInit"))return;var icon="fa-arrows";var $item=$t.parent();if($item.hasClass("InputfieldRepeaterNewItem")){icon="fa-plus";$t.addClass("ui-priority-secondary")}$t.addClass("ui-state-default InputfieldRepeaterHeaderInit");$t.prepend("");if(!renderValueMode){var $controls=$("");var $toggleControl=$toggle.clone(true).addClass($t.parent().hasClass("InputfieldRepeaterOff")?"fa-toggle-off":"fa-toggle-on");var $deleteControl=$delete.clone(true);var $collapseControl=$t.find(".toggle-icon");$controls.prepend($collapseControl);if($t.closest(".InputfieldRepeater").hasClass("InputfieldRepeaterHasSettings")){var $settingsToggle=$("").attr("title",cfg.labels.settings);$controls.prepend($settingsToggle)}if(allowClone)$controls.prepend($clone.clone(true));$controls.prepend($toggleControl);$controls.prepend($deleteControl);$t.prepend($controls);$controls.css("background-color",$t.css("background-color"))}adjustItemLabel($item,false)})}function initRepeater($this){var $inputfields,$inputfieldRepeater,isItem;if($this.hasClass("InputfieldRepeaterItem")){$inputfields=$this;$inputfieldRepeater=$this.closest(".InputfieldRepeater");isItem=true}else{$inputfields=$this.find(".Inputfields:eq(0)");$inputfieldRepeater=$this;isItem=false}if($inputfields.hasClass("InputfieldRepeaterInit"))return;var renderValueMode=$inputfields.closest(".InputfieldRenderValueMode").length>0;$inputfields.addClass("InputfieldRepeaterInit");if(isItem){initHeaders($this.children(".InputfieldHeader"),$inputfieldRepeater,renderValueMode)}else{initHeaders($(".InputfieldRepeaterItem > .InputfieldHeader",$this),$inputfieldRepeater,renderValueMode)}if(renderValueMode){initDepths($inputfieldRepeater);return}$(".InputfieldRepeaterTrash",$this).hover(function(){var $label=$(this).closest("label");if(!$label.parents().hasClass("InputfieldRepeaterDeletePending"))$label.addClass("ui-state-error");$label.find(".InputfieldRepeaterItemControls").css("background-color",$label.css("background-color"))},function(){var $label=$(this).closest("label");if(!$label.parent().hasClass("InputfieldRepeaterDeletePending"))$label.removeClass("ui-state-error");$label.find(".InputfieldRepeaterItemControls").css("background-color",$label.css("background-color"))});if(isItem)$inputfields=$inputfieldRepeater.find(".Inputfields:eq(0)");initSortable($inputfieldRepeater,$inputfields);$(".InputfieldRepeaterAddLink:not(.InputfieldRepeaterAddLinkInit)",$inputfieldRepeater).addClass("InputfieldRepeaterAddLinkInit").click(eventAddLinkClick);if($inputfieldRepeater.hasClass("InputfieldRepeaterMax")){checkMinMax($inputfieldRepeater)}}function checkMinMax($inputfieldRepeater){if(!$inputfieldRepeater.hasClass("InputfieldRepeaterMax")&&!$inputfieldRepeater.hasClass("InputfieldRepeaterMin"))return;var max=parseInt($inputfieldRepeater.attr("data-max"));var min=parseInt($inputfieldRepeater.attr("data-min"));if(max<=0&&min<=0)return;var $content=$inputfieldRepeater.children(".InputfieldContent");var num=$content.children(".Inputfields").children("li:not(.InputfieldRepeaterDeletePending):not(.InputfieldRepeaterOff):visible").length;var $addItem=$content.children(".InputfieldRepeaterAddItem");var cloneChange="";var trashChange="";if(max>0){if(num>=max){$addItem.hide();cloneChange="hide"}else if(!$addItem.is(":visible")){$addItem.show();cloneChange="show"}}if(min>0){if(num<=min){trashChange="hide";$content.addClass("InputfieldRepeaterTrashHidden")}else if($content.hasClass("InputfieldRepeaterTrashHidden")){$content.removeClass("InputfieldRepeaterTrashHidden");trashChange="show"}}if(cloneChange.length||trashChange.length){var $items=$content.children(".Inputfields").children(".InputfieldRepeaterItem");if(cloneChange.length){$items.each(function(){var $clone=$(this).children(".InputfieldHeader").find(".InputfieldRepeaterClone");if(cloneChange==="show"){$clone.removeClass("pw-icon-disabled")}else{$clone.addClass("pw-icon-disabled")}})}if(trashChange.length){$items.each(function(){var $header=$(this).children(".InputfieldHeader");var $trash=$header.find(".InputfieldRepeaterTrash");var $toggle=$header.find(".InputfieldRepeaterToggle.fa-toggle-on");if(trashChange==="show"){$trash.removeClass("pw-icon-disabled");$toggle.removeClass("pw-icon-disabled")}else{$trash.addClass("pw-icon-disabled");$toggle.addClass("pw-icon-disabled")}});if(trashChange=="hide"){$content.children(".Inputfields").children("li.InputfieldRepeaterDeletePending").each(function(){var $trash=$(this).children(".InputfieldHeader").find(".InputfieldRepeaterTrash");$trash.removeClass("pw-icon-disabled")})}}}}function runScripts(data){if(data.indexOf("<\/script>")==-1)return;var d=document.createElement("div");d.innerHTML=data;var scripts=d.querySelectorAll(".Inputfield script");$(scripts).each(function(){$.globalEval(this.text||this.textContent||this.innerHTML||"")})}function updateState($item){if($item.closest(".InputfieldRepeaterRememberOpen").length<1)return;var val="";$(".InputfieldRepeaterItem:not(.InputfieldStateCollapsed)").each(function(){var id=parseInt($(this).attr("data-page"));if(id>0){val+=id+"|"}});$.cookie("repeaters_open",val)}function init(){$(".InputfieldRepeater").each(function(){initRepeater($(this))});$(document).on("reloaded",".InputfieldRepeater",eventReloaded).on("click",".InputfieldRepeaterTrash",eventDeleteClick).on("dblclick",".InputfieldRepeaterTrash",eventDeleteDblClick).on("click",".InputfieldRepeaterClone",eventCloneClick).on("click",".InputfieldRepeaterSettingsToggle",eventSettingsClick).on("dblclick",".InputfieldRepeaterToggle",eventOpenAllClick).on("click",".InputfieldRepeaterToggle",eventToggleClick).on("opened",".InputfieldRepeaterItem",eventItemOpened).on("closed",".InputfieldRepeaterItem",eventItemClosed).on("openReady",".InputfieldRepeaterItem",eventItemOpenReady)}init()}jQuery(document).ready(function($){InputfieldRepeater($)}); \ No newline at end of file +function InputfieldRepeater($){var depthSize=50;var isAdminDefault=$("body").hasClass("AdminThemeDefault");var doubleClickTimer=null;var eventReloaded=function(event,source){if(typeof source!="undefined"){if(source=="InputfieldRepeaterItemEdit"||source=="InputfieldRepeaterItemAdd"){event.stopPropagation();var $r=$(this).find(".InputfieldRepeater");if($r.length)$r.each(function(){initRepeater($(this))});return}}initRepeater($(this))};var eventDeleteClick=function(e){var $this=$(this);var $header=$this.closest(".InputfieldHeader");var $item=$header.parent();if(isActionDisabled($this))return false;if($item.hasClass("InputfieldRepeaterNewItem")){var $numAddInput=$item.children(".InputfieldContent").children(".InputfieldRepeaterAddItem").children("input");$numAddInput.attr("value",parseInt($numAddInput.attr("value")-1));$item.remove()}else{var pageID=$item.attr("data-page");var $checkbox=$item.find("#delete_repeater"+pageID);if($checkbox.is(":checked")){$checkbox.prop("checked",false);$header.removeClass("ui-state-error").addClass("ui-state-default");$item.removeClass("InputfieldRepeaterDeletePending")}else{$checkbox.prop("checked",true);$header.removeClass("ui-state-default").addClass("ui-state-error");if(!$item.hasClass("InputfieldStateCollapsed")){$header.find(".toggle-icon").click()}$item.addClass("InputfieldRepeaterDeletePending");$item.closest(".Inputfield").addClass("InputfieldStateChanged")}$header.find(".InputfieldRepeaterItemControls").css("background-color",$header.css("background-color"))}checkMinMax($item.closest(".InputfieldRepeater"));e.stopPropagation()};var eventDeleteDblClick=function(){var $this=$(this);var $li=$(this).closest("li");var undelete=$li.hasClass("InputfieldRepeaterDeletePending");if(isActionDisabled($this))return false;function selectAll(){$li.parent().children("li").each(function(){var $item=$(this);var $trashLink=$item.children(".InputfieldHeader").find(".InputfieldRepeaterTrash");if($item.hasClass("InputfieldRepeaterDeletePending")){if(undelete)$trashLink.click()}else{if(!undelete)$trashLink.click()}})}if(undelete){selectAll()}else{ProcessWire.confirm(ProcessWire.config.InputfieldRepeater.labels.removeAll,selectAll)}};var eventCloneClick=function(){var $this=$(this);if(isActionDisabled($this))return false;var $item=$this.closest(".InputfieldRepeaterItem");ProcessWire.confirm(ProcessWire.config.InputfieldRepeater.labels.clone,function(){var itemID=$item.attr("data-page");var $addLink=$item.closest(".InputfieldRepeater").children(".InputfieldContent").children(".InputfieldRepeaterAddItem").find(".InputfieldRepeaterAddLink:eq(0)");$addLink.attr("data-clone",itemID).click();$("html, body").animate({scrollTop:$addLink.offset().top-100},250,"swing")});return false};var eventSettingsClick=function(e){var $this=$(this);var $item=$this.closest(".InputfieldRepeaterItem");var $settingsParent=$item.children(".InputfieldContent").children(".Inputfields");var $settings=$settingsParent.children(".InputfieldRepeaterSettings");if(!$settings.length){$settingsParent=$settingsParent.children(".InputfieldWrapper").children(".Inputfields");$settings=$settingsParent.children(".InputfieldRepeaterSettings")}if($item.hasClass("InputfieldStateCollapsed")){$this.closest(".InputfieldHeader").click()}if($settings.is(":visible")){$settings.slideUp("fast");$this.addClass("ui-priority-secondary")}else{$settings.slideDown("fast");$this.removeClass("ui-priority-secondary")}return false};var eventToggleClick=function(e){var $this=$(this);var toggleOn=$this.attr("data-on");var toggleOff=$this.attr("data-off");var $item=$this.closest(".InputfieldRepeaterItem");var $input=$item.find(".InputfieldRepeaterPublish");if(doubleClickTimer)clearTimeout(doubleClickTimer);doubleClickTimer=setTimeout(function(){if(isActionDisabled($this))return false;if($this.hasClass(toggleOn)){$this.removeClass(toggleOn).addClass(toggleOff);$item.addClass("InputfieldRepeaterUnpublished InputfieldRepeaterOff");$input.val("-1")}else{$this.removeClass(toggleOff).addClass(toggleOn);$item.removeClass("InputfieldRepeaterUnpublished InputfieldRepeaterOff").addClass("InputfieldRepeaterWasUnpublished");$input.val("1")}checkMinMax($item.closest(".InputfieldRepeater"))},250);e.stopPropagation()};var eventItemOpenReady=function(){var $item=$(this);var $loaded=$item.find(".InputfieldRepeaterLoaded");if(parseInt($loaded.val())>0)return;$item.addClass("InputfieldRepeaterItemLoading")};var eventItemOpened=function(){var $item=$(this);var $loaded=$item.find(".InputfieldRepeaterLoaded");updateState($item);if(parseInt($loaded.val())>0){updateAccordion($item);return}$loaded.val("1");var $content=$item.find(".InputfieldContent").hide();var $repeater=$item.closest(".InputfieldRepeater");var pageID=$repeater.attr("data-page");var itemID=parseInt($item.attr("data-page"));var repeaterID=$repeater.attr("id");var fieldName=repeaterID.replace("wrap_Inputfield_","").replace("_LPID"+pageID,"");var ajaxURL=ProcessWire.config.InputfieldRepeater.editorUrl+"?id="+pageID+"&field="+fieldName+"&repeater_edit="+itemID;var $spinner=$item.find(".InputfieldRepeaterDrag");var $inputfields=$loaded.closest(".Inputfields");if($repeater.hasClass("InputfieldRenderValueMode"))ajaxURL+="&inrvm=1";if($repeater.hasClass("InputfieldNoDraft"))ajaxURL+="&nodraft=1";$spinner.removeClass("fa-arrows").addClass("fa-spin fa-spinner");repeaterID=repeaterID.replace(/_repeater\d+$/,"").replace("_LPID"+pageID,"");$.get(ajaxURL,function(data){var $inputs=$(data).find("#"+repeaterID+" > "+".InputfieldContent > .Inputfields > "+".InputfieldRepeaterItem > .InputfieldContent > .Inputfields > .InputfieldWrapper > "+".Inputfields > .Inputfield");$inputfields.append($inputs);$item.removeClass("InputfieldRepeaterItemLoading");InputfieldsInit($inputfields);var $repeaters=$inputs.find(".InputfieldRepeater");if($repeaters.length){$repeaters.each(function(){initRepeater($(this))})}else{$item.find(".InputfieldRepeaterSettings").hide()}$content.slideDown("fast",function(){$spinner.removeClass("fa-spin fa-spinner").addClass("fa-arrows");updateAccordion($item)});setTimeout(function(){$inputfields.find(".Inputfield").trigger("reloaded",["InputfieldRepeaterItemEdit"])},50);runScripts(data)})};var eventItemClosed=function(){updateState($(this))};var eventAddLinkClick=function(){var $addLink=$(this);var $inputfields=$addLink.parent("p").prev("ul.Inputfields");var $inputfieldRepeater=$addLink.closest(".InputfieldRepeater");var $numAddInput=$addLink.parent().children("input");var newItemTotal=0;var useAjax=$addLink.attr("data-noajax").length==0;var cloneID=$addLink.attr("data-clone");function addRepeaterItem($addItem){var id=$addItem.attr("id")+"_";while($("#"+id).length>0)id+="_";$addItem.attr("id",id);$inputfields.append($addItem);$addItem.css("display","block");adjustItemLabel($addItem,true);$addLink.trigger("repeateradd",[$addItem])}if(typeof cloneID=="undefined"||!cloneID)cloneID=null;if(cloneID)$addLink.removeAttr("data-clone");if(!useAjax){var $newItem=$inputfields.children(".InputfieldRepeaterNewItem");newItemTotal=$newItem.length;if(newItemTotal>0){if(newItemTotal>1)$newItem=$newItem.slice(0,1);var $addItem=$newItem.clone(true);addRepeaterItem($addItem);$numAddInput.attr("value",newItemTotal);checkMinMax($inputfieldRepeater)}return false}var pageID=$inputfieldRepeater.attr("data-page");var fieldName=$inputfieldRepeater.attr("id").replace("wrap_Inputfield_","");var $spinner=$addLink.parent().find(".InputfieldRepeaterSpinner");var ajaxURL=ProcessWire.config.InputfieldRepeater.editorUrl+"?id="+pageID+"&field="+fieldName;$spinner.removeClass($spinner.attr("data-off")).addClass($spinner.attr("data-on"));if(cloneID){ajaxURL+="&repeater_clone="+cloneID}else{ajaxURL+="&repeater_add="+$addLink.attr("data-type")}var $existingItems=$inputfields.find(".InputfieldRepeaterItem:not(.InputfieldRepeaterNewItem)");if($existingItems.length){ajaxURL+="&repeater_not=";$existingItems.each(function(){ajaxURL+=$(this).attr("data-page")+","})}$.get(ajaxURL,function(data){$spinner.removeClass($spinner.attr("data-on")).addClass($spinner.attr("data-off"));var $addItem=$(data).find(".InputfieldRepeaterItemRequested");if(!$addItem.length){return}addRepeaterItem($addItem);$addItem.wrap("
");InputfieldsInit($addItem.parent());initRepeater($addItem);$addItem.unwrap();$addItem.find(".Inputfield").trigger("reloaded",["InputfieldRepeaterItemAdd"]);if(cloneID){$addItem.find(".Inputfield").trigger("cloned",["InputfieldRepeaterItemAdd"]);$addItem.find(".InputfieldTableRowID").val(0)}$addItem.find(".InputfieldRepeaterSort").val($inputfields.children().length);$("html, body").animate({scrollTop:$addItem.offset().top},500,"swing");updateState($addItem);checkMinMax($inputfieldRepeater);updateAccordion($addItem);var $nestedRepeaters=$addItem.find(".InputfieldRepeater");if($nestedRepeaters.length){$nestedRepeaters.each(function(){initRepeater($(this))})}runScripts(data)});return false};var eventOpenAllClick=function(e){e.stopPropagation();e.preventDefault();if(doubleClickTimer)clearTimeout(doubleClickTimer);if($(this).closest(".InputfieldRepeater").hasClass("InputfieldRepeaterAccordion"))return false;var $repeater=$(this).closest(".InputfieldRepeater");var $items=$repeater.children(".InputfieldContent").children(".Inputfields").children(".InputfieldRepeaterItem");if(!$items.length)return false;var $item=$items.eq(0);var label,selector;if($item.hasClass("InputfieldStateCollapsed")){label=ProcessWire.config.InputfieldRepeater.labels.openAll;selector=".InputfieldStateCollapsed"}else{label=ProcessWire.config.InputfieldRepeater.labels.collapseAll;selector=".InputfieldRepeaterItem:not(.InputfieldStateCollapsed)"}ProcessWire.confirm(label,function(){$items.filter(selector).each(function(){$(this).children(".InputfieldHeader").find(".toggle-icon").click()})});return false};function isActionDisabled($this){if($this.hasClass("pw-icon-disabled")){ProcessWire.alert(ProcessWire.config.InputfieldRepeater.labels.disabledMinMax);return true}return false}function updateAccordion($item){if(!$item.closest(".InputfieldRepeater").hasClass("InputfieldRepeaterAccordion"))return false;var itemID=$item.attr("id");var useScroll=false;var $siblings=$item.parent().children(".InputfieldRepeaterItem");var itemHasPassed=false;var hasOpen=false;$siblings.each(function(){var $sibling=$(this);if($sibling.attr("id")==itemID){itemHasPassed=true;return}if($sibling.hasClass("InputfieldStateCollapsed"))return;if(!$sibling.is(":visible"))return;if(!itemHasPassed)useScroll=true;$sibling.children(".InputfieldHeader").find(".toggle-icon").trigger("click",[{duration:0}]);hasOpen=true});if(useScroll&&hasOpen){$("html, body").animate({scrollTop:$item.offset().top-10},0)}return true}function adjustItemLabel($item,doIncrement){var $label;$label=$item.children(".InputfieldHeader").find(".InputfieldRepeaterItemLabel");if(typeof $label=="undefined")$label=$item.children("label");var labelHTML=$label.html();var _labelHTML=labelHTML;if(typeof labelHTML!="undefined"){if(doIncrement&&labelHTML.indexOf("#")>-1){var num=$item.siblings(".InputfieldRepeaterItem:visible").length+1;labelHTML=labelHTML.replace(/#[0-9]+/,"#"+num)}while(labelHTML.indexOf("}")>-1){labelHTML=labelHTML.replace(/\{/,'');labelHTML=labelHTML.replace(/}/,"")}if(labelHTML!=_labelHTML){$label.html(labelHTML)}}}function sortableDepth(ui,maxDepth,updateNow){var $wrap=ui.item.children(".InputfieldContent").children(".Inputfields").children(".InputfieldRepeaterItemDepth");var $depth=$wrap.find("input");var depth=-1;var prevDepth=parseInt($depth.val());var left=ui.position.left;if(left<0){depth=prevDepth-Math.round(Math.abs(left)/depthSize)}else{depth=Math.round(left/depthSize)+prevDepth}if(updateNow){depth=setItemDepth(ui.item,depth,maxDepth);ui.item.children(".InputfieldHeader").removeClass("ui-state-error")}return depth}function setItemDepth($item,depth,maxDepth,noValidate){noValidate=typeof noValidate==="undefined"?false:noValidate;if(depth<1)depth=0;if(typeof maxDepth!=="undefined"&&depth>maxDepth)depth=maxDepth;if(!$item.hasClass("InputfieldRepeaterItem"))$item=$item.closest(".InputfieldRepeaterItem");if(!$item.length)return-1;var $depthInput=$item.children(".InputfieldContent").children(".Inputfields").children(".InputfieldRepeaterItemDepth").find("input");if(!$depthInput.length){console.log("Cannot find depth input for "+$item.attr("id"))}if(!noValidate&&$item.closest(".InputfieldRepeater").hasClass("InputfieldRepeaterFamilyFriendly")){var $prevItem=$item.prev(".InputfieldRepeaterItem:not(.InputfieldRepeaterNewItem)");if($prevItem.length){var prevItemDepth=parseInt($prevItem.attr("data-depth"));if(depth-prevItemDepth>1)depth=prevItemDepth+1}else{depth=0}}$depthInput.val(depth);$item.attr("data-depth",depth);if(depth>0){$item.css("margin-left",depth*depthSize+"px")}else{$item.css("margin-left",0)}return depth}function getItemDepth($item){if(!$item.hasClass("InputfieldRepeaterItem"))$item=$item.closest(".InputfieldRepeaterItem");if(!$item.length)return-1;return parseInt($item.attr("data-depth"))}function getDepthChildren($item){var children=[];var n=0;var startDepth=parseInt($item.attr("data-depth"));var pageId=$item.attr("data-page");var pageIdClass="Inputfield_repeater_item_"+pageId;while($item.hasClass(pageIdClass)){var $nextItem=$item.next(".InputfieldRepeaterItem:not(.InputfieldRepeaterNewItem)");if(!$nextItem.length||!$nextItem.hasClass(pageIdClass))break;$item=$nextItem}do{var $child=$item.next(".InputfieldRepeaterItem:not(.InputfieldRepeaterNewItem)");if(!$child.length)break;var childDepth=parseInt($child.attr("data-depth"));if(!childDepth||childDepth<=startDepth)break;$item=$child;children[n]=$child;n++}while(true);return children}function initDepths($inputfieldRepeater){$inputfieldRepeater.find(".InputfieldRepeaterItemDepth").each(function(){var $wrap=$(this);var $depth=$wrap.find("input");var depth=$depth.val();var $item=$depth.closest(".InputfieldRepeaterItem");var currentLeft=$item.css("margin-left");if(currentLeft=="auto")currentLeft=0;currentLeft=parseInt(currentLeft);var targetLeft=depth*depthSize;if(targetLeft!=currentLeft){$item.css("margin-left",targetLeft+"px")}});$inputfieldRepeater.children(".InputfieldContent").css("position","relative")}function initSortable($inputfieldRepeater,$inputfields){var maxDepth=parseInt($inputfieldRepeater.attr("data-depth"));var depthChildren=[];var startDepth=0;var familyFriendly=$inputfieldRepeater.hasClass("InputfieldRepeaterFamilyFriendly");var sortableOptions={items:"> li:not(.InputfieldRepeaterNewItem)",handle:".InputfieldRepeaterDrag",start:function(e,ui){ui.item.find(".InputfieldHeader").addClass("ui-state-highlight");ui.item.find("textarea.InputfieldCKEditorNormal.InputfieldCKEditorLoaded").each(function(){$(this).removeClass("InputfieldCKEditorLoaded");var editor=CKEDITOR.instances[$(this).attr("id")];editor.destroy();CKEDITOR.remove($(this).attr("id"))});ui.item.find(".InputfieldTinyMCE textarea").each(function(){tinyMCE.execCommand("mceRemoveControl",false,$(this).attr("id"))});if(familyFriendly&&maxDepth>0){startDepth=parseInt(ui.item.attr("data-depth"));depthChildren=getDepthChildren(ui.item);for(var n=0;n0){sortableDepth(ui,maxDepth,true)}if(maxDepth>0&&familyFriendly&&depthChildren.length){var $item=ui.item;var stopDepth=parseInt($item.attr("data-depth"));var diffDepth=stopDepth-startDepth;for(var n=0;n0){initDepths($inputfieldRepeater);sortableOptions.grid=[depthSize,1];sortableOptions.sort=function(event,ui){var depth=sortableDepth(ui,99,false);var $header=ui.item.children(".InputfieldHeader");if(depth>maxDepth){$header.addClass("ui-state-error")}else if($header.hasClass("ui-state-error")){$header.removeClass("ui-state-error")}}}else{sortableOptions.axis="y"}$(".InputfieldRepeaterDrag",$inputfields).hover(function(){$(this).parent("label").addClass("ui-state-focus")},function(){$(this).parent("label").removeClass("ui-state-focus")});$inputfields.sortable(sortableOptions)}function initHeaders($headers,$inputfieldRepeater,renderValueMode){var $clone=$("").css("display","block");var $delete=$("");var $toggle=$("");var cfg=ProcessWire.config.InputfieldRepeater;var allowClone=!$inputfieldRepeater.hasClass("InputfieldRepeaterNoAjaxAdd");var allowSettings=$inputfieldRepeater.hasClass("InputfieldRepeaterHasSettings");if(cfg){$toggle.attr("title",cfg.labels.toggle);$delete.attr("title",cfg.labels.remove);$clone.attr("title",cfg.labels.clone)}if(allowSettings){$inputfieldRepeater.find(".InputfieldRepeaterSettings").hide()}$headers.each(function(){var $t=$(this);if($t.hasClass("InputfieldRepeaterHeaderInit"))return;var icon="fa-arrows";var $item=$t.parent();if($item.hasClass("InputfieldRepeaterNewItem")){icon="fa-plus";$t.addClass("ui-priority-secondary")}$t.addClass("ui-state-default InputfieldRepeaterHeaderInit");$t.prepend("");if(!renderValueMode){var $controls=$("");var $toggleControl=$toggle.clone(true).addClass($t.parent().hasClass("InputfieldRepeaterOff")?"fa-toggle-off":"fa-toggle-on");var $deleteControl=$delete.clone(true);var $collapseControl=$t.find(".toggle-icon");$controls.prepend($collapseControl);if($t.closest(".InputfieldRepeater").hasClass("InputfieldRepeaterHasSettings")){var $settingsToggle=$("").attr("title",cfg.labels.settings);$controls.prepend($settingsToggle)}if(allowClone)$controls.prepend($clone.clone(true));$controls.prepend($toggleControl);$controls.prepend($deleteControl);$t.prepend($controls);$controls.css("background-color",$t.css("background-color"))}adjustItemLabel($item,false)})}function initRepeater($this){var $inputfields,$inputfieldRepeater,isItem;if($this.hasClass("InputfieldRepeaterItem")){$inputfields=$this;$inputfieldRepeater=$this.closest(".InputfieldRepeater");isItem=true}else{$inputfields=$this.find(".Inputfields:eq(0)");$inputfieldRepeater=$this;isItem=false}if($inputfields.hasClass("InputfieldRepeaterInit"))return;var renderValueMode=$inputfields.closest(".InputfieldRenderValueMode").length>0;$inputfields.addClass("InputfieldRepeaterInit");if(isItem){initHeaders($this.children(".InputfieldHeader"),$inputfieldRepeater,renderValueMode)}else{initHeaders($(".InputfieldRepeaterItem > .InputfieldHeader",$this),$inputfieldRepeater,renderValueMode)}if(renderValueMode){initDepths($inputfieldRepeater);return}$(".InputfieldRepeaterTrash",$this).hover(function(){var $label=$(this).closest("label");if(!$label.parents().hasClass("InputfieldRepeaterDeletePending"))$label.addClass("ui-state-error");$label.find(".InputfieldRepeaterItemControls").css("background-color",$label.css("background-color"))},function(){var $label=$(this).closest("label");if(!$label.parent().hasClass("InputfieldRepeaterDeletePending"))$label.removeClass("ui-state-error");$label.find(".InputfieldRepeaterItemControls").css("background-color",$label.css("background-color"))});if(isItem)$inputfields=$inputfieldRepeater.find(".Inputfields:eq(0)");initSortable($inputfieldRepeater,$inputfields);$(".InputfieldRepeaterAddLink:not(.InputfieldRepeaterAddLinkInit)",$inputfieldRepeater).addClass("InputfieldRepeaterAddLinkInit").click(eventAddLinkClick);if($inputfieldRepeater.hasClass("InputfieldRepeaterMax")){checkMinMax($inputfieldRepeater)}}function checkMinMax($inputfieldRepeater){if(!$inputfieldRepeater.hasClass("InputfieldRepeaterMax")&&!$inputfieldRepeater.hasClass("InputfieldRepeaterMin"))return;var max=parseInt($inputfieldRepeater.attr("data-max"));var min=parseInt($inputfieldRepeater.attr("data-min"));if(max<=0&&min<=0)return;var $content=$inputfieldRepeater.children(".InputfieldContent");var num=$content.children(".Inputfields").children("li:not(.InputfieldRepeaterDeletePending):not(.InputfieldRepeaterOff):visible").length;var $addItem=$content.children(".InputfieldRepeaterAddItem");var cloneChange="";var trashChange="";if(max>0){if(num>=max){$addItem.hide();cloneChange="hide"}else if(!$addItem.is(":visible")){$addItem.show();cloneChange="show"}}if(min>0){if(num<=min){trashChange="hide";$content.addClass("InputfieldRepeaterTrashHidden")}else if($content.hasClass("InputfieldRepeaterTrashHidden")){$content.removeClass("InputfieldRepeaterTrashHidden");trashChange="show"}}if(cloneChange.length||trashChange.length){var $items=$content.children(".Inputfields").children(".InputfieldRepeaterItem");if(cloneChange.length){$items.each(function(){var $clone=$(this).children(".InputfieldHeader").find(".InputfieldRepeaterClone");if(cloneChange==="show"){$clone.removeClass("pw-icon-disabled")}else{$clone.addClass("pw-icon-disabled")}})}if(trashChange.length){$items.each(function(){var $header=$(this).children(".InputfieldHeader");var $trash=$header.find(".InputfieldRepeaterTrash");var $toggle=$header.find(".InputfieldRepeaterToggle.fa-toggle-on");if(trashChange==="show"){$trash.removeClass("pw-icon-disabled");$toggle.removeClass("pw-icon-disabled")}else{$trash.addClass("pw-icon-disabled");$toggle.addClass("pw-icon-disabled")}});if(trashChange=="hide"){$content.children(".Inputfields").children("li.InputfieldRepeaterDeletePending").each(function(){var $trash=$(this).children(".InputfieldHeader").find(".InputfieldRepeaterTrash");$trash.removeClass("pw-icon-disabled")})}}}}function runScripts(data){if(data.indexOf("<\/script>")==-1)return;var d=document.createElement("div");d.innerHTML=data;var scripts=d.querySelectorAll(".Inputfield script");$(scripts).each(function(){$.globalEval(this.text||this.textContent||this.innerHTML||"")})}function updateState($item){if($item.closest(".InputfieldRepeaterRememberOpen").length<1)return;var val="";$(".InputfieldRepeaterItem:not(.InputfieldStateCollapsed)").each(function(){var id=parseInt($(this).attr("data-page"));if(id>0){val+=id+"|"}});$.cookie("repeaters_open",val)}function init(){$(".InputfieldRepeater").each(function(){initRepeater($(this))});$(document).on("reloaded",".InputfieldRepeater",eventReloaded).on("click",".InputfieldRepeaterTrash",eventDeleteClick).on("dblclick",".InputfieldRepeaterTrash",eventDeleteDblClick).on("click",".InputfieldRepeaterClone",eventCloneClick).on("click",".InputfieldRepeaterSettingsToggle",eventSettingsClick).on("dblclick",".InputfieldRepeaterToggle",eventOpenAllClick).on("click",".InputfieldRepeaterToggle",eventToggleClick).on("opened",".InputfieldRepeaterItem",eventItemOpened).on("closed",".InputfieldRepeaterItem",eventItemClosed).on("openReady",".InputfieldRepeaterItem",eventItemOpenReady)}init()}jQuery(document).ready(function($){InputfieldRepeater($)}); \ No newline at end of file diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module index f8c1d0f3..6feb320a 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module @@ -5,12 +5,13 @@ * * Maintains a collection of fields that are repeated for any number of times. * - * ProcessWire 3.x, Copyright 2016 by Ryan Cramer + * ProcessWire 3.x, Copyright 2021 by Ryan Cramer * https://processwire.com * * @property int $repeaterMaxItems * @property int $repeaterMinItems * @property int $repeaterDepth + * @property bool|int $familyFriendly * @property bool $accordionMode * @property bool $singleMode * @@ -25,7 +26,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { return array( 'title' => __('Repeater', __FILE__), // Module Title 'summary' => __('Repeats fields from another template. Provides the input for FieldtypeRepeater.', __FILE__), // Module Summary - 'version' => 106, + 'version' => 107, 'requires' => 'FieldtypeRepeater', ); } @@ -100,6 +101,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { $this->set('repeaterMaxItems', 0); $this->set('repeaterMinItems', 0); $this->set('repeaterDepth', 0); + $this->set('familyFriendly', 0); $this->set('accordionMode', false); $this->set('singleMode', false); } @@ -290,7 +292,11 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { // get field label in user's language if available $label = $this->field->getLabel(); if(!$label) $label = ucfirst($this->field->name); - + + if((int) $this->repeaterDepth > 1 && (int) $this->familyFriendly) { + $this->addClass('InputfieldRepeaterFamilyFriendly', 'wrapClass'); + } + // remember which repeater items are open (as stored in cookie), when enabled $openIDs = array(); if((int) $this->field->get('rememberOpen')) { @@ -371,7 +377,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { if($this->repeaterDepth > 0) { $depth = $this->wire('modules')->get('InputfieldHidden'); $depth->attr('id+name', "depth_repeater{$page->id}"); - $depth->class = 'InputfieldRepeaterDepth'; + $depth->addClass('InputfieldRepeaterItemDepth', 'wrapClass'); $depth->label = $this->_('Depth'); $depthValue = $page->getDepth(); $depth->attr('value', $depthValue); @@ -405,6 +411,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { $wrap->wrapAttr('data-type', $itemType); $wrap->wrapAttr('data-typeName', $itemTypeName); $wrap->wrapAttr('data-fnsx', "_repeater$page->id"); // fnsx=field name suffix + $wrap->wrapAttr('data-depth', $depth ? $depth->val() : '0'); //$wrap->wrapAttr('data-editorPage', $this->page->id); //$wrap->wrapAttr('data-parentPage', $page->parent->id); $wrap->wrapAttr('data-editUrl', $page->editUrl()); // if needed by any Inputfields within like InputfieldFile/InputfieldImage diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/config.php b/wire/modules/Fieldtype/FieldtypeRepeater/config.php index b97ea896..6521daf2 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/config.php +++ b/wire/modules/Fieldtype/FieldtypeRepeater/config.php @@ -252,6 +252,21 @@ class FieldtypeRepeaterConfigHelper extends Wire { $f->notes = $this->_('Depths are zero-based, meaning a depth of 3 allows depths 0, 1, 2 and 3.'); $f->notes .= ' ' . $this->_('Depth can be accessed from a repeater page item via `$item->depth`.'); $f->icon = 'indent'; + $f->columnWidth = 50; + $inputfields->add($f); + + /** @var InputfieldToggle $f */ + $f = $this->wire()->modules->get('InputfieldToggle'); + $f->attr('name', 'familyFriendly'); + $f->label = $this->_('Use family-friendly item depth?'); + $f->description = + $this->_('This setting makes the admin page editor treat item depth as a parent/child relationship.') . ' ' . + $this->_('This means that moving/sorting an item includes child items too.') . ' ' . + $this->_('It also prevents a child item from being dragged to have a depth that exceeds its parent by more than 1.'); + $f->val((int) $field->get('familyFriendly')); + $f->icon = 'indent'; + $f->showIf = 'repeaterDepth>1'; + $f->columnWidth = 50; $inputfields->add($f); // -------------------------------------------------