pageflow 15.1.0.beta6 → 15.1.0.rc0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pageflow might be problematic. Click here for more details.

Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/app/assets/javascripts/pageflow/dist/editor.js +613 -94
  4. data/app/assets/javascripts/pageflow/dist/ui.js +120 -3
  5. data/app/assets/stylesheets/pageflow/editor/base.scss +1 -0
  6. data/app/assets/stylesheets/pageflow/editor/composables.scss +9 -0
  7. data/app/assets/stylesheets/pageflow/editor/file_import.scss +7 -8
  8. data/app/helpers/pageflow/config_helper.rb +1 -1
  9. data/app/helpers/pageflow/entries_helper.rb +6 -1
  10. data/app/helpers/pageflow/social_share_links_helper.rb +5 -1
  11. data/config/locales/de.yml +34 -16
  12. data/config/locales/en.yml +34 -16
  13. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +613 -93
  14. data/entry_types/paged/app/views/layouts/pageflow_paged/application.html.erb +2 -1
  15. data/entry_types/paged/lib/pageflow_paged/engine.rb +1 -0
  16. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/chapters_controller.rb +9 -1
  17. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +2 -0
  18. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +28 -0
  19. data/entry_types/scrolled/config/locales/new/de.yml +46 -0
  20. data/entry_types/scrolled/config/locales/new/en.yml +46 -0
  21. data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +1 -0
  22. data/entry_types/scrolled/package/editor.js +2844 -78
  23. data/entry_types/scrolled/package/frontend.js +955 -443
  24. data/entry_types/scrolled/package/package.json +1 -0
  25. data/lib/pageflow/version.rb +1 -1
  26. data/packages/pageflow/editor.js +485 -90
  27. data/packages/pageflow/ui.js +120 -3
  28. metadata +5 -4
  29. data/config/locales/new/entry_metadata_configuration.de.yml +0 -17
  30. data/config/locales/new/entry_metadata_configuration.en.yml +0 -17
@@ -157,6 +157,48 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
157
157
  findKeyWithTranslation: findKeyWithTranslation,
158
158
  translationKeysWithSuffix: translationKeysWithSuffix
159
159
  });
160
+
161
+ function _arrayWithHoles(arr) {
162
+ if (Array.isArray(arr)) return arr;
163
+ }
164
+
165
+ function _iterableToArrayLimit(arr, i) {
166
+ if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
167
+ return;
168
+ }
169
+
170
+ var _arr = [];
171
+ var _n = true;
172
+ var _d = false;
173
+ var _e = undefined;
174
+
175
+ try {
176
+ for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
177
+ _arr.push(_s.value);
178
+
179
+ if (i && _arr.length === i) break;
180
+ }
181
+ } catch (err) {
182
+ _d = true;
183
+ _e = err;
184
+ } finally {
185
+ try {
186
+ if (!_n && _i["return"] != null) _i["return"]();
187
+ } finally {
188
+ if (_d) throw _e;
189
+ }
190
+ }
191
+
192
+ return _arr;
193
+ }
194
+
195
+ function _nonIterableRest() {
196
+ throw new TypeError("Invalid attempt to destructure non-iterable instance");
197
+ }
198
+
199
+ function _slicedToArray(arr, i) {
200
+ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
201
+ }
160
202
  /**
161
203
  * Create object that can be passed to Marionette ui property from CSS
162
204
  * module object.
@@ -187,7 +229,7 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
187
229
  * <div class=${styles.container}></div>
188
230
  * `,
189
231
  *
190
- * ui: cssModulesUtils.ui(styles, 'container');
232
+ * ui: cssModulesUtils.ui(styles, 'container'),
191
233
  *
192
234
  * onRender() {
193
235
  * this.ui.container // => JQuery wrapper for container element
@@ -197,22 +239,98 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
197
239
  * @memberof cssModulesUtils
198
240
  */
199
241
 
242
+
200
243
  function ui(styles) {
201
244
  for (var _len = arguments.length, classNames = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
202
245
  classNames[_key - 1] = arguments[_key];
203
246
  }
204
247
 
205
248
  return classNames.reduce(function (result, className) {
206
- result[className] = ".".concat(styles[className]);
249
+ result[className] = selector(styles, className);
207
250
  return result;
208
251
  }, {});
209
252
  }
