|
- // jquery.repeater version 1.1.3
- // https://github.com/DubFriend/jquery.repeater
- // (MIT) 06-12-2015
- // Brian Detering <BDeterin@gmail.com> (http://www.briandetering.net/)
- (function ($) {
- 'use strict';
-
- var identity = function (x) {
- return x;
- };
-
- var isArray = function (value) {
- return $.isArray(value);
- };
-
- var isObject = function (value) {
- return !isArray(value) && (value instanceof Object);
- };
-
- var isNumber = function (value) {
- return value instanceof Number;
- };
-
- var isFunction = function (value) {
- return value instanceof Function;
- };
-
- var indexOf = function (object, value) {
- return $.inArray(value, object);
- };
-
- var inArray = function (array, value) {
- return indexOf(array, value) !== -1;
- };
-
- var foreach = function (collection, callback) {
- for(var i in collection) {
- if(collection.hasOwnProperty(i)) {
- callback(collection[i], i, collection);
- }
- }
- };
-
-
- var last = function (array) {
- 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) {
- var mapped = [];
- foreach(collection, function (value, key, coll) {
- mapped.push(callback(value, key, coll));
- });
- return mapped;
- };
-
- var mapToObject = function (collection, callback, keyCallback) {
- var mapped = {};
- foreach(collection, function (value, key, coll) {
- key = keyCallback ? keyCallback(key, value) : key;
- mapped[key] = callback(value, key, coll);
- });
- return mapped;
- };
-
- var map = function (collection, callback, keyCallback) {
- return isArray(collection) ?
- mapToArray(collection, callback) :
- mapToObject(collection, callback, keyCallback);
- };
-
- var pluck = function (arrayOfObjects, key) {
- return map(arrayOfObjects, function (val) {
- return val[key];
- });
- };
-
- var filter = function (collection, callback) {
- var filtered;
-
- if(isArray(collection)) {
- filtered = [];
- foreach(collection, function (val, key, coll) {
- if(callback(val, key, coll)) {
- filtered.push(val);
- }
- });
- }
- else {
- filtered = {};
- foreach(collection, function (val, key, coll) {
- if(callback(val, key, coll)) {
- filtered[key] = val;
- }
- });
- }
-
- return filtered;
- };
-
- var call = function (collection, functionName, args) {
- return map(collection, function (object, name) {
- return object[functionName].apply(object, args || []);
- });
- };
-
- //execute callback immediately and at most one time on the minimumInterval,
- //ignore block attempts
- var throttle = function (minimumInterval, callback) {
- var timeout = null;
- return function () {
- var that = this, args = arguments;
- if(timeout === null) {
- timeout = setTimeout(function () {
- timeout = null;
- }, minimumInterval);
- callback.apply(that, args);
- }
- };
- };
-
-
- var mixinPubSub = function (object) {
- object = object || {};
- var topics = {};
-
- object.publish = function (topic, data) {
- foreach(topics[topic], function (callback) {
- callback(data);
- });
- };
-
- object.subscribe = function (topic, callback) {
- topics[topic] = topics[topic] || [];
- topics[topic].push(callback);
- };
-
- object.unsubscribe = function (callback) {
- foreach(topics, function (subscribers) {
- var index = indexOf(subscribers, callback);
- if(index !== -1) {
- subscribers.splice(index, 1);
- }
- });
- };
-
- return object;
- };
-
- // jquery.input version 0.0.0
- // https://github.com/DubFriend/jquery.input
- // (MIT) 09-04-2014
- // Brian Detering <BDeterin@gmail.com> (http://www.briandetering.net/)
- (function ($) {
- 'use strict';
-
- var createBaseInput = function (fig, my) {
- var self = mixinPubSub(),
- $self = fig.$;
-
- self.getType = function () {
- throw 'implement me (return type. "text", "radio", etc.)';
- };
-
- self.$ = function (selector) {
- return selector ? $self.find(selector) : $self;
- };
-
- self.disable = function () {
- self.$().prop('disabled', true);
- self.publish('isEnabled', false);
- };
-
- self.enable = function () {
- self.$().prop('disabled', false);
- self.publish('isEnabled', true);
- };
-
- my.equalTo = function (a, b) {
- return a === b;
- };
-
- my.publishChange = (function () {
- var oldValue;
- return function (e, domElement) {
- var newValue = self.get();
- if(!my.equalTo(newValue, oldValue)) {
- self.publish('change', { e: e, domElement: domElement });
- }
- oldValue = newValue;
- };
- }());
-
- return self;
- };
-
-
- var createInput = function (fig, my) {
- var self = createBaseInput(fig, my);
-
- self.get = function () {
- return self.$().val();
- };
-
- self.set = function (newValue) {
- self.$().val(newValue);
- };
-
- self.clear = function () {
- self.set('');
- };
-
- my.buildSetter = function (callback) {
- return function (newValue) {
- callback.call(self, newValue);
- };
- };
-
- return self;
- };
-
- var inputEqualToArray = function (a, b) {
- a = isArray(a) ? a : [a];
- b = isArray(b) ? b : [b];
-
- var isEqual = true;
- if(a.length !== b.length) {
- isEqual = false;
- }
- else {
- foreach(a, function (value) {
- if(!inArray(b, value)) {
- isEqual = false;
- }
- });
- }
-
- return isEqual;
- };
-
- var createInputButton = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'button';
- };
-
- self.$().on('change', function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputCheckbox = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'checkbox';
- };
-
- self.get = function () {
- var values = [];
- self.$().filter(':checked').each(function () {
- values.push($(this).val());
- });
- return values;
- };
-
- self.set = function (newValues) {
- newValues = isArray(newValues) ? newValues : [newValues];
-
- self.$().each(function () {
- $(this).prop('checked', false);
- });
-
- foreach(newValues, function (value) {
- self.$().filter('[value="' + value + '"]')
- .prop('checked', true);
- });
- };
-
- my.equalTo = inputEqualToArray;
-
- self.$().change(function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputEmail = function (fig) {
- var my = {},
- self = createInputText(fig, my);
-
- self.getType = function () {
- return 'email';
- };
-
- return self;
- };
-
- var createInputFile = function (fig) {
- var my = {},
- self = createBaseInput(fig, my);
-
- self.getType = function () {
- return 'file';
- };
-
- self.get = function () {
- return last(self.$().val().split('\\'));
- };
-
- self.clear = function () {
- // http://stackoverflow.com/questions/1043957/clearing-input-type-file-using-jquery
- this.$().each(function () {
- $(this).wrap('<form>').closest('form').get(0).reset();
- $(this).unwrap();
- });
- };
-
- self.$().change(function (e) {
- my.publishChange(e, this);
- // self.publish('change', self);
- });
-
- return self;
- };
-
- var createInputHidden = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'hidden';
- };
-
- self.$().change(function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
- var createInputMultipleFile = function (fig) {
- var my = {},
- self = createBaseInput(fig, my);
-
- self.getType = function () {
- return 'file[multiple]';
- };
-
- self.get = function () {
- // http://stackoverflow.com/questions/14035530/how-to-get-value-of-html-5-multiple-file-upload-variable-using-jquery
- var fileListObject = self.$().get(0).files || [],
- names = [], i;
-
- for(i = 0; i < (fileListObject.length || 0); i += 1) {
- names.push(fileListObject[i].name);
- }
-
- return names;
- };
-
- self.clear = function () {
- // http://stackoverflow.com/questions/1043957/clearing-input-type-file-using-jquery
- this.$().each(function () {
- $(this).wrap('<form>').closest('form').get(0).reset();
- $(this).unwrap();
- });
- };
-
- self.$().change(function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputMultipleSelect = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'select[multiple]';
- };
-
- self.get = function () {
- return self.$().val() || [];
- };
-
- self.set = function (newValues) {
- self.$().val(
- newValues === '' ? [] : isArray(newValues) ? newValues : [newValues]
- );
- };
-
- my.equalTo = inputEqualToArray;
-
- self.$().change(function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputPassword = function (fig) {
- var my = {},
- self = createInputText(fig, my);
-
- self.getType = function () {
- return 'password';
- };
-
- return self;
- };
-
- var createInputRadio = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'radio';
- };
-
- self.get = function () {
- return self.$().filter(':checked').val() || null;
- };
-
- self.set = function (newValue) {
- if(!newValue) {
- self.$().each(function () {
- $(this).prop('checked', false);
- });
- }
- else {
- self.$().filter('[value="' + newValue + '"]').prop('checked', true);
- }
- };
-
- self.$().change(function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputRange = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'range';
- };
-
- self.$().change(function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputSelect = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'select';
- };
-
- self.$().change(function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputText = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'text';
- };
-
- self.$().on('change keyup keydown', function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputTextarea = function (fig) {
- var my = {},
- self = createInput(fig, my);
-
- self.getType = function () {
- return 'textarea';
- };
-
- self.$().on('change keyup keydown', function (e) {
- my.publishChange(e, this);
- });
-
- return self;
- };
-
- var createInputURL = function (fig) {
- var my = {},
- self = createInputText(fig, my);
-
- self.getType = function () {
- return 'url';
- };
-
- return self;
- };
-
- var buildFormInputs = function (fig) {
- var inputs = {},
- $self = fig.$;
-
- var constructor = fig.constructorOverride || {
- button: createInputButton,
- text: createInputText,
- url: createInputURL,
- email: createInputEmail,
- password: createInputPassword,
- range: createInputRange,
- textarea: createInputTextarea,
- select: createInputSelect,
- 'select[multiple]': createInputMultipleSelect,
- radio: createInputRadio,
- checkbox: createInputCheckbox,
- file: createInputFile,
- 'file[multiple]': createInputMultipleFile,
- hidden: createInputHidden
- };
-
- var addInputsBasic = function (type, selector) {
- var $input = isObject(selector) ? selector : $self.find(selector);
-
- $input.each(function () {
- var name = $(this).attr('name');
- inputs[name] = constructor[type]({
- $: $(this)
- });
- });
- };
-
- var addInputsGroup = function (type, selector) {
- var names = [],
- $input = isObject(selector) ? selector : $self.find(selector);
-
- if(isObject(selector)) {
- inputs[$input.attr('name')] = constructor[type]({
- $: $input
- });
- }
- else {
- // group by name attribute
- $input.each(function () {
- if(indexOf(names, $(this).attr('name')) === -1) {
- names.push($(this).attr('name'));
- }
- });
-
- foreach(names, function (name) {
- inputs[name] = constructor[type]({
- $: $self.find('input[name="' + name + '"]')
- });
- });
- }
- };
-
-
- if($self.is('input, select, textarea')) {
- if($self.is('input[type="button"], button, input[type="submit"]')) {
- addInputsBasic('button', $self);
- }
- else if($self.is('textarea')) {
- addInputsBasic('textarea', $self);
- }
- else if(
- $self.is('input[type="text"]') ||
- $self.is('input') && !$self.attr('type')
- ) {
- addInputsBasic('text', $self);
- }
- else if($self.is('input[type="password"]')) {
- addInputsBasic('password', $self);
- }
- else if($self.is('input[type="email"]')) {
- addInputsBasic('email', $self);
- }
- else if($self.is('input[type="url"]')) {
- addInputsBasic('url', $self);
- }
- else if($self.is('input[type="range"]')) {
- addInputsBasic('range', $self);
- }
- else if($self.is('select')) {
- if($self.is('[multiple]')) {
- addInputsBasic('select[multiple]', $self);
- }
- else {
- addInputsBasic('select', $self);
- }
- }
- else if($self.is('input[type="file"]')) {
- if($self.is('[multiple]')) {
- addInputsBasic('file[multiple]', $self);
- }
- else {
- addInputsBasic('file', $self);
- }
- }
- else if($self.is('input[type="hidden"]')) {
- addInputsBasic('hidden', $self);
- }
- else if($self.is('input[type="radio"]')) {
- addInputsGroup('radio', $self);
- }
- else if($self.is('input[type="checkbox"]')) {
- addInputsGroup('checkbox', $self);
- }
- else {
- //in all other cases default to a "text" input interface.
- addInputsBasic('text', $self);
- }
- }
- else {
- addInputsBasic('button', 'input[type="button"], button, input[type="submit"]');
- addInputsBasic('text', 'input[type="text"]');
- addInputsBasic('password', 'input[type="password"]');
- addInputsBasic('email', 'input[type="email"]');
- addInputsBasic('url', 'input[type="url"]');
- addInputsBasic('range', 'input[type="range"]');
- addInputsBasic('textarea', 'textarea');
- addInputsBasic('select', 'select:not([multiple])');
- addInputsBasic('select[multiple]', 'select[multiple]');
- addInputsBasic('file', 'input[type="file"]:not([multiple])');
- addInputsBasic('file[multiple]', 'input[type="file"][multiple]');
- addInputsBasic('hidden', 'input[type="hidden"]');
- addInputsGroup('radio', 'input[type="radio"]');
- addInputsGroup('checkbox', 'input[type="checkbox"]');
- }
-
- return inputs;
- };
-
- $.fn.inputVal = function (newValue) {
- var $self = $(this);
-
- var inputs = buildFormInputs({ $: $self });
-
- if($self.is('input, textarea, select')) {
- if(typeof newValue === 'undefined') {
- return inputs[$self.attr('name')].get();
- }
- else {
- inputs[$self.attr('name')].set(newValue);
- return $self;
- }
- }
- else {
- if(typeof newValue === 'undefined') {
- return call(inputs, 'get');
- }
- else {
- foreach(newValue, function (value, inputName) {
- inputs[inputName].set(value);
- });
- return $self;
- }
- }
- };
-
- $.fn.inputOnChange = function (callback) {
- var $self = $(this);
- var inputs = buildFormInputs({ $: $self });
- foreach(inputs, function (input) {
- input.subscribe('change', function (data) {
- callback.call(data.domElement, data.e);
- });
- });
- return $self;
- };
-
- $.fn.inputDisable = function () {
- var $self = $(this);
- call(buildFormInputs({ $: $self }), 'disable');
- return $self;
- };
-
- $.fn.inputEnable = function () {
- var $self = $(this);
- call(buildFormInputs({ $: $self }), 'enable');
- return $self;
- };
-
- $.fn.inputClear = function () {
- var $self = $(this);
- call(buildFormInputs({ $: $self }), 'clear');
- return $self;
- };
-
- }(jQuery));
-
- $.fn.repeaterVal = function () {
- var parse = function (raw) {
- var parsed = [];
-
- 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, '');
- }
- ));
-
- parsed.push({
- val: val,
- key: parsedKey
- });
- }
- });
-
- 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;
- }
-
- 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) {
- fig = fig || {};
-
- $(this).each(function () {
-
- var $self = $(this);
-
- var show = fig.show || function () {
- $(this).show();
- };
-
- var hide = fig.hide || function (removeElement) {
- removeElement();
- };
-
- 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();
-
- var $firstDeleteButton = $(this).find('[data-repeater-item]').first()
- .find('[data-repeater-delete]');
-
- if(fig.isFirstItemUndeletable && $firstDeleteButton) {
- $firstDeleteButton.remove();
- }
-
- var getGroupName = function () {
- var groupName = $list.data('repeater-list');
- return fig.$parent ?
- fig.$parent.data('item-name') + '[' + groupName + ']' :
- groupName;
- };
-
- 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 = $input.attr('name').match(/\[[^\]]+\]/g);
-
- var name = matches ?
- // strip "[" and "]" characters
- last(matches).replace(/\[|\]/g, '') :
- $input.attr('name');
-
-
- var newName = groupName + '[' + index + '][' + name + ']' +
- ($input.is(':checkbox') || $input.attr('multiple') ? '[]' : '');
-
- $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
- );
- });
- });
- });
-
- $list.find('input[name][checked]')
- .removeAttr('checked')
- .prop('checked', true);
- };
-
- setIndexes($items(), getGroupName(), fig.repeaters);
- initNested($items());
-
- if(fig.ready) {
- fig.ready(function () {
- setIndexes($items(), getGroupName(), fig.repeaters);
- });
- }
-
-
-
- var appendItem = (function () {
- 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.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 || []
- );
- });
- });
- };
-
- return function ($item) {
- $list.append($item);
- setIndexes($items(), getGroupName(), fig.repeaters);
- $item.find('[name]').each(function () {
- $(this).inputClear();
- });
- setItemsValues($item, fig.defaultValues, fig.repeaters);
- };
- }());
-
- 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);
- }
- }
- });
-
- $list.on('click', '[data-repeater-delete]', function () {
- var self = $(this).closest('[data-repeater-item]').get(0);
- hide.call(self, function () {
- $(self).remove();
- setIndexes($items(), getGroupName(), fig.repeaters);
- });
- });
- });
-
- return this;
- };
-
- }(jQuery));
|