\n';
return __p
}
var TableView = Marionette.ItemView.extend({
tagName: 'table',
className: 'table_view',
template: template$2,
ui: {
headRow: 'thead tr',
body: 'tbody'
},
onRender: function onRender() {
var view = this;
_(this.options.columns).each(function (column) {
this.ui.headRow.append(this.subview(new TableHeaderCellView({
column: column,
attributeTranslationKeyPrefixes: this.options.attributeTranslationKeyPrefixes
})).el);
}, this);
this.subview(new CollectionView({
el: this.ui.body,
collection: this.collection,
itemViewConstructor: TableRowView,
itemViewOptions: {
columns: this.options.columns,
selection: this.options.selection,
selectionAttribute: this.options.selectionAttribute,
attributeTranslationKeyPrefixes: this.options.attributeTranslationKeyPrefixes
},
blankSlateViewConstructor: Marionette.ItemView.extend({
tagName: 'tr',
className: 'blank_slate',
template: blankSlateTemplate,
serializeData: function serializeData() {
return {
blankSlateText: view.options.blankSlateText,
colSpan: view.options.columns.length
};
}
})
}));
}
});
function template$3(data) {
var __p = '';
__p += '\n\n';
return __p
}
var TooltipView = Marionette.ItemView.extend({
template: template$3,
className: 'tooltip',
ui: {
label: '.label'
},
hide: function hide() {
this.visible = false;
clearTimeout(this.timeout);
this.$el.removeClass('visible');
},
show: function show(text, position, options) {
options = options || {};
this.visible = true;
clearTimeout(this.timeout);
this.timeout = setTimeout(_.bind(function () {
var offsetTop;
var offsetLeft;
this.ui.label.text(text);
this.$el.toggleClass('align_bottom_right', options.align === 'bottom right');
this.$el.toggleClass('align_bottom_left', options.align === 'bottom left');
this.$el.toggleClass('align_top_center', options.align === 'top center');
if (options.align === 'bottom right' || options.align === 'bottom left') {
offsetTop = 10;
offsetLeft = 0;
} else if (options.align === 'top center') {
offsetTop = -10;
offsetLeft = 0;
} else {
offsetTop = -17;
offsetLeft = 10;
}
this.$el.css({
top: position.top + offsetTop + 'px',
left: position.left + offsetLeft + 'px'
});
this.$el.addClass('visible');
}, this), 200);
}
});
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
var attributeBinding = {
setupBooleanAttributeBinding: function setupBooleanAttributeBinding(optionName, updateMethod) {
this.setupAttributeBinding(optionName, updateMethod, Boolean);
},
getBooleanAttributBoundOption: function getBooleanAttributBoundOption(optionName) {
return this.getAttributeBoundOption(optionName, Boolean);
},
setupAttributeBinding: function setupAttributeBinding(optionName, updateMethod) {
var _this = this;
var normalize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function (value) {
return value;
};
var binding = this.options["".concat(optionName, "Binding")];
var view = this;
if (binding) {
_.flatten([binding]).forEach(function (attribute) {
_this.listenTo(_this.model, 'change:' + attribute, update);
});
}
update();
function update() {
updateMethod.call(view, view.getAttributeBoundOption(optionName, normalize));
}
},
getAttributeBoundOption: function getAttributeBoundOption(optionName) {
var _this2 = this;
var normalize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function (value) {
return value;
};
var binding = this.options["".concat(optionName, "Binding")];
var bindingValueOptionName = "".concat(optionName, "BindingValue");
var value = Array.isArray(binding) ? binding.map(function (attribute) {
return _this2.model.get(attribute);
}) : this.model.get(binding);
if (bindingValueOptionName in this.options) {
return value === this.options[bindingValueOptionName];
} else if (typeof this.options[optionName] === 'function') {
return normalize(this.options[optionName](value));
} else if (optionName in this.options) {
return normalize(this.options[optionName]);
} else if (binding) {
return normalize(value);
}
}
};
/**
* Mixin for input views handling common concerns like labels,
* inline help, visiblity and disabling.
*
* ## Label and Inline Help Translations
*
* By default `#labelText` and `#inlineHelpText` are defined through
* translations. If no `attributeTranslationKeyPrefixes` are given,
* translation keys for labels and inline help are constructed from
* the `i18nKey` of the model and the given `propertyName`
* option. Suppose the model's `i18nKey` is "page" and the
* `propertyName` option is "title". Then the key
*
* activerecord.attributes.page.title
*
* will be used for the label. And the key
*
* pageflow.ui.inline_help.page.title_html
* pageflow.ui.inline_help.page.title
*
* will be used for the inline help.
*
* ### Attribute Translation Key Prefixes
*
* The `attributeTranslationKeyPrefixes` option can be used to supply
* an array of scopes in which label and inline help translations
* shall be looked up based on the `propertyName` option.
*
* Suppose the array `['some.attributes', 'fallback.attributes']` is
* given as `attributeTranslationKeyPrefixes` option. Then, in the
* example above, the first existing translation key is used as label:
*
* some.attributes.title.label
* fallback.attributes.title.label
* activerecord.attributes.post.title
*
* Accordingly, for the inline help:
*
* some.attributes.title.inline_help_html
* some.attributes.title.inline_help
* fallback.attributes.title.inline_help_html
* fallback.attributes.title.inline_help
* pageflow.ui.inline_help.post.title_html
* pageflow.ui.inline_help.post.title
*
* This setup allows to keep all translation keys for an attribute
* to share a common prefix:
*
* some:
* attributes:
* title:
* label: "Label"
* inline_help: "..."
* inline_help_disabled: "..."
*
* ### Inline Help for Disabled Inputs
*
* For each inline help translation key, a separate key with an
* `"_disabled"` suffix can be supplied, which provides a help string
* that shall be displayed when the input is disabled. More specific
* attribute translation key prefixes take precedence over suffixed
* keys:
*
* some.attributes.title.inline_help_html
* some.attributes.title.inline_help
* some.attributes.title.inline_help_disabled_html
* some.attributes.title.inline_help_disabled
* fallback.attributes.title.inline_help_html
* fallback.attributes.title.inline_help
* fallback.attributes.title.inline_help_disabled_html
* fallback.attributes.title.inline_help_disabled
* pageflow.ui.inline_help.post.title_html
* pageflow.ui.inline_help.post.title
* pageflow.ui.inline_help.post.title_disabled_html
* pageflow.ui.inline_help.post.title_disabled
*
* @param {string} options
* Common constructor options for all views that include this mixin.
*
* @param {string} options.propertyName
* Name of the attribute on the model to display and edit.
*
* @param {string} [options.label]
* Label text for the input.
*
* @param {string[]} [options.attributeTranslationKeyPrefixes]
* An array of prefixes to lookup translations for labels and
* inline help texts based on attribute names.
*
* @param {string} [options.additionalInlineHelpText]
* A text that will be appended to the translation based inline
* text.
*
* @param {string|string[]} [options.disabledBinding]
* Name of an attribute to control whether the input is disabled. If
* the `disabled` and `disabledBinding` options are not set,
* input will be disabled whenever this attribute has a truthy value.
* When multiple attribute names are passed, the function passed to
* the `disabled` option will receive an array of values in the same
* order.
*
* @param {function|boolean} [options.disabled]
* Render input as disabled. A Function taking the value of the
* `disabledBinding` attribute as parameter. Input will be disabled
* only if function returns `true`.
*
* @param {any} [options.disabledBindingValue]
* Input will be disabled whenever the value of the `disabledBinding`
* attribute equals the value of this option.
*
* @param {string|string[]} [options.visibleBinding]
* Name of an attribute to control whether the input is visible. If
* the `visible` and `visibleBindingValue` options are not set,
* input will be visible whenever this attribute has a truthy value.
* When multiple attribute names are passed, the function passed to
* the `visible` option will receive an array of values in the same
* order.
*
* @param {function|boolean} [options.visible]
* A Function taking the value of the `visibleBinding` attribute as
* parameter. Input will be visible only if function returns `true`.
*
* @param {any} [options.visibleBindingValue]
* Input will be visible whenever the value of the `visibleBinding`
* attribute equals the value of this option.
*
* @mixin
*/
var inputView = _objectSpread2(_objectSpread2({}, attributeBinding), {}, {
ui: {
label: 'label',
labelText: 'label .name',
inlineHelp: 'label .inline_help'
},
/**
* Returns an array of translation keys based on the
* `attributeTranslationKeyPrefixes` option and the given keyName.
*
* Combined with {@link #i18nutils
* i18nUtils.findTranslation}, this can be used inside input views
* to obtain additional translations with the same logic as for
* labels and inline help texts.
*
* findTranslation(this.attributeTranslationKeys('default_value'));
*
* @param {string} keyName
* Suffix to append to prefixes.
*
* @param {string} [options.fallbackPrefix]
* Optional additional prefix to form a model based translation
* key of the form `prefix.modelI18nKey.propertyName.keyName
*
* @return {string[]}
* @since 0.9
* @member
*/
attributeTranslationKeys: function attributeTranslationKeys$1(keyName, options) {
return attributeTranslationKeys(this.options.propertyName, keyName, _.extend({
prefixes: this.options.attributeTranslationKeyPrefixes,
fallbackModelI18nKey: this.model.i18nKey
}, options || {}));
},
onRender: function onRender() {
this.$el.addClass('input');
this.$el.addClass(this.model.modelName + '_' + this.options.propertyName);
this.$el.data('inputPropertyName', this.options.propertyName);
this.$el.data('labelText', this.labelText());
this.$el.data('inlineHelpText', this.inlineHelpText());
this.ui.labelText.text(this.labelText());
this.updateInlineHelp();
this.setLabelFor();
this.setupBooleanAttributeBinding('disabled', this.updateDisabled);
this.setupBooleanAttributeBinding('visible', this.updateVisible);
},
/**
* The label to display in the form.
* @return {string}
*/
labelText: function labelText() {
return this.options.label || this.localizedAttributeName();
},
localizedAttributeName: function localizedAttributeName() {
return findTranslation(this.attributeTranslationKeys('label', {
fallbackPrefix: 'activerecord.attributes'
}));
},
updateInlineHelp: function updateInlineHelp() {
this.ui.inlineHelp.html(this.inlineHelpText());
if (!this.inlineHelpText()) {
this.ui.inlineHelp.hide();
}
},
/**
* The inline help text for the form field.
* @return {string}
*/
inlineHelpText: function inlineHelpText() {
var keys = this.attributeTranslationKeys('inline_help', {
fallbackPrefix: 'pageflow.ui.inline_help'
});
if (this.isDisabled()) {
keys = translationKeysWithSuffix(keys, 'disabled');
}
return _.compact([findTranslation(keys, {
defaultValue: '',
html: true
}), this.options.additionalInlineHelpText]).join(' ');
},
setLabelFor: function setLabelFor() {
if (this.ui.input && this.ui.label.length === 1 && !this.ui.input.attr('id')) {
var id = 'input_' + this.model.modelName + '_' + this.options.propertyName;
this.ui.input.attr('id', id);
this.ui.label.attr('for', id);
}
},
isDisabled: function isDisabled() {
return this.getBooleanAttributBoundOption('disabled');
},
updateDisabled: function updateDisabled() {
this.$el.toggleClass('input-disabled', !!this.isDisabled());
this.updateInlineHelp();
if (this.ui.input) {
this.updateDisabledAttribute(this.ui.input);
}
},
updateDisabledAttribute: function updateDisabledAttribute(element) {
if (this.isDisabled()) {
element.attr('disabled', true);
} else {
element.removeAttr('disabled');
}
},
updateVisible: function updateVisible() {
this.$el.toggleClass('hidden_via_binding', this.getBooleanAttributBoundOption('visible') === false);
}
});
function template$4(data) {
var __p = '';
__p += '\n\n';
return __p
}
/**
* Input view for attributes storing configuration hashes with boolean values.
* See {@link inputView} for further options.
*
* @param {Object} [options]
*
* @class
*/
var CheckBoxGroupInputView = Marionette.ItemView.extend({
mixins: [inputView],
template: template$4,
className: 'check_box_group_input',
events: {
'change': 'save'
},
ui: {
label: 'label',
container: '.check_boxes_container'
},
initialize: function initialize() {
if (!this.options.texts) {
if (!this.options.translationKeys) {
var translationKeyPrefix = this.options.translationKeyPrefix || findKeyWithTranslation(this.attributeTranslationKeys('values', {
fallbackPrefix: 'activerecord.values'
}));
this.options.translationKeys = _.map(this.options.values, function (value) {
return translationKeyPrefix + '.' + value;
}, this);
}
this.options.texts = _.map(this.options.translationKeys, function (key) {
return I18n$1.t(key);
});
}
},
onRender: function onRender() {
this.ui.label.attr('for', this.cid);
this.appendOptions();
this.load();
this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
},
appendOptions: function appendOptions() {
_.each(this.options.values, function (value, index) {
var option = '
' + '
';
this.ui.container.append($(option));
}, this);
},
save: function save() {
var configured = {};
_.each(this.ui.container.find('input'), function (input) {
configured[$(input).attr('name')] = $(input).prop('checked');
});
this.model.set(this.options.propertyName, configured);
},
load: function load() {
if (!this.isClosed) {
_.each(this.options.values, function (value) {
this.ui.container.find('input[name="' + value + '"]').prop('checked', this.model.get(this.options.propertyName)[value]);
}, this);
}
}
});
function template$5(data) {
var __t, __p = '';
__p += '\n\n ' +
((__t = ( I18n.t('pageflow.ui.templates.inputs.url_display.link_text') )) == null ? '' : __t) +
'\n\n';
return __p
}
/**
* Display view for a link to a URL, to be used like an input view.
* See {@link inputView} for further options
*
* @param {Object} [options]
*
* @param {string} [options.propertyName]
* Target URL for link
*
* @class
*/
var UrlDisplayView = Marionette.ItemView.extend({
mixins: [inputView],
template: template$5,
ui: {
link: 'a'
},
modelEvents: {
'change': 'update'
},
events: {
'click a': function clickA(event) {
// Ensure default is not prevented by parent event listener.
event.stopPropagation();
}
},
onRender: function onRender() {
this.update();
},
update: function update() {
var url = this.model.get('original_url');
this.$el.toggle(this.model.isUploaded() && !_.isEmpty(url));
this.ui.link.attr('href', url);
}
});
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* Input view for a number.
*
* See {@link inputView} for further options.
*
* @param {Object} [options]
*
* @param {string} [options.locale]
* Locale used to fomat and parse numbers.
*
* @class
*/
var NumberInputView = Marionette.ItemView.extend({
mixins: [inputView],
template: function template() {
return "\n \n \n ";
},
ui: {
input: 'input'
},
events: {
'change': 'onChange'
},
initialize: function initialize() {
this.parser = new NumberParser(this.options.locale);
},
onRender: function onRender() {
this.load();
this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
},
onChange: function onChange() {
this.save();
this.load();
},
onClose: function onClose() {
this.save();
},
save: function save() {
var inputValue = this.ui.input.val();
this.model.set(this.options.propertyName, this.parser.parse(inputValue) || 0);
},
load: function load() {
var input = this.ui.input;
var value = this.model.get(this.options.propertyName) || 0;
input.val(value.toLocaleString(this.options.locale, {
useGrouping: false
}));
},
displayValidationError: function displayValidationError(message) {
this.$el.addClass('invalid');
this.ui.input.attr('title', message);
},
resetValidationError: function resetValidationError(message) {
this.$el.removeClass('invalid');
this.ui.input.attr('title', '');
}
});
var NumberParser = /*#__PURE__*/function () {
function NumberParser(locale) {
_classCallCheck(this, NumberParser);
var format = new Intl.NumberFormat(locale);
var parts = format.formatToParts(12345.6);
var numerals = Array.from({
length: 10
}).map(function (_, i) {
return format.format(i);
});
var index = new Map(numerals.map(function (d, i) {
return [d, i];
}));
this._group = new RegExp("[".concat(parts.find(function (d) {
return d.type === "group";
}).value, "]"), "g");
this._decimal = new RegExp("[".concat(parts.find(function (d) {
return d.type === "decimal";
}).value, "]"));
this._numeral = new RegExp("[".concat(numerals.join(""), "]"), "g");
this._index = function (d) {
return index.get(d);
};
}
_createClass(NumberParser, [{
key: "parse",
value: function parse(string) {
string = string.trim().replace(this._group, "").replace(this._decimal, ".").replace(this._numeral, this._index);
return string ? +string : NaN;
}
}]);
return NumberParser;
}();
/**
* Text based input view that can display a placeholder.
*
* @param {Object} [options]
*
* @param {string|function} [options.placeholder]
* Display a placeholder string if the input is blank. Either a
* string or a function taking the model as a first parameter and
* returning a string.
*
* @param {string} [options.placeholderBinding]
* Name of an attribute. Recompute the placeholder function whenever
* this attribute changes.
*
* @param {boolean} [options.hidePlaceholderIfDisabled]
* Do not display the placeholder if the input is disabled.
*
* @param {Backbone.Model} [options.placeholderModel]
* Obtain placeholder by looking up the configured `propertyName`
* inside a given model.
*/
var inputWithPlaceholderText = {
onRender: function onRender() {
this.updatePlaceholder();
if (this.options.placeholderBinding) {
this.listenTo(this.model, 'change:' + this.options.placeholderBinding, this.updatePlaceholder);
}
},
updateDisabled: function updateDisabled() {
this.updatePlaceholder();
},
updatePlaceholder: function updatePlaceholder() {
this.ui.input.attr('placeholder', this.placeholderText());
},
placeholderText: function placeholderText() {
if (!this.isDisabled() || !this.options.hidePlaceholderIfDisabled) {
if (this.options.placeholder) {
if (typeof this.options.placeholder == 'function') {
return this.options.placeholder(this.model);
} else {
return this.options.placeholder;
}
} else {
return this.placeholderModelValue();
}
}
return '';
},
placeholderModelValue: function placeholderModelValue() {
return this.options.placeholderModel && this.options.placeholderModel.get(this.options.propertyName);
}
};
var viewWithValidationErrorMessages = {
onRender: function onRender() {
this.listenTo(this.model, 'invalid sync', this.updateValidationErrorMessages);
this.updateValidationErrorMessages();
},
updateValidationErrorMessages: function updateValidationErrorMessages() {
var _this = this;
var errors = this.model.validationErrors && this.model.validationErrors[this.options.propertyName] || [];
if (errors.length) {
this.validationErrorList = this.validationErrorList || $('
"));
});
this.$el.addClass('invalid');
} else if (this.validationErrorList) {
this.validationErrorList.remove();
this.validationErrorList = null;
this.$el.removeClass('invalid');
}
}
};
function template$6(data) {
var __p = '';
__p += '\n\n';
return __p
}
/**
* Input view for a single line of text.
*
* See {@link inputWithPlaceholderText} for placeholder related
* further options. See {@link inputView} for further options.
*
* @param {Object} [options]
*
* @param {boolean} [options.required=false]
* Display an error if the input is blank.
*
* @param {number} [options.maxLength=255]
* Maximum length of characters for this input. To support legacy
* data which consists of more characters than the specified
* maxLength, the option will only take effect for data which is
* shorter than the specified maxLength.
*
* @class
*/
var TextInputView = Marionette.ItemView.extend({
mixins: [inputView, inputWithPlaceholderText, viewWithValidationErrorMessages],
template: template$6,
ui: {
input: 'input'
},
events: {
'change': 'onChange'
},
onRender: function onRender() {
this.load();
this.validate();
this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
},
onChange: function onChange() {
if (this.validate()) {
this.save();
}
},
onClose: function onClose() {
if (this.validate()) {
this.save();
}
},
save: function save() {
this.model.set(this.options.propertyName, this.ui.input.val());
},
load: function load() {
var input = this.ui.input;
input.val(this.model.get(this.options.propertyName)); // set mysql varchar length as default for non-legacy data
this.options.maxLength = this.options.maxLength || 255; // do not validate legacy data which length exceeds the specified maximum
// for new and maxLength-conforming data: add validation
this.validateMaxLength = input.val().length <= this.options.maxLength;
},
validate: function validate() {
var input = this.ui.input;
if (this.options.required && !input.val()) {
this.displayValidationError(I18n$1.t('pageflow.ui.views.inputs.text_input_view.required_field'));
return false;
}
if (this.validateMaxLength && input.val().length > this.options.maxLength) {
this.displayValidationError(I18n$1.t('pageflow.ui.views.inputs.text_input_view.max_characters_exceeded', {
max_length: this.options.maxLength
}));
return false;
} else {
this.resetValidationError();
return true;
}
},
displayValidationError: function displayValidationError(message) {
this.$el.addClass('invalid');
this.ui.input.attr('title', message);
},
resetValidationError: function resetValidationError(message) {
this.$el.removeClass('invalid');
this.ui.input.attr('title', '');
}
});
/**
* Input view for a color value in hex representation.
* See {@link inputView} for further options
*
* @param {Object} [options]
*
* @param {string|function} [options.defaultValue]
* Color value to display by default. The corresponding value is not
* stored in the model. Selecting the default value when a different
* value was set before, unsets the attribute in the model.
*
* @param {string} [options.defaultValueBinding]
* Name of an attribute the default value depends on. If a function
* is used as defaultValue option, it will be passed the value of the
* defaultValueBinding attribute each time it changes. If no
* defaultValue option is set, the value of the defaultValueBinding
* attribute will be used as default value.
*
* @param {string[]} [options.swatches]
* Preset color values to be displayed inside the picker drop
* down. The default value, if present, is always used as the
* first swatch automatically.
*
* @class
*/
var ColorInputView = Marionette.ItemView.extend({
mixins: [inputView],
template: template$6,
className: 'color_input',
ui: {
input: 'input'
},
events: {
'mousedown': 'refreshPicker'
},
onRender: function onRender() {
this.ui.input.minicolors({
changeDelay: 200,
change: _.bind(function (color) {
this._saving = true;
if (color === this.defaultValue()) {
this.model.unset(this.options.propertyName);
} else {
this.model.set(this.options.propertyName, color);
}
this._saving = false;
}, this)
});
this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
if (this.options.defaultValueBinding) {
this.listenTo(this.model, 'change:' + this.options.defaultValueBinding, this.updateSettings);
}
this.updateSettings();
},
updateSettings: function updateSettings() {
this.resetSwatchesInStoredSettings();
this.ui.input.minicolors('settings', {
defaultValue: this.defaultValue(),
swatches: this.getSwatches()
});
this.load();
},
// see https://github.com/claviska/jquery-minicolors/issues/287
resetSwatchesInStoredSettings: function resetSwatchesInStoredSettings() {
var settings = this.ui.input.data('minicolors-settings');
if (settings) {
delete settings.swatches;
this.ui.input.data('minicolors-settings', settings);
}
},
load: function load() {
if (!this._saving) {
this.ui.input.minicolors('value', this.model.get(this.options.propertyName) || this.defaultValue());
}
this.$el.toggleClass('is_default', !this.model.has(this.options.propertyName));
},
refreshPicker: function refreshPicker() {
this.ui.input.minicolors('value', {});
},
getSwatches: function getSwatches() {
return _.chain([this.defaultValue(), this.options.swatches]).flatten().uniq().compact().value();
},
defaultValue: function defaultValue() {
var bindingValue;
if (this.options.defaultValueBinding) {
bindingValue = this.model.get(this.options.defaultValueBinding);
}
if (typeof this.options.defaultValue === 'function') {
return this.options.defaultValue(bindingValue);
} else if ('defaultValue' in this.options) {
return this.options.defaultValue;
} else {
return bindingValue;
}
}
});
function template$7(data) {
var __p = '';
__p += '\n';
return __p
}
/**
* A drop down with support for grouped items.
* See {@link inputView} for further options
*
* @param {Object} [options]
*
* @param {string[]} [options.values]
* List of possible values to persist in the attribute.
*
* @param {string[]} [options.texts]
* List of display texts for drop down items.
*
* @param {string[]} [options.translationKeys]
* Translation keys to obtain item texts from.
*
* @param {string[]} [options.translationKeyPrefix]
* Obtain texts for items from translations by appending the item
* value to this prefix separated by a dot. By default the
* [`attributeTranslationKeyPrefixes` option]{@link inputView}
* is used by appending the suffix `.values` to each candidate.
*
* @param {string[]} [options.groups]
* Array of same length as `values` array, containing the display
* name of a group header each item shall be grouped under.
*
* @param {Backbone.Model[]} [options.collection]
* Create items for each model in the collection. Use the
* `*Property` options to extract values and texts for each items
* from the models.
*
* @param {string} [options.valueProperty]
* Attribute to use as item value.
*
* @param {string} [options.textProperty]
* Attribute to use as item display text.
*
* @param {string} [options.groupProperty]
* Attribute to use as item group name.
*
* @param {string} [options.translationKeyProperty]
* Attribute to use as translation key to obtain display text.
*
* @param {string} [options.groupTranslationKeyProperty]
* Attribute to use as translation key to obtain group name.
*
* @param {boolean} [options.ensureValueDefined]
* Set the attribute to the first value on view creation.
*
* @param {boolean} [options.includeBlank]
* Include an item that sets the value of the attribute to a blank
* string.
*
* @param {string} [options.blankText]
* Display text for the blank item.
*
* @param {string} [options.blankTranslationKey]
* Translation key to obtain display text for blank item.
*
* @param {string} [options.placeholderValue]
* Include an item that sets the value of the attribute to a blank
* string and indicate that the attribute is set to a default
* value. Include the display name of the given value, in the
* text. This option can be used if a fallback to the
* `placeholderValue` occurs whenever the attribute is blank.
*
* @param {Backbone.Model} [options.placeholderModel]
* Behaves like `placeholderValue`, but obtains the value by looking
* up the `propertyName` attribute inside the given model. This
* option can be used if a fallback to the corresponding attribute
* value of the `placeholderModel` occurs whenever the attribute is
* blank.
*
* @param {function} [options.optionDisabled]
* Receives value and has to return boolean indicating whether
* option is disabled.
*
* @class
*/
var SelectInputView = Marionette.ItemView.extend({
mixins: [inputView],
template: template$7,
events: {
'change': 'save'
},
ui: {
select: 'select',
input: 'select'
},
initialize: function initialize() {
if (this.options.collection) {
this.options.values = _.pluck(this.options.collection, this.options.valueProperty);
if (this.options.textProperty) {
this.options.texts = _.pluck(this.options.collection, this.options.textProperty);
} else if (this.options.translationKeyProperty) {
this.options.translationKeys = _.pluck(this.options.collection, this.options.translationKeyProperty);
}
if (this.options.groupProperty) {
this.options.groups = _.pluck(this.options.collection, this.options.groupProperty);
} else if (this.options.groupTranslationKeyProperty) {
this.options.groupTanslationKeys = _.pluck(this.options.collection, this.options.groupTranslationKeyProperty);
}
}
if (!this.options.texts) {
if (!this.options.translationKeys) {
var translationKeyPrefix = this.options.translationKeyPrefix || findKeyWithTranslation(this.attributeTranslationKeys('values', {
fallbackPrefix: 'activerecord.values'
}));
this.options.translationKeys = _.map(this.options.values, function (value) {
return translationKeyPrefix + '.' + value;
}, this);
}
this.options.texts = _.map(this.options.translationKeys, function (key) {
return I18n$1.t(key);
});
}
if (!this.options.groups) {
this.options.groups = _.map(this.options.groupTanslationKeys, function (key) {
return I18n$1.t(key);
});
}
this.optGroups = {};
},
onRender: function onRender() {
this.appendBlank();
this.appendPlaceholder();
this.appendOptions();
this.load();
this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
if (this.options.ensureValueDefined && !this.model.has(this.options.propertyName)) {
this.save();
}
},
appendBlank: function appendBlank() {
if (!this.options.includeBlank) {
return;
}
if (this.options.blankTranslationKey) {
this.options.blankText = I18n$1.t(this.options.blankTranslationKey);
}
var option = document.createElement('option');
option.value = '';
option.text = this.options.blankText || I18n$1.t('pageflow.ui.views.inputs.select_input_view.none');
this.ui.select.append(option);
},
appendPlaceholder: function appendPlaceholder() {
if (!this.options.placeholderModel && !this.options.placeholderValue) {
return;
}
var placeholderValue = this.options.placeholderValue || this.options.placeholderModel.get(this.options.propertyName);
var placeholderIndex = this.options.values.indexOf(placeholderValue);
if (placeholderIndex >= 0) {
var option = document.createElement('option');
option.value = '';
option.text = I18n$1.t('pageflow.ui.views.inputs.select_input_view.placeholder', {
text: this.options.texts[placeholderIndex]
});
this.ui.select.append(option);
}
},
appendOptions: function appendOptions() {
_.each(this.options.values, function (value, index) {
var option = document.createElement('option');
var group = this.options.groups[index];
option.value = value;
option.text = this.options.texts[index];
if (this.options.optionDisabled && this.options.optionDisabled(value)) {
option.setAttribute('disabled', true);
}
if (group) {
option.setAttribute('data-group', group);
this.findOrCreateOptGroup(group).append(option);
} else {
this.ui.select.append(option);
}
}, this);
},
findOrCreateOptGroup: function findOrCreateOptGroup(label) {
if (!this.optGroups[label]) {
this.optGroups[label] = $('', {
label: label
}).appendTo(this.ui.select);
}
return this.optGroups[label];
},
save: function save() {
this.model.set(this.options.propertyName, this.ui.select.val());
},
load: function load() {
if (!this.isClosed) {
var value = this.model.get(this.options.propertyName);
if (this.model.has(this.options.propertyName) && this.ui.select.find('option[value="' + value + '"]:not([disabled])').length) {
this.ui.select.val(value);
} else {
this.ui.select.val(this.ui.select.find('option:not([disabled]):first').val());
}
}
}
});
var ExtendedSelectInputView = SelectInputView.extend({
className: 'extended_select_input',
initialize: function initialize() {
SelectInputView.prototype.initialize.apply(this, arguments);
if (this.options.collection) {
if (this.options.descriptionProperty) {
this.options.descriptions = _.pluck(this.options.collection, this.options.descriptionProperty);
} else if (this.options.descriptionTranslationKeyProperty) {
this.options.descriptionTanslationKeys = _.pluck(this.options.collection, this.options.descriptionTranslationKeyProperty);
}
}
if (!this.options.descriptions) {
this.options.descriptions = _.map(this.options.descriptionTanslationKeys, function (key) {
return I18n$1.t(key);
});
}
},
onRender: function onRender() {
var view = this,
options = this.options;
SelectInputView.prototype.onRender.apply(this, arguments);
$.widget("custom.extendedselectmenu", $.ui.selectmenu, {
_renderItem: function _renderItem(ul, item) {
var widget = this;
var li = $('
', {
"class": item.value
});
var container = $('
', {
"class": 'text-container'
}).appendTo(li);
var index = options.values.indexOf(item.value);
if (item.disabled) {
li.addClass('ui-state-disabled');
}
if (options.pictogramClass) {
$('', {
"class": options.pictogramClass
}).prependTo(li);
}
$('
\n';
return __p
}
/**
* Input view for multi line text with simple formatting options.
* See {@link inputWithPlaceholderText} for placeholder related options.
* See {@link inputView} for further options.
*
* @param {Object} [options]
*
* @param {string} [options.size="normal"]
* Pass `"short"` to reduce the text area height.
*
* @param {boolean} [options.disableLinks=false]
* Do not allow links inside the text.
*
* @param {boolean} [options.disableRichtext=false]
* Do not provide text formatting options.
*
* @param {Backbone.View} [options.fragmentLinkInputView]
* A view to select an id to use in links which only consist
* of a url fragment. Will receive a model with a `linkId`
* attribute.
*
* @class
*/
var TextAreaInputView = Marionette.ItemView.extend({
mixins: [inputView, inputWithPlaceholderText],
template: template$8,
ui: {
input: 'textarea',
toolbar: '.toolbar',
linkButton: '.link_button',
linkDialog: '.link_dialog',
urlInput: '.current_url',
targetInput: '.current_target',
linkTypeSelection: '.link_type_select',
urlLinkRadioButton: '.url_link_radio_button',
fragmentLinkRadioButton: '.fragment_link_radio_button',
urlLinkPanel: '.url_link_panel',
displayUrlInput: '.display_url',
openInNewTabCheckBox: '.open_in_new_tab',
fragmentLinkPanel: '.fragment_link_panel'
},
events: {
'change textarea': 'save',
'click .url_link_radio_button': 'showUrlLinkPanel',
'click .fragment_link_radio_button': 'showFragmentLinkPanel',
'change .open_in_new_tab': 'setTargetFromOpenInNewTabCheckBox',
'change .display_url': 'setUrlFromDisplayUrl'
},
onRender: function onRender() {
this.ui.input.addClass(this.options.size);
this.load();
this.updatePlaceholder();
this.editor = new wysihtml5.Editor(this.ui.input[0], {
toolbar: this.ui.toolbar[0],
autoLink: this.options.disableLinks ? 0 : 1,
parserRules: {
tags: {
em: {
unwrap: this.options.disableRichtext ? 1 : 0,
rename_tag: "i"
},
strong: {
unwrap: this.options.disableRichtext ? 1 : 0,
rename_tag: "b"
},
u: {
unwrap: this.options.disableRichtext ? 1 : 0
},
b: {
unwrap: this.options.disableRichtext ? 1 : 0
},
i: {
unwrap: this.options.disableRichtext ? 1 : 0
},
ol: {
unwrap: this.options.enableLists ? 0 : 1
},
ul: {
unwrap: this.options.enableLists ? 0 : 1
},
li: {
unwrap: this.options.enableLists ? 0 : 1
},
br: {},
a: {
unwrap: this.options.disableLinks ? 1 : 0,
check_attributes: {
href: 'href',
target: 'any'
},
set_attributes: {
rel: 'nofollow'
}
}
}
}
});
if (this.options.disableRichtext) {
this.ui.toolbar.find('a[data-wysihtml5-command="bold"]').hide();
this.ui.toolbar.find('a[data-wysihtml5-command="italic"]').hide();
this.ui.toolbar.find('a[data-wysihtml5-command="underline"]').hide();
this.ui.toolbar.find('a[data-wysihtml5-command="insertOrderedList"]').hide();
this.ui.toolbar.find('a[data-wysihtml5-command="insertUnorderedList"]').hide();
}
if (!this.options.enableLists) {
this.ui.toolbar.find('a[data-wysihtml5-command="insertOrderedList"]').hide();
this.ui.toolbar.find('a[data-wysihtml5-command="insertUnorderedList"]').hide();
}
if (this.options.disableLinks) {
this.ui.toolbar.find('a[data-wysihtml5-command="createLink"]').hide();
} else {
this.setupUrlLinkPanel();
this.setupFragmentLinkPanel();
}
this.editor.on('change', _.bind(this.save, this));
this.editor.on('aftercommand:composer', _.bind(this.save, this));
},
onClose: function onClose() {
this.editor.fire('destroy:composer');
},
save: function save() {
this.model.set(this.options.propertyName, this.editor.getValue());
},
load: function load() {
this.ui.input.val(this.model.get(this.options.propertyName));
},
setupUrlLinkPanel: function setupUrlLinkPanel() {
this.editor.on('show:dialog', _.bind(function () {
this.ui.linkDialog.toggleClass('for_existing_link', this.ui.linkButton.hasClass('wysihtml5-command-active'));
var currentUrl = this.ui.urlInput.val();
if (currentUrl.startsWith('#')) {
this.ui.displayUrlInput.val('');
this.ui.openInNewTabCheckBox.prop('checked', true);
} else {
this.ui.displayUrlInput.val(currentUrl);
this.ui.openInNewTabCheckBox.prop('checked', this.ui.targetInput.val() !== '_self');
}
}, this));
},
setupFragmentLinkPanel: function setupFragmentLinkPanel() {
if (this.options.fragmentLinkInputView) {
this.fragmentLinkModel = new Backbone.Model();
this.listenTo(this.fragmentLinkModel, 'change', function (model, options) {
if (!options.skipCurrentUrlUpdate) {
this.setInputsFromFragmentLinkModel();
}
});
this.editor.on('show:dialog', _.bind(function () {
var currentUrl = this.ui.urlInput.val();
var id = currentUrl.startsWith('#') ? currentUrl.substr(1) : null;
this.fragmentLinkModel.set('linkId', id, {
skipCurrentUrlUpdate: true
});
this.initLinkTypePanels(!id);
}, this));
var fragmentLinkInput = new this.options.fragmentLinkInputView({
model: this.fragmentLinkModel,
propertyName: 'linkId',
label: I18n$1.t('pageflow.ui.templates.inputs.text_area_input.target'),
hideUnsetButton: true
});
this.ui.fragmentLinkPanel.append(fragmentLinkInput.render().el);
} else {
this.ui.linkTypeSelection.hide();
this.ui.fragmentLinkPanel.hide();
}
},
initLinkTypePanels: function initLinkTypePanels(isUrlLink) {
if (isUrlLink) {
this.ui.urlLinkRadioButton.prop('checked', true);
} else {
this.ui.fragmentLinkRadioButton.prop('checked', true);
}
this.ui.toolbar.toggleClass('fragment_link_panel_active', !isUrlLink);
},
showUrlLinkPanel: function showUrlLinkPanel() {
this.ui.toolbar.removeClass('fragment_link_panel_active');
this.setUrlFromDisplayUrl();
this.setTargetFromOpenInNewTabCheckBox();
},
showFragmentLinkPanel: function showFragmentLinkPanel() {
this.ui.toolbar.addClass('fragment_link_panel_active');
this.setInputsFromFragmentLinkModel();
},
setInputsFromFragmentLinkModel: function setInputsFromFragmentLinkModel() {
this.ui.urlInput.val('#' + (this.fragmentLinkModel.get('linkId') || ''));
this.ui.targetInput.val('_self');
},
setUrlFromDisplayUrl: function setUrlFromDisplayUrl() {
this.ui.urlInput.val(this.ui.displayUrlInput.val());
},
setTargetFromOpenInNewTabCheckBox: function setTargetFromOpenInNewTabCheckBox() {
this.ui.targetInput.val(this.ui.openInNewTabCheckBox.is(':checked') ? '_blank' : '_self');
}
});
function template$9(data) {
var __p = '';
__p += '\n\n\n';
return __p
}
/**
* Input view for URLs.
* See {@link inputView} for further options
*
* @param {Object} [options]
*
* @param {string[]} options.supportedHosts
* List of allowed url prefixes.
*
* @param {boolean} [options.required=false]
* Display an error if the url is blank.
*
* @param {boolean} [options.permitHttps=false]
* Allow urls with https protocol.
*
* @class
*/
var UrlInputView = Marionette.Layout.extend(
/** @lends UrlInputView.prototype */
{
mixins: [inputView],
template: template$9,
ui: {
input: 'input',
validation: '.validation'
},
events: {
'change': 'onChange'
},
onRender: function onRender() {
this.ui.validation.hide();
this.load();
this.validate();
},
onChange: function onChange() {
var _this = this;
this.validate().then(function () {
return _this.save();
}, function () {
return _this.saveDisplayProperty();
});
},
saveDisplayProperty: function saveDisplayProperty() {
this.model.unset(this.options.propertyName, {
silent: true
});
this.model.set(this.options.displayPropertyName, this.ui.input.val());
},
save: function save() {
var _this2 = this;
var value = this.ui.input.val();
$.when(this.transformPropertyValue(value)).then(function (transformedValue) {
var _this2$model$set;
_this2.model.set((_this2$model$set = {}, _defineProperty(_this2$model$set, _this2.options.displayPropertyName, value), _defineProperty(_this2$model$set, _this2.options.propertyName, transformedValue), _this2$model$set));
});
},
load: function load() {
this.ui.input.val(this.model.has(this.options.displayPropertyName) ? this.model.get(this.options.displayPropertyName) : this.model.get(this.options.propertyName));
this.onLoad();
},
/**
* Override to be notified when the input has been loaded.
*/
onLoad: function onLoad() {},
/**
* Override to validate the untransformed url. Validation error
* message can be passed as rejected promise. Progress notifications
* are displayed. Only valid urls are stored in the configuration.
*
* @return Promise
*/
validateUrl: function validateUrl(url) {
return $.Deferred().resolve().promise();
},
/**
* Override to transform the property value before it is stored.
*
* @return Promise | String
*/
transformPropertyValue: function transformPropertyValue(value) {
return value;
},
/**
* Override to change the list of supported host names.
*/
supportedHosts: function supportedHosts() {
return this.options.supportedHosts;
},
// Host names used to be expected to include protocols. Remove
// protocols for backwards compatilbity. Since supportedHosts
// is supposed to be overridden in subclasses, we do it in a
// separate method.
supportedHostsWithoutLegacyProtocols: function supportedHostsWithoutLegacyProtocols() {
return _.map(this.supportedHosts(), function (host) {
return host.replace(/^https?:\/\//, '');
});
},
validate: function validate(success) {
var view = this;
var options = this.options;
var value = this.ui.input.val();
if (options.required && !value) {
displayValidationError(I18n$1.t('pageflow.ui.views.inputs.url_input_view.required_field'));
} else if (value && !isValidUrl(value)) {
var errorMessage = I18n$1.t('pageflow.ui.views.inputs.url_input_view.url_hint');
if (options.permitHttps) {
errorMessage = I18n$1.t('pageflow.ui.views.inputs.url_input_view.url_hint_https');
}
displayValidationError(errorMessage);
} else if (value && !hasSupportedHost(value)) {
displayValidationError(I18n$1.t('pageflow.ui.views.inputs.url_input_view.supported_vendors') + _.map(view.supportedHosts(), function (url) {
return '
' + url + '
';
}).join(''));
} else {
return view.validateUrl(value).progress(function (message) {
displayValidationPending(message);
}).done(function () {
resetValidationError();
}).fail(function (error) {
displayValidationError(error);
});
}
return $.Deferred().reject().promise();
function isValidUrl(url) {
return options.permitHttps ? url.match(/^https?:\/\//i) : url.match(/^http:\/\//i);
}
function hasSupportedHost(url) {
return _.any(view.supportedHostsWithoutLegacyProtocols(), function (host) {
return url.match(new RegExp('^https?://' + host));
});
}
function displayValidationError(message) {
view.$el.addClass('invalid');
view.ui.input.attr('aria-invalid', 'true');
view.ui.validation.removeClass('pending').addClass('failed').html(message).show();
}
function displayValidationPending(message) {
view.$el.removeClass('invalid');
view.ui.input.removeAttr('aria-invalid');
view.ui.validation.removeClass('failed').addClass('pending').html(message).show();
}
function resetValidationError(message) {
view.$el.removeClass('invalid');
view.ui.input.attr('aria-invalid', 'false');
view.ui.validation.hide();
}
}
});
/**
* Input view that verifies that a certain URL is reachable via a
* proxy. To conform with same origin restrictions, this input view
* lets the user enter some url and saves a rewritten url where the
* domain is replaced with some path segment.
*
* That way, when `/example` is setup to proxy requests to
* `http://example.com`, the user can enter an url of the form
* `http://example.com/some/path` but the string `/example/some/path`
* is persisited to the database.
*
* See {@link inputView} for further options
*
* @param {Object} options
*
* @param {string} options.displayPropertyName
* Attribute name to store the url entered by the user.
*
* @param {Object[]} options.proxies
* List of supported proxies.
*
* @param {string} options.proxies[].url
* Supported prefix of an url that can be entered by the user.
*
* @param {string} options.proxies[].base_path
* Path to replace the url prefix with.
*
* @param {boolean} [options.required=false]
* Display an error if the url is blank.
*
* @param {boolean} [options.permitHttps=false]
* Allow urls with https protocol.
*
* @example
*
* this.input('url, ProxyUrlInputView, {
* proxies: [
* {
* url: 'http://example.com',
* base_path: '/example'
* }
* ]
* });
*
* @class
*/
var ProxyUrlInputView = UrlInputView.extend(
/** @lends ProxyUrlInputView.prototype */
{
// @override
validateUrl: function validateUrl(url) {
var view = this;
return $.Deferred(function (deferred) {
deferred.notify(I18n$1.t('pageflow.ui.views.inputs.proxy_url_input_view.url_validation'));
$.ajax({
url: view.rewriteUrl(url),
dataType: 'html'
}).done(deferred.resolve).fail(function (xhr) {
deferred.reject(I18n$1.t('pageflow.ui.views.inputs.proxy_url_input_view.http_error', {
status: xhr.status
}));
});
}).promise();
},
// override
transformPropertyValue: function transformPropertyValue(url) {
return this.rewriteUrl(url);
},
// override
supportedHosts: function supportedHosts() {
return _.pluck(this.options.proxies, 'url');
},
rewriteUrl: function rewriteUrl(url) {
_.each(this.options.proxies, function (proxy) {
url = url.replace(new RegExp('^' + proxy.url + '/?'), proxy.base_path + '/');
});
return url;
}
});
function template$a(data) {
var __p = '';
__p += '\n\n\n';
return __p
}
/**
* A slider for numeric inputs.
* See {@link inputView} for options
*
* @param {Object} [options]
*
* @param {number} [options.defaultValue]
* Defaults value to display if property is not set.
*
* @param {number} [options.minValue=0]
* Value when dragging slider to the very left.
*
* @param {number} [options.maxValue=100]
* Value when dragging slider to the very right.
*
* @param {string} [options.unit="%"]
* Unit to display after value.
*
* @param {function} [options.displayText]
* Function that receives value and returns custom text to display as value.
*
* @param {boolean} [options.saveOnSlide]
* Already update the model while dragging the handle - not only after
* handle has been released.
*
* @class
*/
var SliderInputView = Marionette.ItemView.extend({
mixins: [inputView],
className: 'slider_input',
template: template$a,
ui: {
widget: '.slider',
value: '.value'
},
events: {
'slidechange': 'save',
'slide': 'handleSlide'
},
onRender: function onRender() {
var _this = this;
this.ui.widget.slider({
animate: 'fast'
});
this.setupAttributeBinding('minValue', function (value) {
return _this.updateSliderOption('min', value || 0);
});
this.setupAttributeBinding('maxValue', function (value) {
return _this.updateSliderOption('max', value || 100);
});
this.load();
this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
},
updateSliderOption: function updateSliderOption(name, value) {
this.ui.widget.slider('option', name, value);
this.updateText(this.ui.widget.slider('value'));
},
updateDisabled: function updateDisabled(disabled) {
this.$el.toggleClass('disabled', !!disabled);
if (disabled) {
this.ui.widget.slider('disable');
} else {
this.ui.widget.slider('enable');
}
},
handleSlide: function handleSlide(event, ui) {
this.updateText(ui.value);
if (this.options.saveOnSlide) {
this.save(event, ui);
}
},
save: function save(event, ui) {
this.model.set(this.options.propertyName, ui.value);
},
load: function load() {
var value;
if (this.model.has(this.options.propertyName)) {
value = this.model.get(this.options.propertyName);
} else {
value = 'defaultValue' in this.options ? this.options.defaultValue : 0;
}
this.ui.widget.slider('option', 'value', this.clampValue(value));
this.updateText(value);
},
clampValue: function clampValue(value) {
var min = this.ui.widget.slider('option', 'min');
var max = this.ui.widget.slider('option', 'max');
return Math.min(max, Math.max(min, value));
},
updateText: function updateText(value) {
var unit = 'unit' in this.options ? this.options.unit : '%';
var text = 'displayText' in this.options ? this.options.displayText(value) : value + unit;
this.ui.value.text(text);
}
});
function template$b(data) {
var __p = '';
__p += '\n\n\n';
return __p
}
var JsonInputView = Marionette.ItemView.extend({
mixins: [inputView],
template: template$b,
className: 'json_input',
ui: {
input: 'textarea'
},
events: {
'change': 'onChange',
'keyup': 'validate'
},
onRender: function onRender() {
this.load();
this.validate();
this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
},
onChange: function onChange() {
if (this.validate()) {
this.save();
}
},
onClose: function onClose() {
if (this.validate()) {
this.save();
}
},
save: function save() {
this.model.set(this.options.propertyName, this.ui.input.val() ? JSON.parse(this.ui.input.val()) : null);
},
load: function load() {
var input = this.ui.input;
var value = this.model.get(this.options.propertyName);
input.val(value ? JSON.stringify(value, null, 2) : '');
},
validate: function validate() {
var input = this.ui.input;
if (input.val() && !this.isValidJson(input.val())) {
this.displayValidationError(I18n$1.t('pageflow.ui.views.inputs.json_input_view.invalid'));
return false;
} else {
this.resetValidationError();
return true;
}
},
displayValidationError: function displayValidationError(message) {
this.$el.addClass('invalid');
this.ui.input.attr('title', message);
},
resetValidationError: function resetValidationError(message) {
this.$el.removeClass('invalid');
this.ui.input.attr('title', '');
},
isValidJson: function isValidJson(text) {
try {
JSON.parse(text);
return true;
} catch (e) {
return false;
}
}
});
function template$c(data) {
var __p = '';
__p += '\n';
return __p
}
/**
* Input view for boolean values.
* See {@link inputView} for further options
*
* @param {Object} [options]
*
* @param {boolean} [options.displayUncheckedIfDisabled=false]
* Ignore the attribute value if the input is disabled and display
* an unchecked check box.
*
* @param {boolean} [options.displayCheckedIfDisabled=false]
* Ignore the attribute value if the input is disabled and display
* an checked check box.
*
* @param {string} [options.storeInverted]
* Display checked by default and store true in given attribute when
* unchecked. The property name passed to `input` is only used for
* translations.
*
* @class
*/
var CheckBoxInputView = Marionette.ItemView.extend({
mixins: [inputView],
template: template$c,
className: 'check_box_input',
events: {
'change': 'save'
},
ui: {
input: 'input',
label: 'label'
},
onRender: function onRender() {
this.ui.label.attr('for', this.cid);
this.ui.input.attr('id', this.cid);
this.load();
this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
},
updateDisabled: function updateDisabled() {
this.load();
},
save: function save() {
if (!this.isDisabled()) {
var value = this.ui.input.is(':checked');
if (this.options.storeInverted) {
this.model.set(this.options.storeInverted, !value);
} else {
this.model.set(this.options.propertyName, value);
}
}
},
load: function load() {
if (!this.isClosed) {
this.ui.input.prop('checked', this.displayValue());
}
},
displayValue: function displayValue() {
if (this.isDisabled() && this.options.displayUncheckedIfDisabled) {
return false;
} else if (this.isDisabled() && this.options.displayCheckedIfDisabled) {
return true;
} else if (this.options.storeInverted) {
return !this.model.get(this.options.storeInverted);
} else {
return this.model.get(this.options.propertyName);
}
}
});
/**
* Render a separator in a {@link ConfigurationEditorView} tab.
*
* @example
*
* this.view(SeparatorView);
*
* @class
*/
var SeparatorView = Marionette.View.extend({
className: 'separator'
});
/**
* Render an input that is only a label. Can be used to render
* additional inline help.
*
* See {@link inputView} for further options
*
* @class
*/
var LabelOnlyView = Marionette.ItemView.extend({
mixins: [inputView],
template: function template() {
return "\n \n ";
},
ui: {
label: 'label'
}
});
/**
* A table cell mapping column attribute values to a list of
* translations.
*
* ## Attribute Translations
*
* The following attribute translations are used:
*
* - `.cell_text.` - Used as cell content.
* - `.cell_text.blank` - Used as cell content if attribute is blank.
* - `.cell_title.` - Used as title attribute.
* - `.cell_title.blank` - Used as title attribute if attribute is blank.
*
* @since 12.0
*/
var EnumTableCellView = TableCellView.extend({
className: 'enum_table_cell',
update: function update() {
this.$el.text(this.attributeTranslation('cell_text.' + (this.attributeValue() || 'blank')));
this.$el.attr('title', this.attributeTranslation('cell_title.' + (this.attributeValue() || 'blank'), {
defaultValue: ''
}));
}
});
function template$d(data) {
var __t, __p = '';
__p += '\n';
return __p
}
/**
* A table cell providing a button which destroys the model that the
* current row refers to.
*
* ## Attribute Translations
*
* The following attribute translation is used:
*
* - `.cell_title` - Used as title attribute.
*
* @param {Object} [options]
*
* @param {function} [options.toggleDeleteButton]
* A function with boolean return value to be called on
* this.getModel(). Delete button will be visible only if the
* function returns a truthy value.
*
* @param {boolean} [options.invertToggleDeleteButton]
* Invert the return value of `toggleDeleteButton`?
*
* @since 12.0
*/
var DeleteRowTableCellView = TableCellView.extend({
className: 'delete_row_table_cell',
template: template$d,
ui: {
removeButton: '.remove'
},
events: {
'click .remove': 'destroy',
'click': function click() {
return false;
}
},
showButton: function showButton() {
if (this.options.toggleDeleteButton) {
var context = this.getModel();
var toggle = context[this.options.toggleDeleteButton].apply(context);
if (this.options.invertToggleDeleteButton) {
return !toggle;
} else {
return !!toggle;
}
} else {
return true;
}
},
update: function update() {
this.ui.removeButton.toggleClass('remove', this.showButton());
this.ui.removeButton.attr('title', this.attributeTranslation('cell_title'));
},
destroy: function destroy() {
this.getModel().destroy();
}
});
/**
* A table cell representing whether the column attribute is present
* on the row model.
*
* ## Attribute Translations
*
* The following attribute translations are used:
*
* - `.cell_title.present` - Used as title attribute if the attribute
* is present. The current attribute value is provided as
* interpolation `%{value}`.
* - `.cell_title.blank` - Used as title attribute if the
* attribute is blank.
*
* @since 12.0
*/
var PresenceTableCellView = TableCellView.extend({
className: 'presence_table_cell',
update: function update() {
var isPresent = !!this.attributeValue();
this.$el.attr('title', isPresent ? this.attributeTranslation('cell_title.present', {
value: this.attributeValue()
}) : this.attributeTranslation('cell_title.blank'));
this.$el.toggleClass('is_present', isPresent);
}
});
/**
* A table cell mapping column attribute values to icons.
*
* ## Attribute Translations
*
* The following attribute translations are used:
*
* - `.cell_title.` - Used as title attribute.
* - `.cell_title.blank` - Used as title attribute if attribute is blank.
*
* @param {Object} [options]
*
* @param {string[]} [options.icons]
* An array of all possible attribute values to be mapped to HTML
* classes of the same name. A global mapping from those classes to
* icon mixins is provided in
* pageflow/ui/table_cells/icon_table_cell.scss.
*
* @since 12.0
*/
var IconTableCellView = TableCellView.extend({
className: 'icon_table_cell',
update: function update() {
var icon = this.attributeValue();
var isPresent = !!this.attributeValue();
this.removeExistingIcons();
this.$el.attr('title', isPresent ? this.attributeTranslation('cell_title.' + icon, {
value: this.attributeValue()
}) : this.attributeTranslation('cell_title.blank'));
this.$el.addClass(icon);
},
removeExistingIcons: function removeExistingIcons() {
this.$el.removeClass(this.options.icons.join(' '));
}
});
/**
* A table cell using the row model's value of the column attribute as
* text. If attribute value is empty, use most specific default
* available.
*
* @param {Object} [options]
*
* @param {function|string} [options.column.default]
* A function returning a default value for display if attribute
* value is empty.
*
* @param {string} [options.column.contentBinding]
* If this is provided, the function `options.column.default`
* receives the values of `options.column.contentBinding` and of
* this.getModel() via its options hash. No-op if
* `options.column.default` is not a function.
*
* @since 12.0
*/
var TextTableCellView = TableCellView.extend({
className: 'text_table_cell',
update: function update() {
this.$el.text(this._updateText());
},
_updateText: function _updateText() {
if (this.attributeValue()) {
return this.attributeValue();
} else if (typeof this.options.column["default"] === 'function') {
var options = {};
if (this.options.column.contentBinding) {
options = {
contentBinding: this.options.column.contentBinding,
model: this.getModel()
};
}
return this.options.column["default"](options);
} else if ('default' in this.options.column) {
return this.options.column["default"];
} else {
return I18n$1.t('pageflow.ui.text_table_cell_view.empty');
}
}
});
var subviewContainer = {
subview: function subview(view) {
this.subviews = this.subviews || new ChildViewContainer();
this.subviews.add(view.render());
return view;
},
appendSubview: function appendSubview(view) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
to = _ref.to;
return (to || this.$el).append(this.subview(view).el);
},
onClose: function onClose() {
if (this.subviews) {
this.subviews.call('close');
}
}
};
if (!Marionette.View.prototype.appendSubview) {
Cocktail.mixin(Marionette.View, subviewContainer);
}
var tooltipContainer = {
events: {
'mouseover [data-tooltip]': function mouseoverDataTooltip(event) {
if (!this.tooltip.visible) {
var target = $(event.currentTarget);
var key = target.attr('data-tooltip');
var position;
if (target.data('tooltipAlign') === 'bottom left') {
position = {
left: target.position().left,
top: target.position().top + target.outerHeight()
};
} else if (target.data('tooltipAlign') === 'bottom right') {
position = {
left: target.position().left + target.outerWidth(),
top: target.position().top + target.outerHeight()
};
} else if (target.data('tooltipAlign') === 'top center') {
position = {
left: target.position().left + target.outerWidth() / 2,
top: target.position().top + 2
};
} else {
position = {
left: target.position().left + target.outerWidth(),
top: target.position().top + target.outerHeight() / 2
};
}
this.tooltip.show(I18n$1.t(key), position, {
align: target.data('tooltipAlign')
});
}
},
'mouseleave [data-tooltip]': function mouseleaveDataTooltip() {
this.tooltip.hide();
}
},
onRender: function onRender() {
this.appendSubview(this.tooltip = new TooltipView());
}
};
exports.CheckBoxGroupInputView = CheckBoxGroupInputView;
exports.CheckBoxInputView = CheckBoxInputView;
exports.CollectionView = CollectionView;
exports.ColorInputView = ColorInputView;
exports.ConfigurationEditorTabView = ConfigurationEditorTabView;
exports.ConfigurationEditorView = ConfigurationEditorView;
exports.DeleteRowTableCellView = DeleteRowTableCellView;
exports.EnumTableCellView = EnumTableCellView;
exports.ExtendedSelectInputView = ExtendedSelectInputView;
exports.IconTableCellView = IconTableCellView;
exports.JsonInputView = JsonInputView;
exports.LabelOnlyView = LabelOnlyView;
exports.NumberInputView = NumberInputView;
exports.Object = BaseObject;
exports.PresenceTableCellView = PresenceTableCellView;
exports.ProxyUrlInputView = ProxyUrlInputView;
exports.SelectInputView = SelectInputView;
exports.SeparatorView = SeparatorView;
exports.SliderInputView = SliderInputView;
exports.SortableCollectionView = SortableCollectionView;
exports.TableCellView = TableCellView;
exports.TableHeaderCellView = TableHeaderCellView;
exports.TableRowView = TableRowView;
exports.TableView = TableView;
exports.TabsView = TabsView;
exports.TextAreaInputView = TextAreaInputView;
exports.TextInputView = TextInputView;
exports.TextTableCellView = TextTableCellView;
exports.TooltipView = TooltipView;
exports.UrlDisplayView = UrlDisplayView;
exports.UrlInputView = UrlInputView;
exports.attributeBinding = attributeBinding;
exports.cssModulesUtils = cssModulesUtils;
exports.i18nUtils = i18nUtils;
exports.inputView = inputView;
exports.inputWithPlaceholderText = inputWithPlaceholderText;
exports.serverSideValidation = serverSideValidation;
exports.subviewContainer = subviewContainer;
exports.tooltipContainer = tooltipContainer;
exports.viewWithValidationErrorMessages = viewWithValidationErrorMessages;
return exports;
}({}, Backbone.Marionette, _, jQuery, I18n, Backbone, Backbone.ChildViewContainer, IScroll, jQuery, wysihtml5, jQuery, Cocktail));