253
+ /**
254
+ * Create object that can be passed to Marionette events property from CSS
255
+ * module object.
256
+ *
257
+ * @param {Object} styles
258
+ * Class name mapping imported from `.module.css` file.
259
+ *
260
+ * @param {Object} mapping
261
+ * Events mapping using keys from the `styles` instead of CSS class names.
262
+ *
263
+ * @return {Object}
264
+ *
265
+ * @example
266
+ *
267
+ * // MyView.module.css
268
+ *
269
+ * .addButton {}
270
+ *
271
+ * // MyView.js
272
+ *
273
+ * import Marionette from 'marionette';
274
+ * import {cssModulesUtils} from 'pageflow/ui';
275
+ *
276
+ * import styles from './MyView.module.css';
277
+ *
278
+ * export const MyView = Marionette.ItemView({
279
+ * template: () => `
280
+ * <button class=${styles.addButton}></button>
281
+ * `,
282
+ *
283
+ * events: cssModulesUtils.ui(styles, {
284
+ * 'click addButton': () => console.log('clicked add button');
285
+ * })
286
+ * });
287
+ *
288
+ * @memberof cssModulesUtils
289
+ */
290
+
291
+
292
+ function events(styles, mapping) {
293
+ return Object.keys(mapping).reduce(function (result, key) {
294
+ var _key$split = key.split(' '),
295
+ _key$split2 = _slicedToArray(_key$split, 2),
296
+ event = _key$split2[0],
297
+ className = _key$split2[1];
298
+
299
+ result["".concat(event, " ").concat(selector(styles, className))] = mapping[key];
300
+ return result;
301
+ }, {});
302
+ }
303
+ /**
304
+ * Generates a CSS selector from a CSS module rule.
305
+ *
306
+ * @param {Object} styles
307
+ * Class name mapping imported from `.module.css` file.
308
+ *
309
+ * @param {String} className
310
+ * Key from the `styles` object.
311
+ *
312
+ * @return {String} CSS Selector
313
+ * @memberof cssModulesUtils
314
+ */
315
+
316
+
317
+ function selector(styles, className) {
318
+ var classNames = styles[className];
319
+
320
+ if (!classNames) {
321
+ throw new Error("Unknown class name ".concat(className, " in mapping. Knwon names: ").concat(Object.keys(styles).join(', '), "."));
322
+ }
323
+
324
+ return ".".concat(classNames.replace(/ /g, '.'));
325
+ }
210
326
 
211
327
  var cssModulesUtils =
212
328
  /*#__PURE__*/
213
329
  Object.freeze({
214
330
  __proto__: null,
215
- ui: ui
331
+ ui: ui,
332
+ events: events,
333
+ selector: selector
216
334
  }); // https://github.com/jashkenas/backbone/issues/2601
217
335
 
218
336
  function BaseObject(options) {
@@ -1059,6 +1177,8 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
1059
1177
  this.$el.addClass('input');
1060
1178
  this.$el.addClass(this.model.modelName + '_' + this.options.propertyName);
1061
1179
  this.$el.data('inputPropertyName', this.options.propertyName);
1180
+ this.$el.data('labelText', this.labelText());
1181
+ this.$el.data('inlineHelpText', this.inlineHelpText());
1062
1182
  this.ui.labelText.text(this.labelText());
1063
1183
  this.ui.inlineHelp.html(this.inlineHelpText());
1064
1184
 
@@ -3374,8 +3494,61 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
3374
3494
  args.unshift(this.pageTypes);
3375
3495
  return _$1[method].apply(_$1, args);
3376
3496
  };
3497
+ }); // different model types. Backbone.Collection tries to merge records
3498
+ // if they have the same id.
3499
+
3500
+
3501
+ var MultiCollection = function MultiCollection() {
3502
+ this.records = {};
3503
+ this.length = 0;
3504
+ };
3505
+
3506
+ _$1.extend(MultiCollection.prototype, {
3507
+ add: function add(record) {
3508
+ if (!this.records[record.cid]) {
3509
+ this.records[record.cid] = record;
3510
+ this.length = _$1.keys(this.records).length;
3511
+ this.trigger('add', record);
3512
+ }
3513
+ },
3514
+ remove: function remove(record) {
3515
+ if (this.records[record.cid]) {
3516
+ delete this.records[record.cid];
3517
+ this.length = _$1.keys(this.records).length;
3518
+ this.trigger('remove', record);
3519
+ }
3520
+ },
3521
+ isEmpty: function isEmpty() {
3522
+ return this.length === 0;
3523
+ }
3377
3524
  });
3378
3525
 
3526
+ _$1.extend(MultiCollection.prototype, Backbone.Events);
3527
+
3528
+ MultiCollection.extend = Backbone.Collection.extend;
3529
+ /**
3530
+ * Watch Backbone collections to track which models are currently
3531
+ * being saved. Used to update the notifications view displaying
3532
+ * saving status/failutes.
3533
+ */
3534
+
3535
+ var SavingRecordsCollection = MultiCollection.extend({
3536
+ /**
3537
+ * Listen to events of models in collection to track when they are
3538
+ * being saved.
3539
+ *
3540
+ * @param {Backbone.Collection} collection - Collection to watch.
3541
+ */
3542
+ watch: function watch(collection) {
3543
+ var that = this;
3544
+ this.listenTo(collection, 'request', function (model, xhr) {
3545
+ that.add(model);
3546
+ xhr.always(function () {
3547
+ that.remove(model);
3548
+ });
3549
+ });
3550
+ }
3551
+ });
3379
3552
  var WidgetType = BaseObject.extend({
3380
3553
  initialize: function initialize(serverSideConfig, clientSideConfig) {
3381
3554
  this.name = serverSideConfig.name;
@@ -3495,6 +3668,15 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
3495
3668
  */
3496
3669
 
3497
3670
  this.failures = new FailuresAPI();
3671
+ /**
3672
+ * Tracking records that are currently being saved.
3673
+ *
3674
+ * @returns {SavingRecordsCollection}
3675
+ * @memberof editor
3676
+ * @since edge
3677
+ */
3678
+
3679
+ this.savingRecords = new SavingRecordsCollection();
3498
3680
  /**
3499
3681
  * Set up editor integration for page types.
3500
3682
  * @memberof editor
@@ -3541,7 +3723,9 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
3541
3723
  * Backbone view that will be rendered in the side bar.
3542
3724
  */
3543
3725
  registerEntryType: function registerEntryType(name, options) {
3544
- this.entryType = options;
3726
+ this.entryType = _objectSpread({
3727
+ name: name
3728
+ }, options);
3545
3729
  },
3546
3730
  createEntryModel: function createEntryModel(seed, options) {
3547
3731
  var entry = new this.entryType.entryModel(seed.entry, options);
@@ -3754,6 +3938,79 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
3754
3938
  });
3755
3939
  });
