Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Per discussion: removed 'Add...' link from default template for Repea…

…t, added link and button to example instead.

Possibility to remove() item by index
Updated package.xml

git-svn-id: http://svn.php.net/repository/pear/packages/HTML_QuickForm2/trunk@325202 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information...
commit 1becb5ed071b8954cf7a77d123f0dfa669026305 1 parent a841e5c
@sad-spirit sad-spirit authored
View
2  HTML/QuickForm2/Renderer/Default.php
@@ -113,7 +113,7 @@ class HTML_QuickForm2_Renderer_Default extends HTML_QuickForm2_Renderer
),
'html_quickform2_element' => '<div class="row"><p class="label"><qf:required><span class="required">*</span></qf:required><qf:label><label for="{id}">{label}</label></qf:label></p><div class="element<qf:error> error</qf:error>"><qf:error><span class="error">{error}<br /></span></qf:error>{element}</div></div>',
'html_quickform2_container_group' => '<div class="row {class}"><p class="label"><qf:required><span class="required">*</span></qf:required><qf:label><label>{label}</label></qf:label></p><div class="element group<qf:error> error</qf:error>" id="{id}"><qf:error><span class="error">{error}<br /></span></qf:error>{content}</div></div>',
- 'html_quickform2_container_repeat' => '<div class="row repeat" id="{id}"><qf:label><p>{label}</p></qf:label>{content}<br /><a href="#" class="repeatAdd">Add...</a></div>'
+ 'html_quickform2_container_repeat' => '<div class="row repeat" id="{id}"><qf:label><p>{label}</p></qf:label>{content}</div>'
);
/**
View
13 data/js/min/quickform-repeat.js
@@ -8,9 +8,10 @@
http://opensource.org/licenses/bsd-license.php
*/
qf.Repeat=function(a,b,d,e,f){a.repeat=this;this.repeatPrototype=this.form=null;this.container=a;this.itemId=b;this.rulesTpl=e;this.scriptsTpl=f;this.triggers=d;e=this.getElementsByClass("repeatAdd",a);for(b=0;d=e[b];b++)qf.events.addListener(d,"click",qf.Repeat.addHandler);a=this.getElementsByClass("repeatRemove",a);for(b=0;d=a[b];b++)qf.events.addListener(d,"click",qf.Repeat.removeHandler)};
-qf.Repeat.addHandler=function(a){for(var a=qf.events.fixEvent(a),b=a.target;b&&!qf.classes.has(b,"repeat");)b=b.parentNode;b&&b.repeat&&b.repeat.onBeforeAdd()&&b.repeat.add();a.preventDefault()};qf.Repeat.removeHandler=function(a){for(var a=qf.events.fixEvent(a),b=a.target,d;b&&!qf.classes.has(b,"repeat");)qf.classes.has(b,"repeatItem")&&(d=b),b=b.parentNode;b&&b.repeat&&b.repeat.onBeforeRemove(d)&&b.repeat.remove(d);a.preventDefault()};
-qf.Repeat.prototype={getElementsByClass:function(){return document.getElementsByClassName?function(a,b){return b.getElementsByClassName(a)}:function(a,b){for(var d=b.getElementsByTagName("*"),e=[],f=0,c;c=d[f];f++)qf.classes.has(c,a)&&e.push(c);return e}}(),findIndex:function(a){var b=RegExp("^"+this.itemId.replace(":idx:","([a-zA-Z0-9_]+?)")+"$"),d;if(a.id&&(d=b.exec(a.id)))return d[1];for(var a=a.getElementsByTagName("*"),e=0,f;f=a[e];e++)if(f.id&&(d=b.exec(f.id)))return d[1]},findForm:function(){for(var a=
-this.container;a&&"form"!==a.nodeName.toLowerCase();)a=a.parentNode;return a},generateIndex:function(){var a;do a="add"+Math.round(1E4*Math.random());while(document.getElementById(this.itemId.replace(":idx:",a)));return a},add:function(){this.repeatPrototype||(this.repeatPrototype=this.getElementsByClass("repeatPrototype",this.container)[0]);var a=this.getElementsByClass("repeatItem",this.container),b=a[a.length-1],d=this.repeatPrototype.cloneNode(!0),e=this.generateIndex();qf.classes.remove(d,"repeatPrototype");
-d.id&&(d.id=d.id.replace(":idx:",e));for(var f=d.getElementsByTagName("*"),a=0,c;c=f[a];a++){c.id&&(c.id=c.id.replace(":idx:",e));c.name&&(c.name=c.name.replace(":idx:",e));if(c.type&&("checkbox"==c.type||"radio"==c.type))c.value=c.value.replace(":idx:",e);c.htmlFor&&(c.htmlFor=c.htmlFor.replace(":idx:",e));"script"==c.nodeName.toLowerCase()&&eval(c.innerHTML.replace(/:idx:/g,e));qf.classes.has(c,"repeatAdd")&&qf.events.addListener(c,"click",qf.Repeat.addHandler);qf.classes.has(c,"repeatRemove")&&
-qf.events.addListener(c,"click",qf.Repeat.removeHandler)}b.parentNode.insertBefore(d,b.nextSibling);this.scriptsTpl&&eval(this.scriptsTpl.replace(/:idx:/g,e));if(this.rulesTpl&&(this.form||(this.form=this.findForm()),this.form.validator)){b=eval(this.rulesTpl.replace(/:idx:/g,e));for(a=0;d=b[a];a++)this.form.validator.rules.push(d)}this.onChange()},remove:function(a){if(this.rulesTpl&&(this.form||(this.form=this.findForm()),this.form.validator)){var b=new qf.Map,d=this.findIndex(a),e=this.form.validator.rules,
-f,c;for(c=0;f=this.triggers[c];c++)b.set(f.replace(":idx:",d),!0);for(c=e.length-1;d=e[c];c--)b.hasKey(d.owner)&&e.splice(c,1)}a.parentNode.removeChild(a);this.onChange()},onBeforeAdd:function(){return!0},onBeforeRemove:function(){return!0},onChange:function(){}};
+qf.Repeat.addHandler=function(a){for(var a=qf.events.fixEvent(a),b=a.target;b&&!qf.classes.has(b,"repeat");)b=b.parentNode;b&&b.repeat&&b.repeat.onBeforeAdd()&&b.repeat.add();a.preventDefault()};qf.Repeat.removeHandler=function(a){for(var a=qf.events.fixEvent(a),b=a.target,d;b&&!qf.classes.has(b,"repeat");)qf.classes.has(b,"repeatItem")&&(d=b),b=b.parentNode;b&&d&&b.repeat&&b.repeat.onBeforeRemove(d)&&b.repeat.remove(d);a.preventDefault()};
+qf.Repeat.prototype={getElementsByClass:function(){return document.getElementsByClassName?function(a,b){return b.getElementsByClassName(a)}:function(a,b){for(var d=b.getElementsByTagName("*"),e=[],f=0,c;c=d[f];f++)qf.classes.has(c,a)&&e.push(c);return e}}(),findIndexByItem:function(a){var b=RegExp("^"+this.itemId.replace(":idx:","([a-zA-Z0-9_]+?)")+"$"),d;if(a.id&&(d=b.exec(a.id)))return d[1];for(var a=a.getElementsByTagName("*"),e=0,f;f=a[e];e++)if(f.id&&(d=b.exec(f.id)))return d[1];return null},
+findItemByIndex:function(a){a=this.itemId.replace(":idx:",a);if((a=document.getElementById(a))&&!qf.classes.has(a,"repeatItem")){do a=a.parentNode;while(a&&!qf.classes.has(a,"repeatItem"))}return a},findForm:function(){for(var a=this.container;a&&"form"!==a.nodeName.toLowerCase();)a=a.parentNode;return a},generateIndex:function(){var a;do a="add"+Math.round(1E4*Math.random());while(document.getElementById(this.itemId.replace(":idx:",a)));return a},add:function(){this.repeatPrototype||(this.repeatPrototype=
+this.getElementsByClass("repeatPrototype",this.container)[0]);var a=this.getElementsByClass("repeatItem",this.container),b=a[a.length-1],d=this.repeatPrototype.cloneNode(!0),e=this.generateIndex();qf.classes.remove(d,"repeatPrototype");d.id&&(d.id=d.id.replace(":idx:",e));for(var f=d.getElementsByTagName("*"),a=0,c;c=f[a];a++){c.id&&(c.id=c.id.replace(":idx:",e));c.name&&(c.name=c.name.replace(":idx:",e));if(c.type&&("checkbox"==c.type||"radio"==c.type))c.value=c.value.replace(":idx:",e);c.htmlFor&&
+(c.htmlFor=c.htmlFor.replace(":idx:",e));"script"==c.nodeName.toLowerCase()&&eval(c.innerHTML.replace(/:idx:/g,e));qf.classes.has(c,"repeatAdd")&&qf.events.addListener(c,"click",qf.Repeat.addHandler);qf.classes.has(c,"repeatRemove")&&qf.events.addListener(c,"click",qf.Repeat.removeHandler)}b.parentNode.insertBefore(d,b.nextSibling);this.scriptsTpl&&eval(this.scriptsTpl.replace(/:idx:/g,e));if(this.rulesTpl&&(this.form||(this.form=this.findForm()),this.form.validator)){b=eval(this.rulesTpl.replace(/:idx:/g,
+e));for(a=0;d=b[a];a++)this.form.validator.rules.push(d)}this.onChange()},remove:function(a){var b;if("string"==typeof a&&(b=a,!(a=this.findItemByIndex(b))))return;if(this.rulesTpl&&(this.form||(this.form=this.findForm()),this.form.validator)){var d=new qf.Map,e=this.form.validator.rules,f,c;b||(b=this.findIndexByItem(a));for(c=0;f=this.triggers[c];c++)d.set(f.replace(":idx:",b),!0);for(c=e.length-1;b=e[c];c--)d.hasKey(b.owner)&&e.splice(c,1)}a.parentNode.removeChild(a);this.onChange()},onBeforeAdd:function(){return!0},
+onBeforeRemove:function(){return!0},onChange:function(){}};
View
39 data/js/quickform-repeat.js
@@ -114,7 +114,7 @@ qf.Repeat.removeHandler = function(event)
}
parent = parent.parentNode;
}
- if (parent && parent.repeat && parent.repeat.onBeforeRemove(item)) {
+ if (parent && item && parent.repeat && parent.repeat.onBeforeRemove(item)) {
parent.repeat.remove(item);
}
event.preventDefault();
@@ -152,12 +152,12 @@ qf.Repeat.prototype = {
}
})(),
/**
- * Finds a numeric index for a given repeat item
+ * Finds an index for a given repeat item
*
* @param {Node} item
- * @returns {Number}
+ * @returns {String}
*/
- findIndex: function(item)
+ findIndexByItem: function(item)
{
var itemRegexp = new RegExp('^' + this.itemId.replace(':idx:', '([a-zA-Z0-9_]+?)') + '$'),
m;
@@ -174,6 +174,24 @@ qf.Repeat.prototype = {
}
}
}
+ return null;
+ },
+ /**
+ * Finds a repeat item for a given index
+ *
+ * @param {String} index
+ * @returns {Node}
+ */
+ findItemByIndex: function(index)
+ {
+ var id = this.itemId.replace(':idx:', index),
+ el = document.getElementById(id);
+ if (el && !qf.classes.has(el, 'repeatItem')) {
+ do {
+ el = el.parentNode;
+ } while (el && !qf.classes.has(el, 'repeatItem'));
+ }
+ return el;
},
/**
* Finds a form containing repeat element
@@ -270,19 +288,28 @@ qf.Repeat.prototype = {
/**
* Removes an item from repeat element
*
- * @param {Node} item
+ * @param {Node|String} item
*/
remove: function(item)
{
+ var index;
+ if (typeof item == 'string') {
+ index = item;
+ if (!(item = this.findItemByIndex(index))) {
+ return;
+ }
+ }
if (this.rulesTpl) {
if (!this.form) {
this.form = this.findForm();
}
if (this.form.validator) {
var check = new qf.Map(),
- index = this.findIndex(item),
rules = this.form.validator.rules,
trigger, rule, i;
+ if (!index) {
+ index = this.findIndexByItem(item);
+ }
for (i = 0; trigger = this.triggers[i]; i++) {
check.set(trigger.replace(':idx:', index), true);
}
View
22 docs/examples/repeat.php
@@ -49,7 +49,7 @@
$repeatFs->addText('region', array('style' => 'width: 20em;'))->setLabel('Region:');
$street = $repeatFs->addText('street', array('style' => 'width: 20em;'))->setLabel('Street address:');
$repeatFs->addCheckbox('default')->setContent('default shipping address');
-// button to remove a repeated item from a repeat
+// button to remove a repeated item from a repeat, enabled automatically
$repeatFs->addButton('remove', array('type' => 'button'))
->setContent('remove this address')
->addClass('repeatRemove');
@@ -78,10 +78,30 @@
$repeatGroup->addButton('remove', array('type' => 'button'))
->setContent('X')
->addClass('repeatRemove');
+// a button for adding repeated elements, with an explicit onclick
+$fsTwo->addButton('add', array(
+ 'type' => 'button',
+ 'onclick' => "document.getElementById('repeat-group').repeat.add(); return false;"
+))->setContent('Add another link');
+
$form->addSubmit('submit', array('value' => 'Send this form'));
+/* @var $renderer HTML_QuickForm2_Renderer_Default */
$renderer = HTML_QuickForm2_Renderer::factory('default');
+// a custom template for first repeat element, a link for adding repeated
+// elements there will be automatically made active due to repeatAdd class
+$renderer->setTemplateForId(
+ 'repeat-fieldset',
+ <<<HTML
+<div class="row repeat" id="{id}">
+ <qf:label><p>{label}</p></qf:label>
+ {content}<br />
+ <a class="repeatAdd" href="#">Add another address...</a>
+</div>
+HTML
+);
+
$form->render($renderer);
?>
View
39 js/src/repeat.js
@@ -114,7 +114,7 @@ qf.Repeat.removeHandler = function(event)
}
parent = parent.parentNode;
}
- if (parent && parent.repeat && parent.repeat.onBeforeRemove(item)) {
+ if (parent && item && parent.repeat && parent.repeat.onBeforeRemove(item)) {
parent.repeat.remove(item);
}
event.preventDefault();
@@ -152,12 +152,12 @@ qf.Repeat.prototype = {
}
})(),
/**
- * Finds a numeric index for a given repeat item
+ * Finds an index for a given repeat item
*
* @param {Node} item
- * @returns {Number}
+ * @returns {String}
*/
- findIndex: function(item)
+ findIndexByItem: function(item)
{
var itemRegexp = new RegExp('^' + this.itemId.replace(':idx:', '([a-zA-Z0-9_]+?)') + '$'),
m;
@@ -174,6 +174,24 @@ qf.Repeat.prototype = {
}
}
}
+ return null;
+ },
+ /**
+ * Finds a repeat item for a given index
+ *
+ * @param {String} index
+ * @returns {Node}
+ */
+ findItemByIndex: function(index)
+ {
+ var id = this.itemId.replace(':idx:', index),
+ el = document.getElementById(id);
+ if (el && !qf.classes.has(el, 'repeatItem')) {
+ do {
+ el = el.parentNode;
+ } while (el && !qf.classes.has(el, 'repeatItem'));
+ }
+ return el;
},
/**
* Finds a form containing repeat element
@@ -270,19 +288,28 @@ qf.Repeat.prototype = {
/**
* Removes an item from repeat element
*
- * @param {Node} item
+ * @param {Node|String} item
*/
remove: function(item)
{
+ var index;
+ if (typeof item == 'string') {
+ index = item;
+ if (!(item = this.findItemByIndex(index))) {
+ return;
+ }
+ }
if (this.rulesTpl) {
if (!this.form) {
this.form = this.findForm();
}
if (this.form.validator) {
var check = new qf.Map(),
- index = this.findIndex(item),
rules = this.form.validator.rules,
trigger, rule, i;
+ if (!index) {
+ index = this.findIndexByItem(item);
+ }
for (i = 0; trigger = this.triggers[i]; i++) {
check.set(trigger.replace(':idx:', index), true);
}
View
6 package.xml
@@ -53,7 +53,11 @@
<license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
<notes>
Feature additions
- * Repeat element
+ * Repeat element: given 'prototype' Container (either fieldset or group)
+ is repeated multiple times, with possibility to add / remove repeated items
+ via Javascript. Full support for validation of repeated items.
+ * JavascriptBuilder now has separate getSetupCode() and getValidator()
+ methods in addition to combined getFormJavascript()
Bug fixes
* Autogenerated 'id' attributes no longer start with numbers
Please sign in to comment.
Something went wrong with that request. Please try again.