diff --git a/src/Contao/Widgets/MultiColumnWizard.php b/src/Contao/Widgets/MultiColumnWizard.php index cce4cc93..3fd33647 100644 --- a/src/Contao/Widgets/MultiColumnWizard.php +++ b/src/Contao/Widgets/MultiColumnWizard.php @@ -149,6 +149,7 @@ class MultiColumnWizard extends Widget */ protected $arrButtons = [ 'new' => 'new.gif', + 'copy' => 'copy.gif', 'delete' => 'delete.gif', 'move' => 'drag.gif' ]; @@ -594,6 +595,11 @@ protected function validator($varInput) if ($blnHasError) { $this->blnSubmitInput = false; $this->addError($GLOBALS['TL_LANG']['ERR']['general']); + foreach ($this->arrWidgetErrors as $key => $rows) { + foreach ($rows as $rowNumber => $msg) { + $this->addError(sprintf('Row: %s | Widget: %s | Error: %s', $rowNumber, $key, $msg)); + } + } } // Rebuild the order. @@ -626,9 +632,11 @@ protected function validator($varInput) * * @param bool $onlyRows If true, only row's will be output. * - * @return string The HTML code of the widget. + * @param bool $copyRow Flag to use the first row in the list for copy. If u use the validator + * function and have only one row, the validate function will set it as + * first value e.g. on key zero. * - * @throws \Exception If something went wrong. + * @return string The HTML code of the widget. * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -636,11 +644,14 @@ protected function validator($varInput) * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.ShortVariable) */ - public function generate($overwriteRowCurrentRow = null, $onlyRows = false) - { + public function generate( + $overwriteRowCurrentRow = null, + $onlyRows = false, + $copyRow = false + ) { /* * Load the callback data if there's any - * (do not do this in __set() already because then we don't have access to currentRecord) + * (do not do this in __set() already, because then we don't have access to currentRecord) */ if (is_array($this->arrCallback)) { @@ -726,7 +737,12 @@ public function generate($overwriteRowCurrentRow = null, $onlyRows = false) $arrField = array_merge($arrField, $this->arrRowSpecificData[$i][$strKey]); } - $objWidget = $this->initializeWidget($arrField, $i, $strKey, $this->varValue[$i][$strKey]); + // Switch to use a valid copy row or the current data. + if ($overwriteRowCurrentRow !== null && $copyRow === true) { + $objWidget = $this->initializeWidget($arrField, $i, $strKey, $this->varValue[0][$strKey]); + } else { + $objWidget = $this->initializeWidget($arrField, $i, $strKey, $this->varValue[$i][$strKey]); + } // load errors if there are any if (!empty($this->arrWidgetErrors[$strKey][$i])) { diff --git a/src/EventListener/Contao/ExecutePostActions.php b/src/EventListener/Contao/ExecutePostActions.php index d3a1282e..3e97f514 100644 --- a/src/EventListener/Contao/ExecutePostActions.php +++ b/src/EventListener/Contao/ExecutePostActions.php @@ -66,7 +66,45 @@ public function __construct(EventDispatcherInterface $eventDispatcher) } /** - * Create a new row. + * We have to clear the data, because contao set for some widgets a default hidden input with a empty value. + * This force the system to set a empty value in the post and the controller knows there is an empty value. + * But since we use mootools to get the post data out of the form, this input makes some trouble. + * + * So we must check if the widget data are an array and have more than one value. If yes and the first value + * is a empty string we have to remove it and reset the key count. + * + * @param string $widgetName The name of the widget. Used for the post lookup. + * + * @return void + */ + private function clearPostForCopyCommand($widgetName) + { + if (empty($widgetName)) { + return; + } + + $widgetData = Input::post($widgetName); + + foreach ($widgetData as $rowId => $row) { + foreach ($row as $fieldName => $value) { + // See the php function doc, why this is okay. + if (!is_array($value) || 1 === count($value)) { + continue; + } + + if ($value[0] === '') { + unset($value[0]); + $widgetData[$rowId][$fieldName] = array_values($value); + } + } + } + + Input::setPost($widgetName, $widgetData); + } + + /** + * Create a new row or make a copy from the given post data. + * * Will call the event men-at-work.multi-column-wizard-bundle.create-widget to get the widget. * * @param string $action The action. @@ -81,7 +119,8 @@ public function __construct(EventDispatcherInterface $eventDispatcher) public function handleRowCreation($action, $container) { // Check the context. - if ('mcwCreateNewRow' != $action) { + if ('mcwCreateNewRow' !== $action + && 'mcwCopyNewRow' !== $action) { return; } @@ -90,7 +129,8 @@ public function handleRowCreation($action, $container) if (!$container instanceof General) { $container->inputName = $fieldName; } - if (Input::get('act') == 'editAll') { + + if ('editAll' === Input::get('act')) { $fieldName = \preg_replace('/(.*)_[0-9a-zA-Z]+$/', '$1', $fieldName); } @@ -126,7 +166,28 @@ public function handleRowCreation($action, $container) $maxRowCount = 0; } - throw new ResponseException($this->convertToResponse($widget->generate(($maxRowCount + 1), true))); + // Handle the run. Since we check at the start which action it is, we don't need a else part. + $return = null; + if ('mcwCreateNewRow' === $action) { + $return = $widget->generate(($maxRowCount + 1), true); + } + + if ('mcwCopyNewRow' === $action) { + $name = Input::post('name'); + $this->clearPostForCopyCommand($name); + $widget->validate(); + if ($widget->hasErrors()) { + // ToDo handle them. + } + + $return = $widget->generate(($maxRowCount + 1), true, true); + } + + if ($return === null) { + throw new BadRequestHttpException('Bad request'); + } + + throw new ResponseException($this->convertToResponse($return)); } /** diff --git a/src/Resources/public/js/multicolumnwizard_be.js b/src/Resources/public/js/multicolumnwizard_be.js index af348abd..8a113478 100644 --- a/src/Resources/public/js/multicolumnwizard_be.js +++ b/src/Resources/public/js/multicolumnwizard_be.js @@ -1,2 +1,2 @@ -var MultiColumnWizard=new Class({Implements:[Options],options:{table:null,maxCount:0,minCount:0,uniqueFields:[]},asyncBlock:!1,operationLoadCallbacks:[],operationClickCallbacks:[],operationUpdateCallbacks:[],initialize:function(t){this.setOptions(t),this.options.table=document.id(this.options.table),window.Backend&&Backend.getScrollOffset();var e=this;this.options.table.getElement("tbody").getChildren("tr").each((function(t,i){t.getChildren("td.operations a").each((function(i){var a=i.get("data-operations");MultiColumnWizard.operationLoadCallbacks[a]&&MultiColumnWizard.operationLoadCallbacks[a].each((function(a){a.pass([i,t],e)()})),e.operationLoadCallbacks[a]&&e.operationLoadCallbacks[a].each((function(a){a.pass([i,t],e)()}))}))})),this.updateOperations()},updateOperations:function(){var t=this;this.options.table.getElement("tbody").getChildren("tr").each((function(e,i){e.getChildren("td.operations a").each((function(a){var n=a.get("data-operations");a.removeEvents("click"),"move"===n&&t.dragAndDrop(e,a),MultiColumnWizard.operationClickCallbacks[n]&&MultiColumnWizard.operationClickCallbacks[n].each((function(i){a.addEvent("click",(function(n){n.preventDefault(),i.pass([a,e],t)()}))})),t.operationClickCallbacks[n]&&t.operationClickCallbacks[n].each((function(n){a.addEvent("click",(function(l){l.preventDefault(),n.pass([a,e],t)(),t.updateFields(i)}))})),a.addEvent("click",(function(i){i.preventDefault(),t.updateOperations.pass([a,e],t)()})),MultiColumnWizard.operationUpdateCallbacks[n]&&MultiColumnWizard.operationUpdateCallbacks[n].each((function(i){i.pass([a,e],t)()})),t.operationUpdateCallbacks[n]&&t.operationUpdateCallbacks[n].each((function(i){i.pass([a,e],t)()}))}))}))},updateRowAttributes:function(t,e){var i=!0,a=0,n=0,l=0,o=0;return e.getElements(".mcwUpdateFields *").each((function(e){if(e.hasClass("tl_modulewizard")&&e.hasClass("multicolumnwizard")&&(i=!1,a++,e.addClass("mcw_inner_"+a),l=e.getElement("tbody").getElement("tr").getElements("td.mcwUpdateFields").length,o=1),0===a||e.hasClass("tl_modulewizard")&&e.hasClass("multicolumnwizard")||null!==e.getParent(".mcw_inner_"+a)||0===--a&&(i=!0),e.hasClass("chzn-container"))e.destroy();else{if("string"==typeOf(e.getProperty("name"))){var r=e.getProperty("name"),s=r.match(/([^[\]]+)/g),c=null,d="";s.each((function(t,e){!isNaN(parseFloat(t))&&isFinite(t)&&(c=e)})),s.each((function(e,a){0===a?d+=e:a===c&&i?d+="["+t+"]":a!==c-2||i?a!==c||i?d+="["+e+"]":(d+="["+n+"]",n=o>0&&o%l==0?++n:n,o++):d+="["+t+"]"})),"[]"==r.substr(r.length-2)&&(d+="[]"),e.setProperty("name",d)}var u;if("string"==typeOf(e.getProperty("id")))(u=e.getProperty("id").match(/^(.+)_row[0-9]+_(.+)$/i))&&e.setProperty("id",u[1]+"_row"+t+"_"+u[2]);if("string"==typeOf(e.getProperty("onclick")))(u=e.getProperty("onclick").match(/^(.+)_row[0-9]+_(.+)$/i))&&e.setProperty("onclick",u[1]+"_row"+t+"_"+u[2]);if("string"==typeOf(e.getProperty("for")))(u=e.getProperty("for").match(/^(.+)_row[0-9]+_(.+)$/i))&&e.setProperty("for",u[1]+"_row"+t+"_"+u[2]);switch(e.nodeName.toUpperCase()){case"SELECT":e.hasClass("tl_chosen")&&new Chosen(e);break;case"INPUT":"none"==e.getStyle("display").toLowerCase()&&e.setStyle("display","inline"),"string"!=typeOf(e.getProperty("id"))&&e.destroy();break;case"SCRIPT":for(var p="",C=e.get("html").toString(),m=0,h=C.search(/_row[0-9]+_/i);h>0;)m=C.match(/(_row[0-9]+)+_/i)[0].length,p=p+C.substr(0,h)+"_row"+t+"_",h=(C=C.substr(h+m)).search(/_row[0-9]+_/i);e.set("html",p+C)}}})),e},dragAndDrop:function(t,e){new Sortables(t.getParent("table").getElement("tbody"),{constrain:!0,opacity:.6,handle:"a[data-operations=move]",onComplete:function(){t.getParent("table").getElement("tbody").getChildren("tr").each((function(t,e){var i=e--;this.updateRowAttributes(i,t)}),this)}.bind(this)})},addOperationLoadCallback:function(t,e){this.operationLoadCallbacks[t]||(this.operationLoadCallbacks[t]=[]),this.operationLoadCallbacks[t].include(e)},addOperationUpdateCallback:function(t,e){this.operationUpdateCallbacks[t]||(this.operationUpdateCallbacks[t]=[]),this.operationUpdateCallbacks[t].include(e)},addOperationClickCallback:function(t,e){this.operationClickCallbacks[t]||(this.operationClickCallbacks[t]=[]),this.operationClickCallbacks[t].include(e)},killAllTinyMCE:function(t,e){var i=e.getParent(".multicolumnwizard");if(0!=i.getElements(".tinymce").length){var a=i.get("id"),n=new RegExp(a),l=new Array,o=0,r="editorId";tinymce.majorVersion>3&&(r="id"),tinymce.editors.each((function(t,e){null!=t[r].match(n)&&(l[o]=t[r],o++)})),l.each((function(t,e){try{var i=tinymce.get(t);$(i[r]).set("text",i.getContent()),i.remove()}catch(t){console.log(t)}})),i.getElements("span.mceEditor").each((function(t,e){t.dispose()})),i.getElements(".tinymce").each((function(t,e){t.getElements("script").each((function(t,e){t.dispose()}))}))}},reinitTinyMCE:function(t,e,i){var a=null;if(0!=(a=1!=i?e.getParent(".multicolumnwizard"):e).getElements(".tinymce").length){var n=a.getElements(".tinymce textarea"),l="mceAddControl";tinymce.majorVersion>3&&(l="mceAddEditor"),n.each((function(t,e){tinymce.execCommand(l,!1,t.get("id")),tinymce.get(t.get("id")).show(),$(t.get("id")).erase("required"),$(tinymce.get(t.get("id")).editorContainer).getElements("iframe")[0].set("title","MultiColumnWizard - TinyMCE")}))}},reinitStylect:function(){window.Stylect&&versionCompare("3.2.3")>=0&&($$(".styled_select").each((function(t,e){t.dispose()})),Stylect.convertSelects())}});Object.append(MultiColumnWizard,{operationLoadCallbacks:{},operationClickCallbacks:{},operationUpdateCallbacks:{},addOperationLoadCallback:function(t,e){MultiColumnWizard.operationLoadCallbacks[t]||(MultiColumnWizard.operationLoadCallbacks[t]=[]),MultiColumnWizard.operationLoadCallbacks[t].include(e)},addOperationUpdateCallback:function(t,e){MultiColumnWizard.operationUpdateCallbacks[t]||(MultiColumnWizard.operationUpdateCallbacks[t]=[]),MultiColumnWizard.operationUpdateCallbacks[t].include(e)},addOperationClickCallback:function(t,e){MultiColumnWizard.operationClickCallbacks[t]||(MultiColumnWizard.operationClickCallbacks[t]=[]),MultiColumnWizard.operationClickCallbacks[t].include(e)},insertNewElement:function(t,e){if(!0!==this.asyncBlock){this.asyncBlock=!0,t.addClass("rotate");for(var i=$(e).getParent(".tl_modulewizard.multicolumnwizard"),a=$(i).getAttribute("data-name"),n=$(i).getElements("tr"),l=0,o=0;o0&&i>=this.options.maxCount?t.setStyle("display","none"):t.setStyle("display","inline")},newClick:function(t,e){this.killAllTinyMCE(t,e);var i=e.getSiblings().length+1;if(0==this.options.maxCount||this.options.maxCount>0&&i0&&a.getElements("script").each((function(t){Browser.exec(t.get("html"))}));var n=this;a.getAllNext().each((function(t){n.updateRowAttributes(++level,t)}))}this.reinitTinyMCE(t,e,!1),this.reinitStylect()},copyUpdate:function(t,e){var i=e.getSiblings().length+1;this.options.maxCount>0&&i>=this.options.maxCount?t.setStyle("display","none"):t.setStyle("display","inline")},copyClick:function(t,e){this.killAllTinyMCE(t,e);var i=e.getSiblings().length+1;if(0==this.options.maxCount||this.options.maxCount>0&&i0&&a.getElements("script").each((function(t){Browser.exec(t.get("html"))}));var n=this;a.getAllNext().each((function(t){n.updateRowAttributes(++level,t)}))}this.reinitTinyMCE(t,e,!1),this.reinitStylect()},deleteUpdate:function(t,e){var i=e.getSiblings().length+1;this.options.minCount>0&&i<=this.options.minCount?t.setStyle("display","none"):t.setStyle("display","inline")},deleteClick:function(t,e){e.getParent(".multicolumnwizard");if(e.getSiblings().length>0){e.getAllNext();level=e.getAllPrevious().length,e.dispose(),e.destroy.delay(10,e)}},upClick:function(t,e){this.killAllTinyMCE(t,e);var i=e.getPrevious();if(i){var a=i.getAllPrevious().length;i=this.updateRowAttributes(99999,i),e=this.updateRowAttributes(a,e),i=this.updateRowAttributes(a+1,i),e.inject(i,"before")}this.reinitTinyMCE(t,e,!1)},downClick:function(t,e){this.killAllTinyMCE(t,e);var i=e.getNext();if(i){var a=e.getAllPrevious().length;e=this.updateRowAttributes(99999,e),i=this.updateRowAttributes(a,i),(e=this.updateRowAttributes(a+1,e)).inject(i,"after")}this.reinitTinyMCE(t,e,!1)},clearElementValue:function(t){"checkbox"==t.get("type")||"radio"==t.get("type")?t.checked=!1:t.set("value","")}}),MultiColumnWizard.addOperationUpdateCallback("new",MultiColumnWizard.newUpdate),MultiColumnWizard.addOperationClickCallback("new",MultiColumnWizard.insertNewElement),MultiColumnWizard.addOperationUpdateCallback("delete",MultiColumnWizard.deleteUpdate),MultiColumnWizard.addOperationClickCallback("delete",MultiColumnWizard.deleteClick),MultiColumnWizard.addOperationClickCallback("up",MultiColumnWizard.upClick),MultiColumnWizard.addOperationClickCallback("down",MultiColumnWizard.downClick),function(t){t&&(t.openModalSelectorOriginal=t.openModalSelector,t.openModalSelector=function(e){t.openModalSelectorOriginal(e);var i=null,a=60,n=new URI(e.url).getData("field")+"_parent",l=setInterval((function(){a-=1;for(var t=window.frames,o=0;o0||parseInt(i[n])>parseInt(a[n]))return 1;if(a[n]&&!i[n]&&parseInt(a[n])>0||parseInt(i[n])0&&o%l==0?++i:i,o++):d+="["+t+"]"})),"[]"==r.substr(r.length-2)&&(d+="[]"),e.setProperty("name",d)}var u;if("string"==typeOf(e.getProperty("id")))(u=e.getProperty("id").match(/^(.+)_row[0-9]+_(.+)$/i))&&e.setProperty("id",u[1]+"_row"+t+"_"+u[2]);if("string"==typeOf(e.getProperty("onclick")))(u=e.getProperty("onclick").match(/^(.+)_row[0-9]+_(.+)$/i))&&e.setProperty("onclick",u[1]+"_row"+t+"_"+u[2]);if("string"==typeOf(e.getProperty("for")))(u=e.getProperty("for").match(/^(.+)_row[0-9]+_(.+)$/i))&&e.setProperty("for",u[1]+"_row"+t+"_"+u[2]);switch(e.nodeName.toUpperCase()){case"SELECT":e.hasClass("tl_chosen")&&new Chosen(e);break;case"INPUT":"none"==e.getStyle("display").toLowerCase()&&e.setStyle("display","inline"),"string"!=typeOf(e.getProperty("id"))&&e.destroy();break;case"SCRIPT":for(var p="",m=e.get("html").toString(),C=0,h=m.search(/_row[0-9]+_/i);h>0;)C=m.match(/(_row[0-9]+)+_/i)[0].length,p=p+m.substr(0,h)+"_row"+t+"_",h=(m=m.substr(h+C)).search(/_row[0-9]+_/i);e.set("html",p+m)}}})),e},dragAndDrop:function(t,e){new Sortables(t.getParent("table").getElement("tbody"),{constrain:!0,opacity:.6,handle:"a[data-operations=move]",onComplete:function(){t.getParent("table").getElement("tbody").getChildren("tr").each((function(t,e){var a=e--;this.updateRowAttributes(a,t)}),this)}.bind(this)})},addOperationLoadCallback:function(t,e){this.operationLoadCallbacks[t]||(this.operationLoadCallbacks[t]=[]),this.operationLoadCallbacks[t].include(e)},addOperationUpdateCallback:function(t,e){this.operationUpdateCallbacks[t]||(this.operationUpdateCallbacks[t]=[]),this.operationUpdateCallbacks[t].include(e)},addOperationClickCallback:function(t,e){this.operationClickCallbacks[t]||(this.operationClickCallbacks[t]=[]),this.operationClickCallbacks[t].include(e)},killAllTinyMCE:function(t,e){var a=e.getParent(".multicolumnwizard");if(0!=a.getElements(".tinymce").length){var n=a.get("id"),i=new RegExp(n),l=new Array,o=0,r="editorId";tinymce.majorVersion>3&&(r="id"),tinymce.editors.each((function(t,e){null!=t[r].match(i)&&(l[o]=t[r],o++)})),l.each((function(t,e){try{var a=tinymce.get(t);$(a[r]).set("text",a.getContent()),a.remove()}catch(t){console.log(t)}})),a.getElements("span.mceEditor").each((function(t,e){t.dispose()})),a.getElements(".tinymce").each((function(t,e){t.getElements("script").each((function(t,e){t.dispose()}))}))}},reinitTinyMCE:function(t,e,a){var n=null;if(0!=(n=1!=a?e.getParent(".multicolumnwizard"):e).getElements(".tinymce").length){var i=n.getElements(".tinymce textarea"),l="mceAddControl";tinymce.majorVersion>3&&(l="mceAddEditor"),i.each((function(t,e){tinymce.execCommand(l,!1,t.get("id")),tinymce.get(t.get("id")).show(),$(t.get("id")).erase("required"),$(tinymce.get(t.get("id")).editorContainer).getElements("iframe")[0].set("title","MultiColumnWizard - TinyMCE")}))}},reinitStylect:function(){window.Stylect&&versionCompare("3.2.3")>=0&&($$(".styled_select").each((function(t,e){t.dispose()})),Stylect.convertSelects())}});Object.append(MultiColumnWizard,{operationLoadCallbacks:{},operationClickCallbacks:{},operationUpdateCallbacks:{},addOperationLoadCallback:function(t,e){MultiColumnWizard.operationLoadCallbacks[t]||(MultiColumnWizard.operationLoadCallbacks[t]=[]),MultiColumnWizard.operationLoadCallbacks[t].include(e)},addOperationUpdateCallback:function(t,e){MultiColumnWizard.operationUpdateCallbacks[t]||(MultiColumnWizard.operationUpdateCallbacks[t]=[]),MultiColumnWizard.operationUpdateCallbacks[t].include(e)},addOperationClickCallback:function(t,e){MultiColumnWizard.operationClickCallbacks[t]||(MultiColumnWizard.operationClickCallbacks[t]=[]),MultiColumnWizard.operationClickCallbacks[t].include(e)},insertNewElement:function(t,e){if(!0!==this.asyncBlock){this.asyncBlock=!0,t.addClass("rotate");for(var a=$(e).getParent(".tl_modulewizard.multicolumnwizard"),n=$(a).getAttribute("data-name"),i=$(a).getElements("tr"),l=0,o=0;o0&&a>=this.options.maxCount?t.setStyle("display","none"):t.setStyle("display","inline")},copyNewElement:function(t,e){if(!0===this.asyncBlock)return;this.asyncBlock=!0,t.addClass("rotate");let a=new Element("form");$(a).set("html",$(e).get("html"));let n=$(a).toQueryString().parseQueryString(),i=$(e).getParent(".tl_modulewizard.multicolumnwizard"),l=$(i).getAttribute("data-name"),o=$(i).getElements("tr"),r=0;for(let t=0;t0&&a>=this.options.maxCount?t.setStyle("display","none"):t.setStyle("display","inline")},copyClick:function(t,e){this.killAllTinyMCE(t,e);var a=e.getSiblings().length+1;if(0==this.options.maxCount||this.options.maxCount>0&&a0&&n.getElements("script").each((function(t){Browser.exec(t.get("html"))}));var i=this;n.getAllNext().each((function(t){i.updateRowAttributes(++level,t)}))}this.reinitTinyMCE(t,e,!1),this.reinitStylect()},deleteUpdate:function(t,e){var a=e.getSiblings().length+1;this.options.minCount>0&&a<=this.options.minCount?t.setStyle("display","none"):t.setStyle("display","inline")},deleteClick:function(t,e){e.getParent(".multicolumnwizard");if(e.getSiblings().length>0){e.getAllNext();level=e.getAllPrevious().length,e.dispose(),e.destroy.delay(10,e)}},upClick:function(t,e){this.killAllTinyMCE(t,e);var a=e.getPrevious();if(a){var n=a.getAllPrevious().length;a=this.updateRowAttributes(99999,a),e=this.updateRowAttributes(n,e),a=this.updateRowAttributes(n+1,a),e.inject(a,"before")}this.reinitTinyMCE(t,e,!1)},downClick:function(t,e){this.killAllTinyMCE(t,e);var a=e.getNext();if(a){var n=e.getAllPrevious().length;e=this.updateRowAttributes(99999,e),a=this.updateRowAttributes(n,a),(e=this.updateRowAttributes(n+1,e)).inject(a,"after")}this.reinitTinyMCE(t,e,!1)},clearElementValue:function(t){"checkbox"==t.get("type")||"radio"==t.get("type")?t.checked=!1:t.set("value","")}}),MultiColumnWizard.addOperationUpdateCallback("new",MultiColumnWizard.newUpdate),MultiColumnWizard.addOperationClickCallback("new",MultiColumnWizard.insertNewElement),MultiColumnWizard.addOperationUpdateCallback("copy",MultiColumnWizard.copyUpdate),MultiColumnWizard.addOperationClickCallback("copy",MultiColumnWizard.copyNewElement),MultiColumnWizard.addOperationUpdateCallback("delete",MultiColumnWizard.deleteUpdate),MultiColumnWizard.addOperationClickCallback("delete",MultiColumnWizard.deleteClick),MultiColumnWizard.addOperationClickCallback("up",MultiColumnWizard.upClick),MultiColumnWizard.addOperationClickCallback("down",MultiColumnWizard.downClick),function(t){t&&(t.openModalSelectorOriginal=t.openModalSelector,t.openModalSelector=function(e){t.openModalSelectorOriginal(e);var a=null,n=60,i=new URI(e.url).getData("field")+"_parent",l=setInterval((function(){n-=1;for(var t=window.frames,o=0;o0||parseInt(a[i])>parseInt(n[i]))return 1;if(n[i]&&!a[i]&&parseInt(n[i])>0||parseInt(a[i])0) && (innerMCWColCount % innerMCWCols) ==0) ? ++intSubLevels : intSubLevels; + intSubLevels = ((innerMCWColCount > 0) && (innerMCWColCount % innerMCWCols) == 0) ? ++intSubLevels : intSubLevels; innerMCWColCount++ - } - else - { + } else { newName += '[' + element + ']'; } }); - if(oldName.substr(oldName.length - 2) == '[]') { + if (oldName.substr(oldName.length - 2) == '[]') { newName += '[]'; } @@ -248,37 +193,30 @@ var MultiColumnWizard = new Class( } // rewrite elements id or delete input fields without an id - if (typeOf(el.getProperty('id')) == 'string') - { + if (typeOf(el.getProperty('id')) == 'string') { var erg = el.getProperty('id').match(/^(.+)_row[0-9]+_(.+)$/i); - if (erg) - { + if (erg) { el.setProperty('id', erg[1] + '_row' + level + '_' + erg[2]); } } // rewrite elements onclick (e.g. pagePicker) - if (typeOf(el.getProperty('onclick')) == 'string') - { + if (typeOf(el.getProperty('onclick')) == 'string') { var erg = el.getProperty('onclick').match(/^(.+)_row[0-9]+_(.+)$/i); - if (erg) - { + if (erg) { el.setProperty('onclick', erg[1] + '_row' + level + '_' + erg[2]); } } //rewrite elements for attribute - if (typeOf(el.getProperty('for')) == 'string') - { + if (typeOf(el.getProperty('for')) == 'string') { var erg = el.getProperty('for').match(/^(.+)_row[0-9]+_(.+)$/i); - if (erg) - { + if (erg) { el.setProperty('for', erg[1] + '_row' + level + '_' + erg[2]); } } // set attributes depending of the tag type - switch (el.nodeName.toUpperCase()) - { + switch (el.nodeName.toUpperCase()) { case 'SELECT': //create new chosen (2.11 only) @@ -286,7 +224,7 @@ var MultiColumnWizard = new Class( break; case 'INPUT': //set input field to visible - if (el.getStyle('display').toLowerCase() == 'none') el.setStyle('display','inline'); + if (el.getStyle('display').toLowerCase() == 'none') el.setStyle('display', 'inline'); // delete input field without ids (these input fields are created by JS) if (typeOf(el.getProperty('id')) != 'string') el.destroy(); break; @@ -297,15 +235,14 @@ var MultiColumnWizard = new Class( var script = el.get('html').toString(); var length = 0; var start = script.search(/_row[0-9]+_/i); - while(start > 0) - { + while (start > 0) { length = script.match(/(_row[0-9]+)+_/i)[0].length; - newScript = newScript + script.substr(0, start) + '_row' + level + '_'; + newScript = newScript + script.substr(0, start) + '_row' + level + '_'; script = script.substr(start + length); start = script.search(/_row[0-9]+_/i); } - el.set('html', newScript+script); + el.set('html', newScript + script); break; } @@ -319,13 +256,13 @@ var MultiColumnWizard = new Class( * @param element table row * @param element move button */ - dragAndDrop: function(tr, link) { + dragAndDrop: function (tr, link) { new Sortables(tr.getParent('table').getElement('tbody'), { constrain: true, opacity: 0.6, handle: 'a[data-operations=move]', - onComplete: function() { - tr.getParent('table').getElement('tbody').getChildren('tr').each(function(el, i) { + onComplete: function () { + tr.getParent('table').getElement('tbody').getChildren('tr').each(function (el, i) { //Must be substract down 1 because the loop iterator begins with 1 var level = i--; this.updateRowAttributes(level, el); @@ -340,10 +277,8 @@ var MultiColumnWizard = new Class( * @param string the key e.g. 'copy' - your button has to have the matching data-operations="" attribute (...) * @param function callback */ - addOperationLoadCallback: function(key, func) - { - if (!this.operationLoadCallbacks[key]) - { + addOperationLoadCallback: function (key, func) { + if (!this.operationLoadCallbacks[key]) { this.operationLoadCallbacks[key] = []; } @@ -355,10 +290,8 @@ var MultiColumnWizard = new Class( * @param string the key e.g. 'copy' - your button has to have the matching data-operations="" attribute (...) * @param function callback */ - addOperationUpdateCallback: function(key, func) - { - if (!this.operationUpdateCallbacks[key]) - { + addOperationUpdateCallback: function (key, func) { + if (!this.operationUpdateCallbacks[key]) { this.operationUpdateCallbacks[key] = []; } @@ -370,23 +303,19 @@ var MultiColumnWizard = new Class( * @param string the key e.g. 'copy' - your button has to have the matching data-operations="" attribute (...) * @param function callback */ - addOperationClickCallback: function(key, func) - { - if (!this.operationClickCallbacks[key]) - { + addOperationClickCallback: function (key, func) { + if (!this.operationClickCallbacks[key]) { this.operationClickCallbacks[key] = []; } this.operationClickCallbacks[key].include(func); }, - killAllTinyMCE: function(el, row) - { + killAllTinyMCE: function (el, row) { var parent = row.getParent('.multicolumnwizard'); // skip if no tinymce class was found - if(parent.getElements('.tinymce').length == 0) - { + if (parent.getElements('.tinymce').length == 0) { return; } @@ -401,16 +330,15 @@ var MultiColumnWizard = new Class( } // get a list with tinymces - tinymce.editors.each(function(item, index){ - if(item[editorId].match(myRegex) != null) - { + tinymce.editors.each(function (item, index) { + if (item[editorId].match(myRegex) != null) { tinyMCEEditors[counter] = item[editorId]; counter++; } }); // clear tinymces - tinyMCEEditors.each(function(item, index){ + tinyMCEEditors.each(function (item, index) { try { var editor = tinymce.get(item); $(editor[editorId]).set('text', editor.getContent()); @@ -421,34 +349,29 @@ var MultiColumnWizard = new Class( }); // search for dmg tinymces - parent.getElements('span.mceEditor').each(function(item, index){ + parent.getElements('span.mceEditor').each(function (item, index) { item.dispose(); }); // search for scripttags tinymces - parent.getElements('.tinymce').each(function(item, index){ - item.getElements('script').each(function(item, index){ + parent.getElements('.tinymce').each(function (item, index) { + item.getElements('script').each(function (item, index) { item.dispose(); }); }); }, - reinitTinyMCE: function(el, row, isParent) - { + reinitTinyMCE: function (el, row, isParent) { var parent = null; - if(isParent != true) - { + if (isParent != true) { parent = row.getParent('.multicolumnwizard'); - } - else - { + } else { parent = row; } // skip if no tinymce class was found - if(parent.getElements('.tinymce').length == 0) - { + if (parent.getElements('.tinymce').length == 0) { return; } @@ -459,22 +382,20 @@ var MultiColumnWizard = new Class( addEditorCommand = 'mceAddEditor'; } - varTinys.each(function(item, index){ + varTinys.each(function (item, index) { tinymce.execCommand(addEditorCommand, false, item.get('id')); tinymce.get(item.get('id')).show(); $(item.get('id')).erase('required'); - $(tinymce.get(item.get('id')).editorContainer).getElements('iframe')[0].set('title','MultiColumnWizard - TinyMCE'); + $(tinymce.get(item.get('id')).editorContainer).getElements('iframe')[0].set('title', 'MultiColumnWizard - TinyMCE'); }); }, - reinitStylect: function() - { + reinitStylect: function () { - if(window.Stylect) - { + if (window.Stylect) { if (versionCompare('3.2.3') >= 0) { - $$('.styled_select').each(function(item, index){ + $$('.styled_select').each(function (item, index) { item.dispose(); }); Stylect.convertSelects(); @@ -498,10 +419,8 @@ Object.append(MultiColumnWizard, * @param string the key e.g. 'copy' - your button has to have the matching data-operations="" attribute (...) * @param function callback */ - addOperationLoadCallback: function(key, func) - { - if (!MultiColumnWizard.operationLoadCallbacks[key]) - { + addOperationLoadCallback: function (key, func) { + if (!MultiColumnWizard.operationLoadCallbacks[key]) { MultiColumnWizard.operationLoadCallbacks[key] = []; } @@ -513,10 +432,8 @@ Object.append(MultiColumnWizard, * @param string the key e.g. 'copy' - your button has to have the matching data-operations="" attribute (...) * @param function callback */ - addOperationUpdateCallback: function(key, func) - { - if (!MultiColumnWizard.operationUpdateCallbacks[key]) - { + addOperationUpdateCallback: function (key, func) { + if (!MultiColumnWizard.operationUpdateCallbacks[key]) { MultiColumnWizard.operationUpdateCallbacks[key] = []; } @@ -529,10 +446,8 @@ Object.append(MultiColumnWizard, * @param string the key e.g. 'copy' - your button has to have the matching data-operations="" attribute (...) * @param function callback */ - addOperationClickCallback: function(key, func) - { - if (!MultiColumnWizard.operationClickCallbacks[key]) - { + addOperationClickCallback: function (key, func) { + if (!MultiColumnWizard.operationClickCallbacks[key]) { MultiColumnWizard.operationClickCallbacks[key] = []; } @@ -549,7 +464,7 @@ Object.append(MultiColumnWizard, * @return void */ insertNewElement: function (el, row) { - if(this.asyncBlock === true){ + if (this.asyncBlock === true) { return; } this.asyncBlock = true; @@ -559,60 +474,57 @@ Object.append(MultiColumnWizard, var fieldName = $(parentMcw).getAttribute('data-name'); var rows = $(parentMcw).getElements('tr'); var maxRowId = 0; - for (var i = 0; i < rows.length; i++) - { + for (var i = 0; i < rows.length; i++) { maxRowId = Math.max(maxRowId, ($(rows[i]).getAttribute('data-rowid'))); } var self = this; new Request.Contao({ evalScripts: false, - onSuccess: function (txt, json) { - el.removeClass('rotate'); - // Text to html. - var newEl = new Element('div', { - html: json.content - }); - // Inject it on the right place. - var newRow = $(newEl).getElement('tr') - newRow.inject(row, 'after'); - // Execute the JS from widgets. - json.javascript && Browser.exec(json.javascript); - // Rebind the events. - newRow.getElements('td.operations a').each(function (operation) { - var key = operation.get('data-operations'); - - // call static load callbacks - if (MultiColumnWizard.operationLoadCallbacks[key]) - { - MultiColumnWizard.operationLoadCallbacks[key].each(function (callback) { - callback.pass([operation, el], self)(); - }); - } - - // call instance load callbacks - if (self.operationLoadCallbacks[key]) - { - self.operationLoadCallbacks[key].each(function (callback) { - callback.pass([operation, el], self)(); - }); - } - }); - // Add init chosen if tl_chosen defined in select for the new row. - if (Elements.chosen !== undefined) { - newRow.getElements('select.tl_chosen').chosen(); - } - self.updateOperations(); - self.asyncBlock = false; + onSuccess: function (txt, json) { + el.removeClass('rotate'); + // Text to html. + var newEl = new Element('div', { + html: json.content + }); + // Inject it on the right place. + var newRow = $(newEl).getElement('tr') + newRow.inject(row, 'after'); + // Execute the JS from widgets. + json.javascript && Browser.exec(json.javascript); + // Rebind the events. + newRow.getElements('td.operations a').each(function (operation) { + var key = operation.get('data-operations'); + + // call static load callbacks + if (MultiColumnWizard.operationLoadCallbacks[key]) { + MultiColumnWizard.operationLoadCallbacks[key].each(function (callback) { + callback.pass([operation, el], self)(); + }); + } + + // call instance load callbacks + if (self.operationLoadCallbacks[key]) { + self.operationLoadCallbacks[key].each(function (callback) { + callback.pass([operation, el], self)(); + }); + } + }); + // Add init chosen if tl_chosen defined in select for the new row. + if (Elements.chosen !== undefined) { + newRow.getElements('select.tl_chosen').chosen(); + } + self.updateOperations(); + self.asyncBlock = false; }, - onFailure: function(xhr){ + onFailure: function (xhr) { el.removeClass('rotate'); self.asyncBlock = false; } }).post({ - "action": "mcwCreateNewRow", - "name": fieldName, - "maxRowId": maxRowId, + "action": "mcwCreateNewRow", + "name": fieldName, + "maxRowId": maxRowId, "REQUEST_TOKEN": Contao.request_token }); }, @@ -622,75 +534,99 @@ Object.append(MultiColumnWizard, * @param Element the icon element * @param Element the row */ - newUpdate: function(el, row) - { + newUpdate: function (el, row) { var rowCount = row.getSiblings().length + 1; // remove the copy possibility if we have already reached maxCount - if (this.options.maxCount > 0 && rowCount >= this.options.maxCount) - { + if (this.options.maxCount > 0 && rowCount >= this.options.maxCount) { el.setStyle('display', 'none'); - }else{ + } else { el.setStyle('display', 'inline'); } }, - /** - * Operation "new" - click - * @param Element the icon element - * @param Element the row + * Call PHP/Contao/Widget to get a new row. + * + * @param el The button. + * + * @param row The tr of the table. + * + * @return void */ - newClick: function(el, row) - { - this.killAllTinyMCE(el, row); - - var rowCount = row.getSiblings().length + 1; - - // check maxCount for an inject - if (this.options.maxCount == 0 || (this.options.maxCount > 0 && rowCount < this.options.maxCount)) - { - var copy = row.clone(true,true); - - // clear all elements - copy.getElements('input,select,textarea').each(function(el){ - MultiColumnWizard.clearElementValue(el); - }); + copyNewElement: function (el, row) { + // Check if already a run is running. + if (this.asyncBlock === true) { + return; + } + this.asyncBlock = true; + el.addClass('rotate'); - // get the current level of the row - level = row.getAllPrevious().length; + // First try to make a form from it and get a usable query string. + let newEl = new Element('form'); + $(newEl).set('html', $(row).get('html')); + let queryString = $(newEl).toQueryString().parseQueryString() + + // Define some more information. + let parentMcw = $(row).getParent('.tl_modulewizard.multicolumnwizard'); + let fieldName = $(parentMcw).getAttribute('data-name'); + let rows = $(parentMcw).getElements('tr'); + let maxRowId = 0; + for (let i = 0; i < rows.length; i++) { + maxRowId = Math.max(maxRowId, ($(rows[i]).getAttribute('data-rowid'))); + } - // update the row attributes - copy = this.updateRowAttributes(++level, copy); - copy.inject(row, 'after'); + // Add al missing query parameter. + queryString.action = 'mcwCopyNewRow'; + queryString.name = fieldName; + queryString.maxRowId = maxRowId; + queryString.REQUEST_TOKEN = Contao.request_token; - // update tooltips - copy.getElements('a[data-operations]').each(function(el) { - $$(el).set('title', $$(el).getElement('img').get('alt')); - new Tips.Contao($$(el).filter(function(i) { - return i.title != ''; - }), { - offset: {x:0, y:26} + // Setup request. + let self = this; + new Request.Contao({ + evalScripts: false, + onSuccess: function (txt, json) { + el.removeClass('rotate'); + // Text to html. + let newEl = new Element('div', { + html: json.content }); - }); + // Inject it on the right place. + let newRow = $(newEl).getElement('tr') + newRow.inject(row, 'after'); + // Execute the JS from widgets. + json.javascript && Browser.exec(json.javascript); + // Rebind the events. + newRow.getElements('td.operations a').each(function (operation) { + let key = operation.get('data-operations'); + + // call static load callbacks + if (MultiColumnWizard.operationLoadCallbacks[key]) { + MultiColumnWizard.operationLoadCallbacks[key].each(function (callback) { + callback.pass([operation, el], self)(); + }); + } - // exec script - if (copy.getElements('script').length > 0) - { - copy.getElements('script').each(function(script){ - Browser.exec(script.get('html')); + // call instance load callbacks + if (MultiColumnWizard.operationLoadCallbacks[key]) { + MultiColumnWizard.operationLoadCallbacks[key].each(function (callback) { + callback.pass([operation, el], self)(); + }); + } }); + // Add init chosen if tl_chosen defined in select for the new row. + if (Elements.chosen !== undefined) { + newRow.getElements('select.tl_chosen').chosen(); + } + self.updateOperations(); + self.asyncBlock = false; + }, + onFailure: function (xhr) { + el.removeClass('rotate'); + self.asyncBlock = false; } - - // update the row attribute of the following rows - var that = this; - copy.getAllNext().each(function(row){ - that.updateRowAttributes(++level, row); - }); - } - - this.reinitTinyMCE(el, row, false); - this.reinitStylect(); + }).post(queryString); }, /** @@ -698,15 +634,13 @@ Object.append(MultiColumnWizard, * @param Element the icon element * @param Element the row */ - copyUpdate: function(el, row) - { + copyUpdate: function (el, row) { var rowCount = row.getSiblings().length + 1; // remove the copy possibility if we have already reached maxCount - if (this.options.maxCount > 0 && rowCount >= this.options.maxCount) - { + if (this.options.maxCount > 0 && rowCount >= this.options.maxCount) { el.setStyle('display', 'none'); - }else{ + } else { el.setStyle('display', 'inline'); } }, @@ -716,16 +650,14 @@ Object.append(MultiColumnWizard, * @param Element the icon element * @param Element the row */ - copyClick: function(el, row) - { + copyClick: function (el, row) { this.killAllTinyMCE(el, row); var rowCount = row.getSiblings().length + 1; // check maxCount for an inject - if (this.options.maxCount == 0 || (this.options.maxCount > 0 && rowCount < this.options.maxCount)) - { - var copy = row.clone(true,true); + if (this.options.maxCount == 0 || (this.options.maxCount > 0 && rowCount < this.options.maxCount)) { + var copy = row.clone(true, true); // get the current level of the row level = row.getAllPrevious().length; @@ -735,26 +667,25 @@ Object.append(MultiColumnWizard, copy.inject(row, 'after'); // update tooltips - copy.getElements('a[data-operations]').each(function(el) { + copy.getElements('a[data-operations]').each(function (el) { $$(el).set('title', $$(el).getElement('img').get('alt')); - new Tips.Contao($$(el).filter(function(i) { + new Tips.Contao($$(el).filter(function (i) { return i.title != ''; }), { - offset: {x:0, y:26} + offset: {x: 0, y: 26} }); }); // exec script - if (copy.getElements('script').length > 0) - { - copy.getElements('script').each(function(script){ + if (copy.getElements('script').length > 0) { + copy.getElements('script').each(function (script) { Browser.exec(script.get('html')); }); } // update the row attribute of the following rows var that = this; - copy.getAllNext().each(function(row){ + copy.getAllNext().each(function (row) { that.updateRowAttributes(++level, row); }); } @@ -768,17 +699,13 @@ Object.append(MultiColumnWizard, * @param Element the icon element * @param Element the row */ - deleteUpdate: function(el, row) - { + deleteUpdate: function (el, row) { var rowCount = row.getSiblings().length + 1; // remove the delete possibility if necessary - if (this.options.minCount > 0 && rowCount <= this.options.minCount) - { + if (this.options.minCount > 0 && rowCount <= this.options.minCount) { el.setStyle('display', 'none'); - } - else - { + } else { el.setStyle('display', 'inline'); } }, @@ -788,8 +715,7 @@ Object.append(MultiColumnWizard, * @param Element the icon element * @param Element the row */ - deleteClick: function(el, row) - { + deleteClick: function (el, row) { var parent = row.getParent('.multicolumnwizard'); if (row.getSiblings().length > 0) { @@ -809,13 +735,11 @@ Object.append(MultiColumnWizard, * @param Element the icon element * @param Element the row */ - upClick: function(el, row) - { + upClick: function (el, row) { this.killAllTinyMCE(el, row); var previous = row.getPrevious(); - if (previous) - { + if (previous) { // update the attributes so the order remains as desired // we have to set it to a value that is not in the DOM first, otherwise the values will get lost!! var previousPosition = previous.getAllPrevious().length; @@ -825,7 +749,7 @@ Object.append(MultiColumnWizard, // now set the correct values again row = this.updateRowAttributes(previousPosition, row); - previous = this.updateRowAttributes(previousPosition+1, previous); + previous = this.updateRowAttributes(previousPosition + 1, previous); row.inject(previous, 'before'); } @@ -838,13 +762,11 @@ Object.append(MultiColumnWizard, * @param Element the icon element * @param Element the row */ - downClick: function(el, row) - { + downClick: function (el, row) { this.killAllTinyMCE(el, row); var next = row.getNext(); - if (next) - { + if (next) { // update the attributes so the order remains as desired // we have to set it to a value that is not in the DOM first, otherwise the values will get lost!! var rowPosition = row.getAllPrevious().length; @@ -854,7 +776,7 @@ Object.append(MultiColumnWizard, // now set the correct values again next = this.updateRowAttributes(rowPosition, next); - row = this.updateRowAttributes(rowPosition+1, row); + row = this.updateRowAttributes(rowPosition + 1, row); row.inject(next, 'after'); } @@ -865,50 +787,47 @@ Object.append(MultiColumnWizard, /** * @param Element the element which should be cleared */ - clearElementValue: function(el) - { - if (el.get('type') == 'checkbox' || el.get('type') == 'radio') - { + clearElementValue: function (el) { + if (el.get('type') == 'checkbox' || el.get('type') == 'radio') { el.checked = false; - } - else - { + } else { el.set('value', ''); } } }); - /** * Register default callbacks */ -// MultiColumnWizard.addOperationClickCallback('new', MultiColumnWizard.newClick); -// MultiColumnWizard.addOperationUpdateCallback('copy', MultiColumnWizard.copyUpdate); -// MultiColumnWizard.addOperationClickCallback('copy', MultiColumnWizard.copyClick); - +// New callback. MultiColumnWizard.addOperationUpdateCallback('new', MultiColumnWizard.newUpdate); MultiColumnWizard.addOperationClickCallback('new', MultiColumnWizard.insertNewElement); +// Copy callback. +MultiColumnWizard.addOperationUpdateCallback('copy', MultiColumnWizard.copyUpdate); +MultiColumnWizard.addOperationClickCallback('copy', MultiColumnWizard.copyNewElement); +// Delete callback. MultiColumnWizard.addOperationUpdateCallback('delete', MultiColumnWizard.deleteUpdate); MultiColumnWizard.addOperationClickCallback('delete', MultiColumnWizard.deleteClick); +// Movements. MultiColumnWizard.addOperationClickCallback('up', MultiColumnWizard.upClick); MultiColumnWizard.addOperationClickCallback('down', MultiColumnWizard.downClick); /** * Patch Contao Core to support file & page tree */ -(function(Backend) { - if(!Backend) return; +(function (Backend) { + if (!Backend) return; Backend.openModalSelectorOriginal = Backend.openModalSelector; - Backend.openModalSelector = function(options) { + Backend.openModalSelector = function (options) { Backend.openModalSelectorOriginal(options); var frm = null; var tProtect = 60; - var id = new URI(options.url).getData('field')+'_parent'; - var timer = setInterval(function() { + var id = new URI(options.url).getData('field') + '_parent'; + var timer = setInterval(function () { tProtect -= 1; var frms = window.frames; - for (var i=0; i 0) || (parseInt(a[i]) > parseInt(b[i]))) {