3756
3940
  };
3941
+ /**
3942
+ * Mixins for Backbone models and collections that use entry type
3943
+ * specific editor controllers registered via the `editor_app` entry
3944
+ * type option.
3945
+ */
3946
+
3947
+
3948
+ var entryTypeEditorControllerUrls = {
3949
+ /**
3950
+ * Mixins for Backbone collections that defines `url` method.
3951
+ *
3952
+ * @param {Object} options
3953
+ * @param {String} options.resources - Path suffix of the controller route
3954
+ *
3955
+ * @example
3956
+ *
3957
+ * import {editor, entryTypeEditorControllerUrls} from 'pageflow/editor';
3958
+ *
3959
+ * editor.registerEntryType('test', {
3960
+ // ...
3961
+ });
3962
+ *
3963
+ * export const ItemsCollection = Backbone.Collection.extend({
3964
+ * mixins: [entryTypeEditorControllerUrls.forCollection({resources: 'items'})
3965
+ * });
3966
+ *
3967
+ * new ItemsCollection().url() // => '/editor/entries/10/test/items'
3968
+ */
3969
+ forCollection: function forCollection(_ref) {
3970
+ var resources = _ref.resources;
3971
+ return {
3972
+ url: function url() {
3973
+ return entryTypeEditorControllerUrl(resources);
3974
+ },
3975
+ urlSuffix: function urlSuffix() {
3976
+ return "/".concat(resources);
3977
+ }
3978
+ };
3979
+ },
3980
+
3981
+ /**
3982
+ * Mixins for Backbone models that defines `urlRoot` method.
3983
+ *
3984
+ * @param {Object} options
3985
+ * @param {String} options.resources - Path suffix of the controller route
3986
+ *
3987
+ * @example
3988
+ *
3989
+ * import {editor, entryTypeEditorControllerUrls} from 'pageflow/editor';
3990
+ *
3991
+ * editor.registerEntryType('test', {
3992
+ // ...
3993
+ });
3994
+ *
3995
+ * export const Item = Backbone.Model.extend({
3996
+ * mixins: [entryTypeEditorControllerUrls.forModel({resources: 'items'})
3997
+ * });
3998
+ *
3999
+ * new Item({id: 20}).url() // => '/editor/entries/10/test/items/20'
4000
+ */
4001
+ forModel: function forModel(_ref2) {
4002
+ var resources = _ref2.resources;
4003
+ return {
4004
+ urlRoot: function urlRoot() {
4005
+ return this.isNew() ? this.collection.url() : entryTypeEditorControllerUrl(resources);
4006
+ }
4007
+ };
4008
+ }
4009
+ };
4010
+
4011
+ function entryTypeEditorControllerUrl(resources) {
4012
+ return [state.entry.url(), editor$1.entryType.name, resources].join('/');
4013
+ }
3757
4014
 
3758
4015
  var formDataUtils = {
3759
4016
  fromModel: function fromModel(model) {
@@ -3860,7 +4117,7 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
3860
4117
  this.reset();
3861
4118
  },
3862
4119
  url: function url() {
3863
- return this.parentModel.url() + _$1.result(this.parent, 'url');
4120
+ return this.parentModel.url() + (_$1.result(this.parent, 'urlSuffix') || _$1.result(this.parent, 'url'));
3864
4121
  },
3865
4122
  dispose: function dispose() {
3866
4123
  this.stopListening();
@@ -4164,10 +4421,23 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
4164
4421
  app.on('mixin:configuration', function (mixin) {
4165
4422
  Cocktail.mixin(Configuration, mixin);
4166
4423
  });
4424
+ /**
4425
+ * Remove model from collection only after the `DELETE` request has
4426
+ * succeeded. Still allow tracking that the model is being destroyed
4427
+ * by triggering a `destroying` event and adding a `isDestroying`
4428
+ * method.
4429
+ */
4430
+
4167
4431
  var delayedDestroying = {
4168
4432
  initialize: function initialize() {
4169
4433
  this._destroying = false;
4434
+ this._destroyed = false;
4170
4435
  },
4436
+
4437
+ /**
4438
+ * Trigger `destroying` event and send `DELETE` request. Only remove
4439
+ * model from collection once the request is done.
4440
+ */
4171
4441
  destroyWithDelay: function destroyWithDelay() {
4172
4442
  var model = this;
4173
4443
  this._destroying = true;
@@ -4176,16 +4446,33 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
4176
4446
  wait: true,
4177
4447
  success: function success() {
4178
4448
  model._destroying = false;
4449
+ model._destroyed = true;
4179
4450
  },
4180
4451
  error: function error() {
4181
4452
  model._destroying = false;
4182
4453
  }
4183
4454
  });
4184
4455
  },
4456
+
4457
+ /**
4458
+ * Get whether the model is currently being destroyed.
4459
+ */
4185
4460
  isDestroying: function isDestroying() {
4186
4461
  return this._destroying;
4462
+ },
4463
+
4464
+ /**
4465
+ * Get whether the model has been destroyed.
4466
+ */
4467
+ isDestroyed: function isDestroyed() {
4468
+ return this._destroyed;
4187
4469
  }
4188
4470
  };
