diff --git a/administrator/templates/hathor/css/colour_blue.css b/administrator/templates/hathor/css/colour_blue.css
index 3517a37764e39..c7d391a6a8489 100644
--- a/administrator/templates/hathor/css/colour_blue.css
+++ b/administrator/templates/hathor/css/colour_blue.css
@@ -328,6 +328,21 @@
border-radius: 3px;
width: 175px;
}
+.subform-repeatable-wrapper div.btn-toolbar {
+ float: none;
+}
+.subform-repeatable-wrapper .text-right {
+ text-align: right;
+}
+.subform-repeatable-wrapper .ui-sortable-helper {
+ background: #ffffff;
+}
+.subform-repeatable-wrapper tr.ui-sortable-helper {
+ display: table;
+}
+.subform-repeatable-wrapper .subform-repeatable-group {
+ clear: both;
+}
.label,
.badge {
display: inline-block;
diff --git a/administrator/templates/hathor/css/colour_brown.css b/administrator/templates/hathor/css/colour_brown.css
index 8f98d221f0316..cd941f3badedb 100644
--- a/administrator/templates/hathor/css/colour_brown.css
+++ b/administrator/templates/hathor/css/colour_brown.css
@@ -328,6 +328,21 @@
border-radius: 3px;
width: 175px;
}
+.subform-repeatable-wrapper div.btn-toolbar {
+ float: none;
+}
+.subform-repeatable-wrapper .text-right {
+ text-align: right;
+}
+.subform-repeatable-wrapper .ui-sortable-helper {
+ background: #ffffff;
+}
+.subform-repeatable-wrapper tr.ui-sortable-helper {
+ display: table;
+}
+.subform-repeatable-wrapper .subform-repeatable-group {
+ clear: both;
+}
.label,
.badge {
display: inline-block;
diff --git a/administrator/templates/hathor/css/colour_standard.css b/administrator/templates/hathor/css/colour_standard.css
index cf849953d8d32..90fd8e02f9db5 100644
--- a/administrator/templates/hathor/css/colour_standard.css
+++ b/administrator/templates/hathor/css/colour_standard.css
@@ -328,6 +328,21 @@
border-radius: 3px;
width: 175px;
}
+.subform-repeatable-wrapper div.btn-toolbar {
+ float: none;
+}
+.subform-repeatable-wrapper .text-right {
+ text-align: right;
+}
+.subform-repeatable-wrapper .ui-sortable-helper {
+ background: #ffffff;
+}
+.subform-repeatable-wrapper tr.ui-sortable-helper {
+ display: table;
+}
+.subform-repeatable-wrapper .subform-repeatable-group {
+ clear: both;
+}
.label,
.badge {
display: inline-block;
diff --git a/administrator/templates/hathor/less/forms.less b/administrator/templates/hathor/less/forms.less
index d4ba17dc371a5..bf43faa937dc7 100644
--- a/administrator/templates/hathor/less/forms.less
+++ b/administrator/templates/hathor/less/forms.less
@@ -170,3 +170,27 @@
.border-radius(@inputBorderRadius);
width: 175px;
}
+
+/* Field subform repeatable */
+.subform-repeatable-wrapper{
+
+ div.btn-toolbar{
+ float: none;
+ }
+
+ .text-right{
+ text-align: right;
+ }
+
+ .ui-sortable-helper{
+ background: @white;
+ }
+
+ tr.ui-sortable-helper{
+ display: table;
+ }
+
+ .subform-repeatable-group{
+ clear: both;
+ }
+}
diff --git a/administrator/templates/isis/css/template-rtl.css b/administrator/templates/isis/css/template-rtl.css
index a50d5fa4ca508..1d2be9a2e2cc8 100644
--- a/administrator/templates/isis/css/template-rtl.css
+++ b/administrator/templates/isis/css/template-rtl.css
@@ -1674,6 +1674,15 @@ legend + .control-group {
.control-label .hasTooltip {
display: inline-block;
}
+.subform-repeatable-wrapper .btn-group>.btn.button {
+ min-width: 0;
+}
+.subform-repeatable-wrapper .ui-sortable-helper {
+ background: #fff;
+}
+.subform-repeatable-wrapper tr.ui-sortable-helper {
+ display: table;
+}
table {
max-width: 100%;
background-color: transparent;
diff --git a/administrator/templates/isis/css/template.css b/administrator/templates/isis/css/template.css
index fa27790ee7a31..1a4dfe32d6c0a 100644
--- a/administrator/templates/isis/css/template.css
+++ b/administrator/templates/isis/css/template.css
@@ -1674,6 +1674,15 @@ legend + .control-group {
.control-label .hasTooltip {
display: inline-block;
}
+.subform-repeatable-wrapper .btn-group>.btn.button {
+ min-width: 0;
+}
+.subform-repeatable-wrapper .ui-sortable-helper {
+ background: #fff;
+}
+.subform-repeatable-wrapper tr.ui-sortable-helper {
+ display: table;
+}
table {
max-width: 100%;
background-color: transparent;
diff --git a/layouts/joomla/form/field/subform/default.php b/layouts/joomla/form/field/subform/default.php
new file mode 100644
index 0000000000000..ae5908de372cc
--- /dev/null
+++ b/layouts/joomla/form/field/subform/default.php
@@ -0,0 +1,37 @@
+
+
+
+getGroup('') as $field): ?>
+ renderField(); ?>
+
+
+
diff --git a/layouts/joomla/form/field/subform/repeatable-table.php b/layouts/joomla/form/field/subform/repeatable-table.php
new file mode 100644
index 0000000000000..938aaf27c9d63
--- /dev/null
+++ b/layouts/joomla/form/field/subform/repeatable-table.php
@@ -0,0 +1,104 @@
+getFieldsets() as $fieldset) {
+ $table_head .= '' . JText::_($fieldset->label);
+
+ if (!empty($fieldset->description))
+ {
+ $table_head .= ' ' . JText::_($fieldset->description) . '';
+ }
+
+ $table_head .= ' | ';
+ }
+
+ $sublayout = 'section-byfieldsets';
+}
+else
+{
+ foreach($tmpl->getGroup('') as $field) {
+ $table_head .= '' . strip_tags($field->label);
+ $table_head .= ' ' . JText::_($field->description) . '';
+ $table_head .= ' | ';
+ }
+
+ $sublayout = 'section';
+}
+
+?>
+
+
diff --git a/layouts/joomla/form/field/subform/repeatable-table/section-byfieldsets.php b/layouts/joomla/form/field/subform/repeatable-table/section-byfieldsets.php
new file mode 100644
index 0000000000000..8386cc2e0dd89
--- /dev/null
+++ b/layouts/joomla/form/field/subform/repeatable-table/section-byfieldsets.php
@@ -0,0 +1,41 @@
+
+
+
+ getFieldsets() as $fieldset): ?>
+
+ getFieldset($fieldset->name) as $field): ?>
+ renderField(); ?>
+
+ |
+
+
+
+
+ |
+
+
diff --git a/layouts/joomla/form/field/subform/repeatable-table/section.php b/layouts/joomla/form/field/subform/repeatable-table/section.php
new file mode 100644
index 0000000000000..3769d41366b86
--- /dev/null
+++ b/layouts/joomla/form/field/subform/repeatable-table/section.php
@@ -0,0 +1,39 @@
+
+
+
+ getGroup('') as $field): ?>
+
+ renderField(); ?>
+ |
+
+
+
+
+ |
+
+
diff --git a/layouts/joomla/form/field/subform/repeatable.php b/layouts/joomla/form/field/subform/repeatable.php
new file mode 100644
index 0000000000000..a15fe26be9190
--- /dev/null
+++ b/layouts/joomla/form/field/subform/repeatable.php
@@ -0,0 +1,63 @@
+
+
+
diff --git a/layouts/joomla/form/field/subform/repeatable/section-byfieldsets.php b/layouts/joomla/form/field/subform/repeatable/section-byfieldsets.php
new file mode 100644
index 0000000000000..980c81f285d4b
--- /dev/null
+++ b/layouts/joomla/form/field/subform/repeatable/section-byfieldsets.php
@@ -0,0 +1,45 @@
+
+
+
diff --git a/layouts/joomla/form/field/subform/repeatable/section.php b/layouts/joomla/form/field/subform/repeatable/section.php
new file mode 100644
index 0000000000000..1a6962e865285
--- /dev/null
+++ b/layouts/joomla/form/field/subform/repeatable/section.php
@@ -0,0 +1,38 @@
+
+
+
diff --git a/libraries/joomla/form/fields/repeatable.php b/libraries/joomla/form/fields/repeatable.php
index cc316d343b4db..80dcb895d2462 100644
--- a/libraries/joomla/form/fields/repeatable.php
+++ b/libraries/joomla/form/fields/repeatable.php
@@ -13,7 +13,9 @@
* Form Field class for the Joomla Platform.
* Display a JSON loaded window with a repeatable set of sub fields
*
- * @since 3.2
+ * @since 3.2
+ *
+ * @deprecated 4.0 Use JFormFieldSubform
*/
class JFormFieldRepeatable extends JFormField
{
@@ -34,6 +36,8 @@ class JFormFieldRepeatable extends JFormField
*/
protected function getInput()
{
+ JLog::add('JFormFieldRepeatable is deprecated. Use JFormFieldSubform instead.', JLog::WARNING, 'deprecated');
+
// Initialize variables.
$subForm = new JForm($this->name, array('control' => 'jform'));
$xml = $this->element->children()->asXml();
diff --git a/libraries/joomla/form/fields/subform.php b/libraries/joomla/form/fields/subform.php
new file mode 100644
index 0000000000000..d9338849c0329
--- /dev/null
+++ b/libraries/joomla/form/fields/subform.php
@@ -0,0 +1,348 @@
+
+ *
+ * @since 3.6
+ */
+class JFormFieldSubform extends JFormField
+{
+ /**
+ * The form field type.
+ * @var string
+ */
+ protected $type = 'Subform';
+
+ /**
+ * Form source
+ * @var string
+ */
+ protected $formsource;
+
+ /**
+ * Minimum items in repeat mode
+ * @var int
+ */
+ protected $min = 0;
+
+ /**
+ * Maximum items in repeat mode
+ * @var int
+ */
+ protected $max = 1000;
+
+ /**
+ * Layout to render the form
+ * @var string
+ */
+ protected $layout = 'joomla.form.field.subform.default';
+
+ /**
+ * Whether group subform fields by it`s fieldset
+ * @var boolean
+ */
+ protected $groupByFieldset = false;
+
+ /**
+ * Which buttons to show in miltiple mode
+ * @var array $buttons
+ */
+ protected $buttons = array('add' => true, 'remove' => true, 'move' => true);
+
+ /**
+ * Method to get certain otherwise inaccessible properties from the form field object.
+ *
+ * @param string $name The property name for which to the the value.
+ *
+ * @return mixed The property value or null.
+ *
+ * @since 3.6
+ */
+ public function __get($name)
+ {
+ switch ($name)
+ {
+ case 'formsource':
+ case 'min':
+ case 'max':
+ case 'layout':
+ case 'groupByFieldset':
+ case 'buttons':
+ return $this->$name;
+ }
+
+ return parent::__get($name);
+ }
+
+ /**
+ * Method to set certain otherwise inaccessible properties of the form field object.
+ *
+ * @param string $name The property name for which to the the value.
+ * @param mixed $value The value of the property.
+ *
+ * @return void
+ *
+ * @since 3.6
+ */
+ public function __set($name, $value)
+ {
+ switch ($name)
+ {
+ case 'formsource':
+ $this->formsource = (string) $value;
+
+ // Add root path if we have a path to XML file
+ if (strrpos($this->formsource, '.xml') === strlen($this->formsource) - 4)
+ {
+ $this->formsource = JPath::clean(JPATH_ROOT . '/' . $this->formsource);
+ }
+
+ break;
+
+ case 'min':
+ $this->min = (int) $value;
+ break;
+
+ case 'max':
+ $this->max = max(1, (int) $value);
+ break;
+
+ case 'groupByFieldset':
+ $value = (string) $value;
+ $this->groupByFieldset = !($value === 'false' || $value === 'off' || $value === '0');
+ break;
+
+ case 'layout':
+ $this->layout = (string) $value;
+
+ // Make sure the layout is not empty.
+ if (!$this->layout)
+ {
+ // Set default value depend from "multiple" mode
+ $this->layout = !$this->multiple ? 'joomla.form.field.subform.default' : 'joomla.form.field.subform.repeatable';
+ }
+
+ break;
+
+ case 'buttons':
+
+ if (!$this->multiple)
+ {
+ $this->buttons = array();
+ break;
+ }
+
+ if ($value && !is_array($value))
+ {
+ $value = explode(',', (string) $value);
+ $value = array_fill_keys(array_filter($value), true);
+ }
+
+ if ($value)
+ {
+ $value = array_merge(array('add' => false, 'remove' => false, 'move' => false), $value);
+ $this->buttons = $value;
+ }
+
+ break;
+
+ default:
+ parent::__set($name, $value);
+ }
+ }
+
+ /**
+ * Method to attach a JForm object to the field.
+ *
+ * @param SimpleXMLElement $element The SimpleXMLElement object representing the tag for the form field object.
+ * @param mixed $value The form field value to validate.
+ * @param string $group The field name group control value.
+ *
+ * @return boolean True on success.
+ *
+ * @since 3.6
+ */
+ public function setup(SimpleXMLElement $element, $value, $group = null)
+ {
+ if (!parent::setup($element, $value, $group))
+ {
+ return false;
+ }
+
+ foreach (array('formsource', 'min', 'max', 'layout', 'groupByFieldset', 'buttons') as $attributeName)
+ {
+ $this->__set($attributeName, $element[$attributeName]);
+ }
+
+ if ($this->value && is_string($this->value))
+ {
+ // Guess here is the JSON string from 'default' attribute
+ $this->value = json_decode($this->value, true);
+ }
+
+ return true;
+ }
+
+ /**
+ * Method to get the field input markup.
+ *
+ * @return string The field input markup.
+ *
+ * @since 3.6
+ */
+ protected function getInput()
+ {
+ $value = $this->value ? $this->value : array();
+
+ // Prepare data for renderer
+ $data = parent::getLayoutData();
+ $tmpl = null;
+ $forms = array();
+ $control = $this->name;
+
+ try
+ {
+ // Prepare the form template
+ $formname = 'subform' . ($this->group ? $this->group . '.' : '.') . $this->fieldname;
+ $tmplcontrol = !$this->multiple ? $control : $control . '[' . $this->fieldname . 'X]';
+ $tmpl = JForm::getInstance($formname, $this->formsource, array('control' => $tmplcontrol));
+
+ // Prepare the forms for exiting values
+ if ($this->multiple)
+ {
+ $value = array_values($value);
+ $c = max($this->min, min(count($value), $this->max));
+ for ($i = 0; $i < $c; $i++)
+ {
+ $itemcontrol = $control . '[' . $this->fieldname . $i . ']';
+ $itemform = JForm::getInstance($formname . $i, $this->formsource, array('control' => $itemcontrol));
+
+ if (!empty($value[$i]))
+ {
+ $itemform->bind($value[$i]);
+ }
+
+ $forms[] = $itemform;
+ }
+ }
+ else
+ {
+ $tmpl->bind($value);
+ $forms[] = $tmpl;
+ }
+ }
+ catch (Exception $e)
+ {
+ return $e->getMessage();
+ }
+
+ $data['tmpl'] = $tmpl;
+ $data['forms'] = $forms;
+ $data['min'] = $this->min;
+ $data['max'] = $this->max;
+ $data['control'] = $control;
+ $data['buttons'] = $this->buttons;
+ $data['fieldname'] = $this->fieldname;
+ $data['groupByFieldset'] = $this->groupByFieldset;
+
+ // Prepare renderer
+ $renderer = $this->getRenderer($this->layout);
+
+ // Allow to define some JLayout options as attribute of the element
+ if ($this->element['component'])
+ {
+ $renderer->setComponent((string) $this->element['component']);
+ }
+
+ if ($this->element['client'])
+ {
+ $renderer->setClient((string) $this->element['client']);
+ }
+
+ // Render
+ $html = $renderer->render($data);
+
+ // Add hidden input on front of the subform inputs, in multiple mode
+ // for allow to submit an empty value
+ if ($this->multiple)
+ {
+ $html = '' . $html;
+ }
+
+ return $html;
+ }
+
+ /**
+ * Method to get the name used for the field input tag.
+ *
+ * @param string $fieldName The field element name.
+ *
+ * @return string The name to be used for the field input tag.
+ *
+ * @since 3.6
+ */
+ protected function getName($fieldName)
+ {
+ $name = '';
+
+ // If there is a form control set for the attached form add it first.
+ if ($this->formControl)
+ {
+ $name .= $this->formControl;
+ }
+
+ // If the field is in a group add the group control to the field name.
+ if ($this->group)
+ {
+ // If we already have a name segment add the group control as another level.
+ $groups = explode('.', $this->group);
+
+ if ($name)
+ {
+ foreach ($groups as $group)
+ {
+ $name .= '[' . $group . ']';
+ }
+ }
+ else
+ {
+ $name .= array_shift($groups);
+
+ foreach ($groups as $group)
+ {
+ $name .= '[' . $group . ']';
+ }
+ }
+ }
+
+ // If we already have a name segment add the field name as another level.
+ if ($name)
+ {
+ $name .= '[' . $fieldName . ']';
+ }
+ else
+ {
+ $name .= $fieldName;
+ }
+
+ return $name;
+ }
+
+}
diff --git a/media/jui/less/forms.less b/media/jui/less/forms.less
index 99c9b96fa6d5a..0d3371c352511 100644
--- a/media/jui/less/forms.less
+++ b/media/jui/less/forms.less
@@ -693,3 +693,19 @@ legend + .control-group {
.control-label .hasTooltip {
display: inline-block;
}
+
+/* Field subform repeatable */
+.subform-repeatable-wrapper{
+
+ .btn-group>.btn.button{
+ min-width: 0;
+ }
+
+ .ui-sortable-helper{
+ background: @white;
+ }
+
+ tr.ui-sortable-helper{
+ display: table;
+ }
+}
diff --git a/media/system/js/subform-repeatable-uncompressed.js b/media/system/js/subform-repeatable-uncompressed.js
new file mode 100644
index 0000000000000..31df79a879b47
--- /dev/null
+++ b/media/system/js/subform-repeatable-uncompressed.js
@@ -0,0 +1,324 @@
+/**
+ * @copyright Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+;(function($){
+ "use strict";
+ $.subformRepeatable = function(container, options){
+ this.$container = $(container);
+
+ // check if alredy exist
+ if(this.$container.data("subformRepeatable")){
+ return self;
+ }
+
+ // Add a reverse reference to the DOM object
+ this.$container.data("subformRepeatable", self);
+
+ // merge options
+ this.options = $.extend({}, $.subformRepeatable.defaults, options);
+
+ // template for the repeating group
+ this.template = '';
+
+ // prepare a row template, and find available field names
+ this.prepareTemplate();
+
+ // check rows container
+ this.$containerRows = this.options.rowsContainer ? this.$container.find(this.options.rowsContainer) : this.$container;
+
+ // last row number, help to avoid the name duplications
+ this.lastRowNum = this.$containerRows.find(this.options.repeatableElement).length;
+
+ // To avoid scope issues,
+ var self = this;
+
+ // bind add button
+ this.$container.on('click', this.options.btAdd, function (e) {
+ e.preventDefault();
+ var after = $(this).parents(self.options.repeatableElement);
+ if(!after.length){
+ after = null;
+ }
+ self.addRow(after);
+ });
+
+ // bind remove button
+ this.$container.on('click', this.options.btRemove, function (e) {
+ e.preventDefault();
+ var $row = $(this).parents(self.options.repeatableElement);
+ self.removeRow($row);
+ });
+
+ // bind move button
+ if(this.options.btMove){
+ this.$containerRows.sortable({
+ items: this.options.repeatableElement,
+ handle: this.options.btMove,
+ tolerance: 'pointer'
+ });
+ }
+
+ // tell all that we a ready
+ this.$container.trigger('subform-ready');
+ };
+
+ // prepare a template that we will use repeating
+ $.subformRepeatable.prototype.prepareTemplate = function(){
+ // create from template
+ if(this.options.rowTemplateSelector){
+ var tmplElement = this.$container.find(this.options.rowTemplateSelector)[0] || {};
+ this.template = $.trim(tmplElement.text || tmplElement.textContent); //(text || textContent) is IE8 fix
+ }
+ // create from existing rows
+ else {
+ //find first available
+ var row = this.$container.find(this.options.repeatableElement).get(0),
+ $row = $(row).clone();
+
+ // clear scripts that can be attached to the fields
+ try {
+ this.clearScripts($row);
+ } catch (e) {
+ if(window.console){
+ console.log(e);
+ }
+ }
+
+ this.template = $row.prop('outerHTML');
+ }
+ };
+
+ // add new row
+ $.subformRepeatable.prototype.addRow = function(after){
+ // count how much we already have
+ var count = this.$containerRows.find(this.options.repeatableElement).length;
+ if(count >= this.options.maximum){
+ return null;
+ }
+
+ // make new from template
+ var row = $.parseHTML(this.template);
+
+ //add to container
+ if(after){
+ $(after).after(row);
+ } else {
+ this.$containerRows.append(row);
+ }
+
+ var $row = $(row);
+ //add marker that it is new
+ $row.attr('data-new', 'true');
+ // fix names and id`s, and reset values
+ this.fixUniqueAttributes($row, count);
+
+ // try find out with related scripts,
+ // tricky thing, so be careful
+ try {
+ this.fixScripts($row);
+ } catch (e) {
+ if(window.console){
+ console.log(e);
+ }
+ }
+
+ // tell everyone about the new row
+ this.$container.trigger('subform-row-add', $row);
+ return $row;
+ };
+
+ // remove row
+ $.subformRepeatable.prototype.removeRow = function($row){
+ // count how much we have
+ var count = this.$containerRows.find(this.options.repeatableElement).length;
+ if(count <= this.options.minimum){
+ return;
+ }
+
+ // tell everyoune about the row will be removed
+ this.$container.trigger('subform-row-remove', $row);
+ $row.remove();
+ };
+
+ // fix names ind id`s for field that in $row
+ $.subformRepeatable.prototype.fixUniqueAttributes = function($row, count){
+ this.lastRowNum++;
+ var group = $row.attr('data-group'),// current group name
+ basename = $row.attr('data-base-name'), // group base name, without count
+ count = count || 0,
+ countnew = Math.max(this.lastRowNum, count + 1),
+ groupnew = basename + countnew; // new group name
+
+ this.lastRowNum = countnew;
+ $row.attr('data-group', groupnew);
+
+ // fix inputs that have a "name" attribute
+ var haveName = $row.find('*[name]'),
+ ids = {}; // collect existing id`s for fix checkboxes and radio
+ for(var i=0, l = haveName.length; i fix
+ //check if multiple
+ if(name.match(/\[\]$/)){
+ // replace a group label "for"
+ var groupLbl = $row.find('label[for="' + id + '"]');
+ if(groupLbl.length){
+ groupLbl.attr('for', idNew);
+ $el.parents('fieldset.checkboxes').attr('id', idNew);
+ }
+ // recount id
+ var count = ids[id] ? ids[id].length : 0;
+ forOldAttr = forOldAttr + count;
+ idNew = idNew + count;
+ }
+ }
+ else if($el.prop('type') === 'radio'){// fix
+ // recount id
+ var count = ids[id] ? ids[id].length : 0;
+ forOldAttr = forOldAttr + count;
+ idNew = idNew + count;
+ }
+
+ //cache ids
+ if(ids[id]){
+ ids[id].push(true);
+ } else {
+ ids[id] = [true];
+ }
+
+ // replace name to new
+ $el.attr('name', nameNew);
+ // set new id
+ $el.attr('id', idNew);
+ // guess there a lable for this input
+ $row.find('label[for="' + forOldAttr + '"]').attr('for', idNew);
+ }
+ };
+
+ // remove scripts attached to fields
+ // @TODO: make thing better when something like that will be accepted https://github.com/joomla/joomla-cms/pull/6357
+ $.subformRepeatable.prototype.clearScripts = function($row){
+ // destroy chosen if any
+ if($.fn.chosen){
+ $row.find('select.chzn-done').each(function(){
+ var $el = $(this);
+ $el.next('.chzn-container').remove();
+ $el.show().addClass('fix-chosen');
+ });
+ }
+
+ // colorpicker
+ if($.fn.minicolors){
+ $row.find('.minicolors input').each(function(){
+ $(this).removeData('minicolors-initialized')
+ .removeData('minicolors-settings')
+ .removeProp('size')
+ .removeProp('maxlength')
+ .removeClass('minicolors-input')
+ // move out from
+ .parents('span.minicolors').parent().append(this);
+ });
+ $row.find('span.minicolors').remove();
+ }
+ };
+
+ // method for hack the scripts that can be related
+ // to the one of field that in given $row
+ $.subformRepeatable.prototype.fixScripts = function($row){
+ // init chosen if any
+ if($.fn.chosen){
+ $row.find('select.advancedSelect').chosen();
+ }
+
+ //color picker
+ $row.find('.minicolors').each(function() {
+ var $el = $(this);
+ $el.minicolors({
+ control: $el.attr('data-control') || 'hue',
+ position: $el.attr('data-position') || 'right',
+ theme: 'bootstrap'
+ });
+ });
+
+ // fix media field
+ $row.find('a[onclick*="jInsertFieldValue"]').each(function(){
+ var $el = $(this),
+ inputId = $el.siblings('input[type="text"]').attr('id'),
+ $select = $el.prev(),
+ oldHref = $select.attr('href');
+ // update the clear button
+ $el.attr('onclick', "jInsertFieldValue('', '" + inputId + "');return false;")
+ // update select button
+ $select.attr('href', oldHref.replace(/&fieldid=(.+)&/, '&fieldid=' + inputId + '&'));
+ });
+
+ // bootstrap based Media field
+ if($.fn.fieldMedia){
+ $row.find('.field-media-wrapper').fieldMedia();
+ }
+
+ // bootstrap tooltips
+ if($.fn.tooltip){
+ $row.find('.hasTooltip').tooltip({html: true, container: "body"});
+ }
+
+ // bootstrap based User field
+ if($.fn.fieldUser){
+ $row.find('.field-user-wrapper').fieldUser();
+ }
+
+ // another modals
+ if(window.SqueezeBox && window.SqueezeBox.assign){
+ SqueezeBox.assign($row.find('a.modal').get(), {parse: 'rel'});
+ }
+ };
+
+ // defaults
+ $.subformRepeatable.defaults = {
+ btAdd: ".group-add", // button selector for "add" action
+ btRemove: ".group-remove",// button selector for "remove" action
+ btMove: ".group-move",// button selector for "move" action
+ minimum: 0, // minimum repeating
+ maximum: 10, // maximum repeating
+ repeatableElement: ".subform-repeatable-group",
+ rowTemplateSelector: 'script.subform-repeatable-template-section', // selector for the row template