jquery.repeater
Advanced tools
Comparing version 1.1.2 to 1.1.3
{ | ||
"name": "jquery.repeater", | ||
"main": "jquery.repeater.js", | ||
"version": "1.1.0", | ||
"version": "1.1.3", | ||
"homepage": "https://github.com/DubFriend/jquery.repeater", | ||
@@ -6,0 +6,0 @@ "description": "repeatable form input interface", |
@@ -1,4 +0,4 @@ | ||
// jquery.repeater version 1.1.2 | ||
// jquery.repeater version 1.1.3 | ||
// https://github.com/DubFriend/jquery.repeater | ||
// (MIT) 07-11-2015 | ||
// (MIT) 06-12-2015 | ||
// Brian Detering <BDeterin@gmail.com> (http://www.briandetering.net/) | ||
@@ -20,2 +20,6 @@ (function ($) { | ||
var isNumber = function (value) { | ||
return value instanceof Number; | ||
}; | ||
var isFunction = function (value) { | ||
@@ -41,2 +45,3 @@ return value instanceof Function; | ||
var last = function (array) { | ||
@@ -46,2 +51,16 @@ return array[array.length - 1]; | ||
var argumentsToArray = function (args) { | ||
return Array.prototype.slice.call(args); | ||
}; | ||
var extend = function () { | ||
var extended = {}; | ||
foreach(argumentsToArray(arguments), function (o) { | ||
foreach(o, function (val, key) { | ||
extended[key] = val; | ||
}); | ||
}); | ||
return extended; | ||
}; | ||
var mapToArray = function (collection, callback) { | ||
@@ -70,2 +89,8 @@ var mapped = []; | ||
var pluck = function (arrayOfObjects, key) { | ||
return map(arrayOfObjects, function (val) { | ||
return val[key]; | ||
}); | ||
}; | ||
var filter = function (collection, callback) { | ||
@@ -142,2 +167,3 @@ var filtered; | ||
}; | ||
// jquery.input version 0.0.0 | ||
@@ -706,29 +732,73 @@ // https://github.com/DubFriend/jquery.input | ||
$.fn.repeaterVal = function () { | ||
var rawData = $(this).inputVal(); | ||
var mapped = {}; | ||
var parse = function (raw) { | ||
var parsed = []; | ||
foreach(rawData, function (val, key) { | ||
var group, index, name; | ||
var matches; | ||
if(key !== "undefined") { | ||
matches = key.match(/^([^\[]+)\[([0-9]+)\]\[([^\]]+)/); | ||
group = matches[1]; | ||
index = matches[2]; | ||
name = matches[3]; | ||
if(!mapped[group]) { | ||
mapped[group] = []; | ||
} | ||
foreach(raw, function (val, key) { | ||
var parsedKey = []; | ||
if(key !== "undefined") { | ||
parsedKey.push(key.match(/^[^\[]*/)[0]); | ||
parsedKey = parsedKey.concat(map( | ||
key.match(/\[[^\]]*\]/g), | ||
function (bracketed) { | ||
return bracketed.replace(/[\[\]]/g, ''); | ||
} | ||
)); | ||
if(!mapped[group][index]) { | ||
mapped[group][index] = {}; | ||
parsed.push({ | ||
val: val, | ||
key: parsedKey | ||
}); | ||
} | ||
}); | ||
mapped[group][index][name] = val; | ||
return parsed; | ||
}; | ||
var build = function (parsed) { | ||
if( | ||
parsed.length === 1 && | ||
(parsed[0].key.length === 0 || parsed[0].key.length === 1 && !parsed[0].key[0]) | ||
) { | ||
return parsed[0].val; | ||
} | ||
}); | ||
return mapped; | ||
foreach(parsed, function (p) { | ||
p.head = p.key.shift(); | ||
}); | ||
var grouped = (function () { | ||
var grouped = {}; | ||
foreach(parsed, function (p) { | ||
if(!grouped[p.head]) { | ||
grouped[p.head] = []; | ||
} | ||
grouped[p.head].push(p); | ||
}); | ||
return grouped; | ||
}()); | ||
var built; | ||
if(/^[0-9]+$/.test(parsed[0].head)) { | ||
built = []; | ||
foreach(grouped, function (group) { | ||
built.push(build(group)); | ||
}); | ||
} | ||
else { | ||
built = {}; | ||
foreach(grouped, function (group, key) { | ||
built[key] = build(group); | ||
}); | ||
} | ||
return built; | ||
}; | ||
return build(parse($(this).inputVal())); | ||
}; | ||
$.fn.repeater = function(fig) { | ||
$.fn.repeater = function (fig) { | ||
fig = fig || {}; | ||
@@ -748,9 +818,21 @@ | ||
var $list = $self.find('[data-repeater-list]'); | ||
var $list = $self.find('[data-repeater-list]').first(); | ||
var $filterNested = function ($items, repeaters) { | ||
return $items.filter(function () { | ||
return repeaters ? | ||
$(this).closest( | ||
pluck(repeaters, 'selector').join(',') | ||
).length === 0 : true; | ||
}); | ||
}; | ||
var $items = function () { | ||
return $filterNested($list.find('[data-repeater-item]'), fig.repeaters); | ||
}; | ||
var $itemTemplate = $list.find('[data-repeater-item]') | ||
.first().clone().hide(); | ||
.first().clone().hide(); | ||
var $firstDeleteButton = $(this).find('[data-repeater-item]') | ||
.first() | ||
var $firstDeleteButton = $(this).find('[data-repeater-item]').first() | ||
.find('[data-repeater-delete]'); | ||
@@ -762,9 +844,39 @@ | ||
var groupName = $list.data('repeater-list'); | ||
var getGroupName = function () { | ||
var groupName = $list.data('repeater-list'); | ||
return fig.$parent ? | ||
fig.$parent.data('item-name') + '[' + groupName + ']' : | ||
groupName; | ||
}; | ||
var setIndexes = function () { | ||
$list.find('[data-repeater-item]').each(function (index) { | ||
$(this).find('[name]').each(function () { | ||
var initNested = function ($listItems) { | ||
if(fig.repeaters) { | ||
$listItems.each(function () { | ||
var $item = $(this); | ||
foreach(fig.repeaters, function (nestedFig) { | ||
$item.find(nestedFig.selector).repeater(extend( | ||
nestedFig, { $parent: $item } | ||
)); | ||
}); | ||
}); | ||
} | ||
}; | ||
var $foreachRepeaterInItem = function (repeaters, $item, cb) { | ||
if(repeaters) { | ||
foreach(repeaters, function (nestedFig) { | ||
cb.call($item.find(nestedFig.selector)[0], nestedFig); | ||
}); | ||
} | ||
}; | ||
var setIndexes = function ($items, groupName, repeaters) { | ||
$items.each(function (index) { | ||
var $item = $(this); | ||
$item.data('item-name', groupName + '[' + index + ']'); | ||
$filterNested($item.find('[name]'), repeaters) | ||
.each(function () { | ||
var $input = $(this); | ||
// match non empty brackets (ex: "[foo]") | ||
var matches = $(this).attr('name').match(/\[[^\]]+\]/g); | ||
var matches = $input.attr('name').match(/\[[^\]]+\]/g); | ||
@@ -774,8 +886,19 @@ var name = matches ? | ||
last(matches).replace(/\[|\]/g, '') : | ||
$(this).attr('name'); | ||
$input.attr('name'); | ||
var newName = groupName + '[' + index + '][' + name + ']' + | ||
($(this).is(':checkbox') || $(this).attr('multiple') ? '[]' : ''); | ||
($input.is(':checkbox') || $input.attr('multiple') ? '[]' : ''); | ||
$(this).attr('name', newName); | ||
$input.attr('name', newName); | ||
$foreachRepeaterInItem(repeaters, $item, function (nestedFig) { | ||
var $repeater = $(this); | ||
setIndexes( | ||
$filterNested($repeater.find('[data-repeater-item]'), nestedFig.repeaters || []), | ||
groupName + '[' + index + ']' + | ||
'[' + $repeater.find('[data-repeater-list]').first().data('repeater-list') + ']', | ||
nestedFig.repeaters | ||
); | ||
}); | ||
}); | ||
@@ -789,31 +912,41 @@ }); | ||
setIndexes(); | ||
setIndexes($items(), getGroupName(), fig.repeaters); | ||
initNested($items()); | ||
if(fig.ready) { | ||
fig.ready(setIndexes); | ||
fig.ready(function () { | ||
setIndexes($items(), getGroupName(), fig.repeaters); | ||
}); | ||
} | ||
var setItemsValues = function ($item, values) { | ||
var index; | ||
index = $item.find('[name]').first() | ||
.attr('name').match(/\[([0-9]*)\]/)[1]; | ||
$item.inputVal(map(values, identity, function (name) { | ||
var nameIfNotCheckbox = groupName + '[' + index + '][' + name + ']'; | ||
return $item.find('[name="' + nameIfNotCheckbox + '"]').length ? | ||
nameIfNotCheckbox : nameIfNotCheckbox + '[]'; | ||
})); | ||
}; | ||
var appendItem = (function () { | ||
var setupTemplate = function ($item) { | ||
var defaultValues = fig.defaultValues; | ||
var setItemsValues = function ($item, values, repeaters) { | ||
if(values) { | ||
var inputNames = {}; | ||
$filterNested($item.find('[name]'), repeaters).each(function () { | ||
var key = $(this).attr('name').match(/\[([^\]]*)(\]|\]\[\])$/)[1]; | ||
inputNames[key] = $(this).attr('name'); | ||
}); | ||
$item.find('[name]').each(function () { | ||
$(this).inputClear(); | ||
$item.inputVal(map(values, identity, function (name) { | ||
return inputNames[name]; | ||
})); | ||
} | ||
$foreachRepeaterInItem(repeaters, $item, function (nestedFig) { | ||
var $repeater = $(this); | ||
$filterNested( | ||
$repeater.find('[data-repeater-item]'), | ||
nestedFig.repeaters | ||
) | ||
.each(function () { | ||
setItemsValues( | ||
$(this), | ||
nestedFig.defaultValues, | ||
nestedFig.repeaters || [] | ||
); | ||
}); | ||
}); | ||
if(defaultValues) { | ||
setItemsValues($item, defaultValues); | ||
} | ||
}; | ||
@@ -823,11 +956,31 @@ | ||
$list.append($item); | ||
setIndexes(); | ||
setupTemplate($item); | ||
setIndexes($items(), getGroupName(), fig.repeaters); | ||
$item.find('[name]').each(function () { | ||
$(this).inputClear(); | ||
}); | ||
setItemsValues($item, fig.defaultValues, fig.repeaters); | ||
}; | ||
}()); | ||
$self.find('[data-repeater-create]').click(function () { | ||
var addItem = function () { | ||
var $item = $itemTemplate.clone(); | ||
appendItem($item); | ||
if(fig.repeaters) { | ||
initNested($item); | ||
} | ||
show.call($item.get(0)); | ||
}; | ||
$self.children().each(function () { | ||
if( | ||
!$(this).is('[data-repeater-list]') && | ||
$(this).find('[data-repeater-list]').length === 0 | ||
) { | ||
if($(this).is('[data-repeater-create]')) { | ||
$(this).click(addItem); | ||
} | ||
else if($(this).find('[data-repeater-create]').length !== 0) { | ||
$(this).find('[data-repeater-create]').click(addItem); | ||
} | ||
} | ||
}); | ||
@@ -839,7 +992,5 @@ | ||
$(self).remove(); | ||
setIndexes(); | ||
setIndexes($items(), getGroupName(), fig.repeaters); | ||
}); | ||
}); | ||
}); | ||
@@ -846,0 +997,0 @@ |
@@ -1,5 +0,5 @@ | ||
// jquery.repeater version 1.1.2 | ||
// jquery.repeater version 1.1.3 | ||
// https://github.com/DubFriend/jquery.repeater | ||
// (MIT) 07-11-2015 | ||
// (MIT) 06-12-2015 | ||
// Brian Detering <BDeterin@gmail.com> (http://www.briandetering.net/) | ||
!function(a){"use strict";var b=function(a){return a},c=function(b){return a.isArray(b)},d=function(a){return!c(a)&&a instanceof Object},e=function(b,c){return a.inArray(c,b)},f=function(a,b){return-1!==e(a,b)},g=function(a,b){for(var c in a)a.hasOwnProperty(c)&&b(a[c],c,a)},h=function(a){return a[a.length-1]},i=function(a,b){var c=[];return g(a,function(a,d,e){c.push(b(a,d,e))}),c},j=function(a,b,c){var d={};return g(a,function(a,e,f){e=c?c(e,a):e,d[e]=b(a,e,f)}),d},k=function(a,b,d){return c(a)?i(a,b):j(a,b,d)},l=function(a,b,c){return k(a,function(a,d){return a[b].apply(a,c||[])})},m=function(a){a=a||{};var b={};return a.publish=function(a,c){g(b[a],function(a){a(c)})},a.subscribe=function(a,c){b[a]=b[a]||[],b[a].push(c)},a.unsubscribe=function(a){g(b,function(b){var c=e(b,a);-1!==c&&b.splice(c,1)})},a};!function(a){var b=function(a,b){var c=m(),d=a.$;return c.getType=function(){throw'implement me (return type. "text", "radio", etc.)'},c.$=function(a){return a?d.find(a):d},c.disable=function(){c.$().prop("disabled",!0),c.publish("isEnabled",!1)},c.enable=function(){c.$().prop("disabled",!1),c.publish("isEnabled",!0)},b.equalTo=function(a,b){return a===b},b.publishChange=function(){var a;return function(d,e){var f=c.get();b.equalTo(f,a)||c.publish("change",{e:d,domElement:e}),a=f}}(),c},i=function(a,c){var d=b(a,c);return d.get=function(){return d.$().val()},d.set=function(a){d.$().val(a)},d.clear=function(){d.set("")},c.buildSetter=function(a){return function(b){a.call(d,b)}},d},j=function(a,b){a=c(a)?a:[a],b=c(b)?b:[b];var d=!0;return a.length!==b.length?d=!1:g(a,function(a){f(b,a)||(d=!1)}),d},k=function(a){var b={},c=i(a,b);return c.getType=function(){return"button"},c.$().on("change",function(a){b.publishChange(a,this)}),c},n=function(b){var d={},e=i(b,d);return e.getType=function(){return"checkbox"},e.get=function(){var b=[];return e.$().filter(":checked").each(function(){b.push(a(this).val())}),b},e.set=function(b){b=c(b)?b:[b],e.$().each(function(){a(this).prop("checked",!1)}),g(b,function(a){e.$().filter('[value="'+a+'"]').prop("checked",!0)})},d.equalTo=j,e.$().change(function(a){d.publishChange(a,this)}),e},o=function(a){var b={},c=x(a,b);return c.getType=function(){return"email"},c},p=function(c){var d={},e=b(c,d);return e.getType=function(){return"file"},e.get=function(){return h(e.$().val().split("\\"))},e.clear=function(){this.$().each(function(){a(this).wrap("<form>").closest("form").get(0).reset(),a(this).unwrap()})},e.$().change(function(a){d.publishChange(a,this)}),e},q=function(a){var b={},c=i(a,b);return c.getType=function(){return"hidden"},c.$().change(function(a){b.publishChange(a,this)}),c},r=function(c){var d={},e=b(c,d);return e.getType=function(){return"file[multiple]"},e.get=function(){var a,b=e.$().get(0).files||[],c=[];for(a=0;a<(b.length||0);a+=1)c.push(b[a].name);return c},e.clear=function(){this.$().each(function(){a(this).wrap("<form>").closest("form").get(0).reset(),a(this).unwrap()})},e.$().change(function(a){d.publishChange(a,this)}),e},s=function(a){var b={},d=i(a,b);return d.getType=function(){return"select[multiple]"},d.get=function(){return d.$().val()||[]},d.set=function(a){d.$().val(""===a?[]:c(a)?a:[a])},b.equalTo=j,d.$().change(function(a){b.publishChange(a,this)}),d},t=function(a){var b={},c=x(a,b);return c.getType=function(){return"password"},c},u=function(b){var c={},d=i(b,c);return d.getType=function(){return"radio"},d.get=function(){return d.$().filter(":checked").val()||null},d.set=function(b){b?d.$().filter('[value="'+b+'"]').prop("checked",!0):d.$().each(function(){a(this).prop("checked",!1)})},d.$().change(function(a){c.publishChange(a,this)}),d},v=function(a){var b={},c=i(a,b);return c.getType=function(){return"range"},c.$().change(function(a){b.publishChange(a,this)}),c},w=function(a){var b={},c=i(a,b);return c.getType=function(){return"select"},c.$().change(function(a){b.publishChange(a,this)}),c},x=function(a){var b={},c=i(a,b);return c.getType=function(){return"text"},c.$().on("change keyup keydown",function(a){b.publishChange(a,this)}),c},y=function(a){var b={},c=i(a,b);return c.getType=function(){return"textarea"},c.$().on("change keyup keydown",function(a){b.publishChange(a,this)}),c},z=function(a){var b={},c=x(a,b);return c.getType=function(){return"url"},c},A=function(b){var c={},f=b.$,h=b.constructorOverride||{button:k,text:x,url:z,email:o,password:t,range:v,textarea:y,select:w,"select[multiple]":s,radio:u,checkbox:n,file:p,"file[multiple]":r,hidden:q},i=function(b,e){var g=d(e)?e:f.find(e);g.each(function(){var d=a(this).attr("name");c[d]=h[b]({$:a(this)})})},j=function(b,i){var j=[],k=d(i)?i:f.find(i);d(i)?c[k.attr("name")]=h[b]({$:k}):(k.each(function(){-1===e(j,a(this).attr("name"))&&j.push(a(this).attr("name"))}),g(j,function(a){c[a]=h[b]({$:f.find('input[name="'+a+'"]')})}))};return f.is("input, select, textarea")?f.is('input[type="button"], button, input[type="submit"]')?i("button",f):f.is("textarea")?i("textarea",f):f.is('input[type="text"]')||f.is("input")&&!f.attr("type")?i("text",f):f.is('input[type="password"]')?i("password",f):f.is('input[type="email"]')?i("email",f):f.is('input[type="url"]')?i("url",f):f.is('input[type="range"]')?i("range",f):f.is("select")?f.is("[multiple]")?i("select[multiple]",f):i("select",f):f.is('input[type="file"]')?f.is("[multiple]")?i("file[multiple]",f):i("file",f):f.is('input[type="hidden"]')?i("hidden",f):f.is('input[type="radio"]')?j("radio",f):f.is('input[type="checkbox"]')?j("checkbox",f):i("text",f):(i("button",'input[type="button"], button, input[type="submit"]'),i("text",'input[type="text"]'),i("password",'input[type="password"]'),i("email",'input[type="email"]'),i("url",'input[type="url"]'),i("range",'input[type="range"]'),i("textarea","textarea"),i("select","select:not([multiple])"),i("select[multiple]","select[multiple]"),i("file",'input[type="file"]:not([multiple])'),i("file[multiple]",'input[type="file"][multiple]'),i("hidden",'input[type="hidden"]'),j("radio",'input[type="radio"]'),j("checkbox",'input[type="checkbox"]')),c};a.fn.inputVal=function(b){var c=a(this),d=A({$:c});return c.is("input, textarea, select")?"undefined"==typeof b?d[c.attr("name")].get():(d[c.attr("name")].set(b),c):"undefined"==typeof b?l(d,"get"):(g(b,function(a,b){d[b].set(a)}),c)},a.fn.inputOnChange=function(b){var c=a(this),d=A({$:c});return g(d,function(a){a.subscribe("change",function(a){b.call(a.domElement,a.e)})}),c},a.fn.inputDisable=function(){var b=a(this);return l(A({$:b}),"disable"),b},a.fn.inputEnable=function(){var b=a(this);return l(A({$:b}),"enable"),b},a.fn.inputClear=function(){var b=a(this);return l(A({$:b}),"clear"),b}}(jQuery),a.fn.repeaterVal=function(){var b=a(this).inputVal(),c={};return g(b,function(a,b){var d,e,f,g;"undefined"!==b&&(g=b.match(/^([^\[]+)\[([0-9]+)\]\[([^\]]+)/),d=g[1],e=g[2],f=g[3],c[d]||(c[d]=[]),c[d][e]||(c[d][e]={}),c[d][e][f]=a)}),c},a.fn.repeater=function(c){return c=c||{},a(this).each(function(){var d=a(this),e=c.show||function(){a(this).show()},f=c.hide||function(a){a()},g=d.find("[data-repeater-list]"),i=g.find("[data-repeater-item]").first().clone().hide(),j=a(this).find("[data-repeater-item]").first().find("[data-repeater-delete]");c.isFirstItemUndeletable&&j&&j.remove();var l=g.data("repeater-list"),m=function(){g.find("[data-repeater-item]").each(function(b){a(this).find("[name]").each(function(){var c=a(this).attr("name").match(/\[[^\]]+\]/g),d=c?h(c).replace(/\[|\]/g,""):a(this).attr("name"),e=l+"["+b+"]["+d+"]"+(a(this).is(":checkbox")||a(this).attr("multiple")?"[]":"");a(this).attr("name",e)})}),g.find("input[name][checked]").removeAttr("checked").prop("checked",!0)};m(),c.ready&&c.ready(m);var n=function(a,c){var d;d=a.find("[name]").first().attr("name").match(/\[([0-9]*)\]/)[1],a.inputVal(k(c,b,function(b){var c=l+"["+d+"]["+b+"]";return a.find('[name="'+c+'"]').length?c:c+"[]"}))},o=function(){var b=function(b){var d=c.defaultValues;b.find("[name]").each(function(){a(this).inputClear()}),d&&n(b,d)};return function(a){g.append(a),m(),b(a)}}();d.find("[data-repeater-create]").click(function(){var a=i.clone();o(a),e.call(a.get(0))}),g.on("click","[data-repeater-delete]",function(){var b=a(this).closest("[data-repeater-item]").get(0);f.call(b,function(){a(b).remove(),m()})})}),this}}(jQuery); | ||
!function(a){"use strict";var b=function(a){return a},c=function(b){return a.isArray(b)},d=function(a){return!c(a)&&a instanceof Object},e=function(b,c){return a.inArray(c,b)},f=function(a,b){return-1!==e(a,b)},g=function(a,b){for(var c in a)a.hasOwnProperty(c)&&b(a[c],c,a)},h=function(a){return a[a.length-1]},i=function(a){return Array.prototype.slice.call(a)},j=function(){var a={};return g(i(arguments),function(b){g(b,function(b,c){a[c]=b})}),a},k=function(a,b){var c=[];return g(a,function(a,d,e){c.push(b(a,d,e))}),c},l=function(a,b,c){var d={};return g(a,function(a,e,f){e=c?c(e,a):e,d[e]=b(a,e,f)}),d},m=function(a,b,d){return c(a)?k(a,b):l(a,b,d)},n=function(a,b){return m(a,function(a){return a[b]})},o=function(a,b,c){return m(a,function(a,d){return a[b].apply(a,c||[])})},p=function(a){a=a||{};var b={};return a.publish=function(a,c){g(b[a],function(a){a(c)})},a.subscribe=function(a,c){b[a]=b[a]||[],b[a].push(c)},a.unsubscribe=function(a){g(b,function(b){var c=e(b,a);-1!==c&&b.splice(c,1)})},a};!function(a){var b=function(a,b){var c=p(),d=a.$;return c.getType=function(){throw'implement me (return type. "text", "radio", etc.)'},c.$=function(a){return a?d.find(a):d},c.disable=function(){c.$().prop("disabled",!0),c.publish("isEnabled",!1)},c.enable=function(){c.$().prop("disabled",!1),c.publish("isEnabled",!0)},b.equalTo=function(a,b){return a===b},b.publishChange=function(){var a;return function(d,e){var f=c.get();b.equalTo(f,a)||c.publish("change",{e:d,domElement:e}),a=f}}(),c},i=function(a,c){var d=b(a,c);return d.get=function(){return d.$().val()},d.set=function(a){d.$().val(a)},d.clear=function(){d.set("")},c.buildSetter=function(a){return function(b){a.call(d,b)}},d},j=function(a,b){a=c(a)?a:[a],b=c(b)?b:[b];var d=!0;return a.length!==b.length?d=!1:g(a,function(a){f(b,a)||(d=!1)}),d},k=function(a){var b={},c=i(a,b);return c.getType=function(){return"button"},c.$().on("change",function(a){b.publishChange(a,this)}),c},l=function(b){var d={},e=i(b,d);return e.getType=function(){return"checkbox"},e.get=function(){var b=[];return e.$().filter(":checked").each(function(){b.push(a(this).val())}),b},e.set=function(b){b=c(b)?b:[b],e.$().each(function(){a(this).prop("checked",!1)}),g(b,function(a){e.$().filter('[value="'+a+'"]').prop("checked",!0)})},d.equalTo=j,e.$().change(function(a){d.publishChange(a,this)}),e},m=function(a){var b={},c=x(a,b);return c.getType=function(){return"email"},c},n=function(c){var d={},e=b(c,d);return e.getType=function(){return"file"},e.get=function(){return h(e.$().val().split("\\"))},e.clear=function(){this.$().each(function(){a(this).wrap("<form>").closest("form").get(0).reset(),a(this).unwrap()})},e.$().change(function(a){d.publishChange(a,this)}),e},q=function(a){var b={},c=i(a,b);return c.getType=function(){return"hidden"},c.$().change(function(a){b.publishChange(a,this)}),c},r=function(c){var d={},e=b(c,d);return e.getType=function(){return"file[multiple]"},e.get=function(){var a,b=e.$().get(0).files||[],c=[];for(a=0;a<(b.length||0);a+=1)c.push(b[a].name);return c},e.clear=function(){this.$().each(function(){a(this).wrap("<form>").closest("form").get(0).reset(),a(this).unwrap()})},e.$().change(function(a){d.publishChange(a,this)}),e},s=function(a){var b={},d=i(a,b);return d.getType=function(){return"select[multiple]"},d.get=function(){return d.$().val()||[]},d.set=function(a){d.$().val(""===a?[]:c(a)?a:[a])},b.equalTo=j,d.$().change(function(a){b.publishChange(a,this)}),d},t=function(a){var b={},c=x(a,b);return c.getType=function(){return"password"},c},u=function(b){var c={},d=i(b,c);return d.getType=function(){return"radio"},d.get=function(){return d.$().filter(":checked").val()||null},d.set=function(b){b?d.$().filter('[value="'+b+'"]').prop("checked",!0):d.$().each(function(){a(this).prop("checked",!1)})},d.$().change(function(a){c.publishChange(a,this)}),d},v=function(a){var b={},c=i(a,b);return c.getType=function(){return"range"},c.$().change(function(a){b.publishChange(a,this)}),c},w=function(a){var b={},c=i(a,b);return c.getType=function(){return"select"},c.$().change(function(a){b.publishChange(a,this)}),c},x=function(a){var b={},c=i(a,b);return c.getType=function(){return"text"},c.$().on("change keyup keydown",function(a){b.publishChange(a,this)}),c},y=function(a){var b={},c=i(a,b);return c.getType=function(){return"textarea"},c.$().on("change keyup keydown",function(a){b.publishChange(a,this)}),c},z=function(a){var b={},c=x(a,b);return c.getType=function(){return"url"},c},A=function(b){var c={},f=b.$,h=b.constructorOverride||{button:k,text:x,url:z,email:m,password:t,range:v,textarea:y,select:w,"select[multiple]":s,radio:u,checkbox:l,file:n,"file[multiple]":r,hidden:q},i=function(b,e){var g=d(e)?e:f.find(e);g.each(function(){var d=a(this).attr("name");c[d]=h[b]({$:a(this)})})},j=function(b,i){var j=[],k=d(i)?i:f.find(i);d(i)?c[k.attr("name")]=h[b]({$:k}):(k.each(function(){-1===e(j,a(this).attr("name"))&&j.push(a(this).attr("name"))}),g(j,function(a){c[a]=h[b]({$:f.find('input[name="'+a+'"]')})}))};return f.is("input, select, textarea")?f.is('input[type="button"], button, input[type="submit"]')?i("button",f):f.is("textarea")?i("textarea",f):f.is('input[type="text"]')||f.is("input")&&!f.attr("type")?i("text",f):f.is('input[type="password"]')?i("password",f):f.is('input[type="email"]')?i("email",f):f.is('input[type="url"]')?i("url",f):f.is('input[type="range"]')?i("range",f):f.is("select")?f.is("[multiple]")?i("select[multiple]",f):i("select",f):f.is('input[type="file"]')?f.is("[multiple]")?i("file[multiple]",f):i("file",f):f.is('input[type="hidden"]')?i("hidden",f):f.is('input[type="radio"]')?j("radio",f):f.is('input[type="checkbox"]')?j("checkbox",f):i("text",f):(i("button",'input[type="button"], button, input[type="submit"]'),i("text",'input[type="text"]'),i("password",'input[type="password"]'),i("email",'input[type="email"]'),i("url",'input[type="url"]'),i("range",'input[type="range"]'),i("textarea","textarea"),i("select","select:not([multiple])"),i("select[multiple]","select[multiple]"),i("file",'input[type="file"]:not([multiple])'),i("file[multiple]",'input[type="file"][multiple]'),i("hidden",'input[type="hidden"]'),j("radio",'input[type="radio"]'),j("checkbox",'input[type="checkbox"]')),c};a.fn.inputVal=function(b){var c=a(this),d=A({$:c});return c.is("input, textarea, select")?"undefined"==typeof b?d[c.attr("name")].get():(d[c.attr("name")].set(b),c):"undefined"==typeof b?o(d,"get"):(g(b,function(a,b){d[b].set(a)}),c)},a.fn.inputOnChange=function(b){var c=a(this),d=A({$:c});return g(d,function(a){a.subscribe("change",function(a){b.call(a.domElement,a.e)})}),c},a.fn.inputDisable=function(){var b=a(this);return o(A({$:b}),"disable"),b},a.fn.inputEnable=function(){var b=a(this);return o(A({$:b}),"enable"),b},a.fn.inputClear=function(){var b=a(this);return o(A({$:b}),"clear"),b}}(jQuery),a.fn.repeaterVal=function(){var b=function(a){var b=[];return g(a,function(a,c){var d=[];"undefined"!==c&&(d.push(c.match(/^[^\[]*/)[0]),d=d.concat(m(c.match(/\[[^\]]*\]/g),function(a){return a.replace(/[\[\]]/g,"")})),b.push({val:a,key:d}))}),b},c=function(a){if(1===a.length&&(0===a[0].key.length||1===a[0].key.length&&!a[0].key[0]))return a[0].val;g(a,function(a){a.head=a.key.shift()});var b,d=function(){var b={};return g(a,function(a){b[a.head]||(b[a.head]=[]),b[a.head].push(a)}),b}();return/^[0-9]+$/.test(a[0].head)?(b=[],g(d,function(a){b.push(c(a))})):(b={},g(d,function(a,d){b[d]=c(a)})),b};return c(b(a(this).inputVal()))},a.fn.repeater=function(c){return c=c||{},a(this).each(function(){var d=a(this),e=c.show||function(){a(this).show()},f=c.hide||function(a){a()},i=d.find("[data-repeater-list]").first(),k=function(b,c){return b.filter(function(){return c?0===a(this).closest(n(c,"selector").join(",")).length:!0})},l=function(){return k(i.find("[data-repeater-item]"),c.repeaters)},o=i.find("[data-repeater-item]").first().clone().hide(),p=a(this).find("[data-repeater-item]").first().find("[data-repeater-delete]");c.isFirstItemUndeletable&&p&&p.remove();var q=function(){var a=i.data("repeater-list");return c.$parent?c.$parent.data("item-name")+"["+a+"]":a},r=function(b){c.repeaters&&b.each(function(){var b=a(this);g(c.repeaters,function(a){b.find(a.selector).repeater(j(a,{$parent:b}))})})},s=function(a,b,c){a&&g(a,function(a){c.call(b.find(a.selector)[0],a)})},t=function(b,c,d){b.each(function(b){var e=a(this);e.data("item-name",c+"["+b+"]"),k(e.find("[name]"),d).each(function(){var f=a(this),g=f.attr("name").match(/\[[^\]]+\]/g),i=g?h(g).replace(/\[|\]/g,""):f.attr("name"),j=c+"["+b+"]["+i+"]"+(f.is(":checkbox")||f.attr("multiple")?"[]":"");f.attr("name",j),s(d,e,function(d){var e=a(this);t(k(e.find("[data-repeater-item]"),d.repeaters||[]),c+"["+b+"]["+e.find("[data-repeater-list]").first().data("repeater-list")+"]",d.repeaters)})})}),i.find("input[name][checked]").removeAttr("checked").prop("checked",!0)};t(l(),q(),c.repeaters),r(l()),c.ready&&c.ready(function(){t(l(),q(),c.repeaters)});var u=function(){var d=function(c,e,f){if(e){var g={};k(c.find("[name]"),f).each(function(){var b=a(this).attr("name").match(/\[([^\]]*)(\]|\]\[\])$/)[1];g[b]=a(this).attr("name")}),c.inputVal(m(e,b,function(a){return g[a]}))}s(f,c,function(b){var c=a(this);k(c.find("[data-repeater-item]"),b.repeaters).each(function(){d(a(this),b.defaultValues,b.repeaters||[])})})};return function(b){i.append(b),t(l(),q(),c.repeaters),b.find("[name]").each(function(){a(this).inputClear()}),d(b,c.defaultValues,c.repeaters)}}(),v=function(){var a=o.clone();u(a),c.repeaters&&r(a),e.call(a.get(0))};d.children().each(function(){a(this).is("[data-repeater-list]")||0!==a(this).find("[data-repeater-list]").length||(a(this).is("[data-repeater-create]")?a(this).click(v):0!==a(this).find("[data-repeater-create]").length&&a(this).find("[data-repeater-create]").click(v))}),i.on("click","[data-repeater-delete]",function(){var b=a(this).closest("[data-repeater-item]").get(0);f.call(b,function(){a(b).remove(),t(l(),q(),c.repeaters)})})}),this}}(jQuery); |
{ | ||
"name": "jquery.repeater", | ||
"version": "1.1.2", | ||
"version": "1.1.3", | ||
"description": "repeatable form input interface", | ||
@@ -5,0 +5,0 @@ "main": "jquery.repeater.js", |
@@ -33,3 +33,3 @@ #Repeater | ||
data-repeater-item's name attribute would become group-a[0][text-input], | ||
and the second data-repeater-item woulc become group-a[1][text-input] | ||
and the second data-repeater-item would become group-a[1][text-input] | ||
--> | ||
@@ -97,2 +97,49 @@ <div data-repeater-list="group-a"> | ||
## Nested Example | ||
```html | ||
<!-- outer repeater --> | ||
<form class="repeater"> | ||
<div data-repeater-list="outer-list"> | ||
<div data-repeater-item> | ||
<input type="text" name="text-input" value="A"/> | ||
<input data-repeater-delete type="button" value="Delete"/> | ||
<!-- innner repeater --> | ||
<div class="inner-repeater"> | ||
<div data-repeater-list="inner-list"> | ||
<div data-repeater-item> | ||
<input type="text" name="inner-text-input" value="B"/> | ||
<input data-repeater-delete type="button" value="Delete"/> | ||
</div> | ||
</div> | ||
<input data-repeater-create type="button" value="Add"/> | ||
</div> | ||
</div> | ||
</div> | ||
<input data-repeater-create type="button" value="Add"/> | ||
</form> | ||
<script src="path/to/jquery.js"></script> | ||
<script src="path/to/jquery.repeater/jquery.repeater.js"></script> | ||
<script> | ||
$(document).ready(function () { | ||
$('.repeater').repeater({ | ||
// (Required if there is a nested repeater) | ||
// Specify the configuration of the nested repeaters. | ||
// Nested configuration follows the same format as the base configuration, | ||
// supporting options "defaultValues", "show", "hide", etc. | ||
// Nested repeaters additionally require a "selector" field. | ||
repeaters: [{ | ||
// (Required) | ||
// Specify the jQuery selector for this nested repeater | ||
selector: '.inner-repeater' | ||
}] | ||
}); | ||
}); | ||
</script> | ||
``` | ||
## repeaterVal | ||
@@ -99,0 +146,0 @@ |
@@ -13,2 +13,6 @@ var identity = function (x) { | ||
var isNumber = function (value) { | ||
return value instanceof Number; | ||
}; | ||
var isFunction = function (value) { | ||
@@ -34,2 +38,3 @@ return value instanceof Function; | ||
var last = function (array) { | ||
@@ -39,2 +44,16 @@ return array[array.length - 1]; | ||
var argumentsToArray = function (args) { | ||
return Array.prototype.slice.call(args); | ||
}; | ||
var extend = function () { | ||
var extended = {}; | ||
foreach(argumentsToArray(arguments), function (o) { | ||
foreach(o, function (val, key) { | ||
extended[key] = val; | ||
}); | ||
}); | ||
return extended; | ||
}; | ||
var mapToArray = function (collection, callback) { | ||
@@ -63,2 +82,8 @@ var mapped = []; | ||
var pluck = function (arrayOfObjects, key) { | ||
return map(arrayOfObjects, function (val) { | ||
return val[key]; | ||
}); | ||
}; | ||
var filter = function (collection, callback) { | ||
@@ -134,2 +159,2 @@ var filtered; | ||
return object; | ||
}; | ||
}; |
$.fn.repeaterVal = function () { | ||
var rawData = $(this).inputVal(); | ||
var mapped = {}; | ||
var parse = function (raw) { | ||
var parsed = []; | ||
foreach(rawData, function (val, key) { | ||
var group, index, name; | ||
var matches; | ||
if(key !== "undefined") { | ||
matches = key.match(/^([^\[]+)\[([0-9]+)\]\[([^\]]+)/); | ||
group = matches[1]; | ||
index = matches[2]; | ||
name = matches[3]; | ||
if(!mapped[group]) { | ||
mapped[group] = []; | ||
} | ||
foreach(raw, function (val, key) { | ||
var parsedKey = []; | ||
if(key !== "undefined") { | ||
parsedKey.push(key.match(/^[^\[]*/)[0]); | ||
parsedKey = parsedKey.concat(map( | ||
key.match(/\[[^\]]*\]/g), | ||
function (bracketed) { | ||
return bracketed.replace(/[\[\]]/g, ''); | ||
} | ||
)); | ||
if(!mapped[group][index]) { | ||
mapped[group][index] = {}; | ||
parsed.push({ | ||
val: val, | ||
key: parsedKey | ||
}); | ||
} | ||
}); | ||
mapped[group][index][name] = val; | ||
return parsed; | ||
}; | ||
var build = function (parsed) { | ||
if( | ||
parsed.length === 1 && | ||
(parsed[0].key.length === 0 || parsed[0].key.length === 1 && !parsed[0].key[0]) | ||
) { | ||
return parsed[0].val; | ||
} | ||
}); | ||
return mapped; | ||
foreach(parsed, function (p) { | ||
p.head = p.key.shift(); | ||
}); | ||
var grouped = (function () { | ||
var grouped = {}; | ||
foreach(parsed, function (p) { | ||
if(!grouped[p.head]) { | ||
grouped[p.head] = []; | ||
} | ||
grouped[p.head].push(p); | ||
}); | ||
return grouped; | ||
}()); | ||
var built; | ||
if(/^[0-9]+$/.test(parsed[0].head)) { | ||
built = []; | ||
foreach(grouped, function (group) { | ||
built.push(build(group)); | ||
}); | ||
} | ||
else { | ||
built = {}; | ||
foreach(grouped, function (group, key) { | ||
built[key] = build(group); | ||
}); | ||
} | ||
return built; | ||
}; | ||
return build(parse($(this).inputVal())); | ||
}; | ||
$.fn.repeater = function(fig) { | ||
$.fn.repeater = function (fig) { | ||
fig = fig || {}; | ||
@@ -43,9 +87,21 @@ | ||
var $list = $self.find('[data-repeater-list]'); | ||
var $list = $self.find('[data-repeater-list]').first(); | ||
var $filterNested = function ($items, repeaters) { | ||
return $items.filter(function () { | ||
return repeaters ? | ||
$(this).closest( | ||
pluck(repeaters, 'selector').join(',') | ||
).length === 0 : true; | ||
}); | ||
}; | ||
var $items = function () { | ||
return $filterNested($list.find('[data-repeater-item]'), fig.repeaters); | ||
}; | ||
var $itemTemplate = $list.find('[data-repeater-item]') | ||
.first().clone().hide(); | ||
.first().clone().hide(); | ||
var $firstDeleteButton = $(this).find('[data-repeater-item]') | ||
.first() | ||
var $firstDeleteButton = $(this).find('[data-repeater-item]').first() | ||
.find('[data-repeater-delete]'); | ||
@@ -57,9 +113,39 @@ | ||
var groupName = $list.data('repeater-list'); | ||
var getGroupName = function () { | ||
var groupName = $list.data('repeater-list'); | ||
return fig.$parent ? | ||
fig.$parent.data('item-name') + '[' + groupName + ']' : | ||
groupName; | ||
}; | ||
var setIndexes = function () { | ||
$list.find('[data-repeater-item]').each(function (index) { | ||
$(this).find('[name]').each(function () { | ||
var initNested = function ($listItems) { | ||
if(fig.repeaters) { | ||
$listItems.each(function () { | ||
var $item = $(this); | ||
foreach(fig.repeaters, function (nestedFig) { | ||
$item.find(nestedFig.selector).repeater(extend( | ||
nestedFig, { $parent: $item } | ||
)); | ||
}); | ||
}); | ||
} | ||
}; | ||
var $foreachRepeaterInItem = function (repeaters, $item, cb) { | ||
if(repeaters) { | ||
foreach(repeaters, function (nestedFig) { | ||
cb.call($item.find(nestedFig.selector)[0], nestedFig); | ||
}); | ||
} | ||
}; | ||
var setIndexes = function ($items, groupName, repeaters) { | ||
$items.each(function (index) { | ||
var $item = $(this); | ||
$item.data('item-name', groupName + '[' + index + ']'); | ||
$filterNested($item.find('[name]'), repeaters) | ||
.each(function () { | ||
var $input = $(this); | ||
// match non empty brackets (ex: "[foo]") | ||
var matches = $(this).attr('name').match(/\[[^\]]+\]/g); | ||
var matches = $input.attr('name').match(/\[[^\]]+\]/g); | ||
@@ -69,8 +155,19 @@ var name = matches ? | ||
last(matches).replace(/\[|\]/g, '') : | ||
$(this).attr('name'); | ||
$input.attr('name'); | ||
var newName = groupName + '[' + index + '][' + name + ']' + | ||
($(this).is(':checkbox') || $(this).attr('multiple') ? '[]' : ''); | ||
($input.is(':checkbox') || $input.attr('multiple') ? '[]' : ''); | ||
$(this).attr('name', newName); | ||
$input.attr('name', newName); | ||
$foreachRepeaterInItem(repeaters, $item, function (nestedFig) { | ||
var $repeater = $(this); | ||
setIndexes( | ||
$filterNested($repeater.find('[data-repeater-item]'), nestedFig.repeaters || []), | ||
groupName + '[' + index + ']' + | ||
'[' + $repeater.find('[data-repeater-list]').first().data('repeater-list') + ']', | ||
nestedFig.repeaters | ||
); | ||
}); | ||
}); | ||
@@ -84,31 +181,41 @@ }); | ||
setIndexes(); | ||
setIndexes($items(), getGroupName(), fig.repeaters); | ||
initNested($items()); | ||
if(fig.ready) { | ||
fig.ready(setIndexes); | ||
fig.ready(function () { | ||
setIndexes($items(), getGroupName(), fig.repeaters); | ||
}); | ||
} | ||
var setItemsValues = function ($item, values) { | ||
var index; | ||
index = $item.find('[name]').first() | ||
.attr('name').match(/\[([0-9]*)\]/)[1]; | ||
$item.inputVal(map(values, identity, function (name) { | ||
var nameIfNotCheckbox = groupName + '[' + index + '][' + name + ']'; | ||
return $item.find('[name="' + nameIfNotCheckbox + '"]').length ? | ||
nameIfNotCheckbox : nameIfNotCheckbox + '[]'; | ||
})); | ||
}; | ||
var appendItem = (function () { | ||
var setupTemplate = function ($item) { | ||
var defaultValues = fig.defaultValues; | ||
var setItemsValues = function ($item, values, repeaters) { | ||
if(values) { | ||
var inputNames = {}; | ||
$filterNested($item.find('[name]'), repeaters).each(function () { | ||
var key = $(this).attr('name').match(/\[([^\]]*)(\]|\]\[\])$/)[1]; | ||
inputNames[key] = $(this).attr('name'); | ||
}); | ||
$item.find('[name]').each(function () { | ||
$(this).inputClear(); | ||
$item.inputVal(map(values, identity, function (name) { | ||
return inputNames[name]; | ||
})); | ||
} | ||
$foreachRepeaterInItem(repeaters, $item, function (nestedFig) { | ||
var $repeater = $(this); | ||
$filterNested( | ||
$repeater.find('[data-repeater-item]'), | ||
nestedFig.repeaters | ||
) | ||
.each(function () { | ||
setItemsValues( | ||
$(this), | ||
nestedFig.defaultValues, | ||
nestedFig.repeaters || [] | ||
); | ||
}); | ||
}); | ||
if(defaultValues) { | ||
setItemsValues($item, defaultValues); | ||
} | ||
}; | ||
@@ -118,11 +225,31 @@ | ||
$list.append($item); | ||
setIndexes(); | ||
setupTemplate($item); | ||
setIndexes($items(), getGroupName(), fig.repeaters); | ||
$item.find('[name]').each(function () { | ||
$(this).inputClear(); | ||
}); | ||
setItemsValues($item, fig.defaultValues, fig.repeaters); | ||
}; | ||
}()); | ||
$self.find('[data-repeater-create]').click(function () { | ||
var addItem = function () { | ||
var $item = $itemTemplate.clone(); | ||
appendItem($item); | ||
if(fig.repeaters) { | ||
initNested($item); | ||
} | ||
show.call($item.get(0)); | ||
}; | ||
$self.children().each(function () { | ||
if( | ||
!$(this).is('[data-repeater-list]') && | ||
$(this).find('[data-repeater-list]').length === 0 | ||
) { | ||
if($(this).is('[data-repeater-create]')) { | ||
$(this).click(addItem); | ||
} | ||
else if($(this).find('[data-repeater-create]').length !== 0) { | ||
$(this).find('[data-repeater-create]').click(addItem); | ||
} | ||
} | ||
}); | ||
@@ -134,7 +261,5 @@ | ||
$(self).remove(); | ||
setIndexes(); | ||
setIndexes($items(), getGroupName(), fig.repeaters); | ||
}); | ||
}); | ||
}); | ||
@@ -141,0 +266,0 @@ |
@@ -17,19 +17,2 @@ QUnit.module('repeater', { | ||
var getNamedInputValues = function ($scope) { | ||
return filter($scope.inputVal(), function (val, key) { | ||
return key !== 'undefined'; | ||
}); | ||
}; | ||
var generateNameMappedInputValues = function (group, index, defaultValue, override) { | ||
var defaultObject = {}; | ||
defaultObject['group-' + group + '[' + index + '][text-input]'] = defaultValue; | ||
defaultObject['group-' + group + '[' + index + '][textarea-input]'] = defaultValue; | ||
defaultObject['group-' + group + '[' + index + '][select-input]'] = defaultValue || null; | ||
defaultObject['group-' + group + '[' + index + '][radio-input]'] = defaultValue || null; | ||
defaultObject['group-' + group + '[' + index + '][checkbox-input][]'] = defaultValue ? [defaultValue] : []; | ||
defaultObject['group-' + group + '[' + index + '][multiple-select-input][]'] = defaultValue ? [defaultValue] : []; | ||
return $.extend(defaultObject, override || {}); | ||
}; | ||
QUnit.test('add item', function (assert) { | ||
@@ -36,0 +19,0 @@ this.$repeater.repeater(); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
453948
29
12923
160