From 903e6b3527896c161f8b1373f71d104750451438 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Thu, 20 Jun 2019 10:47:01 -0400 Subject: [PATCH] Improvements to InputfieldSelector so that it is no longer necessary to have separate "Field" and "Field..." options, as now there is just "Field..." with default behavior being the same as the previous "Field" but with the ability to select subfields. This makes selection quite a bit simpler and less verbose and and saves a step. --- .../InputfieldSelector/InputfieldSelector.js | 59 ++- .../InputfieldSelector.min.js | 2 +- .../InputfieldSelector.module | 374 +++++++++++------- 3 files changed, 278 insertions(+), 157 deletions(-) diff --git a/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.js b/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.js index db5145ff..23301428 100644 --- a/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.js +++ b/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.js @@ -244,6 +244,7 @@ var InputfieldSelector = { //console.log('changeField'); var $select = $(this); + var $option = $select.children('option:selected'); var field = $select.val(); if(!field || field.length == 0) return; if(field == 'toggle-names-labels') return InputfieldSelector.changeFieldToggle($select); @@ -255,9 +256,9 @@ var InputfieldSelector = { var $hiddenInput = $select.parents('.InputfieldSelector').find('.selector-value'); // .selector-value intentional! var name = $hiddenInput.attr('name'); var type = $select.attr('data-type'); - + if(field.match(/\.$/)) { - action = 'subfield'; + action = 'subfield-opval'; if(field.indexOf('@') > -1) field = field.substring(1, field.length-1); else field = field.substring(0, field.length-1); $row.addClass('has-subfield'); @@ -269,32 +270,51 @@ var InputfieldSelector = { $row.children('.subfield').html(''); $row.removeClass('has-subfield'); } + + // subfieldopval action var url = './?InputfieldSelector=' + action + '&field=' + field + '&type=' + type + '&name=' + name; var $spinner = $(InputfieldSelector.spinner); $row.append($spinner); - + $.get(url, function(data) { $spinner.remove(); - var $data = $(data); - $data.hide(); - - if(action == 'opval') { - var $opval = $row.children('.opval'); + + function actionOpval($data) { + $data.hide(); + var $opval = $row.children('.opval'); $opval.html('').append($data); - $opval.children(':not(.input-or)').fadeIn('fast'); + $opval.children(':not(.input-or)').fadeIn('fast'); //$data.fadeIn('fast'); InputfieldSelector.changeAny($select); - var $ac = $opval.find(".input-value-autocomplete"); + var $ac = $opval.find(".input-value-autocomplete"); if($ac.length > 0) InputfieldSelector.setupAutocomplete($ac, field, name); - } else { + } + + function actionSubfield($data) { + $data.hide(); var $subfield = $row.children('.subfield'); - $subfield.html('').append($data); - $data.fadeIn('fast'); + $subfield.html('').append($data); + $data.fadeIn('fast', function() { + // if there is a default selected option, select it now + //var $option = $subfield.find('.select-subfield-default'); + //if($option.length) $option.attr('selected', 'selected').parent('select').change(); + }); //$row.children('.subfield').html(data); } + + if(action == 'subfield-opval') { + data = data.split(''); + actionSubfield($(data[0])); + actionOpval($(data[1])); + + } else if(action == 'opval') { + actionOpval($(data)); + } else { + actionSubfield($(data)); + } InputfieldSelector.normalizeHeightRow($row); @@ -429,13 +449,18 @@ var InputfieldSelector = { if(templateID > 0) templateIDs.push(templateID); } - if($row.is(".has-subfield")) { - var subfield = $row.find(".select-subfield").val(); + if($row.hasClass('has-subfield')) { + var $selectSubfield = $row.find('.select-subfield'); + var $selectOption = $selectSubfield.children('option:selected'); + var subfield = $selectSubfield.val(); if(subfield.length > 0) { if(subfield.indexOf('.') > 0) { // fieldName was already specified with subfield - if(fieldName.indexOf('@') > -1) fieldName = '@' + subfield; - else fieldName = subfield; + if(fieldName.indexOf('@') > -1) fieldName = '@' + subfield; + else fieldName = subfield; + } else if($selectOption.hasClass('select-subfield-default')) { + // indicates subfield IS the fieldName + fieldName = subfield; } else { // subfield needs to be appended to fieldName fieldName += subfield; diff --git a/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.min.js b/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.min.js index ba3c9614..bcc4bc46 100644 --- a/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.min.js +++ b/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.min.js @@ -1 +1 @@ -var InputfieldSelector={selector:"",spinner:"",borderColor:"#eee",init:function(){$(document).on("change",".InputfieldSelector select.select-field",InputfieldSelector.changeField);$(document).on("change",".InputfieldSelector select.select-subfield",InputfieldSelector.changeField);$(document).on("change",".InputfieldSelector :input:not(.select-field):not(.input-value-autocomplete)",function(){InputfieldSelector.changeAny($(this))});$(document).on("opened",".InputfieldSelector",function(){InputfieldSelector.normalizeHeightRows($(this))});var b=null;$(document).on("keyup",".InputfieldSelector input.input-value",function(){var d=$(this);clearTimeout(b);if(d.hasClass("input-value-subselect")&&InputfieldSelector.valueHasOperator(d.val())){var c=d.closest(".InputfieldContent").find(".selector-preview");c.html('Subselect detected: when done click here to commit your change.');return}b=setTimeout(function(){InputfieldSelector.changeAny(d)},100)});$(document).on("click",".InputfieldSelector .selector-add",function(){InputfieldSelector.addRow($(this));return false});$(document).on("click",".InputfieldSelector a.delete-row",InputfieldSelector.deleteRow);$(".InputfieldSelector .selector-preview").hide();$(document).on("wiretabclick",function(f,d,c){var e=d.find(".InputfieldSelector");if(e.length==0){return}InputfieldSelector.normalizeHeightRows(e)});var a=$(".InputfieldSelector .selector-row");if(a.length>0){a.eq(0).find(".select-field").each(function(){InputfieldSelector.changeAny($(this))});a.eq(1).find(".input-value").change();a.each(function(){var c=$(this);c.css("border-color",InputfieldSelector.borderColor);InputfieldSelector.normalizeHeightRow(c);var f=c.find(".input-value-autocomplete");if(f.length>0){var d=c.find(".select-subfield");var g=d.length?d.val():c.find(".select-field").val();var e=c.parents(".InputfieldSelector").find("input.selector-value").attr("name");InputfieldSelector.setupAutocomplete(f,g,e)}})}$(".InputfieldSelector").each(function(){if($(this).find(".selector-preview-disabled").length>0){return}$(this).find(".input-value:eq(0)").change()})},disableOption:function(a){a.attr("disabled","disabled")},enableOption:function(a){a.removeAttr("disabled")},valueHasOperator:function(b){var a=["=","<",">"];var d=false;for(n=0;n-1&&b.substring(c-1,1)!="\\"){d=true;break}}return d},addRow:function(b){var c=b.parents(".InputfieldSelector").find(".selector-list");var a=c.find(".selector-template-row");var d=a.clone();d.removeClass("selector-template-row");d.find(".opval").html("");d.find(".select-field").val("");d.hide();d.find("option[disabled=disabled]").remove();c.append(d);d.slideDown("fast");InputfieldSelector.normalizeHeightRow(d)},deleteRow:function(){var b=$(this).parents(".selector-row");var a=b.find(".select-field");if(a.val()=="template"){b.parents(".InputfieldSelector").find("select.select-field").each(function(){$(this).find("option[disabled=disabled]").each(function(){InputfieldSelector.enableOption($(this))})})}var c=b.siblings();b.slideUp("fast",function(){b.remove();InputfieldSelector.changeAny(c.eq(0))});return false},changeFieldToggle:function(b){var d=b.parents(".InputfieldSelector");var c=d.hasClass("InputfieldSelector_names")?"names":"labels";var a=(c==="labels"?"names":"labels");d.find(".select-field, .select-subfield").each(function(){$(this).find("option").each(function(){var e=$(this).attr("data-name");if(!e){if($(this).attr("value")=="toggle-names-labels"){$(this).html($(this).attr("data-"+c))}return}if(c=="labels"){$(this).html(e)}else{$(this).html($(this).attr("data-label"))}})});d.removeClass("InputfieldSelector_"+c).addClass("InputfieldSelector_"+a);b.val(b.attr("data-selected"));return false},changeField:function(f){var f=$(this);var i=f.val();if(!i||i.length==0){return}if(i=="toggle-names-labels"){return InputfieldSelector.changeFieldToggle(f)}var g=f.parents(".selector-row");var e="opval";g.children(".opval").html("");f.attr("data-selected",i);var c=f.parents(".InputfieldSelector").find(".selector-value");var a=c.attr("name");var h=f.attr("data-type");if(i.match(/\.$/)){e="subfield";if(i.indexOf("@")>-1){i=i.substring(1,i.length-1)}else{i=i.substring(0,i.length-1)}g.addClass("has-subfield")}else{if(i.match(/\.id$/)){i="id";e="opval";h="selector"}else{if(f.is(".select-field")){g.children(".subfield").html("");g.removeClass("has-subfield")}}}var b="./?InputfieldSelector="+e+"&field="+i+"&type="+h+"&name="+a;var d=$(InputfieldSelector.spinner);g.append(d);$.get(b,function(m){d.remove();var j=$(m);j.hide();if(e=="opval"){var l=g.children(".opval");l.html("").append(j);l.children(":not(.input-or)").fadeIn("fast");InputfieldSelector.changeAny(f);var k=l.find(".input-value-autocomplete");if(k.length>0){InputfieldSelector.setupAutocomplete(k,i,a)}}else{var o=g.children(".subfield");o.html("").append(j);j.fadeIn("fast")}InputfieldSelector.normalizeHeightRow(g);g.closest(".InputfieldContent").find(".hasDatepicker").datepicker("destroy").removeAttr("id").removeClass("hasDatepicker")})},normalizeHeightRow:function(a){InputfieldSelector.normalizeHeight(a.find(":input, i.fa"))},normalizeHeightRows:function(a){a.find(".selector-row").each(function(){InputfieldSelector.normalizeHeightRow($(this))})},normalizeHeight:function(b){var a=0;b.each(function(){$(this).css("margin-top",0);var c=$(this).outerHeight();if(c>a){a=c}});b.each(function(){var d=$(this).outerHeight();if(d0){f.push(C)}}if(E.is(".has-subfield")){var H=E.find(".select-subfield").val();if(H.length>0){if(H.indexOf(".")>0){if(K.indexOf("@")>-1){K="@"+H}else{K=H}}else{K+=H}if(K.indexOf(".data")>0){K=K.replace(/\.data$/,"")}}}var y=z.siblings(".opval").children(".select-operator");var B=y.val();var G=y.next(".input-value");var J=G.val();if(B&&B.indexOf('"')>-1){J=" ";G.attr("disabled","disabled")}else{if(G.is(":disabled")){G.removeAttr("disabled")}}if(typeof J!="undefined"){if(J.length){if(G.hasClass("input-value-subselect")&&InputfieldSelector.valueHasOperator(J)){J="["+J+"]"}else{if(J.indexOf(",")>-1&&K!="_custom"){if(J.indexOf('"')>-1){if(J.indexOf("'")==-1){J="'"+J+"'"}else{J='"'+J.replace(/"/g,"")+'"'}}else{J='"'+J+'"'}}}}}var D=","+K+"~"+B+"~";var F="~"+B+"~"+J+",";var A=J&&J.length>0&&q.indexOf(D)>-1;var x=J&&J.length>0&&q.indexOf(F)>-1;var I=E.find(".input-or");var s=A&&I.is(":checked");var i=x&&I.is(":checked");var w=(i||s)&&K=="_custom";if(s){E.addClass("has-or-value");E.find(".select-field, .select-operator, .select-subfield").attr("disabled","disabled")}else{if(E.is(".has-or-value")){E.removeClass("has-or-value");E.find(".select-field, .select-operator, .select-subfield").removeAttr("disabled")}}if(i){E.addClass("has-or-field");E.find(".input-value, .select-operator").attr("disabled","disabled")}else{if(E.is(".has-or-field")){E.removeClass("has-or-field");E.find(".input-value, .select-operator").removeAttr("disabled")}}c[k++]={field:K,operator:B,value:J,mayOrValue:A,mayOrField:x,useOrValue:s,useOrField:i,isOrGroup:w,checkbox:I};if(x||A){h=true}q+=","+K+"~"+B+"~"+J+",";m+=","+K+B+J});if(f.length>0){var d=null;t.each(function(){var i=$(this);var s=0;i.find("option").each(function(){var x=$(this);var w=x.attr("data-templates");if(typeof w!="undefined"&&w!="*"){InputfieldSelector.enableOption(x);var y=0;for(p=0;p-1){y++}}if(y){InputfieldSelector.enableOption(x)}else{if(!x.is(":selected")){InputfieldSelector.disableOption(x)}s++}}});if(s>0&&!i.parent().is(".selector-template-row")){i.find("option[disabled=disabled]").remove()}})}m="";for(k=0;k0){m+=", "}for(var p=0;p0&&m!=InputfieldSelector.selector){if(!j.is(".selector-preview-disabled")){j.html(""+e+m+"");j.fadeIn()}var u=j.siblings(".selector-counter");if(u.length>0&&!u.is(".selector-counter-disabled")){u.html(InputfieldSelector.spinner).fadeIn("fast");$.post("./?InputfieldSelector=test&name="+o.attr("name"),{selector:m},function(i){u.hide();u.html(i);u.show()})}}if(o.val()!=m){o.val(m);if(m.length==0){j.hide();j.siblings(".selector-counter").html("")}o.change()}InputfieldSelector.selector=m;var a=l.find(".or-notes");if(h){a.fadeIn()}else{a.hide()}}};$(document).ready(function(){InputfieldSelector.init()}); \ No newline at end of file +var InputfieldSelector={selector:"",spinner:"",borderColor:"#eee",init:function(){$(document).on("change",".InputfieldSelector select.select-field",InputfieldSelector.changeField);$(document).on("change",".InputfieldSelector select.select-subfield",InputfieldSelector.changeField);$(document).on("change",".InputfieldSelector :input:not(.select-field):not(.input-value-autocomplete)",function(){InputfieldSelector.changeAny($(this))});$(document).on("opened",".InputfieldSelector",function(){InputfieldSelector.normalizeHeightRows($(this))});var timeout=null;$(document).on("keyup",".InputfieldSelector input.input-value",function(){var $t=$(this);clearTimeout(timeout);if($t.hasClass("input-value-subselect")&&InputfieldSelector.valueHasOperator($t.val())){var $preview=$t.closest(".InputfieldContent").find(".selector-preview");$preview.html('Subselect detected: when done click here to commit your change.');return}timeout=setTimeout(function(){InputfieldSelector.changeAny($t)},100)});$(document).on("click",".InputfieldSelector .selector-add",function(){InputfieldSelector.addRow($(this));return false});$(document).on("click",".InputfieldSelector a.delete-row",InputfieldSelector.deleteRow);$(".InputfieldSelector .selector-preview").hide();$(document).on("wiretabclick",function(event,$newTab,$oldTab){var $inputfield=$newTab.find(".InputfieldSelector");if($inputfield.length==0)return;InputfieldSelector.normalizeHeightRows($inputfield)});var $rows=$(".InputfieldSelector .selector-row");if($rows.length>0){$rows.eq(0).find(".select-field").each(function(){InputfieldSelector.changeAny($(this))});$rows.eq(1).find(".input-value").change();$rows.each(function(){var $row=$(this);$row.css("border-color",InputfieldSelector.borderColor);InputfieldSelector.normalizeHeightRow($row);var $ac=$row.find(".input-value-autocomplete");if($ac.length>0){var subfield=$row.find(".select-subfield");var field=subfield.length?subfield.val():$row.find(".select-field").val();var name=$row.parents(".InputfieldSelector").find("input.selector-value").attr("name");InputfieldSelector.setupAutocomplete($ac,field,name)}})}$(".InputfieldSelector").each(function(){if($(this).find(".selector-preview-disabled").length>0)return;$(this).find(".input-value:eq(0)").change()})},disableOption:function($option){$option.attr("disabled","disabled")},enableOption:function($option){$option.removeAttr("disabled")},valueHasOperator:function(value){var operators=["=","<",">"];var hasOperator=false;for(n=0;n-1&&value.substring(pos-1,1)!="\\"){hasOperator=true;break}}return hasOperator},addRow:function($context){var $list=$context.parents(".InputfieldSelector").find(".selector-list");var $row=$list.find(".selector-template-row");var $newRow=$row.clone();$newRow.removeClass("selector-template-row");$newRow.find(".opval").html("");$newRow.find(".select-field").val("");$newRow.hide();$newRow.find("option[disabled=disabled]").remove();$list.append($newRow);$newRow.slideDown("fast");InputfieldSelector.normalizeHeightRow($newRow)},deleteRow:function(){var $row=$(this).parents(".selector-row");var $selectField=$row.find(".select-field");if($selectField.val()=="template"){$row.parents(".InputfieldSelector").find("select.select-field").each(function(){$(this).find("option[disabled=disabled]").each(function(){InputfieldSelector.enableOption($(this))})})}var $siblings=$row.siblings();$row.slideUp("fast",function(){$row.remove();InputfieldSelector.changeAny($siblings.eq(0))});return false},changeFieldToggle:function($select){var $rootParent=$select.parents(".InputfieldSelector");var currentSetting=$rootParent.hasClass("InputfieldSelector_names")?"names":"labels";var newSetting=currentSetting==="labels"?"names":"labels";$rootParent.find(".select-field, .select-subfield").each(function(){$(this).find("option").each(function(){var name=$(this).attr("data-name");if(!name){if($(this).attr("value")=="toggle-names-labels"){$(this).html($(this).attr("data-"+currentSetting))}return}if(currentSetting=="labels"){$(this).html(name)}else{$(this).html($(this).attr("data-label"))}})});$rootParent.removeClass("InputfieldSelector_"+currentSetting).addClass("InputfieldSelector_"+newSetting);$select.val($select.attr("data-selected"));return false},changeField:function($select){var $select=$(this);var $option=$select.children("option:selected");var field=$select.val();if(!field||field.length==0)return;if(field=="toggle-names-labels")return InputfieldSelector.changeFieldToggle($select);var $row=$select.parents(".selector-row");var action="opval";$row.children(".opval").html("");$select.attr("data-selected",field);var $hiddenInput=$select.parents(".InputfieldSelector").find(".selector-value");var name=$hiddenInput.attr("name");var type=$select.attr("data-type");if(field.match(/\.$/)){action="subfield-opval";if(field.indexOf("@")>-1)field=field.substring(1,field.length-1);else field=field.substring(0,field.length-1);$row.addClass("has-subfield")}else if(field.match(/\.id$/)){field="id";action="opval";type="selector"}else if($select.is(".select-field")){$row.children(".subfield").html("");$row.removeClass("has-subfield")}var url="./?InputfieldSelector="+action+"&field="+field+"&type="+type+"&name="+name;var $spinner=$(InputfieldSelector.spinner);$row.append($spinner);$.get(url,function(data){$spinner.remove();function actionOpval($data){$data.hide();var $opval=$row.children(".opval");$opval.html("").append($data);$opval.children(":not(.input-or)").fadeIn("fast");InputfieldSelector.changeAny($select);var $ac=$opval.find(".input-value-autocomplete");if($ac.length>0)InputfieldSelector.setupAutocomplete($ac,field,name)}function actionSubfield($data){$data.hide();var $subfield=$row.children(".subfield");$subfield.html("").append($data);$data.fadeIn("fast",function(){})}if(action=="subfield-opval"){data=data.split("");actionSubfield($(data[0]));actionOpval($(data[1]))}else if(action=="opval"){actionOpval($(data))}else{actionSubfield($(data))}InputfieldSelector.normalizeHeightRow($row);$row.closest(".InputfieldContent").find(".hasDatepicker").datepicker("destroy").removeAttr("id").removeClass("hasDatepicker")})},normalizeHeightRow:function($row){InputfieldSelector.normalizeHeight($row.find(":input, i.fa"))},normalizeHeightRows:function($parent){$parent.find(".selector-row").each(function(){InputfieldSelector.normalizeHeightRow($(this))})},normalizeHeight:function($items){var maxHeight=0;$items.each(function(){$(this).css("margin-top",0);var h=$(this).outerHeight();if(h>maxHeight)maxHeight=h});$items.each(function(){var h=$(this).outerHeight();if(h0)templateIDs.push(templateID)}if($row.hasClass("has-subfield")){var $selectSubfield=$row.find(".select-subfield");var $selectOption=$selectSubfield.children("option:selected");var subfield=$selectSubfield.val();if(subfield.length>0){if(subfield.indexOf(".")>0){if(fieldName.indexOf("@")>-1)fieldName="@"+subfield;else fieldName=subfield}else if($selectOption.hasClass("select-subfield-default")){fieldName=subfield}else{fieldName+=subfield}if(fieldName.indexOf(".data")>0)fieldName=fieldName.replace(/\.data$/,"")}}var $op=$select.siblings(".opval").children(".select-operator");var op=$op.val();var $value=$op.next(".input-value");var value=$value.val();if(op&&op.indexOf('"')>-1){value=" ";$value.attr("disabled","disabled")}else if($value.is(":disabled")){$value.removeAttr("disabled")}if(typeof value!="undefined")if(value.length){if($value.hasClass("input-value-subselect")&&InputfieldSelector.valueHasOperator(value)){value="["+value+"]"}else if(value.indexOf(",")>-1&&fieldName!="_custom"){if(value.indexOf('"')>-1){if(value.indexOf("'")==-1)value="'"+value+"'";else value='"'+value.replace(/"/g,"")+'"'}else{value='"'+value+'"'}}}var testField=","+fieldName+"~"+op+"~";var testValue="~"+op+"~"+value+",";var mayOrValue=value&&value.length>0&&test.indexOf(testField)>-1;var mayOrField=value&&value.length>0&&test.indexOf(testValue)>-1;var $orCheckbox=$row.find(".input-or");var useOrValue=mayOrValue&&$orCheckbox.is(":checked");var useOrField=mayOrField&&$orCheckbox.is(":checked");var isOrGroup=(useOrField||useOrValue)&&fieldName=="_custom";if(useOrValue){$row.addClass("has-or-value");$row.find(".select-field, .select-operator, .select-subfield").attr("disabled","disabled")}else if($row.is(".has-or-value")){$row.removeClass("has-or-value");$row.find(".select-field, .select-operator, .select-subfield").removeAttr("disabled")}if(useOrField){$row.addClass("has-or-field");$row.find(".input-value, .select-operator").attr("disabled","disabled")}else if($row.is(".has-or-field")){$row.removeClass("has-or-field");$row.find(".input-value, .select-operator").removeAttr("disabled")}selectors[n++]={field:fieldName,operator:op,value:value,mayOrValue:mayOrValue,mayOrField:mayOrField,useOrValue:useOrValue,useOrField:useOrField,isOrGroup:isOrGroup,checkbox:$orCheckbox};if(mayOrField||mayOrValue)showOrNotes=true;test+=","+fieldName+"~"+op+"~"+value+",";selector+=","+fieldName+op+value});if(templateIDs.length>0){var $masterSelect=null;$selectFields.each(function(){var $select=$(this);var numDisabledOptions=0;$select.find("option").each(function(){var $option=$(this);var templates=$option.attr("data-templates");if(typeof templates!="undefined"&&templates!="*"){InputfieldSelector.enableOption($option);var numFound=0;for(i=0;i-1)numFound++}if(numFound){InputfieldSelector.enableOption($option)}else{if(!$option.is(":selected"))InputfieldSelector.disableOption($option);numDisabledOptions++}}});if(numDisabledOptions>0&&!$select.parent().is(".selector-template-row")){$select.find("option[disabled=disabled]").remove()}})}selector="";for(n=0;n0)selector+=", ";for(var i=0;i0&&selector!=InputfieldSelector.selector){if(!$preview.is(".selector-preview-disabled")){$preview.html(""+initValue+selector+"");$preview.fadeIn()}var $counter=$preview.siblings(".selector-counter");if($counter.length>0&&!$counter.is(".selector-counter-disabled")){$counter.html(InputfieldSelector.spinner).fadeIn("fast");$.post("./?InputfieldSelector=test&name="+$hiddenInput.attr("name"),{selector:selector},function(data){$counter.hide();$counter.html(data);$counter.show()})}}if($hiddenInput.val()!=selector){$hiddenInput.val(selector);if(selector.length==0){$preview.hide();$preview.siblings(".selector-counter").html("")}$hiddenInput.change()}InputfieldSelector.selector=selector;var $orNotes=$inputfield.find(".or-notes");if(showOrNotes)$orNotes.fadeIn();else $orNotes.hide()}};$(document).ready(function(){InputfieldSelector.init()}); \ No newline at end of file diff --git a/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.module b/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.module index 4b97ea86..bb63d563 100644 --- a/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.module +++ b/wire/modules/Inputfield/InputfieldSelector/InputfieldSelector.module @@ -53,6 +53,7 @@ * @property string $selectClass * @property string $inputClass * @property string $checkboxClass + * @property string $lastSelector * * */ @@ -62,7 +63,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { public static function getModuleInfo() { return array( 'title' => 'Selector', - 'version' => 27, + 'version' => 28, 'summary' => 'Build a page finding selector visually.', 'author' => 'Avoine + ProcessWire', 'autoload' => "template=admin", @@ -191,6 +192,8 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { $this->setting('addLabel', $this->_('Add Field')); $this->setting('parseVars', true); // whether variables like [user.id] will be parsed + $this->set('lastSelector', ''); // last created selector + $this->set('subfieldIdentifier', ' …'); $this->set('groupIdentifier', ' ' . $this->_('(1)')); // group identifier @@ -214,6 +217,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { $this->setting('timeFormat', $this->_('H:i')); // time format $this->setting('timePlaceholder', $this->_('hh:mm')); // time format placeholder (what users see) $this->setting('maxUsers', 20); // text input rather than select used when useres qty is greater than this + parent::__construct(); } @@ -294,30 +298,33 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { } $this->setup(); + + $fieldName = $input->get('field'); + if($fieldName !== null) $fieldName = $sanitizer->name($fieldName); + + $type = $input->get('type'); + if($type !== null) $type = $sanitizer->name($type); + + $q = $input->get('q'); + if($q !== null) $q = $sanitizer->text($q); - if($action == 'field') { + if($action === 'field') { $out = $this->renderSelectField(); - } else if($action == 'subfield' && ($fieldName = $input->get->field)) { - $fieldName = $sanitizer->name($fieldName); - if(strpos($fieldName, '.')) { - list($fieldName, $subfieldName) = explode('.', $fieldName); - if($subfieldName) {} // ignore - } + } else if($action === 'subfield' && $fieldName) { $out = $this->renderSelectSubfield($fieldName); - } else if($action == 'opval' && ($fieldName = $input->get->field)) { - $fieldName = $sanitizer->name($fieldName); - //$subfield = $input->get->subfield ? $sanitizer->name($input->get->subfield) : ''; - //if($subfield) $fieldName = "$fieldName.$subfield"; - $type = $sanitizer->name($input->get->type); - $out = $this->renderOpval($fieldName, $type); + } else if($action === 'opval' && $fieldName) { + $out = $this->renderOpval($fieldName, $type); + + } else if($action === 'subfield-opval') { + $out = $this->renderSelectSubfield($fieldName) . '' . $this->renderOpval($fieldName, $type); - } else if($action == 'test' && ($selector = $input->post->selector)) { + } else if($action === 'test' && ($selector = $input->post('selector'))) { $out = $this->renderTestSelector($selector); - } else if($action == 'autocomplete' && ($fieldName = $input->get('field')) && ($q = $input->get('q'))) { - $out = $this->renderAutocompleteJSON($sanitizer->name($fieldName), $sanitizer->text($q)); + } else if($action === 'autocomplete' && $fieldName && strlen($q)) { + $out = $this->renderAutocompleteJSON($fieldName, $q); } else { $out = "Ajax request missing required info"; @@ -425,55 +432,41 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { // system fields definitions $this->systemFields = array( - 'template' => array( - 'input' => 'select', - 'label' => $this->_('Template'), - 'options' => $templates, + 'count' => array( + 'input' => 'number', + 'label' => $this->_('Count'), 'sanitizer' => 'integer', - 'operators' => array('=', '!='), - ), - 'title' => array( + ), + 'created' => array( + 'input' => 'datetime', + 'label' => $this->_('Created date'), + 'operators' => $this->operatorsByType['datetime'], + ), + 'created_users_id' => array( + 'input' => $usersInput, + 'label' => $this->_('Created by user'), + 'options' => $users, + 'operators' => $usersOperators + ), + '_custom' => array( 'input' => 'text', - 'label' => ($titleField ? $titleField->getLabel() : 'Title'), - 'operators' => $this->operatorsByType['text'] - ), + 'label' => $this->_('Custom (field=value)'), + 'operators' => array(), + 'placeholder' => $this->_('field=value'), + ), + 'has_parent' => array( + 'input' => 'text', + 'label' => $this->_('Has parent/ancestor'), + 'operators' => array('=', '!='), + ), 'id' => array( 'input' => 'number', 'label' => $this->_('ID'), 'sanitizer' => 'integer', - ), - 'name' => array( - 'input' => 'text', - 'label' => $this->_('Name'), - 'sanitizer' => 'pageNameUTF8', - 'operators' => array('=', '!=', '%='), - ), - 'status' => array( - 'input' => 'select', - 'label' => $this->_('Status'), - 'options' => array( - 'hidden' => $this->_('Hidden'), - 'unpublished' => $this->_('Unpublished'), - 'locked' => $this->_('Locked'), - 'trash' => $this->_('Trash'), - 'temp' => $this->_('Temp'), - ), - 'sanitizer' => 'integer', - 'operators' => array('@=', '@!='), - ), + ), 'modified' => array( 'input' => 'datetime', 'label' => $this->_('Modified date'), - 'operators' => $this->operatorsByType['datetime'], - ), - 'created' => array( - 'input' => 'datetime', - 'label' => $this->_('Created date'), - 'operators' => $this->operatorsByType['datetime'], - ), - 'published' => array( - 'input' => 'datetime', - 'label' => $this->_('Published date'), 'operators' => $this->operatorsByType['datetime'], ), 'modified_users_id' => array( @@ -481,49 +474,63 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { 'label' => $this->_('Modified by user'), 'options' => $users, 'operators' => $usersOperators - ), - 'created_users_id' => array( - 'input' => $usersInput, - 'label' => $this->_('Created by user'), - 'options' => $users, - 'operators' => $usersOperators - ), + ), + 'name' => array( + 'input' => 'text', + 'label' => $this->_('Name'), + 'sanitizer' => 'pageNameUTF8', + 'operators' => array('=', '!=', '%='), + ), 'num_children' => array( 'input' => 'number', 'label' => $this->_('Number of children'), 'sanitizer' => 'integer', - ), - 'count' => array( - 'input' => 'number', - 'label' => $this->_('Count'), - 'sanitizer' => 'integer', - ), - 'path' => array( - 'input' => 'text', - 'label' => $this->_('Path/URL'), - 'operators' => $this->operatorsByType['text'], - ), + ), 'parent' => array( 'input' => 'number', // select - 'label' => $this->_x('Parent', 'parent-only'), + 'label' => $this->_x('Parent', 'parent-only'), 'operators' => array('=', '!='), // $this->operatorsByType['page'] // 'options' => array(id => label) - ), + ), 'parent.' => array( 'input' => 'subfields', - 'label' => $this->_x('Parent',' parent-with-subfield'), - ), - 'has_parent' => array( - 'input' => 'text', - 'label' => $this->_('Has parent/ancestor'), - 'operators' => array('=', '!='), - ), - '_custom' => array( - 'input' => 'text', - 'label' => $this->_('Custom (field=value)'), - 'operators' => array(), - 'placeholder' => $this->_('field=value'), + 'label' => $this->_x('Parent',' parent-with-subfield'), ), + 'path' => array( + 'input' => 'text', + 'label' => $this->_('Path/URL'), + 'operators' => $this->operatorsByType['text'], + ), + 'published' => array( + 'input' => 'datetime', + 'label' => $this->_('Published date'), + 'operators' => $this->operatorsByType['datetime'], + ), + 'status' => array( + 'input' => 'select', + 'label' => $this->_('Status'), + 'options' => array( + 'hidden' => $this->_('Hidden'), + 'unpublished' => $this->_('Unpublished'), + 'locked' => $this->_('Locked'), + 'trash' => $this->_('Trash'), + 'temp' => $this->_('Temp'), + ), + 'sanitizer' => 'integer', + 'operators' => array('@=', '@!='), + ), + 'template' => array( + 'input' => 'select', + 'label' => $this->_('Template'), + 'options' => $templates, + 'sanitizer' => 'integer', + 'operators' => array('=', '!='), + ), + 'title' => array( + 'input' => 'text', + 'label' => ($titleField ? $titleField->getLabel() : 'Title'), + 'operators' => $this->operatorsByType['text'] + ), //'parent' => $this->_('parent'), ); @@ -549,27 +556,15 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { // system fields for page references $this->systemPageFields = array( - 'id' => $this->systemFields['id'], - 'name' => $this->systemFields['name'], - 'status' => $this->systemFields['status'], - 'modified' => $this->systemFields['modified'], 'created' => $this->systemFields['created'], + 'id' => $this->systemFields['id'], + 'modified' => $this->systemFields['modified'], + 'name' => $this->systemFields['name'], 'published' => $this->systemFields['published'], - ); + 'status' => $this->systemFields['status'], + ); $this->modifierFields = array( - 'sort' => array( - 'input' => 'select', - 'label' => $this->_('Sort'), - 'sanitizer' => 'fieldName', - 'operators' => array('.=', '.=-'), - 'options' => array() // populated below - ), - 'limit' => array( - 'input' => 'integer', - 'label' => $this->_('Limit'), - 'operators' => array('=') - ), 'include' => array( 'input' => 'select', 'label' => $this->_('Include'), @@ -578,10 +573,22 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { 'unpublished' => $this->_('Hidden + Unpublished'), 'trash' => $this->_('Hidden + Unpublished + Trash'), 'all' => $this->_('All'), - ), + ), 'operators' => array('=') - ) - ); + ), + 'limit' => array( + 'input' => 'integer', + 'label' => $this->_('Limit'), + 'operators' => array('=') + ), + 'sort' => array( + 'input' => 'select', + 'label' => $this->_('Sort'), + 'sanitizer' => 'fieldName', + 'operators' => array('.=', '.=-'), + 'options' => array() // populated below + ), + ); // populate the sort options $options = array(); @@ -607,7 +614,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { protected function sessionSet($key, $value) { $s = $this->wire('session')->get($this->className()); if(!is_array($s)) $s = array(); - if(count($s) > 30) $s = array_slice($s, -9); // prevent from growing too large + if(count($s) > 100) $s = array_slice($s, -100); // prevent from growing too large $id = 'id' . $this->wire('page')->id . "_" . $this->wire('sanitizer')->fieldName($this->attr('name')); if(!isset($s[$id])) $s[$id] = array(); $s[$id][$key] = $value; @@ -781,11 +788,12 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { 'showFieldLabels' => $this->showFieldLabels, 'showOptgroups' => $this->showOptgroups, 'customFields' => $this->wire('fields'), - 'prepend' => '', 'exclude' => array('count', 'pass'), 'limitFields' => array(), 'templates' => array(), 'type' => '', + 'prepend' => '', + 'append' => '', ); $settings = array_merge($defaults, $settings); @@ -869,6 +877,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { if($settings['showFieldLabels'] == 2) $label .= " [$name]"; if($hasSubfields) $label .= $this->subfieldIdentifier; $selected = $selectedValue == $name ? ' selected' : ''; + if($name === 'parent' && empty($selected)) continue; // we only show "parent." unless "parent" it was somehow already selected if($_name == '_custom') $_name = strtolower($label); // always use label for _custom $text = $settings['showFieldLabels'] ? $label : $_name; $o = "attr('name') . "__" . $settings['name'] . "[]"; $selectClass = trim("$this->selectClass select-$settings[name]"); - $out = "$settings[prepend]"; if(!$this->showOptgroups) { ksort($outAll); @@ -960,7 +969,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { $out .= "$outSections[$name]"; } - $out .= ""; + $out .= "$settings[append]"; return $out; } @@ -996,36 +1005,42 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { protected function renderSelectFieldOption(Field $field, array $settings, $selectedValue = '') { if($field->type instanceof FieldtypeFieldsetOpen) return array(); - $out = array('field' => '', 'subfield' => '', 'group' => '', 'all' => array()); - $selected = $field->name == $selectedValue ? ' selected' : ''; + $fieldSelected = $field->name == $selectedValue ? ' selected' : ''; $templatesStr = $this->getTemplateIdsUsingField($field); $selectorInfo = $this->getSelectorInfo($field); if(empty($selectorInfo)) return array(); + $out = array( + 'field' => '', + 'subfield' => '', // no longer used (now replaces 'field') + 'group' => '', + 'all' => array() + ); + $label = $this->wire('sanitizer')->entities($field->getLabel()); if($settings['showFieldLabels'] == 2) $label .= " [$field->name]"; $text = $settings['showFieldLabels'] ? $label : $field->name; if($selectorInfo['input'] != 'none' && count($selectorInfo['operators'])) { - $o = "$text"; - $out['field'] .= $o; + $out['field'] = $o; $out['all']["$field->name 1"] = $o; } if(!empty($settings['showSubfields'])) { $hasSubfields = count($selectorInfo['subfields']) > 0; - $selected = "$field->name." == $selectedValue ? ' selected' : ''; + $subfieldSelected = "$field->name." == $selectedValue || $fieldSelected !== '' ? ' selected' : ''; if($settings['showFieldLabels'] == 2) $label .= " [$field->name]"; - $option = "name 2"] = $option; } @@ -1044,8 +1060,8 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { $name = $field->name . ' ' . $this->groupIdentifier . $this->subfieldIdentifier; $label .= ' ' . $this->groupIdentifier . $this->subfieldIdentifier; $text .= ' ' . $this->groupIdentifier . $this->subfieldIdentifier; - $selected = "@$field->name." == $selectedValue ? ' selected' : ''; - $o = "message("fieldName: $fieldName"); @@ -1137,39 +1152,54 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { $this->message("selectedValue: $selectedValue"); $this->message("orChecked: $orChecked"); */ + + /** @var Templates $templates */ + $templates = $this->wire('templates'); + + /** @var Sanitizer $sanitizer */ + $sanitizer = $this->wire('sanitizer'); + if($selector) {} $field = null; $inputName = $this->attr('name') . "__value[]"; $options = array(); $out = ''; - $selectedValueEntities = $this->wire('sanitizer')->entities($selectedValue); + $selectedValueEntities = $sanitizer->entities($selectedValue); $operators = array(); $subfield = ''; $placeholder = ''; $_type = ''; // previous type, if changed $inputfield = null; - if(strpos($fieldName, '.') !== false) list($fieldName, $subfield) = explode('.', $fieldName); + $lastTemplates = $this->initTemplate ? array($this->initTemplate->id) : $this->sessionGet('lastTemplates'); + + if(strpos($fieldName, '.') !== false) list($fieldName, $subfield) = explode('.', $fieldName, 2); if(isset($this->systemFields[$fieldName]) && !$subfield) { // system field $info = $this->systemFields[$fieldName]; - if($fieldName == 'parent' && $this->initTemplate) { + + if($fieldName == 'parent' && !empty($lastTemplates)) { // allow for selection of parent, when the template of items is known //$operators = $this->operatorsByType['page']; $info['input'] = 'select'; $info['options'] = array(); - foreach($this->wire('templates')->getParentPages($this->initTemplate) as $p) { - if(!$this->wire('user')->hasPermission('page-view', $p)) continue; - $info['options'][$p->id] = $p->get('title|path'); + foreach($lastTemplates as $templateID) { + $template = $templates->get($templateID); + if(!$template) continue; + foreach($this->getParentPages($template) as $p) { + if(!$this->wire('user')->hasPermission('page-view', $p)) continue; + $info['options'][$p->id] = $p->get('title|path'); + } } asort($info['options']); + if(empty($info['options'])) unset($info['options']); } $type = $info['input']; if(isset($info['options'])) $options = $info['options']; if(!empty($info['placeholder'])) $placeholder = $info['placeholder']; if($fieldName == 'template' && $selectedValue && !ctype_digit("$selectedValue")) { - $template = $this->wire('templates')->get($this->wire('sanitizer')->name($selectedValue)); + $template = $templates->get($sanitizer->name($selectedValue)); if($template) $selectedValue = $template->id; } @@ -1352,7 +1382,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { $inputfield = $field->getInputfield($this->wire('pages')->newNullPage(), $field); if($inputfield instanceof InputfieldPage) { /** @var InputfieldPage $inputfield */ - $selectedValueTitle = $this->wire('sanitizer')->entities1($inputfield->getPageLabel($selectedValuePage)); + $selectedValueTitle = $sanitizer->entities1($inputfield->getPageLabel($selectedValuePage)); } } } @@ -1425,6 +1455,11 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { * */ protected function renderSelectSubfield($fieldName, $selectedValue = '', Selector $selector = null) { + + if(strpos($fieldName, '.')) list($fieldName,) = explode('.', $fieldName, 2); + + $valueLabel = $this->_('Value'); + $valueName = strtolower($valueLabel); if($fieldName == 'parent' || $fieldName == 'children') { // for parent or children, use the existing functionality in renderSelectField @@ -1437,8 +1472,12 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { 'showModifiers' => false, 'showSubfields' => false, 'customFields' => $fields, - 'exclude' => array() // prevent exclusion of 'count' - ), $selectedValue); + 'exclude' => array(), // prevent exclusion of 'count' + 'prepend' => + "", + ), $selectedValue); return $out; } @@ -1458,8 +1497,16 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule { $outSub = ''; // subselector optgroup output $selectClass = trim("$this->selectClass select-subfield"); $out = "