4471
+ /**
4472
+ * Mixin for Backbone models that shall be watched by {@link
4473
+ * modelLifecycleTrackingView} mixin.
4474
+ */
4475
+
4189
4476
  var failureTracking = {
4190
4477
  initialize: function initialize() {
4191
4478
  this._saveFailed = false;
@@ -5611,6 +5898,76 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
5611
5898
  };
5612
5899
 
5613
5900
  editor$1.registerFileSelectionHandler('pageLink', PageLinkFileSelectionHandler);
5901
+ /**
5902
+ * Mixins for models with a nested configuration model.
5903
+ *
5904
+ * Triggers events on the parent model of the form
5905
+ * `change:configuration` and `change:configuration:<attribute>`, when
5906
+ * the configuration changes.
5907
+ *
5908
+ * @param {Object} [options]
5909
+ * @param {Function} [options.configurationModel] -
5910
+ * Backbone model to use for nested configuration model.
5911
+ * @param {Boolean} [options.autoSave] -
5912
+ * Save model when configuration changes.
5913
+ * @param {Boolean|Array<String>} [options.includeAttributesInJSON] -
5914
+ * Include all or specific attributes of the parent model in the
5915
+ * data returned by `toJSON` besides the `configuration` property.
5916
+ * @returns {Object} - Mixin to be included in model.
5917
+ *
5918
+ * @example
5919
+ *
5920
+ * import {configurationContainer} from 'pageflow/editor';
5921
+ *
5922
+ * const Section = Backbone.Model.extend({
5923
+ * mixins: [configurationContainer({autoSave: true})]
5924
+ * });
5925
+ *
5926
+ * const section = new Section({configuration: {some: 'value'}});
5927
+ * section.configuration.get('some') // => 'value';
5928
+ */
5929
+
5930
+ function configurationContainer() {
5931
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
5932
+ configurationModel = _ref.configurationModel,
5933
+ autoSave = _ref.autoSave,
5934
+ includeAttributesInJSON = _ref.includeAttributesInJSON;
5935
+
5936
+ configurationModel = configurationModel || Configuration.extend({
5937
+ defaults: {}
5938
+ });
5939
+ return {
5940
+ initialize: function initialize() {
5941
+ this.configuration = new configurationModel(this.get('configuration'));
5942
+ this.configuration.parent = this;
5943
+ this.listenTo(this.configuration, 'change', function () {
5944
+ if (!this.isNew() && (!this.isDestroying || !this.isDestroying()) && (!this.isDestroyed || !this.isDestroyed()) && autoSave) {
5945
+ this.save();
5946
+ }
5947
+
5948
+ this.trigger('change:configuration', this);
5949
+
5950
+ _$1.chain(this.configuration.changed).keys().each(function (name) {
5951
+ this.trigger('change:configuration:' + name, this, this.configuration.get(name));
5952
+ }, this);
5953
+ });
5954
+ },
5955
+ toJSON: function toJSON() {
5956
+ var attributes = {};
5957
+
5958
+ if (includeAttributesInJSON === true) {
5959
+ attributes = _$1.clone(this.attributes);
5960
+ } else if (includeAttributesInJSON) {
5961
+ attributes = _$1.pick(this.attributes, includeAttributesInJSON);
5962
+ }
5963
+
5964
+ return _$1.extend(attributes, {
5965
+ configuration: this.configuration.toJSON()
5966
+ });
5967
+ }
5968
+ };
5969
+ }
5970
+
5614
5971
  var persistedPromise = {
5615
5972
  persisted: function persisted() {
5616
5973
  var model = this;
@@ -5966,6 +6323,60 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
5966
6323
  return chapter.get('position');
5967
6324
  }
5968
6325
  });
