generator-craftskeleton
Version:
Starterkit for Craft CMS 2.x (Multi-Environment Configs with HeartyConfig, Gulp, and Bower for Dependencie-Management) made by interpunkt. ag
408 lines (322 loc) • 16 kB
JavaScript
(function($) {
Craft.SuperTableConfigurator = Garnish.Base.extend({
fieldTypeInfo: null,
inputNamePrefix: null,
inputIdPrefix: null,
blockTypeId: null,
blockTypeNamePrefix: null,
blockTypeIdPrefix: null,
$container: null,
$fieldsColumnContainer: null,
$fieldSettingsColumnContainer: null,
$fieldItemsContainer: null,
$fieldSettingItemsContainer: null,
$newFieldBtn: null,
fields: null,
selectedField: null,
fieldSort: null,
totalNewFields: 0,
fieldSettings: null,
init: function(id, idPrefix, fieldTypeInfo, inputNamePrefix) {
this.fieldTypeInfo = fieldTypeInfo;
this.blockTypeId = id;
this.blockTypeNamePrefix = inputNamePrefix+'[blockTypes]['+this.blockTypeId+']';
this.blockTypeIdPrefix = Craft.formatInputId(this.blockTypeNamePrefix);
this.inputNamePrefix = inputNamePrefix;
this.inputIdPrefix = Craft.formatInputId(this.inputNamePrefix);
this.$container = $('#' + this.inputIdPrefix + '-supertable-configurator:first .input:first');
this.$fieldsColumnContainer = this.$container.children('.fields').children();
this.$fieldSettingsColumnContainer = this.$container.children('.stc-settings').children();
this.$fieldItemsOuterContainer = this.$fieldsColumnContainer.children('.field-items');
this.$fieldSettingItemsContainer = this.$fieldSettingsColumnContainer.children('.field-items');
this.$newFieldBtn = this.$fieldItemsOuterContainer.children('.btn');
// Find the field items container if it exists, otherwise create it
this.$fieldItemsContainer = this.$fieldItemsOuterContainer.children('[data-id="'+this.blockTypeId+'"]:first');
if (!this.$fieldItemsContainer.length) {
this.$fieldItemsContainer = $('<div data-id="'+this.blockTypeId+'"/>').insertBefore(this.$newFieldBtn);
}
// Find the field settings container if it exists, otherwise create it
this.$fieldSettingsContainer = this.$fieldSettingItemsContainer.children('[data-id="'+this.blockTypeId+'"]:first');
if (!this.$fieldSettingsContainer.length) {
this.$fieldSettingsContainer = $('<div data-id="'+this.blockTypeId+'"/>').appendTo(this.$fieldSettingItemsContainer);
}
// Find the existing fields
this.fields = {};
var $fieldItems = this.$fieldItemsContainer.children();
for (var i = 0; i < $fieldItems.length; i++) {
var $fieldItem = $($fieldItems[i]),
id = $fieldItem.data('id');
this.fields[id] = new Craft.SuperTableField(this, $fieldItem);
// Pre-select first field
if (i == 0) {
this.selectedField = this.fields[id];
}
// Is this a new field?
var newMatch = (typeof id == 'string' && id.match(/new(\d+)/));
if (newMatch && newMatch[1] > this.totalNewFields) {
this.totalNewFields = parseInt(newMatch[1]);
}
}
this.addListener(this.$newFieldBtn, 'click', 'addField');
this.fieldSort = new Garnish.DragSort($fieldItems, {
handle: '.move',
axis: 'y',
onSortChange: $.proxy(function() {
// Adjust the field setting containers to match the new sort order
for (var i = 0; i < this.fieldSort.$items.length; i++) {
var $item = $(this.fieldSort.$items[i]),
id = $item.data('id'),
field = this.fields[id];
if (field) {
field.$fieldSettingsContainer.appendTo(this.$fieldSettingsContainer);
}
}
}, this)
});
},
getFieldTypeInfo: function(type) {
for (var i = 0; i < this.fieldTypeInfo.length; i++) {
if (this.fieldTypeInfo[i].type == type) {
return this.fieldTypeInfo[i];
}
}
},
addField: function() {
this.totalNewFields++;
var id = 'new'+this.totalNewFields;
var $item = $(
'<div class="supertableconfigitem stci-field" data-id="'+id+'">' +
'<div class="name"><em class="light">'+Craft.t('(blank)')+'</em> </div>' +
'<div class="handle code"> </div>' +
'<div class="actions">' +
'<a class="move icon" title="'+Craft.t('Reorder')+'"></a>' +
'</div>' +
'</div>'
).appendTo(this.$fieldItemsContainer);
this.fields[id] = new Craft.SuperTableField(this, $item);
this.fields[id].select();
this.fieldSort.addItems($item);
},
});
Craft.SuperTableField = Garnish.Base.extend({
configurator: null,
id: null,
inputNamePrefix: null,
inputIdPrefix: null,
selectedFieldType: null,
initializedFieldTypeSettings: null,
$item: null,
$nameLabel: null,
$handleLabel: null,
$fieldSettingsContainer: null,
$nameInput: null,
$handleInput: null,
$requiredCheckbox: null,
$typeSelect: null,
$typeSettingsContainer: null,
$deleteBtn: null,
init: function(configurator, $item) {
this.configurator = configurator;
this.$item = $item;
this.id = this.$item.data('id');
this.inputNamePrefix = this.configurator.blockTypeNamePrefix+'[fields]['+this.id+']';
this.inputIdPrefix = this.configurator.blockTypeIdPrefix+'-fields-'+this.id;
this.initializedFieldTypeSettings = {};
this.$nameLabel = this.$item.children('.name');
this.$handleLabel = this.$item.children('.handle');
// Find the field settings container if it exists, otherwise create it
this.$fieldSettingsContainer = this.configurator.$fieldSettingsContainer.children('[data-id="'+this.id+'"]:first');
var isNew = (!this.$fieldSettingsContainer.length);
if (isNew) {
this.$fieldSettingsContainer = $(this.getDefaultFieldSettingsHtml()).appendTo(this.configurator.$fieldSettingsContainer);
}
this.$nameInput = this.$fieldSettingsContainer.find('input[name$="[name]"]:first');
this.$handleInput = this.$fieldSettingsContainer.find('input[name$="[handle]"]:first');
this.$requiredCheckbox = this.$fieldSettingsContainer.find('input[type="checkbox"][name$="[required]"]:first');
this.$typeSelect = this.$fieldSettingsContainer.find('select[name$="[type]"]:first');
this.$typeSettingsContainer = this.$fieldSettingsContainer.children('.fieldtype-settings:first');
this.$deleteBtn = this.$fieldSettingsContainer.children('a.delete:first');
if (isNew) {
this.setFieldType('PlainText');
} else {
this.selectedFieldType = this.$typeSelect.val();
this.initializedFieldTypeSettings[this.selectedFieldType] = this.$typeSettingsContainer.children();
//this.setFieldType(this.selectedFieldType);
}
if (!this.$handleInput.val()) {
new Craft.HandleGenerator(this.$nameInput, this.$handleInput);
}
this.addListener(this.$item, 'click', 'select');
this.addListener(this.$nameInput, 'textchange', 'updateNameLabel');
this.addListener(this.$handleInput, 'textchange', 'updateHandleLabel');
this.addListener(this.$requiredCheckbox, 'change', 'updateRequiredIcon');
this.addListener(this.$typeSelect, 'change', 'onTypeSelectChange');
this.addListener(this.$deleteBtn, 'click', 'confirmDelete');
},
select: function() {
if (this.configurator.selectedField == this) {
return;
}
if (this.configurator.selectedField) {
this.configurator.selectedField.deselect();
}
this.configurator.$fieldSettingsContainer.removeClass('hidden');
this.$fieldSettingsContainer.removeClass('hidden');
this.$item.addClass('sel');
this.configurator.selectedField = this;
this.setFieldType(this.selectedFieldType);
if (!Garnish.isMobileBrowser()) {
setTimeout($.proxy(function() {
this.$nameInput.focus()
}, this), 100);
}
},
deselect: function() {
this.$item.removeClass('sel');
this.configurator.$fieldSettingsContainer.addClass('hidden');
this.$fieldSettingsContainer.addClass('hidden');
this.configurator.selectedField = null;
},
updateNameLabel: function() {
var val = this.$nameInput.val();
this.$nameLabel.html((val ? Craft.escapeHtml(val) : '<em class="light">'+Craft.t('(blank)')+'</em>')+' ');
},
updateHandleLabel: function() {
this.$handleLabel.html(Craft.escapeHtml(this.$handleInput.val())+' ');
},
updateRequiredIcon: function() {
if (this.$requiredCheckbox.prop('checked')) {
this.$nameLabel.addClass('required');
} else {
this.$nameLabel.removeClass('required');
}
},
onTypeSelectChange: function() {
this.setFieldType(this.$typeSelect.val());
},
setFieldType: function(type) {
if (this.selectedFieldType) {
this.initializedFieldTypeSettings[this.selectedFieldType].detach();
}
this.selectedFieldType = type;
this.$typeSelect.val(type);
var firstTime = (typeof this.initializedFieldTypeSettings[type] == 'undefined');
if (firstTime) {
var info = this.configurator.getFieldTypeInfo(type),
bodyHtml = this.getParsedFieldTypeHtml(info.settingsBodyHtml),
footHtml = this.getParsedFieldTypeHtml(info.settingsFootHtml),
$body = $('<div>'+bodyHtml+'</div>');
this.initializedFieldTypeSettings[type] = $body;
} else {
var $body = this.initializedFieldTypeSettings[type];
}
$body.appendTo(this.$typeSettingsContainer);
if (firstTime) {
Craft.initUiElements($body);
Garnish.$bod.append(footHtml);
}
// Firefox might have been sleeping on the job.
this.$typeSettingsContainer.trigger('resize');
},
getParsedFieldTypeHtml: function(html) {
var newHtml = html;
if (typeof newHtml == 'string') {
newHtml = newHtml.replace(/__BLOCK_TYPE_ST__/g, this.configurator.blockTypeId);
newHtml = newHtml.replace(/__FIELD_ST__/g, this.id);
} else {
newHtml = '';
}
return newHtml;
},
getDefaultFieldSettingsHtml: function() {
var html = '<div data-id="'+this.id+'">' +
'<div class="field" id="'+this.inputIdPrefix+'-name-field">' +
'<div class="heading">' +
'<label class="required" for="'+this.inputIdPrefix+'-name">'+Craft.t('Name')+'</label>' +
'<div class="instructions"><p>'+Craft.t('What this field will be called in the CP.')+'</p></div>' +
'</div>' +
'<div class="input">' +
'<input class="text fullwidth" type="text" id="'+this.inputIdPrefix+'-name" name="'+this.inputNamePrefix+'[name]" autofocus="" autocomplete="off"/>' +
'</div>' +
'</div>' +
'<div class="field" id="'+this.inputIdPrefix+'-handle-field">' +
'<div class="heading">' +
'<label class="required" for="'+this.inputIdPrefix+'-handle">'+Craft.t('Handle')+'</label>' +
'<div class="instructions"><p>'+Craft.t('How you’ll refer to this field in the templates.')+'</p></div>' +
'</div>' +
'<div class="input">' +
'<input class="text fullwidth code" type="text" id="'+this.inputIdPrefix+'-handle" name="'+this.inputNamePrefix+'[handle]" autofocus="" autocomplete="off"/>' +
'</div>' +
'</div>' +
'<div class="field" id="'+this.inputIdPrefix+'-instructions-field">' +
'<div class="heading">' +
'<label for="'+this.inputIdPrefix+'-instructions">'+Craft.t('Instructions')+'</label>' +
'<div class="instructions"><p>'+Craft.t('Helper text to guide the author. Will appear as a small <code>info</code> icon in table headers.')+'</p></div>' +
'</div>' +
'<div class="input">' +
'<textarea class="text nicetext fullwidth" rows="2" cols="50" id="'+this.inputIdPrefix+'-instructions" name="'+this.inputNamePrefix+'[instructions]"></textarea>' +
'</div>' +
'</div>' +
'<div class="field" id="'+this.inputIdPrefix+'-width-field">' +
'<div class="heading">' +
'<label for="'+this.inputIdPrefix+'-width">'+Craft.t('Column Width')+'</label>' +
'<div class="instructions"><p>'+Craft.t('Only applies for Table Layout. Set the width for this column in either pixels or percentage. i.e. <code>10px</code> or <code>10%</code>.')+'</p></div>' +
'</div>' +
'<div class="input ltr">' +
'<input class="text" type="text" id="'+this.inputIdPrefix+'-width" size="4" name="'+this.inputNamePrefix+'[width]" autocomplete="off">' +
'</div>' +
'</div>' +
'<div class="field checkboxfield">' +
'<label>' +
'<input type="hidden" name="'+this.inputNamePrefix+'[required]" value=""/>' +
'<input type="checkbox" value="1" name="'+this.inputNamePrefix+'[required]"/> ' +
Craft.t('This field is required') +
'</label>' +
'</div>';
if (Craft.isLocalized) {
html += '<div class="field checkboxfield">' +
'<label>' +
'<input type="hidden" name="'+this.inputNamePrefix+'[translatable]" value=""/>' +
'<input type="checkbox" value="1" name="'+this.inputNamePrefix+'[translatable]"/> ' +
Craft.t('This field is translatable') +
'</label>' +
'</div>';
}
html += '<hr/>' +
'<div class="field" id="type-field">' +
'<div class="heading">' +
'<label for="type">'+Craft.t('Field Type')+'</label>' +
'</div>' +
'<div class="input">' +
'<div class="select">' +
'<select id="type" class="fieldtoggle" name="'+this.inputNamePrefix+'[type]">';
for (var i = 0; i < this.configurator.fieldTypeInfo.length; i++) {
var info = this.configurator.fieldTypeInfo[i],
selected = (info.type == 'PlainText');
html += '<option value="'+info.type+'"'+(selected ? ' selected=""' : '')+'>'+info.name+'</option>';
}
html +=
'</select>' +
'</div>' +
'</div>' +
'</div>' +
'<div class="fieldtype-settings"/>' +
'<hr/>' +
'<a class="error delete">'+Craft.t('Delete')+'</a>' +
'</div>';
return html;
},
confirmDelete: function() {
if (confirm(Craft.t('Are you sure you want to delete this field?'))) {
this.selfDestruct();
}
},
selfDestruct: function() {
this.deselect();
this.$item.remove();
this.$fieldSettingsContainer.remove();
this.configurator.fields[this.id] = null;
delete this.configurator.fields[this.id];
}
});
})(jQuery);