app/assets/javascripts/pageflow/dist/ui.js in pageflow-15.6.1 vs app/assets/javascripts/pageflow/dist/ui.js in pageflow-15.7.0
- old
+ new
@@ -350,10 +350,28 @@
}); // The self-propagating extend function that Backbone classes use.
BaseObject.extend = Backbone.Model.extend;
+ var serverSideValidation = {
+ initialize: function initialize() {
+ var _this = this;
+
+ this.validationErrors = {};
+ this.listenTo(this, 'error', function (model, request) {
+ if (request.status === 422) {
+ _this.validationErrors = JSON.parse(request.responseText).errors;
+
+ _this.trigger('invalid');
+ }
+ });
+ this.listenTo(this, 'sync', function () {
+ _this.validationErrors = {};
+ });
+ }
+ };
+
var CollectionView = Marionette.View.extend({
initialize: function initialize() {
this.rendered = false;
this.itemViews = new ChildViewContainer();
this.collection.map(this.addItem, this);
@@ -750,14 +768,16 @@
defaultTab: this.options.tab
});
this.configure();
},
configure: function configure() {},
- tab: function tab(name, callback) {
+ tab: function tab(name, callbackOrOptions, callback) {
+ callback = callback || callbackOrOptions;
+ var options = callback ? callbackOrOptions : {};
this.tabsView.tab(name, _.bind(function () {
var tabView = new ConfigurationEditorTabView({
- model: this.model,
+ model: options.model || this.model,
placeholderModel: this.options.placeholderModel,
tab: name,
attributeTranslationKeyPrefixes: this.options.attributeTranslationKeyPrefixes
});
callback.call(tabView);
@@ -894,11 +914,11 @@
});
var TableHeaderCellView = TableCellView.extend({
tagName: 'th',
render: function render() {
- this.$el.text(this.attributeTranslation('column_header'));
+ this.$el.text(this.options.column.headerText || this.attributeTranslation('column_header'));
this.$el.data('columnName', this.options.column.name);
return this;
}
});
@@ -1271,10 +1291,11 @@
},
isDisabled: function isDisabled() {
return this.getAttributeBoundOption('disabled');
},
updateDisabled: function updateDisabled() {
+ this.$el.toggleClass('input-disabled', !!this.isDisabled());
this.updateInlineHelp();
if (this.ui.input) {
this.updateDisabledAttribute(this.ui.input);
}
@@ -1498,10 +1519,35 @@
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 || $('<ul class="validation_error_messages" />').appendTo(this.el);
+ this.validationErrorList.html('');
+ errors.forEach(function (error) {
+ return _this.validationErrorList.append("<li>".concat(error, "</li>"));
+ });
+ 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 += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<input type="text" dir="auto" />\n';
return __p
}
@@ -1525,11 +1571,11 @@
*
* @class
*/
var TextInputView = Marionette.ItemView.extend({
- mixins: [inputView, inputWithPlaceholderText],
+ mixins: [inputView, inputWithPlaceholderText, viewWithValidationErrorMessages],
template: template$6,
ui: {
input: 'input'
},
events: {
@@ -2025,13 +2071,13 @@
((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.insert_unordered_list') )) == null ? '' : __t) +
'"></a>\n\n <div data-wysihtml5-dialog="createLink" class="dialog link_dialog" style="display: none;">\n <div class="link_type_select">\n <label>\n <input type="radio" name="link_type" class="url_link_radio_button">\n ' +
((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.link_type.url') )) == null ? '' : __t) +
'\n </label>\n <label>\n <input type="radio" name="link_type" class="fragment_link_radio_button">\n ' +
((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.link_type.page_link') )) == null ? '' : __t) +
- '\n </label>\n </div>\n <div class="url_link_panel">\n <label>\n ' +
+ '\n </label>\n </div>\n <div class="url_link_panel">\n <label>\n <span>\n ' +
((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.target') )) == null ? '' : __t) +
- '\n </label>\n <input type="text" class="display_url">\n <div class="open_in_new_tab_section">\n <label>\n <input type="checkbox" class="open_in_new_tab">\n ' +
+ '\n </span>\n </label>\n <input type="text" class="display_url">\n <div class="open_in_new_tab_section">\n <label>\n <input type="checkbox" class="open_in_new_tab">\n ' +
((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.open_in_new_tab') )) == null ? '' : __t) +
'\n </label>\n <span class="inline_help">\n ' +
((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.open_in_new_tab_help') )) == null ? '' : __t) +
'\n </span>\n </div>\n </div>\n <div class="fragment_link_panel">\n <!-- LinkInputView is inserted here -->\n </div>\n\n <!-- wysihtml5 does not handle hidden fields correctly -->\n <div class="internal">\n <input type="text" data-wysihtml5-dialog-field="href" class="current_url" value="">\n <input type="text" data-wysihtml5-dialog-field="target" class="current_target" value="_blank">\n </div>\n\n <a class="button" data-wysihtml5-dialog-action="save">\n ' +
((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.save') )) == null ? '' : __t) +
@@ -2361,10 +2407,19 @@
* 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();
@@ -2397,27 +2452,30 @@
function isValidUrl(url) {
return options.permitHttps ? url.match(/^https?:\/\//i) : url.match(/^http:\/\//i);
}
function hasSupportedHost(url) {
- return _.any(view.supportedHosts(), function (host) {
- return url.match(new RegExp('^' + host));
+ 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();
}
}
});
@@ -2648,10 +2706,19 @@
*
* @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],
@@ -2673,28 +2740,71 @@
updateDisabled: function updateDisabled() {
this.load();
},
save: function save() {
if (!this.isDisabled()) {
- this.model.set(this.options.propertyName, this.ui.input.is(':checked'));
+ 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 <label>\n <span class=\"name\"></span>\n <span class=\"inline_help\"></span>\n </label>\n ";
+ },
+ ui: {
+ label: 'label'
+ }
+ });
+
+ /**
* A table cell mapping column attribute values to a list of
* translations.
*
* ## Attribute Translations
*
@@ -2909,12 +3019,15 @@
if (this.subviews) {
this.subviews.call('close');
}
}
};
- Cocktail.mixin(Marionette.View, subviewContainer);
+ 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);
@@ -2966,14 +3079,16 @@
exports.DeleteRowTableCellView = DeleteRowTableCellView;
exports.EnumTableCellView = EnumTableCellView;
exports.ExtendedSelectInputView = ExtendedSelectInputView;
exports.IconTableCellView = IconTableCellView;
exports.JsonInputView = JsonInputView;
+ exports.LabelOnlyView = LabelOnlyView;
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;
@@ -2987,11 +3102,13 @@
exports.UrlInputView = UrlInputView;
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, Cocktail));