6326
+ /**
6327
+ * A Backbone collection that is automatically updated to only
6328
+ * contain models with a foreign key matching the id of a parent
6329
+ * model.
6330
+ *
6331
+ * @param {Object} options
6332
+ * @param {Backbone.Model} options.parentModel -
6333
+ * Model whose id is compared to foreign keys.
6334
+ * @param {Backbone.Collection} options.parent -
6335
+ * Collection to filter items with matching foreign key from.
6336
+ * @param {String} options.foreignKeyAttribute -
6337
+ * Attribute to compare to id of parent model.
6338
+ * @param {String} options.parentReferenceAttribute -
6339
+ * Set reference to parent model on models in collection.
6340
+ *
6341
+ * @since edge
6342
+ */
6343
+
6344
+ var ForeignKeySubsetCollection = SubsetCollection.extend({
6345
+ mixins: [orderedCollection],
6346
+ constructor: function constructor(options) {
6347
+ var parent = options.parent;
6348
+ var parentModel = options.parentModel;
6349
+ SubsetCollection.prototype.constructor.call(this, {
6350
+ parent: parent,
6351
+ parentModel: parentModel,
6352
+ filter: function filter(item) {
6353
+ return !parentModel.isNew() && item.get(options.foreignKeyAttribute) === parentModel.id;
6354
+ },
6355
+ comparator: function comparator(item) {
6356
+ return item.get('position');
6357
+ }
6358
+ });
6359
+ this.listenTo(this, 'add', function (model) {
6360
+ if (options.parentReferenceAttribute) {
6361
+ model[options.parentReferenceAttribute] = parentModel;
6362
+ }
6363
+
6364
+ model.set(options.foreignKeyAttribute, parentModel.id);
6365
+ });
6366
+ this.listenTo(parentModel, 'destroy', function () {
6367
+ this.clear();
6368
+ });
6369
+
6370
+ if (options.parentReferenceAttribute) {
6371
+ this.each(function (model) {
6372
+ return model[options.parentReferenceAttribute] = parentModel;
6373
+ });
6374
+ this.listenTo(this, 'remove', function (model) {
6375
+ model[options.parentReferenceAttribute] = null;
6376
+ });
6377
+ }
6378
+ }
6379
+ });
5969
6380
  var PageLinksCollection = Backbone.Collection.extend({
5970
6381
  model: PageLink,
5971
6382
  initialize: function initialize(models, options) {
@@ -6176,48 +6587,7 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
6176
6587
  return model;
6177
6588
  }
6178
6589
  };
6179
- Cocktail.mixin(Backbone.Collection, addAndReturnModel); // different model types. Backbone.Collection tries to merge records
6180
- // if they have the same id.
6181
-
6182
- var MultiCollection = function MultiCollection() {
6183
- this.records = {};
6184
- this.length = 0;
6185
- };
6186
-
6187
- _$1.extend(MultiCollection.prototype, {
6188
- add: function add(record) {
6189
- if (!this.records[record.cid]) {
6190
- this.records[record.cid] = record;
6191
- this.length = _$1.keys(this.records).length;
6192
- this.trigger('add', record);
6193
- }
6194
- },
6195
- remove: function remove(record) {
6196
- if (this.records[record.cid]) {
6197
- delete this.records[record.cid];
6198
- this.length = _$1.keys(this.records).length;
6199
- this.trigger('remove', record);
6200
- }
6201
- },
6202
- isEmpty: function isEmpty() {
6203
- return this.length === 0;
6204
- }
6205
- });
6206
-
6207
- _$1.extend(MultiCollection.prototype, Backbone.Events);
6208
-
6209
- MultiCollection.extend = Backbone.Collection.extend;
6210
- var SavingRecordsCollection = MultiCollection.extend({
6211
- watch: function watch(collection) {
6212
- var that = this;
6213
- this.listenTo(collection, 'request', function (model, xhr) {
6214
- that.add(model);
6215
- xhr.always(function () {
6216
- that.remove(model);
6217
- });
6218
- });
6219
- }
6220
- });
6590
+ Cocktail.mixin(Backbone.Collection, addAndReturnModel);
6221
6591
  var SidebarRouter = Marionette.AppRouter.extend({
6222
6592
  appRoutes: {
6223
6593
  'page_links/:id': 'pageLink',
@@ -6389,25 +6759,88 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
6389
6759
  view: new ConfirmEncodingView(options)
6390
6760
  });
6391
6761
  };
6762
+ /**
6763
+ * Mixin for Marionette Views that sets css class names according to
6764
+ * life cycle events of its model.
6765
+ *
6766
+ * @param {Object} options
6767
+ * @param {Object} options.classNames
6768
+ * @param {String} options.classNames.creating -
6769
+ * Class name to add to root element while model is still being created.
6770
+ * @param {String} options.classNames.destroying -
6771
+ * Class name to add to root element while model is being destroyed.
6772
+ * @param {String} options.classNames.failed -
6773
+ * Class name to add to root element while model is in failed state.
6774
+ * Model needs to include {@link failureTracking} mixin.
6775
+ * @param {String} options.classNames.failureMessage -
6776
+ * Class name of the element that shall be updated with the failure
6777
+ * message. Model needs to include {@link failureTracking} mixin.
6778
+ * @param {String} options.classNames.retryButton -
6779
+ * Class name of the element that shall act as a retry button.
6780
+ */
6392
6781
 
6393
- var failureIndicatingView = {
6394
- modelEvents: {
6395
- 'change:failed': 'updateFailIndicator'
6396
- },
6397
- events: {
6398
- 'click .retry': function clickRetry() {
6782
+
6783
+ function modelLifecycleTrackingView(_ref) {
6784
+ var classNames = _ref.classNames;
6785
+ return {
6786
+ events: _defineProperty({}, "click .".concat(classNames.retryButton), function click() {
6399
6787
  editor$1.failures.retry();
6400
6788
  return false;
6789
+ }),
6790
+ initialize: function initialize() {
6791
+ var _this = this;
6792
+
6793
+ if (classNames.creating) {
6794
+ this.listenTo(this.model, 'change:id', function () {
6795
+ this.$el.removeClass(classNames.creating);
6796
+ });
6797
+ }
6798
+
6799
+ if (classNames.destroying) {
6800
+ this.listenTo(this.model, 'destroying', function () {
6801
+ this.$el.addClass(classNames.destroying);
6802
+ });
6803
+ this.listenTo(this.model, 'error', function () {
6804
+ this.$el.removeClass(classNames.destroying);
6805
+ });
6806
+ }
6807
+
6808
+ if (classNames.failed || classNames.failureMessage) {
6809
+ this.listenTo(this.model, 'change:failed', function () {
6810
+ return _this.updateFailIndicator();
6811
+ });
6812
+ }
6813
+ },
6814
+ render: function render() {
6815
+ if (this.model.isNew()) {
6816
+ this.$el.addClass(classNames.creating);
6817
+ }
6818
+
6819
+ if (this.model.isDestroying && this.model.isDestroying()) {
6820
+ this.$el.addClass(classNames.destroying);
6821
+ }
6822
+
6823
+ this.updateFailIndicator();
6824
+ },
6825
+ updateFailIndicator: function updateFailIndicator() {
6826
+ if (classNames.failed) {
6827
+ this.$el.toggleClass(classNames.failed, this.model.isFailed());
6828
+ }
6829
+
6830
+ if (classNames.failureMessage) {
6831
+ this.$el.find(".".concat(classNames.failureMessage)).text(this.model.getFailureMessage());
6832
+ }
6401
6833
  }
6402
- },
6403
- onRender: function onRender() {
6404
- this.updateFailIndicator();
6405
- },
6406
- updateFailIndicator: function updateFailIndicator() {
6407
- this.$el.toggleClass('failed', this.model.isFailed());
6408
- this.$el.find('.failure .message').text(this.model.getFailureMessage());
6834
+ };
6835
+ }
6836
+
6837
+ var failureIndicatingView = modelLifecycleTrackingView({
6838
+ classNames: {
6839
+ failed: 'failed',
6840
+ failureMessage: 'failure .message',
6841
+ retryButton: 'retry'
6409
6842
  }
6410
- };
6843
+ });
6411
6844
 
6412
6845
  function template$6$1(data) {
6413
6846
  var __t,
@@ -7563,7 +7996,8 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
7563
7996
  var editor = this.options.editor || {};
7564
7997
  var configurationEditor = new ConfigurationEditorView({
7565
7998
  model: entry.metadata.configuration,
7566
- tab: this.options.tab
7999
+ tab: this.options.tab,
8000
+ attributeTranslationKeyPrefixes: ['pageflow.entry_types.' + editor.entryType.name + '.editor.entry_metadata_configuration_attributes']
7567
8001
  });
7568
8002
  configurationEditor.tab('general', function () {
7569
8003
  this.input('title', TextInputView, {
@@ -7596,7 +8030,10 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
7596
8030
  });
7597
8031
  });
7598
8032
  configurationEditor.tab('widgets', function () {
7599
- editor.entryType.appearanceInputs && editor.entryType.appearanceInputs(this, entry, state.theming);
8033
+ editor.entryType.appearanceInputs && editor.entryType.appearanceInputs(this, {
8034
+ entry: entry,
8035
+ theming: state.theming
8036
+ });
7600
8037
  entry.widgets && this.view(EditWidgetsView, {
7601
8038
  model: entry,
7602
8039
  widgetTypes: editor.widgetTypes
@@ -7889,28 +8326,12 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
7889
8326
  this.appendSubview(configurationEditor);
7890
8327
  }
7891
8328
  });
7892
- var loadable = {
7893
- modelEvents: {
7894
- 'change:id': function changeId() {
7895
- this.$el.removeClass('creating');
7896
- },
7897
- destroying: function destroying() {
7898
- this.$el.addClass('destroying');
7899
- },
7900
- error: function error() {
7901
- this.$el.removeClass('destroying');
7902
- }
7903
- },
7904
- render: function render() {
7905
- if (this.model.isNew()) {
7906
- this.$el.addClass('creating');
7907
- }
7908
-
7909
- if (this.model.isDestroying && this.model.isDestroying()) {
7910
- this.$el.addClass('destroying');
7911
- }
8329
+ var loadable = modelLifecycleTrackingView({
8330
+ classNames: {
8331
+ creating: 'creating',
8332
+ destroying: 'destroying'
7912
8333
  }
7913
- };
8334
+ });
7914
8335
 
7915
8336
  function template$r(data) {
7916
8337
  var __p = '';
@@ -9903,8 +10324,8 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
9903
10324
  onRender: function onRender() {
9904
10325
  this.listenTo(state.entry, 'change:uploading_files_count', this.notifyUploadCount);
9905
10326
  this.listenTo(state.entry, 'change:confirmable_files_count', this.notifyConfirmableFilesCount);
9906
- this.listenTo(state.savingRecords, 'add', this.update);
9907
- this.listenTo(state.savingRecords, 'remove', this.update);
10327
+ this.listenTo(editor$1.savingRecords, 'add', this.update);
10328
+ this.listenTo(editor$1.savingRecords, 'remove', this.update);
9908
10329
  this.listenTo(editor$1.failures, 'add', this.update);
9909
10330
  this.listenTo(editor$1.failures, 'remove', this.update);
9910
10331
  this.update();
@@ -9912,7 +10333,7 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
9912
10333
  },
9913
10334
  update: function update() {
9914
10335
  this.$el.toggleClass('failed', !editor$1.failures.isEmpty());
9915
- this.$el.toggleClass('saving', !state.savingRecords.isEmpty());
10336
+ this.$el.toggleClass('saving', !editor$1.savingRecords.isEmpty());
9916
10337
  this.ui.failedCount.text(editor$1.failures.count());
9917
10338
  },
9918
10339
  notifyUploadCount: function notifyUploadCount(model, uploadCount) {
@@ -10284,7 +10705,101 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
10284
10705
  ConfirmUploadView.open = function (options) {
10285
10706
  app.dialogRegion.show(new ConfirmUploadView(options));
10286
10707
  };
10708
+ /**
10709
+ * Base view to edit configuration container models. Extend and
10710
+ * override the `configure` method which receives a {@link
10711
+ * ConfigurationEditorView} to define the tabs and inputs that shall
10712
+ * be displayed.
10713
+ *
10714
+ * Add a `translationKeyPrefix` property to the prototype and define
10715
+ * the following translations:
10716
+ *
10717
+ * * `<translationKeyPrefix>.tabs`: used as `tabTranslationKeyPrefix`
10718
+ * of the `ConfigurationEditorView`.
10719
+ *
10720
+ * * `<translationKeyPrefix>.attributes`: used as one of the
10721
+ * `attributeTranslationKeyPrefixes` of the
10722
+ * `ConfigurationEditorView`.
10723
+ *
10724
+ * * `<translationKeyPrefix>.back` (optional): Back button label.
10725
+ *
10726
+ * * `<translationKeyPrefix>.destroy` (optional): Destroy button
10727
+ * label.
10728
+ *
10729
+ * * `<translationKeyPrefix>.confirm_destroy` (optional): Confirm
10730
+ * message displayed before destroying.
10731
+ *
10732
+ * * `<translationKeyPrefix>.save_error` (optional): Header of the
10733
+ * failure message that is displayed if the model cannot be saved.
10734
+ *
10735
+ * * `<translationKeyPrefix>.retry` (optional): Label of the retry
10736
+ * button of the failure message.
10737
+ *
10738
+ * @param {Object} options
10739
+ * @param {Backbone.Model} options.model -
10740
+ * Model including the {@link configurationContainer},
10741
+ * {@link failureTracking} and {@link delayedDestroying} mixins.
10742
+ *
10743
+ * @since edge
10744
+ */
10745
+
10746
+
10747
+ var EditConfigurationView = Marionette.Layout.extend({
10748
+ className: 'edit_configuration_view',
10749
+ template: function template(_ref) {
10750
+ var t = _ref.t;
10751
+ return "\n <a class=\"back\">".concat(t('back'), "</a>\n <a class=\"destroy\">").concat(t('destroy'), "</a>\n\n <div class=\"failure\">\n <p>").concat(t('save_error'), "</p>\n <p class=\"message\"></p>\n <a class=\"retry\" href=\"\">").concat(t('retry'), "</a>\n </div>\n\n <div class=\"configuration_container\"></div>\n ");
10752
+ },
10753
+ serializeData: function serializeData() {
10754
+ var _this = this;
10755
+
10756
+ return {
10757
+ t: function t(key) {
10758
+ return _this.t(key);
10759
+ }
10760
+ };
10761
+ },
10762
+ mixins: [failureIndicatingView],
10763
+ regions: {
10764
+ configurationContainer: '.configuration_container'
10765
+ },
10766
+ events: {
10767
+ 'click a.back': 'goBack',
10768
+ 'click a.destroy': 'destroy'
10769
+ },
10770
+ onRender: function onRender() {
10771
+ var translationKeyPrefix = _$1.result(this, 'translationKeyPrefix');
10287
10772
 
10773
+ this.configurationEditor = new ConfigurationEditorView({
10774
+ tabTranslationKeyPrefix: "".concat(translationKeyPrefix, ".tabs"),
10775
+ attributeTranslationKeyPrefixes: ["".concat(translationKeyPrefix, ".attributes")],
10776
+ model: this.model.configuration
10777
+ });
10778
+ this.configure(this.configurationEditor);
10779
+ this.configurationContainer.show(this.configurationEditor);
10780
+ },
10781
+ onShow: function onShow() {
10782
+ this.configurationEditor.refreshScroller();
10783
+ },
10784
+ destroy: function destroy() {
10785
+ if (window.confirm(this.t('confirm_destroy'))) {
10786
+ this.model.destroyWithDelay();
10787
+ this.goBack();
10788
+ }
10789
+ },
10790
+ goBack: function goBack() {
10791
+ editor$1.navigate('/', {
10792
+ trigger: true
10793
+ });
10794
+ },
10795
+ t: function t(suffix) {
10796
+ var translationKeyPrefix = _$1.result(this, 'translationKeyPrefix');
10797
+
10798
+ return I18n$1.t("".concat(translationKeyPrefix, ".").concat(suffix), {
10799
+ defaultValue: I18n$1.t("pageflow.editor.views.edit_configuration.".concat(suffix))
10800
+ });
10801
+ }
10802
+ });
10288
10803
  ConfigurationEditorView.register('audio', {
10289
10804
  configure: function configure() {
10290
10805
  this.tab('general', function () {
@@ -10769,9 +11284,8 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
10769
11284
  editor$1.failures.watch(state.entry);
10770
11285
  editor$1.failures.watch(state.pages);
10771
11286
  editor$1.failures.watch(state.chapters);
10772
- state.savingRecords = new SavingRecordsCollection();
10773
- state.savingRecords.watch(state.pages);
10774
- state.savingRecords.watch(state.chapters);
11287
+ editor$1.savingRecords.watch(state.pages);
11288
+ editor$1.savingRecords.watch(state.chapters);
10775
11289
  pageflow.events.trigger('seed:loaded');
10776
11290
  });
10777
11291
  app.addInitializer(function (options) {
@@ -11449,8 +11963,10 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
11449
11963
  }
11450
11964
  });
11451
11965
 
11452
- var appearanceInputs = function appearanceInputs(tabView, entry, theming) {
11966
+ var appearanceInputs = function appearanceInputs(tabView, options) {
11967
+ var entry = options.entry;
11453
11968
  var theme = entry.getTheme();
11969
+ var theming = options.theming;
11454
11970
  tabView.input('manual_start', CheckBoxInputView);
11455
11971
  tabView.input('emphasize_chapter_beginning', CheckBoxInputView);
11456
11972
  tabView.input('emphasize_new_pages', CheckBoxInputView);
@@ -11562,6 +12078,7 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
11562
12078
  exports.DropDownButtonItemView = DropDownButtonItemView;
11563
12079
  exports.DropDownButtonView = DropDownButtonView;
11564
12080
  exports.EditChapterView = EditChapterView;
12081
+ exports.EditConfigurationView = EditConfigurationView;
11565
12082
  exports.EditEntryView = EditEntryView;
11566
12083
  exports.EditFileView = EditFileView;
11567
12084
  exports.EditLock = EditLock;
@@ -11585,6 +12102,7 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
11585
12102
  exports.EnumTableCellView = EnumTableCellView;
11586
12103
  exports.ExplorerFileItemView = ExplorerFileItemView;
11587
12104
  exports.ExtendedSelectInputView = ExtendedSelectInputView;
12105
+ exports.Failure = Failure;
11588
12106
  exports.FileConfiguration = FileConfiguration;
11589
12107
  exports.FileImport = FileImport;
11590
12108
  exports.FileInputView = FileInputView;
@@ -11605,6 +12123,7 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
11605
12123
  exports.FilesImporterView = FilesImporterView;
11606
12124
  exports.FilesView = FilesView;
11607
12125
  exports.FilteredFilesView = FilteredFilesView;
12126
+ exports.ForeignKeySubsetCollection = ForeignKeySubsetCollection;
11608
12127
  exports.HelpButtonView = HelpButtonView;
11609
12128
  exports.HelpImageView = HelpImageView;
11610
12129
  exports.HelpView = HelpView;
@@ -11619,7 +12138,6 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
11619
12138
  exports.LoadingView = LoadingView;
11620
12139
  exports.LockedView = LockedView;
11621
12140
  exports.ModelThumbnailView = ModelThumbnailView;
11622
- exports.MultiCollection = MultiCollection;
11623
12141
  exports.NestedFilesCollection = NestedFilesCollection;
11624
12142
  exports.NestedFilesView = NestedFilesView;
11625
12143
  exports.NestedTypeError = NestedTypeError;
@@ -11647,7 +12165,6 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
11647
12165
  exports.PublishEntryView = PublishEntryView;
11648
12166
  exports.ReferenceInputView = ReferenceInputView;
11649
12167
  exports.ReusableFile = ReusableFile;
11650
- exports.SavingRecordsCollection = SavingRecordsCollection;
11651
12168
  exports.Scaffold = Scaffold;
11652
12169
  exports.ScrollingView = ScrollingView;
11653
12170
  exports.SelectButtonView = SelectButtonView;
@@ -11701,10 +12218,12 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
11701
12218
  exports.addAndReturnModel = addAndReturnModel;
11702
12219
  exports.app = app;
11703
12220
  exports.authenticationProvider = authenticationProvider;
12221
+ exports.configurationContainer = configurationContainer;
11704
12222
  exports.cssModulesUtils = cssModulesUtils;
11705
12223
  exports.delayedDestroying = delayedDestroying;
11706
12224
  exports.dialogView = dialogView;
11707
12225
  exports.editor = editor$1;
12226
+ exports.entryTypeEditorControllerUrls = entryTypeEditorControllerUrls;
11708
12227
  exports.failureIndicatingView = failureIndicatingView;
11709
12228
  exports.failureTracking = failureTracking;
11710
12229
  exports.fileWithType = fileWithType;
@@ -11714,6 +12233,7 @@ var pageflow_paged = (function (exports, Backbone, _$1, Marionette, $, I18n$1, C
11714
12233
  exports.inputView = inputView;
11715
12234
  exports.inputWithPlaceholderText = inputWithPlaceholderText;
11716
12235
  exports.loadable = loadable;
12236
+ exports.modelLifecycleTrackingView = modelLifecycleTrackingView;
11717
12237
  exports.orderedCollection = orderedCollection;
11718
12238
  exports.persistedPromise = persistedPromise;
11719
12239
  exports.polling = polling;