app/assets/javascripts/pageflow/dist/editor.js in pageflow-15.1.0.beta2 vs app/assets/javascripts/pageflow/dist/editor.js in pageflow-15.1.0.beta3

- old
+ new

@@ -23,10 +23,25 @@ return sync(method, model, options); }; })(); + 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; + } + /*global JST*/ Marionette.Renderer.render = function (template, data) { if (_$1.isFunction(template)) { return template(data); @@ -40,18 +55,11 @@ throw "Template '" + template + "' not found!"; } return JST[template](data); }; - /** - * Helpers functions for handling translations. - * - * @memberof module:pageflow/ui - */ - - /** * Returns an array of translation keys based on the `prefixes` * option and the given `keyName`. * * @param {string} keyName * Suffix to append to prefixes. @@ -66,13 +74,15 @@ * * @param {string} [options.fallbackModelI18nKey] * Required if `fallbackPrefix` option is present. * * @return {string[]} + * @memberof i18nUtils * @since 12.0 - */ + */ + function attributeTranslationKeys(attributeName, keyName, options) { var result = []; if (options.prefixes) { result = result.concat(_$1(options.prefixes).map(function (prefix) { @@ -86,18 +96,19 @@ return result; } /** * Takes the same parameters as {@link - * module:pageflow/ui.attributeTranslationKeys - * i18nUtils.attributeTranslationKeys}, but returns the first - * existing translation. + * #i18nutilsattributetranslationkeys attributeTranslationKeys}, but returns the first existing + * translation. * * @return {string} + * @memberof i18nUtils * @since 12.0 */ + function attributeTranslation(attributeName, keyName, options) { return findTranslation(attributeTranslationKeys(attributeName, keyName, options)); } /** * Find the first key for which a translation exists and return the @@ -112,13 +123,15 @@ * * @param {boolean} [options.html] * If true, also search for keys ending in '_html' and HTML-escape * keys that do not end in 'html' * + * @memberof i18nUtils * @return {string} */ + function findTranslation(keys, options) { options = options || {}; if (options.html) { keys = translationKeysWithSuffix(keys, 'html'); @@ -141,49 +154,226 @@ * first if non of the keys has a translation. * * @param {string[]} keys * Translation key candidates. * + * @memberof i18nUtils * @return {string} */ + function findKeyWithTranslation(keys) { var missing = '_not_translated'; return _$1(keys).detect(function (key) { return I18n$1.t(key, { defaultValue: missing }) !== missing; }) || _$1.first(keys); } + function translationKeysWithSuffix(keys, suffix) { return _$1.chain(keys).map(function (key) { return [key + '_' + suffix, key]; }).flatten().value(); } - var i18nUtils = /*#__PURE__*/Object.freeze({ + var i18nUtils = + /*#__PURE__*/ + Object.freeze({ __proto__: null, attributeTranslationKeys: attributeTranslationKeys, attributeTranslation: attributeTranslation, findTranslation: findTranslation, findKeyWithTranslation: findKeyWithTranslation, translationKeysWithSuffix: translationKeysWithSuffix }); - // https://github.com/jashkenas/backbone/issues/2601 + function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; + } + function _iterableToArrayLimit(arr, i) { + if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { + return; + } + + var _arr = []; + var _n = true; + var _d = false; + var _e = undefined; + + try { + for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"] != null) _i["return"](); + } finally { + if (_d) throw _e; + } + } + + return _arr; + } + + function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance"); + } + + function _slicedToArray(arr, i) { + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); + } + /** + * Create object that can be passed to Marionette ui property from CSS + * module object. + * + * @param {Object} styles + * Class name mapping imported from `.module.css` file. + * + * @param {...string} classNames + * Keys from the styles object that shall be used in the ui object. + * + * @return {Object} + * + * @example + * + * // MyView.module.css + * + * .container {} + * + * // MyView.js + * + * import Marionette from 'marionette'; + * import {cssModulesUtils} from 'pageflow/ui'; + * + * import styles from './MyView.module.css'; + * + * export const MyView = Marionette.ItemView({ + * template: () => ` + * <div class=${styles.container}></div> + * `, + * + * ui: cssModulesUtils.ui(styles, 'container'), + * + * onRender() { + * this.ui.container // => JQuery wrapper for container element + * } + * }); + * + * @memberof cssModulesUtils + */ + + + function ui(styles) { + for (var _len = arguments.length, classNames = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + classNames[_key - 1] = arguments[_key]; + } + + return classNames.reduce(function (result, className) { + result[className] = selector(styles, className); + return result; + }, {}); + } + /** + * Create object that can be passed to Marionette events property from CSS + * module object. + * + * @param {Object} styles + * Class name mapping imported from `.module.css` file. + * + * @param {Object} mapping + * Events mapping using keys from the `styles` instead of CSS class names. + * + * @return {Object} + * + * @example + * + * // MyView.module.css + * + * .addButton {} + * + * // MyView.js + * + * import Marionette from 'marionette'; + * import {cssModulesUtils} from 'pageflow/ui'; + * + * import styles from './MyView.module.css'; + * + * export const MyView = Marionette.ItemView({ + * template: () => ` + * <button class=${styles.addButton}></button> + * `, + * + * events: cssModulesUtils.ui(styles, { + * 'click addButton': () => console.log('clicked add button'); + * }) + * }); + * + * @memberof cssModulesUtils + */ + + + function events(styles, mapping) { + return Object.keys(mapping).reduce(function (result, key) { + var _key$split = key.split(' '), + _key$split2 = _slicedToArray(_key$split, 2), + event = _key$split2[0], + className = _key$split2[1]; + + result["".concat(event, " ").concat(selector(styles, className))] = mapping[key]; + return result; + }, {}); + } + /** + * Generates a CSS selector from a CSS module rule. + * + * @param {Object} styles + * Class name mapping imported from `.module.css` file. + * + * @param {String} className + * Key from the `styles` object. + * + * @return {String} CSS Selector + * @memberof cssModulesUtils + */ + + + function selector(styles, className) { + var classNames = styles[className]; + + if (!classNames) { + throw new Error("Unknown class name ".concat(className, " in mapping. Knwon names: ").concat(Object.keys(styles).join(', '), ".")); + } + + return ".".concat(classNames.replace(/ /g, '.')); + } + + var cssModulesUtils = + /*#__PURE__*/ + Object.freeze({ + __proto__: null, + ui: ui, + events: events, + selector: selector + }); // https://github.com/jashkenas/backbone/issues/2601 + function BaseObject(options) { this.initialize.apply(this, arguments); } _$1.extend(BaseObject.prototype, Backbone.Events, { initialize: function initialize(options) {} }); // The self-propagating extend function that Backbone classes use. BaseObject.extend = Backbone.Model.extend; - var CollectionView = Marionette.View.extend({ initialize: function initialize() { this.rendered = false; this.itemViews = new ChildViewContainer(); this.collection.map(this.addItem, this); @@ -299,11 +489,10 @@ } else if (this.options.blankSlateViewConstructor) { return this.options.blankSlateViewConstructor; } } }); - var SortableCollectionView = CollectionView.extend({ render: function render() { CollectionView.prototype.render.call(this); this.$el.sortable({ connectWith: this.options.connectWith, @@ -348,11 +537,10 @@ this.$el.children().each(function (index) { $(this).data('view').model.set('position', index); }); } }); - var ConfigurationEditorTabView = Marionette.View.extend({ className: 'configuration_editor_tab', initialize: function initialize() { this.inputs = new ChildViewContainer(); this.groups = this.options.groups || ConfigurationEditorTabView.groups; @@ -407,19 +595,21 @@ }; ConfigurationEditorTabView.groups = new ConfigurationEditorTabView.Groups(); function template(data) { - var __p = ''; - __p += '<div class="tabs_view-scroller">\n <ul class="tabs_view-headers"></ul>\n</div>\n<div class="tabs_view-container"></div>\n'; - return __p + var __p = ''; + __p += '<div class="tabs_view-scroller">\n <ul class="tabs_view-headers"></ul>\n</div>\n<div class="tabs_view-container"></div>\n'; + return __p; } - /*global pageflow*/ + /** * Switch between different views using tabs. * + * @param {Object} [options] + * * @param {string} [options.defaultTab] * Name of the tab to enable by default. * * @param {string[]} [options.translationKeyPrefixes] * List of prefixes to append tab name to. First exisiting translation is used as label. @@ -430,14 +620,16 @@ * * @param {string} [options.i18n] * Legacy alias for `fallbackTranslationKeyPrefix`. * * @class - * @memberof module:pageflow/ui */ - var TabsView = Marionette.Layout.extend({ + + var TabsView = Marionette.Layout.extend( + /* @lends TabView.prototype */ + { template: template, className: 'tabs_view', ui: { headers: '.tabs_view-headers', scroller: '.tabs_view-scroller' @@ -539,14 +731,15 @@ this.scroller.refresh(); }); } } }); - /** * Render a inputs on multiple tabs. * + * @param {Object} [options] + * * @param {string} [options.model] * Backbone model to use for input views. * * @param {string} [options.placeholderModel] * Backbone model to read placeholder values from. @@ -562,11 +755,10 @@ * * @param {string} [options.tabTranslationKeyPrefix] * Prefixes to append tab name to. * * @class - * @memberof module:pageflow/ui */ var ConfigurationEditorView = Marionette.View.extend({ className: 'configuration_editor', initialize: function initialize() { @@ -616,28 +808,30 @@ this.repository[pageTypeName] = ConfigurationEditorView.extend(prototype); } }); function template$1(data) { - var __p = ''; - __p += ''; - return __p + var __p = ''; + __p += ''; + return __p; } - /** * Base class for table cell views. * * Inside sub classes the name of the column options are available as * `this.options.column`. Override the `update` method to populate the * element. * + * @param {Object} [options] + * * @param {string} [options.className] * Class attribute to apply to the cell element. * * @since 12.0 */ + var TableCellView = Marionette.ItemView.extend({ tagName: 'td', template: template$1, className: function className() { return this.options.className; @@ -714,20 +908,18 @@ this.listenTo(this.getModel(), 'change:' + this.options.column.contentBinding, this.update); this.update(); } } }); - var TableHeaderCellView = TableCellView.extend({ tagName: 'th', render: function render() { this.$el.text(this.attributeTranslation('column_header')); this.$el.data('columnName', this.options.column.name); return this; } }); - var TableRowView = Marionette.View.extend({ tagName: 'tr', events: { 'click': function click() { if (this.options.selection) { @@ -762,23 +954,21 @@ return this.options.selectionAttribute || 'current'; } }); function template$2(data) { - var __p = ''; - __p += '<table>\n <thead>\n <tr></tr>\n </thead>\n <tbody>\n </tbody>\n</table>\n'; - return __p + var __p = ''; + __p += '<table>\n <thead>\n <tr></tr>\n </thead>\n <tbody>\n </tbody>\n</table>\n'; + return __p; } function blankSlateTemplate(data) { - var __t, __p = ''; - __p += '<td colspan="' + - ((__t = ( data.colSpan )) == null ? '' : __t) + - '">\n ' + - ((__t = ( data.blankSlateText )) == null ? '' : __t) + - '\n</td>\n'; - return __p + var __t, + __p = ''; + + __p += '<td colspan="' + ((__t = data.colSpan) == null ? '' : __t) + '">\n ' + ((__t = data.blankSlateText) == null ? '' : __t) + '\n</td>\n'; + return __p; } var TableView = Marionette.ItemView.extend({ tagName: 'table', className: 'table_view', @@ -821,13 +1011,13 @@ })); } }); function template$3(data) { - var __p = ''; - __p += '<span class="label">\n</span>\n'; - return __p + var __p = ''; + __p += '<span class="label">\n</span>\n'; + return __p; } var TooltipView = Marionette.ItemView.extend({ template: template$3, className: 'tooltip', @@ -864,11 +1054,10 @@ }); this.$el.addClass('visible'); }, this), 200); } }); - /** * Mixin for input views handling common concerns like labels, * inline help, visiblity and disabling. * * ## Label and Inline Help Translations @@ -975,11 +1164,10 @@ * @param {any} [options.visibleBindingValue] * Input will be visible whenever the value of the `visibleBinding` * attribute equals the value of this option. * * @mixin - * @memberof module:pageflow/ui */ var inputView = { ui: { labelText: 'label .name', @@ -988,17 +1176,16 @@ /** * Returns an array of translation keys based on the * `attributeTranslationKeyPrefixes` option and the given keyName. * - * Combined with {@link - * module:pageflow/ui.findTranslation + * 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')); + * findTranslation(this.attributeTranslationKeys('default_value')); * * @param {string} keyName * Suffix to append to prefixes. * * @param {string} [options.fallbackPrefix] @@ -1098,23 +1285,24 @@ } } }; function template$4(data) { - var __p = ''; - __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="check_boxes_container" />\n'; - return __p + var __p = ''; + __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="check_boxes_container" />\n'; + return __p; } - /** * Input view for attributes storing configuration hashes with boolean values. + * See {@link inputView} for further options. * - * @see {@link module:pageflow/ui.inputView inputView} for further options + * @param {Object} [options] + * * @class - * @memberof module:pageflow/ui */ + var CheckBoxGroupInputView = Marionette.ItemView.extend({ mixins: [inputView], template: template$4, className: 'check_box_group_input', events: { @@ -1169,28 +1357,29 @@ } } }); function template$5(data) { - var __t, __p = ''; - __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<a class="original" href="#" download target="_blank">\n ' + - ((__t = ( I18n.t('pageflow.ui.templates.inputs.url_display.link_text') )) == null ? '' : __t) + - '\n</a>\n'; - return __p - } + var __t, + __p = ''; + __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<a class="original" href="#" download target="_blank">\n ' + ((__t = I18n.t('pageflow.ui.templates.inputs.url_display.link_text')) == null ? '' : __t) + '\n</a>\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 * - * @see {@link module:pageflow/ui.inputView inputView} for further options * @class - * @memberof module:pageflow/ui */ + var UrlDisplayView = Marionette.ItemView.extend({ mixins: [inputView], template: template$5, ui: { link: 'a' @@ -1211,14 +1400,15 @@ var url = this.model.get('original_url'); this.$el.toggle(this.model.isUploaded() && !_$1.isEmpty(url)); this.ui.link.attr('href', url); } }); - /** * 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. * @@ -1230,14 +1420,12 @@ * 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. - * - * @mixin - * @memberof module:pageflow/ui */ + var inputWithPlaceholderText = { onRender: function onRender() { this.updatePlaceholder(); if (this.options.placeholderBinding) { @@ -1264,35 +1452,35 @@ return this.options.placeholderModel && this.options.placeholderModel.get(this.options.propertyName); } }; 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 + 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; } - /** * 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. + * 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. + * 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. * - * @see - * {@link module:pageflow/ui.inputWithPlaceholderText inputWithPlaceholderText} - * for placeholder related further options - * - * @see {@link module:pageflow/ui.inputView inputView} for further options * @class - * @memberof module:pageflow/ui */ + var TextInputView = Marionette.ItemView.extend({ mixins: [inputView, inputWithPlaceholderText], template: template$6, ui: { input: 'input' @@ -1352,14 +1540,16 @@ 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. * @@ -1373,13 +1563,11 @@ * @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. * - * @see {@link module:pageflow/ui.inputView inputView} for further options * @class - * @memberof module:pageflow/ui */ var ColorInputView = Marionette.ItemView.extend({ mixins: [inputView], template: template$6, @@ -1442,18 +1630,20 @@ } } }); function template$7(data) { - var __p = ''; - __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<select></select>'; - return __p + var __p = ''; + __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<select></select>'; + 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. @@ -1462,11 +1652,11 @@ * 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 module:pageflow/ui:inputView} + * [`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. @@ -1516,15 +1706,14 @@ * 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. * - * @see {@link module:pageflow/ui.inputView inputView} for further options * @class - * @memberof module:pageflow/ui */ + var SelectInputView = Marionette.ItemView.extend({ mixins: [inputView], template: template$7, events: { 'change': 'save' @@ -1652,11 +1841,10 @@ this.ui.select.val(this.ui.select.find('option:first').val()); } } } }); - var ExtendedSelectInputView = SelectInputView.extend({ className: 'extended_select_input', initialize: function initialize() { SelectInputView.prototype.initialize.apply(this, arguments); @@ -1753,46 +1941,23 @@ this.save(); } }); function template$8(data) { - var __t, __p = ''; - __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n\n<!-- inline style for wysihtml5 to pick up -->\n<textarea style="width: 100%;" dir="auto"></textarea>\n\n<div class="toolbar">\n <a data-wysihtml5-command="bold" title="' + - ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.bold') )) == null ? '' : __t) + - '"></a>\n <a data-wysihtml5-command="italic" title="' + - ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.italic') )) == null ? '' : __t) + - '"></a>\n <a data-wysihtml5-command="underline" title="' + - ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.underline') )) == null ? '' : __t) + - '"></a>\n <a data-wysihtml5-command="createLink" class="link_button" title="' + - ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.create_link') )) == null ? '' : __t) + - '"></a>\n <a data-wysihtml5-command="insertOrderedList" title="' + - ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.insert_ordered_list') )) == null ? '' : __t) + - '"></a>\n <a data-wysihtml5-command="insertUnorderedList" title="' + - ((__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 ' + - ((__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 ' + - ((__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="http://">\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) + - '\n </a>\n <a class="button" data-wysihtml5-dialog-action="cancel">\n ' + - ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.cancel') )) == null ? '' : __t) + - '\n </a>\n\n <a data-wysihtml5-command="removeLink">' + - ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.remove_link') )) == null ? '' : __t) + - '</a>\n </div>\n</div>\n'; - return __p - } + var __t, + __p = ''; + __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n\n<!-- inline style for wysihtml5 to pick up -->\n<textarea style="width: 100%;" dir="auto"></textarea>\n\n<div class="toolbar">\n <a data-wysihtml5-command="bold" title="' + ((__t = I18n.t('pageflow.ui.templates.inputs.text_area_input.bold')) == null ? '' : __t) + '"></a>\n <a data-wysihtml5-command="italic" title="' + ((__t = I18n.t('pageflow.ui.templates.inputs.text_area_input.italic')) == null ? '' : __t) + '"></a>\n <a data-wysihtml5-command="underline" title="' + ((__t = I18n.t('pageflow.ui.templates.inputs.text_area_input.underline')) == null ? '' : __t) + '"></a>\n <a data-wysihtml5-command="createLink" class="link_button" title="' + ((__t = I18n.t('pageflow.ui.templates.inputs.text_area_input.create_link')) == null ? '' : __t) + '"></a>\n <a data-wysihtml5-command="insertOrderedList" title="' + ((__t = I18n.t('pageflow.ui.templates.inputs.text_area_input.insert_ordered_list')) == null ? '' : __t) + '"></a>\n <a data-wysihtml5-command="insertUnorderedList" title="' + ((__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 ' + ((__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 ' + ((__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="http://">\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) + '\n </a>\n <a class="button" data-wysihtml5-dialog-action="cancel">\n ' + ((__t = I18n.t('pageflow.ui.templates.inputs.text_area_input.cancel')) == null ? '' : __t) + '\n </a>\n\n <a data-wysihtml5-command="removeLink">' + ((__t = I18n.t('pageflow.ui.templates.inputs.text_area_input.remove_link')) == null ? '' : __t) + '</a>\n </div>\n</div>\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. @@ -1803,19 +1968,14 @@ * @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. * - * @see - * {@link module:pageflow/ui.inputWithPlaceholderText inputWithPlaceholderText} - * for placeholder related options - * - * @see {@link module:pageflow/ui.inputView inputView} for further options * @class - * @memberof module:pageflow/ui */ + var TextAreaInputView = Marionette.ItemView.extend({ mixins: [inputView, inputWithPlaceholderText], template: template$8, ui: { input: 'textarea', @@ -1992,34 +2152,35 @@ this.ui.targetInput.val(this.ui.openInNewTabCheckBox.is(':checked') ? '_blank' : '_self'); } }); function template$9(data) { - var __p = ''; - __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<input type="text" />\n<div class="validation"></div>\n'; - return __p + var __p = ''; + __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<input type="text" />\n<div class="validation"></div>\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. * - * @see {@link module:pageflow/ui.inputView inputView} for further options * @class - * @memberof module:pageflow/ui */ + var UrlInputView = Marionette.Layout.extend( - /** @lends module:pageflow/ui.UrlInputView# */ + /** @lends UrlInputView.prototype */ { mixins: [inputView], template: template$9, ui: { input: 'input', @@ -2141,11 +2302,10 @@ view.$el.removeClass('invalid'); 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. @@ -2153,10 +2313,14 @@ * 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. @@ -2182,17 +2346,15 @@ * base_path: '/example' * } * ] * }); * - * @see {@link module:pageflow/ui.inputView inputView} for further options * @class - * @memberof module:pageflow/ui */ var ProxyUrlInputView = UrlInputView.extend( - /** @lends module:pageflow/ui.ProxyUrlInputView# */ + /** @lends ProxyUrlInputView.prototype */ { // @override validateUrl: function validateUrl(url) { var view = this; return $.Deferred(function (deferred) { @@ -2205,15 +2367,15 @@ status: xhr.status })); }); }).promise(); }, - // @override + // override transformPropertyValue: function transformPropertyValue(url) { return this.rewriteUrl(url); }, - // @override + // override supportedHosts: function supportedHosts() { return _$1.pluck(this.options.proxies, 'url'); }, rewriteUrl: function rewriteUrl(url) { _$1.each(this.options.proxies, function (proxy) { @@ -2223,23 +2385,24 @@ return url; } }); function template$a(data) { - var __p = ''; - __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="value"></div>\n<div class="slider"></div>\n'; - return __p + var __p = ''; + __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="value"></div>\n<div class="slider"></div>\n'; + return __p; } - /** * A slider for numeric inputs. + * See {@link inputView} for options * - * @see {@link module:pageflow/ui.inputView inputView} for options + * @param {Object} [options] + * * @class - * @memberof module:pageflow/ui */ + var SliderInputView = Marionette.ItemView.extend({ mixins: [inputView], className: 'slider_input', template: template$a, ui: { @@ -2275,13 +2438,13 @@ this.ui.widget.slider('option', 'value', value); } }); function template$b(data) { - var __p = ''; - __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n\n<textarea></textarea>\n'; - return __p + var __p = ''; + __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n\n<textarea></textarea>\n'; + return __p; } var JsonInputView = Marionette.ItemView.extend({ mixins: [inputView], template: template$b, @@ -2344,27 +2507,28 @@ } } }); function template$c(data) { - var __p = ''; - __p += '<input type="checkbox" />\n<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>'; - return __p + var __p = ''; + __p += '<input type="checkbox" />\n<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>'; + 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. * - * @see {@link module:pageflow/ui.inputView inputView} for further options * @class - * @memberof module:pageflow/ui */ + var CheckBoxInputView = Marionette.ItemView.extend({ mixins: [inputView], template: template$c, className: 'check_box_input', events: { @@ -2396,11 +2560,10 @@ } else { return this.model.get(this.options.propertyName); } } }); - /** * A table cell mapping column attribute values to a list of * translations. * * ## Attribute Translations @@ -2424,27 +2587,28 @@ })); } }); function template$d(data) { - var __t, __p = ''; - __p += '<a class="remove" title="' + - ((__t = ( I18n.t('pageflow.editor.templates.row.destroy') )) == null ? '' : __t) + - '"></a>\n'; - return __p - } + var __t, + __p = ''; + __p += '<a class="remove" title="' + ((__t = I18n.t('pageflow.editor.templates.row.destroy')) == null ? '' : __t) + '"></a>\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. * @@ -2452,10 +2616,11 @@ * Invert the return value of `toggleDeleteButton`? * * @since 12.0 */ + var DeleteRowTableCellView = TableCellView.extend({ className: 'delete_row_table_cell', template: template$d, ui: { removeButton: '.remove' @@ -2486,11 +2651,10 @@ }, destroy: function destroy() { this.getModel().destroy(); } }); - /** * A table cell representing whether the column attribute is present * on the row model. * * ## Attribute Translations @@ -2514,21 +2678,22 @@ 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.<attribute_value>` - 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. @@ -2549,16 +2714,17 @@ }, 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] @@ -2594,11 +2760,10 @@ } 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; @@ -2611,11 +2776,10 @@ this.subviews.call('close'); } } }; Cocktail.mixin(Marionette.View, subviewContainer); - var tooltipContainer = { events: { 'mouseover [data-tooltip]': function mouseoverDataTooltip(event) { if (!this.tooltip.visible) { var target = $(event.target); @@ -2683,11 +2847,10 @@ * Subclasses that represent failures that are can not be retried should * override `catRetry` with false. * Retryable failures should implement `retryAction`. * * @class - * @memberof module:pageflow/editor */ var Failure = BaseObject.extend({ canRetry: true, type: 'Failure', @@ -2704,29 +2867,13 @@ }, key: function key() { return this.model.cid + '-' + this.type; } }); - /** - * SavingFailure represents a unsuccessful attempt to save - * a model on the server. - * - * @class - * @memberof module:pageflow/editor - */ - var SavingFailure = Failure.extend({ type: 'SavingFailure' }); - /** - * OrderingFailure represent a unsuccessful attempt to save - * the ordering of a orderedCollection. - * - * @class - * @memberof module:pageflow/editor - */ - var OrderingFailure = Failure.extend({ type: 'OrderingFailure', initialize: function initialize(model, collection) { Failure.prototype.initialize.call(this, model); this.collection = collection; @@ -2746,26 +2893,24 @@ * * It's possible to add failures to the UI by adding instances of subclasses of Failure: * * editor.failures.add(new OrderingFailure(model, collection)); * - * @alias pageflow.Failures - * @memberof module:pageflow/editor + * @alias Failures */ var FailuresAPI = BaseObject.extend( - /** @lends module:pageflow/editor.pageflow.Failures */ + /** @lends Failures.prototype */ { initialize: function initialize() { this.failures = {}; this.length = 0; }, /** * Listen to the `error` and `sync` events of a collection and - * create {@link module:pageflow/editor.SavingFailure - * SavingFailure} objects. + * create failure objects. */ watch: function watch(collection) { this.listenTo(collection, 'sync', this.remove); this.listenTo(collection, 'error', function (model) { if (!model.isNew()) { @@ -2914,12 +3059,11 @@ args.unshift(this._fileTypes); return _$1[method].apply(_$1, args); }; }); - /*global pageflow*/ - var state = pageflow; + var state = window.pageflow || {}; function template$e(data) { var __p = ''; __p += ''; return __p @@ -3029,21 +3173,22 @@ /** * Base class for views used as `valueView` for file type meta data * attributes. * + * @param {Object} [options] + * * @param {string} [options.name] * Name of the meta data item used in translation keys. * * @param {string} [options.settingsDialogTabLink] * Dispaly a link to open the specified tab of the file settings * dialog. * * @since 12.0 * * @class - * @memberof module:pageflow/editor */ var FileMetaDataItemValueView = Marionette.ItemView.extend({ template: template$g, ui: { @@ -3075,22 +3220,10 @@ toggleEditLink: function toggleEditLink() { this.ui.editLink.toggle(!!this.options.settingsDialogTabLink && !this.model.isNew()); } }); - /** - * Renders the value of the attribute given in `options.name`. - * - * @see {@link module:pageflow/editor.FileMetaDataItemValueView - * FileMetaDataItemValueView} for further options. - * - * @since 12.0 - * - * @class - * @memberof module:pageflow/editor - */ - var TextFileMetaDataItemValueView = FileMetaDataItemValueView.extend({ getText: function getText() { var model; if (this.options.fromConfiguration) { @@ -3247,16 +3380,20 @@ this.importers[name] = config; config.key = name; }, setup: function setup(serverSideConfigs) { this._setup = true; - var importers = this.importers; + var registeredImporters = this.importers; + var importers = {}; serverSideConfigs.forEach(function (importer) { - importers[importer.importerName]['authenticationRequired'] = importer.authenticationRequired; - importers[importer.importerName]['authenticationProvider'] = importer.authenticationProvider; - importers[importer.importerName]['logoSource'] = importer.logoSource; + var regImporter = registeredImporters[importer.importerName]; + regImporter['authenticationRequired'] = importer.authenticationRequired; + regImporter['authenticationProvider'] = importer.authenticationProvider; + regImporter['logoSource'] = importer.logoSource; + importers[importer.importerName] = regImporter; }); + this.importers = importers; }, find: function find(name) { if (!this.importers[name]) { throw 'Could not find file importer with name "' + name + '"'; } @@ -3367,10 +3504,66 @@ args.unshift(this.pageTypes); return _$1[method].apply(_$1, args); }; }); + // different model types. Backbone.Collection tries to merge records + // if they have the same id. + + var MultiCollection = function MultiCollection() { + this.records = {}; + this.length = 0; + }; + + _$1.extend(MultiCollection.prototype, { + add: function add(record) { + if (!this.records[record.cid]) { + this.records[record.cid] = record; + this.length = _$1.keys(this.records).length; + this.trigger('add', record); + } + }, + remove: function remove(record) { + if (this.records[record.cid]) { + delete this.records[record.cid]; + this.length = _$1.keys(this.records).length; + this.trigger('remove', record); + } + }, + isEmpty: function isEmpty() { + return this.length === 0; + } + }); + + _$1.extend(MultiCollection.prototype, Backbone.Events); + + MultiCollection.extend = Backbone.Collection.extend; + + /** + * Watch Backbone collections to track which models are currently + * being saved. Used to update the notifications view displaying + * saving status/failutes. + */ + + var SavingRecordsCollection = MultiCollection.extend({ + /** + * Listen to events of models in collection to track when they are + * being saved. + * + * @param {Backbone.Collection} collection - Collection to watch. + */ + watch: function watch(collection) { + var that = this; + this.listenTo(collection, 'request', function (model, xhr) { + that.add(model); + xhr.always(function () { + that.remove(model); + }); + }); + } + }); + var WidgetType = BaseObject.extend({ initialize: function initialize(serverSideConfig, clientSideConfig) { this.name = serverSideConfig.name; this.translationKey = serverSideConfig.translationKey; this.configurationEditorView = clientSideConfig.configurationEditorView; @@ -3431,73 +3624,105 @@ isOptional: function isOptional(role) { return !!this._optionalRoles[role]; } }); + 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 _objectSpread(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; } /** * Interface for engines providing editor extensions. * @alias editor - * @memberof module:pageflow/editor */ var EditorApi = BaseObject.extend( - /** @lends module:pageflow/editor.editor */ + /** @lends editor */ { initialize: function initialize(options) { this.router = options && options.router; this.sideBarRoutings = []; this.mainMenuItems = []; this.initializers = []; this.fileSelectionHandlers = {}; /** * Failures API * - * @returns {pageflow.Failures} - * - * @alias failures - * @memberof module:pageflow/editor.editor + * @returns {Failures} + * @memberof editor */ this.failures = new FailuresAPI(); /** + * Tracking records that are currently being saved. + * + * @returns {SavingRecordsCollection} + * @memberof editor + * @since 15.1 + */ + + this.savingRecords = new SavingRecordsCollection(); + /** * Set up editor integration for page types. - * @alias pageTypes - * @memberof module:pageflow/editor.editor + * @memberof editor */ this.pageTypes = new PageTypes(); /** * Add tabs to the configuration editor of all pages. - * @alias commonPageConfigurationTabs - * @memberof module:pageflow/editor.editor + * @memberof editor */ this.commonPageConfigurationTabs = new CommonPageConfigurationTabs(); /** * Setup editor integration for widget types. - * @alias widgetType - * @memberof module:pageflow/editor.editor + * @memberof editor */ this.widgetTypes = new WidgetTypes(); /** - * @alias fileTypes - * @memberof module:pageflow/editor.editor * Set up editor integration for file types + * @memberof editor */ this.fileTypes = new FileTypes(); /** * List of available file import plugins - * @alias fileImporters - * + * @memberof editor */ this.fileImporters = new FileImporters(); }, /** + * Configure editor for entry type. + * + * @param {string} name + * Must match name of entry type registered in Ruby configuration. + * @param {Object} options + * @param {function} options.EntryModel + * Backbone model extending {Entry} to store entry state. + * @param {function} options.EntryPreviewView + * Backbone view that will render the live preview of the entry. + * @param {function} options.EntryOutlineView + * Backbone view that will be rendered in the side bar. + */ + registerEntryType: function registerEntryType(name, options) { + this.entryType = _objectSpread({ + name: name + }, options); + }, + createEntryModel: function createEntryModel(seed, options) { + var entry = new this.entryType.entryModel(seed.entry, options); + + if (entry.setupFromEntryTypeSeed) { + entry.setupFromEntryTypeSeed(seed.entry_type, state); + } + + return entry; + }, + + /** * Display Backbone/Marionette View inside the main panel * of the editor. */ showViewInMainPanel: function showViewInMainPanel(view) { app.mainRegion.show(view); @@ -3596,11 +3821,11 @@ /** * File selection handlers let editor extensions use the files view * to select files for usage in their custom models. * - * See {@link module:pageflow/editor.editor.selectFile + * See {@link #editorselectfile * selectFile} method for details how to trigger file selection. * * Example: * * function MyFileSelectionHandler(options) { this.call = @@ -3627,11 +3852,11 @@ * the collection name a file type and a the name of a file type * filter. * * @param {string} handlerName * The name of a handler registered via {@link - * module:pageflow/editor.editor.registerFileSelectionHandler}. + * #editorregisterfileselectionhandler registerFileSelectionHandler}. * * @param {Object} payload * Options passed to the file selection handler. * * @example @@ -3663,34 +3888,33 @@ * Supported options: * - isAllowed: function which given a page returns true or false depending on * whether the page is a valid selection */ selectPage: function selectPage(options) { - return this.pageSelectionView.selectPage(options); + return this.pageSelectionView.selectPage(_objectSpread({}, options, { + entry: state.entry + })); }, createFileSelectionHandler: function createFileSelectionHandler(handlerName, encodedPayload) { if (!this.fileSelectionHandlers[handlerName]) { throw 'Unknown FileSelectionHandler ' + handlerName; } var payloadJson = JSON.parse(decodeURIComponent(encodedPayload)); - return new this.fileSelectionHandlers[handlerName](payloadJson); + return new this.fileSelectionHandlers[handlerName](_objectSpread({}, payloadJson, { + entry: state.entry + })); }, createPageConfigurationEditorView: function createPageConfigurationEditorView(page, options) { var view = this.pageTypes.findByPage(page).createConfigurationEditorView(_$1.extend(options, { model: page.configuration })); this.commonPageConfigurationTabs.apply(view); return view; } }); - /** - * The Pageflow editor. - * @module pageflow/editor - */ - var editor = new EditorApi(); var startEditor = function startEditor(options) { $(function () { $.when($.getJSON('/editor/entries/' + options.entryId + '/seed'), pageflow.browser.detectFeatures()).done(function (result) { app.start(result[0]); @@ -3698,10 +3922,83 @@ alert('Error while starting editor.'); }); }); }; + /** + * Mixins for Backbone models and collections that use entry type + * specific editor controllers registered via the `editor_app` entry + * type option. + */ + + var entryTypeEditorControllerUrls = { + /** + * Mixins for Backbone collections that defines `url` method. + * + * @param {Object} options + * @param {String} options.resources - Path suffix of the controller route + * + * @example + * + * import {editor, entryTypeEditorControllerUrls} from 'pageflow/editor'; + * + * editor.registerEntryType('test', { + // ... + }); + * + * export const ItemsCollection = Backbone.Collection.extend({ + * mixins: [entryTypeEditorControllerUrls.forCollection({resources: 'items'}) + * }); + * + * new ItemsCollection().url() // => '/editor/entries/10/test/items' + */ + forCollection: function forCollection(_ref) { + var resources = _ref.resources; + return { + url: function url() { + return entryTypeEditorControllerUrl(resources); + }, + urlSuffix: function urlSuffix() { + return "/".concat(resources); + } + }; + }, + + /** + * Mixins for Backbone models that defines `urlRoot` method. + * + * @param {Object} options + * @param {String} options.resources - Path suffix of the controller route + * + * @example + * + * import {editor, entryTypeEditorControllerUrls} from 'pageflow/editor'; + * + * editor.registerEntryType('test', { + // ... + }); + * + * export const Item = Backbone.Model.extend({ + * mixins: [entryTypeEditorControllerUrls.forModel({resources: 'items'}) + * }); + * + * new Item({id: 20}).url() // => '/editor/entries/10/test/items/20' + */ + forModel: function forModel(_ref2) { + var resources = _ref2.resources; + return { + urlRoot: function urlRoot() { + return this.isNew() ? this.collection.url() : entryTypeEditorControllerUrl(resources); + } + }; + } + }; + + function entryTypeEditorControllerUrl(resources) { + return [state.entry.url(), editor.entryType.name, resources].join('/'); + } + var formDataUtils = { fromModel: function fromModel(model) { var object = {}; object[model.modelName] = model.toJSON(); return this.fromObject(object); @@ -3805,11 +4102,11 @@ clear: function clear() { this.parent.remove(this.models); this.reset(); }, url: function url() { - return this.parentModel.url() + _$1.result(this.parent, 'url'); + return this.parentModel.url() + (_$1.result(this.parent, 'urlSuffix') || _$1.result(this.parent, 'url')); }, dispose: function dispose() { this.stopListening(); this.reset(); } @@ -3916,11 +4213,11 @@ }); var EditLock = Backbone.Model.extend({ paramRoot: 'edit_lock', url: function url() { - return state.entry.url() + '/edit_lock?timestamp=' + new Date().getTime(); + return '/entries/' + state.entry.get('id') + '/edit_lock?timestamp=' + new Date().getTime(); }, toJSON: function toJSON() { return { id: this.id, force: this.get('force') @@ -4114,14 +4411,26 @@ }); app.on('mixin:configuration', function (mixin) { Cocktail.mixin(Configuration, mixin); }); + /** + * Remove model from collection only after the `DELETE` request has + * succeeded. Still allow tracking that the model is being destroyed + * by triggering a `destroying` event and adding a `isDestroying` + * method. + */ + var delayedDestroying = { initialize: function initialize() { this._destroying = false; }, + + /** + * Trigger `destroying` event and send `DELETE` request. Only remove + * model from collection once the request is done. + */ destroyWithDelay: function destroyWithDelay() { var model = this; this._destroying = true; this.trigger('destroying', this); return Backbone.Model.prototype.destroy.call(this, { @@ -4132,15 +4441,23 @@ error: function error() { model._destroying = false; } }); }, + + /** + * Get whether the model is currently being destroyed. + */ isDestroying: function isDestroying() { return this._destroying; } }; + /** + * Mixin for Backbone models that shall be watched by {@link + * modelLifecycleTrackingView} mixin. + */ var failureTracking = { initialize: function initialize() { this._saveFailed = false; this.listenTo(this, 'sync', function () { this._saveFailed = false; @@ -4733,13 +5050,27 @@ return ''; } }); - var EntryConfiguration = Configuration.extend({ + var EntryMetadataConfiguration = Configuration.extend({ + modelName: 'entry_metadata_configuration', + i18nKey: 'pageflow/entry_metadata_configuration', + defaults: {} + }); + + var EntryMetadata = Configuration.extend({ modelName: 'entry', - i18nKey: 'pageflow/entry' + i18nKey: 'pageflow/entry', + defaults: {}, + initialize: function initialize(attributes, options) { + this.configuration = new EntryMetadataConfiguration(_$1.clone(attributes.configuration) || {}); + this.listenTo(this.configuration, 'change', function () { + this.trigger('change'); + this.parent.save(); + }); + } }); var StorylineConfiguration = Configuration.extend({ modelName: 'storyline', i18nKey: 'pageflow/storyline', @@ -4914,20 +5245,20 @@ isPositionable: function isPositionable() { return this.isReady(); } }); - var EntryConfigurationFileSelectionHandler = function EntryConfigurationFileSelectionHandler(options) { + var EntryMetadataFileSelectionHandler = function EntryMetadataFileSelectionHandler(options) { this.call = function (file) { - state.entry.configuration.setReference(options.attributeName, file); + state.entry.metadata.setReference(options.attributeName, file); }; this.getReferer = function () { return '/meta_data/' + (options.returnToTab || 'general'); }; }; - editor.registerFileSelectionHandler('entryConfiguration', EntryConfigurationFileSelectionHandler); + editor.registerFileSelectionHandler('entryMetadata', EntryMetadataFileSelectionHandler); var EntryPublication = Backbone.Model.extend({ paramRoot: 'entry_publication', quota: function quota() { return new Backbone.Model(this.get('quota') || {}); @@ -4970,11 +5301,24 @@ this.chapter.set(response.chapter); this.page.set(response.page); } }); - var EntryData = BaseObject.extend({ + // https://github.com/jashkenas/backbone/issues/2601 + + function BaseObject$1(options) { + this.initialize.apply(this, arguments); + } + + _$1.extend(BaseObject$1.prototype, Backbone.Events, { + initialize: function initialize(options) {} + }); // The self-propagating extend function that Backbone classes use. + + + BaseObject$1.extend = Backbone.Model.extend; + + var EntryData = BaseObject$1.extend({ getThemingOption: function getThemingOption(name) { throw 'Not implemented'; }, getFile: function getFile(collectionName, id) { throw 'Not implemented'; @@ -5567,10 +5911,80 @@ return '/page_links/' + pageLink.id; }; }; editor.registerFileSelectionHandler('pageLink', PageLinkFileSelectionHandler); + /** + * Mixins for models with a nested configuration model. + * + * Triggers events on the parent model of the form + * `change:configuration` and `change:configuration:<attribute>`, when + * the configuration changes. + * + * @param {Object} [options] + * @param {Function} [options.configurationModel] - + * Backbone model to use for nested configuration model. + * @param {Boolean} [options.autoSave] - + * Save model when configuration changes. + * @param {Boolean|Array<String>} [options.includeAttributesInJSON] - + * Include all or specific attributes of the parent model in the + * data returned by `toJSON` besides the `configuration` property. + * @returns {Object} - Mixin to be included in model. + * + * @example + * + * import {configurationContainer} from 'pageflow/editor'; + * + * const Section = Backbone.Model.extend({ + * mixins: [configurationContainer({autoSave: true})] + * }); + * + * const section = new Section({configuration: {some: 'value'}}); + * section.configuration.get('some') // => 'value'; + */ + + function configurationContainer() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + configurationModel = _ref.configurationModel, + autoSave = _ref.autoSave, + includeAttributesInJSON = _ref.includeAttributesInJSON; + + configurationModel = configurationModel || Configuration.extend({ + defaults: {} + }); + return { + initialize: function initialize() { + this.configuration = new configurationModel(this.get('configuration')); + this.configuration.parent = this; + this.listenTo(this.configuration, 'change', function () { + if (!this.isNew() && autoSave) { + this.save(); + } + + this.trigger('change:configuration', this); + + _$1.chain(this.configuration.changed).keys().each(function (name) { + this.trigger('change:configuration:' + name, this, this.configuration.get(name)); + }, this); + }); + }, + toJSON: function toJSON() { + var attributes = {}; + + if (includeAttributesInJSON === true) { + attributes = _$1.clone(this.attributes); + } else if (includeAttributesInJSON) { + attributes = _$1.pick(this.attributes, includeAttributesInJSON); + } + + return _$1.extend(attributes, { + configuration: this.configuration.toJSON() + }); + } + }; + } + var persistedPromise = { persisted: function persisted() { var model = this; this._persistedDeferred = this._persistedDeferred || $.Deferred(function (deferred) { if (model.isNew()) { @@ -5643,19 +6057,19 @@ } }; var Entry = Backbone.Model.extend({ paramRoot: 'entry', - urlRoot: '/entries', + urlRoot: '/editor/entries', modelName: 'entry', i18nKey: 'pageflow/entry', collectionName: 'entries', mixins: [filesCountWatcher, polling, failureTracking], initialize: function initialize(attributes, options) { options = options || {}; - this.configuration = new EntryConfiguration(this.get('configuration') || {}); - this.configuration.parent = this; + this.metadata = new EntryMetadata(this.get('metadata') || {}); + this.metadata.parent = this; this.themes = options.themes || state.themes; this.files = options.files || state.files; this.fileTypes = options.fileTypes || editor.fileTypes; this.storylines = options.storylines || state.storylines; this.storylines.parentModel = this; @@ -5673,25 +6087,28 @@ this.pages.sort(); }); this.listenTo(this.chapters, 'sort', function () { this.pages.sort(); }); - this.listenTo(this.configuration, 'change', function () { - this.trigger('change:configuration'); + this.listenTo(this.metadata, 'change', function () { + this.trigger('change:metadata'); this.save(); }); - this.listenTo(this.configuration, 'change:locale', function () { + this.listenTo(this.metadata, 'change:locale', function () { this.once('sync', function () { // No other way of updating page templates used in // EntryPreviewView at the moment. location.reload(); }); }); }, getTheme: function getTheme() { - return this.themes.findByName(this.configuration.get('theme_name')); + return this.themes.findByName(this.metadata.get('theme_name')); }, + supportsPhoneEmulation: function supportsPhoneEmulation() { + return pageflow.features.isEnabled('editor_emulation_mode'); + }, addStoryline: function addStoryline(attributes) { var storyline = this.buildStoryline(attributes); storyline.save(); return storyline; }, @@ -5769,11 +6186,14 @@ }, options)); delete response[fileType.collectionName]; }, this); }, toJSON: function toJSON() { - return this.configuration.toJSON(); + var metadataJSON = this.metadata.toJSON(); + var configJSON = this.metadata.configuration.toJSON(); + metadataJSON.configuration = configJSON; + return metadataJSON; } }); var AuthenticationProvider = BaseObject.extend({ authenticate: function authenticate(parent, provider) { @@ -5927,10 +6347,65 @@ comparator: function comparator(chapter) { return chapter.get('position'); } }); + /** + * A Backbone collection that is automatically updated to only + * contain models with a foreign key matching the id of a parent + * model. + * + * @param {Object} options + * @param {Backbone.Model} options.parentModel - + * Model whose id is compared to foreign keys. + * @param {Backbone.Collection} options.parent - + * Collection to filter items with matching foreign key from. + * @param {String} options.foreignKeyAttribute - + * Attribute to compare to id of parent model. + * @param {String} options.parentReferenceAttribute - + * Set reference to parent model on models in collection. + * + * @since 15.1 + */ + + var ForeignKeySubsetCollection = SubsetCollection.extend({ + mixins: [orderedCollection], + constructor: function constructor(options) { + var parent = options.parent; + var parentModel = options.parentModel; + SubsetCollection.prototype.constructor.call(this, { + parent: parent, + parentModel: parentModel, + filter: function filter(item) { + return !parentModel.isNew() && item.get(options.foreignKeyAttribute) === parentModel.id; + }, + comparator: function comparator(item) { + return item.get('position'); + } + }); + this.listenTo(this, 'add', function (model) { + if (options.parentReferenceAttribute) { + model[options.parentReferenceAttribute] = parentModel; + } + + model.set(options.foreignKeyAttribute, parentModel.id); + }); + this.listenTo(parentModel, 'destroy', function () { + this.clear(); + }); + + if (options.parentReferenceAttribute) { + this.each(function (model) { + return model[options.parentReferenceAttribute] = parentModel; + }); + this.listenTo(this, 'remove', function (model) { + model[options.parentReferenceAttribute] = null; + }); + } + } + }); + var PageLinksCollection = Backbone.Collection.extend({ model: PageLink, initialize: function initialize(models, options) { this.configuration = options.configuration; this.page = options.configuration.page; @@ -6146,54 +6621,10 @@ return model; } }; Cocktail.mixin(Backbone.Collection, addAndReturnModel); - // different model types. Backbone.Collection tries to merge records - // if they have the same id. - - var MultiCollection = function MultiCollection() { - this.records = {}; - this.length = 0; - }; - - _$1.extend(MultiCollection.prototype, { - add: function add(record) { - if (!this.records[record.cid]) { - this.records[record.cid] = record; - this.length = _$1.keys(this.records).length; - this.trigger('add', record); - } - }, - remove: function remove(record) { - if (this.records[record.cid]) { - delete this.records[record.cid]; - this.length = _$1.keys(this.records).length; - this.trigger('remove', record); - } - }, - isEmpty: function isEmpty() { - return this.length === 0; - } - }); - - _$1.extend(MultiCollection.prototype, Backbone.Events); - - MultiCollection.extend = Backbone.Collection.extend; - - var SavingRecordsCollection = MultiCollection.extend({ - watch: function watch(collection) { - var that = this; - this.listenTo(collection, 'request', function (model, xhr) { - that.add(model); - xhr.always(function () { - that.remove(model); - }); - }); - } - }); - var SidebarRouter = Marionette.AppRouter.extend({ appRoutes: { 'page_links/:id': 'pageLink', 'pages/:id': 'page', 'pages/:id/:tab': 'page', @@ -6233,10 +6664,11 @@ }, onRender: function onRender() { this.outlet.show(this.options.view); }, goBack: function goBack() { + this.options.view.onGoBack && this.options.view.onGoBack(); editor.navigate('/', { trigger: true }); } }); @@ -6370,28 +6802,91 @@ return new BackButtonDecoratorView({ view: new ConfirmEncodingView(options) }); }; - var failureIndicatingView = { - modelEvents: { - 'change:failed': 'updateFailIndicator' - }, - events: { - 'click .retry': function clickRetry() { + /** + * Mixin for Marionette Views that sets css class names according to + * life cycle events of its model. + * + * @param {Object} options + * @param {Object} options.classNames + * @param {String} options.classNames.creating - + * Class name to add to root element while model is still being created. + * @param {String} options.classNames.destroying - + * Class name to add to root element while model is being destroyed. + * @param {String} options.classNames.failed - + * Class name to add to root element while model is in failed state. + * Model needs to include {@link failureTracking} mixin. + * @param {String} options.classNames.failureMessage - + * Class name of the element that shall be updated with the failure + * message. Model needs to include {@link failureTracking} mixin. + * @param {String} options.classNames.retryButton - + * Class name of the element that shall act as a retry button. + */ + + function modelLifecycleTrackingView(_ref) { + var classNames = _ref.classNames; + return { + events: _defineProperty({}, "click .".concat(classNames.retryButton), function click() { editor.failures.retry(); return false; + }), + initialize: function initialize() { + var _this = this; + + if (classNames.creating) { + this.listenTo(this.model, 'change:id', function () { + this.$el.removeClass(classNames.creating); + }); + } + + if (classNames.destroying) { + this.listenTo(this.model, 'destroying', function () { + this.$el.addClass(classNames.destroying); + }); + this.listenTo(this.model, 'error', function () { + this.$el.removeClass(classNames.destroying); + }); + } + + if (classNames.failed || classNames.failureMessage) { + this.listenTo(this.model, 'change:failed', function () { + return _this.updateFailIndicator(); + }); + } + }, + render: function render() { + if (this.model.isNew()) { + this.$el.addClass(classNames.creating); + } + + if (this.model.isDestroying && this.model.isDestroying()) { + this.$el.addClass(classNames.destroying); + } + + this.updateFailIndicator(); + }, + updateFailIndicator: function updateFailIndicator() { + if (classNames.failed) { + this.$el.toggleClass(classNames.failed, this.model.isFailed()); + } + + if (classNames.failureMessage) { + this.$el.find(".".concat(classNames.failureMessage)).text(this.model.getFailureMessage()); + } } - }, - onRender: function onRender() { - this.updateFailIndicator(); - }, - updateFailIndicator: function updateFailIndicator() { - this.$el.toggleClass('failed', this.model.isFailed()); - this.$el.find('.failure .message').text(this.model.getFailureMessage()); + }; + } + + var failureIndicatingView = modelLifecycleTrackingView({ + classNames: { + failed: 'failed', + failureMessage: 'failure .message', + retryButton: 'retry' } - }; + }); function template$k(data) { var __t, __p = ''; __p += '<a class="back">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_chapter.outline') )) == null ? '' : __t) + @@ -6447,449 +6942,36 @@ }); } }); function template$l(data) { - var __p = ''; - __p += '<div class="pictogram"></div>\n'; - return __p - } - - var FileThumbnailView = Marionette.ItemView.extend({ - className: 'file_thumbnail', - template: template$l, - modelEvents: { - 'change:state': 'update' - }, - ui: { - pictogram: '.pictogram' - }, - onRender: function onRender() { - this.update(); - }, - update: function update() { - if (this.model) { - var stage = this.model.currentStage(); - - if (stage) { - this.setStageClassName(stage.get('name')); - this.ui.pictogram.toggleClass('action_required', stage.get('action_required')); - this.ui.pictogram.toggleClass('failed', stage.get('failed')); - } else { - this.ui.pictogram.removeClass(this.model.stages.pluck('name').join(' ')); - } - - this.ui.pictogram.addClass(this.model.thumbnailPictogram); - this.$el.css('background-image', this._imageUrl() ? 'url(' + this._imageUrl() + ')' : ''); - this.$el.removeClass('empty').toggleClass('always_picogram', !!this.model.thumbnailPictogram).toggleClass('ready', this.model.isReady()); - } else { - this.$el.css('background-image', ''); - this.$el.removeClass('ready'); - this.ui.pictogram.addClass('empty'); - } - }, - setStageClassName: function setStageClassName(name) { - if (!this.$el.hasClass(name)) { - this.ui.pictogram.removeClass('empty'); - this.ui.pictogram.removeClass(this.model.stages.pluck('name').join(' ')); - this.ui.pictogram.addClass(name); - } - }, - _imageUrl: function _imageUrl() { - return this.model.get(this.options.imageUrlPropertyName || 'thumbnail_url'); - } - }); - - function template$m(data) { - var __p = ''; - __p += '\n'; - return __p - } - - var StaticThumbnailView = Marionette.ItemView.extend({ - template: template$m, - className: 'static_thumbnail', - modelEvents: { - 'change:configuration': 'update' - }, - onRender: function onRender() { - this.update(); - }, - update: function update() { - this.$el.css('background-image', 'url(' + this._imageUrl() + ')'); - }, - _imageUrl: function _imageUrl() { - return this.model.thumbnailUrl(); - } - }); - - /** - * Base thumbnail view for models supporting a `thumbnailFile` method. - * - * @class - * @memberof module:pageflow/editor - */ - - var ModelThumbnailView = Marionette.View.extend({ - className: 'model_thumbnail', - modelEvents: { - 'change:configuration': 'update' - }, - render: function render() { - this.update(); - return this; - }, - update: function update() { - if (this.model) { - if (_$1.isFunction(this.model.thumbnailFile)) { - var file = this.model && this.model.thumbnailFile(); - - if (this.thumbnailView && this.currentFileThumbnail == file) { - return; - } - - this.currentFileThumbnail = file; - this.newThumbnailView = new FileThumbnailView({ - model: file, - className: 'thumbnail file_thumbnail', - imageUrlPropertyName: this.options.imageUrlPropertyName - }); - } else { - this.newThumbnailView = this.newThumbnailView || new StaticThumbnailView({ - model: this.model - }); - } - } - - if (this.thumbnailView) { - this.thumbnailView.close(); - } - - if (this.model) { - this.thumbnailView = this.subview(this.newThumbnailView); - this.$el.append(this.thumbnailView.el); - } - } - }); - - var PageThumbnailView = ModelThumbnailView.extend({ - className: 'model_thumbnail page_thumbnail' - }); - - function template$n(data) { var __t, __p = ''; - __p += '<a href="">\n <span class="type_pictogram"></span>\n <span class="page_thumbnail"></span>\n <span class="title"></span>\n <span class="failure_icon" title="' + - ((__t = ( I18n.t('pageflow.editor.templates.page_item.save_error') )) == null ? '' : __t) + - '" />\n</a>\n'; - return __p - } - - var PageItemView = Marionette.ItemView.extend({ - tagName: 'li', - template: template$n, - ui: { - title: '.title', - pictogram: '.type_pictogram', - pageThumbnail: '.page_thumbnail' - }, - modelEvents: { - 'change:title': 'update', - 'change:active': 'update' - }, - onRender: function onRender() { - this.subview(new PageThumbnailView({ - el: this.ui.pageThumbnail, - model: this.model - })); - this.update(); - }, - update: function update() { - this.$el.attr('data-id', this.model.id); - this.$el.attr('data-perma-id', this.model.get('perma_id')); - this.$el.toggleClass('active', this.model.get('active')); - this.$el.toggleClass('disabled', !!(this.options.isDisabled && this.options.isDisabled(this.model))); - this.$el.toggleClass('display_in_navigation', !!this.model.configuration.get('display_in_navigation')); - this.$el.removeClass(editor.pageTypes.pluck('name').join(' ')).addClass(this.model.get('template')); - this.ui.pictogram.attr('title', this._getPictogramTitle()); - this.ui.title.text(this.model.title() || I18n$1.t('pageflow.editor.views.page_item_view.unnamed')); - }, - _getPictogramTitle: function _getPictogramTitle() { - var result = I18n$1.t(this.model.pageType().translationKey()); - result += ' Seite'; - - if (this.options.displayInNavigationHint && !this.model.configuration.get('display_in_navigation')) { - result += ' (nicht in Navigationsleiste)'; - } - - return result; - } - }); - - var loadable = { - modelEvents: { - 'change:id': function changeId() { - this.$el.removeClass('creating'); - }, - destroying: function destroying() { - this.$el.addClass('destroying'); - }, - error: function error() { - this.$el.removeClass('destroying'); - } - }, - render: function render() { - if (this.model.isNew()) { - this.$el.addClass('creating'); - } - - if (this.model.isDestroying && this.model.isDestroying()) { - this.$el.addClass('destroying'); - } - } - }; - - var NavigatablePageItemView = PageItemView.extend({ - mixins: [loadable, failureIndicatingView], - className: 'draggable', - events: { - 'click': function click() { - if (!this.model.isNew() && !this.model.isDestroying()) { - editor.navigate('/pages/' + this.model.get('id'), { - trigger: true - }); - } - - return false; - } - } - }); - - function template$o(data) { - var __t, __p = ''; - __p += '<a class="edit_chapter" href="">\n <span class="pictogram"></span>\n <span class="number"></span>\n <span class="title"></span>\n <span class="failure_icon" title=' + - ((__t = ( I18n.t('pageflow.editor.templates.chapter_item.save_error') )) == null ? '' : __t) + - ' />\n</a>\n\n<ul class="pages outline"></ul>\n\n<a href="" class="add_page">' + - ((__t = ( I18n.t('pageflow.editor.templates.chapter_item.new_page') )) == null ? '' : __t) + - '</a>\n'; - return __p - } - - var ChapterItemView = Marionette.ItemView.extend({ - tagName: 'li', - template: template$o, - ui: { - title: '> a > .title', - number: '> a > .number', - pages: 'ul.pages' - }, - modelEvents: { - change: 'update' - }, - onRender: function onRender() { - var collectionView = this.options.sortable ? SortableCollectionView : CollectionView; - this.subview(new collectionView({ - el: this.ui.pages, - collection: this.model.pages, - itemViewConstructor: this.options.pageItemView || NavigatablePageItemView, - itemViewOptions: this.options.pageItemViewOptions, - connectWith: 'ul.pages' - })); - this.update(); - }, - update: function update() { - this.ui.title.text(this.model.get('title') || I18n$1.t('pageflow.editor.views.chapter_item_view.unnamed')); - this.ui.number.text(I18n$1.t('pageflow.editor.views.chapter_item_view.chapter') + ' ' + (this.model.get('position') + 1)); - } - }); - - var NavigatableChapterItemView = ChapterItemView.extend({ - mixins: [loadable, failureIndicatingView], - events: { - 'click a.add_page': function clickAAdd_page() { - this.model.addPage(); - }, - 'click a.edit_chapter': function clickAEdit_chapter() { - if (!this.model.isNew() && !this.model.isDestroying()) { - editor.navigate('/chapters/' + this.model.get('id'), { - trigger: true - }); - } - - return false; - } - } - }); - - function template$p(data) { - var __t, __p = ''; - __p += '<h2>' + - ((__t = ( I18n.t('pageflow.editor.templates.storyline_outline.header') )) == null ? '' : __t) + - '</h2>\n<ul class="storyline_outline_chapters chapters"></ul>\n\n<a class="add_chapter" href="">' + - ((__t = ( I18n.t('pageflow.editor.templates.storyline_outline.new_chapter') )) == null ? '' : __t) + - '</a>\n'; - return __p - } - - var StorylineOutlineView = Marionette.Layout.extend({ - template: template$p, - className: 'storyline_outline', - ui: { - chapters: 'ul.storyline_outline_chapters' - }, - events: { - 'click a.add_chapter': function clickAAdd_chapter() { - this.model.scaffoldChapter(); - } - }, - onRender: function onRender() { - this.ui.chapters.toggleClass('outline navigatable', !!this.options.navigatable); - var collectionView = this.options.sortable ? SortableCollectionView : CollectionView; - new collectionView({ - el: this.ui.chapters, - collection: this.model.chapters, - itemViewConstructor: this.options.navigatable ? NavigatableChapterItemView : ChapterItemView, - itemViewOptions: { - sortable: this.options.sortable, - pageItemView: this.options.navigatable ? NavigatablePageItemView : PageItemView, - pageItemViewOptions: _$1.extend({ - displayInNavigationHint: this.options.displayInNavigationHint - }, this.options.pageItemViewOptions || {}) - } - }).render(); - } - }); - - function template$q(data) { - var __t, __p = ''; - __p += '<div class="storyline_picker_storylines">\n <div class="storyline_picker_select_region"></div>\n <a href="" class="add_storyline" title="' + - ((__t = ( I18n.t('pageflow.editor.templates.storyline_picker.add') )) == null ? '' : __t) + - '"></a>\n <a href="" class="edit_storyline" title="' + - ((__t = ( I18n.t('pageflow.editor.templates.storyline_picker.edit') )) == null ? '' : __t) + - '"></a>\n</div>\n\n<div class="storyline_picker_main_region"></div>\n'; - return __p - } - - var StorylinePickerView = Marionette.Layout.extend({ - template: template$q, - className: 'storyline_picker', - regions: { - selectRegion: '.storyline_picker_select_region', - mainRegion: '.storyline_picker_main_region' - }, - ui: { - storylines: '.storyline_picker_storylines' - }, - events: { - 'click .add_storyline': function clickAdd_storyline() { - var storyline = state.entry.scaffoldStoryline({ - depth: 'page' - }).storyline; - this.listenToOnce(storyline, 'sync', function () { - this.updateSelect(); - this.model.set('storyline_id', storyline.id); - }); - return false; - }, - 'click .edit_storyline': function clickEdit_storyline() { - editor.navigate('storylines/' + this.model.get('storyline_id'), { - trigger: true - }); - return false; - } - }, - initialize: function initialize() { - this.model = new Backbone.Model({ - storyline_id: this.defaultStorylineId() - }); - this.listenTo(state.storylines, 'add sort remove', this.updateSelect); - this.listenTo(this.model, 'change', this.load); - }, - onRender: function onRender() { - this.$el.toggleClass('editable', !!this.options.editable); - this.ui.storylines.toggle(!!pageflow.features.isEnabled('storylines')); - this.updateSelect(); - this.load(); - }, - updateSelect: function updateSelect() { - this.selectRegion.show(new SelectInputView({ - model: this.model, - label: I18n$1.t('pageflow.editor.views.storylines_picker_view.label'), - propertyName: 'storyline_id', - values: state.storylines.pluck('id'), - texts: state.storylines.map(function (storyline) { - return this.indentation(storyline) + storyline.displayTitle(); - }, this), - groups: state.storylines.reduce(function (result, storyline) { - if (storyline.isMain() || storyline.parentPage()) { - result.push(_$1.last(result)); - } else { - result.push(I18n$1.t('pageflow.editor.views.storylines_picker_view.without_parent_page')); - } - - return result; - }, []) - })); - }, - load: function load() { - var storyline = state.storylines.get(this.model.get('storyline_id')); - this.saveRememberedStorylineId(storyline.id); - this.mainRegion.show(new StorylineOutlineView({ - model: storyline, - navigatable: this.options.navigatable, - sortable: this.options.editable, - chapterItemView: this.options.chapterItemView, - pageItemView: this.options.pageItemView, - pageItemViewOptions: this.options.pageItemViewOptions, - displayInNavigationHint: this.options.displayInNavigationHint - })); - }, - defaultStorylineId: function defaultStorylineId() { - var storyline = state.storylines.get(this.options.storylineId) || state.storylines.get(this.rememberedStorylineId()) || state.storylines.first(); - return storyline.id; - }, - rememberedStorylineId: function rememberedStorylineId() { - if (this.options.rememberLastSelection) { - return StorylinePickerView._rememberedStorylineId; - } - }, - saveRememberedStorylineId: function saveRememberedStorylineId(id) { - if (this.options.rememberLastSelection) { - StorylinePickerView._rememberedStorylineId = id; - } - }, - indentation: function indentation(storyline) { - return _$1(storyline.get('level') || 0).times(function () { - return "\xA0\xA0\xA0"; - }).join(''); - } - }); - - function template$r(data) { - var __t, __p = ''; __p += '<a class="close" href="#">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_entry.close') )) == null ? '' : __t) + '</a>\n<a class="publish" href="#" data-tooltip-align="bottom right">\n ' + ((__t = ( I18n.t('pageflow.editor.templates.edit_entry.publish') )) == null ? '' : __t) + '\n</a>\n\n<ul class="menu">\n <li>\n <a class="edit_entry_meta_data" href="#" data-path="/meta_data">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_entry.metadata') )) == null ? '' : __t) + '</a>\n <span class="failure_icon" title="' + ((__t = ( I18n.t('pageflow.editor.templates.edit_entry.save_error') )) == null ? '' : __t) + '" />\n </li>\n <li>\n <a class="manage_files" href="#" data-path="/files">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_entry.manage_files') )) == null ? '' : __t) + - '</a>\n </li>\n</ul>\n\n<div class="edit_entry_storylines storyline_picker"></div>\n'; + '</a>\n </li>\n</ul>\n\n<div class="edit_entry_outline_region"></div>\n'; return __p } - var EditEntryView = Marionette.ItemView.extend({ - template: template$r, + var EditEntryView = Marionette.Layout.extend({ + template: template$l, mixins: [failureIndicatingView, tooltipContainer], ui: { publishButton: 'a.publish', publicationStateButton: 'a.publication_state', - menu: '.menu', - storylines: '.edit_entry_storylines' + menu: '.menu' }, + regions: { + outlineRegion: '.edit_entry_outline_region' + }, events: { 'click a.close': function clickAClose() { $.when(state.editLock.release()).then(function () { window.location = '/admin/entries/' + state.entry.id; }); @@ -6913,21 +6995,21 @@ onRender: function onRender() { this._addMenuItems(); this._updatePublishButton(); - this.subview(new StorylinePickerView({ - el: this.ui.storylines, + this.outlineRegion.show(new editor.entryType.outlineView({ + entry: state.entry, navigatable: true, editable: true, displayInNavigationHint: true, rememberLastSelection: true, storylineId: this.options.storylineId })); }, _updatePublishButton: function _updatePublishButton() { - var disabled = !state.entry.get('publishable'); + var disabled = !this.model.get('publishable'); this.ui.publishButton.toggleClass('disabled', disabled); if (disabled) { this.ui.publishButton.attr('data-tooltip', 'pageflow.editor.views.edit_entry_view.cannot_publish'); } else { @@ -6954,20 +7036,20 @@ view.ui.menu.append(item); }); } }); - function template$s(data) { + function template$m(data) { var __t, __p = ''; __p += '<div class="widget_type">\n</div>\n<a class="settings" title="' + ((__t = ( I18n.t('pageflow.editor.templates.widget_item.settings') )) == null ? '' : __t) + '"></a>\n'; return __p } var WidgetItemView = Marionette.Layout.extend({ - template: template$s, + template: template$m, tagName: 'li', className: 'widget_item', regions: { widgetTypeContainer: '.widget_type' }, @@ -7003,18 +7085,18 @@ update: function update() { this.$el.toggleClass('has_settings', this.model.hasConfiguration()); } }); - function template$t(data) { + function template$n(data) { var __p = ''; __p += '<ol class="widgets">\n</ol>\n'; return __p } var EditWidgetsView = Marionette.Layout.extend({ - template: template$t, + template: template$n, ui: { widgets: '.widgets' }, onRender: function onRender() { this.subview(new CollectionView({ @@ -7026,18 +7108,18 @@ } }).render()); } }); - function template$u(data) { + function template$o(data) { var __p = ''; __p += '<div class="image"></div>\n<div class="label"></div>\n'; return __p } var BackgroundPositioningPreviewView = Marionette.ItemView.extend({ - template: template$u, + template: template$o, className: 'preview', modelEvents: { change: 'update' }, ui: { @@ -7064,18 +7146,18 @@ var file = this.model.getReference(this.options.propertyName, this.options.filesCollection); return file ? 'url("' + file.getBackgroundPositioningImageUrl() + '")' : 'none'; } }); - function template$v(data) { + function template$p(data) { var __p = ''; __p += '<div class="container">\n <div class="slider horizontal">\n </div>\n <div class="slider vertical">\n </div>\n <div class="percent horizontal">\n <input type="number" min="0" max="100">\n %\n </div>\n <div class="percent vertical">\n <input type="number" min="0" max="100">\n %\n </div>\n</div>\n'; return __p } var BackgroundPositioningSlidersView = Marionette.ItemView.extend({ - template: template$v, + template: template$p, className: '', ui: { container: '.container', sliderHorizontal: '.horizontal.slider', sliderVertical: '.vertical.slider', @@ -7152,11 +7234,11 @@ save: function save(coord, value) { this.model.setFilePosition(this.options.propertyName, coord, Math.min(100, Math.max(0, value))); } }); - function template$w(data) { + function template$q(data) { var __t, __p = ''; __p += '<div class="box">\n <h2>' + ((__t = ( I18n.t('pageflow.editor.templates.background_positioning.title') )) == null ? '' : __t) + '</h2>\n <p>' + ((__t = ( I18n.t('pageflow.editor.templates.background_positioning.help') )) == null ? '' : __t) + @@ -7169,11 +7251,11 @@ '</a>\n </div>\n</div>\n'; return __p } var BackgroundPositioningView = Marionette.ItemView.extend({ - template: template$w, + template: template$q, className: 'background_positioning dialog', mixins: [dialogView], ui: { previews: '.previews > div', wrapper: '.wrapper' @@ -7223,20 +7305,18 @@ BackgroundPositioningView.open = function (options) { app.dialogRegion.show(new BackgroundPositioningView(options)); }; - function template$x(data) { + function template$r(data) { var __p = ''; __p += '<div class="label"></div>\n<a href="#"></a>\n'; return __p } - /** @api private */ - var DropDownButtonItemView = Marionette.ItemView.extend({ - template: template$x, + template: template$r, tagName: 'li', className: 'drop_down_button_item', ui: { link: '> a', label: '> .label' @@ -7270,12 +7350,10 @@ this.$el.toggleClass('is_checked', !!this.model.get('checked')); this.$el.data('name', this.model.get('name')); } }); - /** @api private */ - var DropDownButtonItemListView = function DropDownButtonItemListView(options) { return new CollectionView({ tagName: 'ul', className: 'drop_down_button_items', collection: options.items, @@ -7284,19 +7362,21 @@ listView: DropDownButtonItemListView } }); }; - function template$y(data) { + function template$s(data) { var __p = ''; __p += '<button></button>\n\n<div class="drop_down_button_menu">\n</div>\n'; return __p } /** * A button that displays a drop down menu on hover. * + * @param {Object} options + * * @param {String} options.label * Button text. * * @param {Backbone.Collection} options.items * Collection of menu items. See below for supported attributes. @@ -7314,15 +7394,14 @@ * * If the menu item model provdised a `selected` method, it is called * when the menu item is clicked. * * @class - * @memberof module:pageflow/editor */ var DropDownButtonView = Marionette.ItemView.extend({ - template: template$y, + template: template$s, className: 'drop_down_button', ui: { button: '> button', menu: '.drop_down_button_menu' }, @@ -7385,11 +7464,62 @@ scheduleHideMenu: function scheduleHideMenu() { this.hideMenuTimeout = setTimeout(_$1.bind(this.hideMenu, this), 300); } }); - function template$z(data) { + function template$t(data) { + var __p = ''; + __p += '<div class="pictogram"></div>\n'; + return __p + } + + var FileThumbnailView = Marionette.ItemView.extend({ + className: 'file_thumbnail', + template: template$t, + modelEvents: { + 'change:state': 'update' + }, + ui: { + pictogram: '.pictogram' + }, + onRender: function onRender() { + this.update(); + }, + update: function update() { + if (this.model) { + var stage = this.model.currentStage(); + + if (stage) { + this.setStageClassName(stage.get('name')); + this.ui.pictogram.toggleClass('action_required', stage.get('action_required')); + this.ui.pictogram.toggleClass('failed', stage.get('failed')); + } else { + this.ui.pictogram.removeClass(this.model.stages.pluck('name').join(' ')); + } + + this.ui.pictogram.addClass(this.model.thumbnailPictogram); + this.$el.css('background-image', this._imageUrl() ? 'url(' + this._imageUrl() + ')' : ''); + this.$el.removeClass('empty').toggleClass('always_picogram', !!this.model.thumbnailPictogram).toggleClass('ready', this.model.isReady()); + } else { + this.$el.css('background-image', ''); + this.$el.removeClass('ready'); + this.ui.pictogram.addClass('empty'); + } + }, + setStageClassName: function setStageClassName(name) { + if (!this.$el.hasClass(name)) { + this.ui.pictogram.removeClass('empty'); + this.ui.pictogram.removeClass(this.model.stages.pluck('name').join(' ')); + this.ui.pictogram.addClass(name); + } + }, + _imageUrl: function _imageUrl() { + return this.model.get(this.options.imageUrlPropertyName || 'thumbnail_url'); + } + }); + + function template$u(data) { var __t, __p = ''; __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="file_thumbnail"></div>\n<div class="file_name"></div>\n\n<a href="" class="unset" title="' + ((__t = ( I18n.t('pageflow.ui.templates.inputs.file_input.reset') )) == null ? '' : __t) + '"></a>\n<a href="" class="choose" title="' + ((__t = ( I18n.t('pageflow.ui.templates.inputs.file_input.edit') )) == null ? '' : __t) + @@ -7399,16 +7529,15 @@ /** * Input view to reference a file. * * @class - * @memberof module:pageflow/editor */ var FileInputView = Marionette.ItemView.extend({ mixins: [inputView], - template: template$z, + template: template$u, className: 'file_input', ui: { fileName: '.file_name', thumbnail: '.file_thumbnail' }, @@ -7576,18 +7705,18 @@ getDefaultTextTrackFile: function getDefaultTextTrackFile() { return this.options.inputModel.getReference(this.options.propertyName, this.options.textTrackFiles); } }); - function template$A(data) { + function template$v(data) { var __p = ''; __p += '<div class="spinner">\n <div class="rect1"></div>\n <div class="rect2"></div>\n <div class="rect3"></div>\n <div class="rect4"></div>\n <div class="rect5"></div>\n</div>\n'; return __p } var LoadingView = Marionette.ItemView.extend({ - template: template$A, + template: template$v, className: 'loading', tagName: 'li' }); var selectableView = { @@ -7606,23 +7735,21 @@ this.options.selection.set(this.selectionAttribute, null); } } }; - function template$B(data) { + function template$w(data) { var __t, __p = ''; __p += '<span class="theme_name"></span>\n<span class="button_or_checkmark">\n <p class="theme_in_use"></p>\n <a class="use_theme">' + ((__t = ( I18n.t('pageflow.editor.templates.theme.use') )) == null ? '' : __t) + '</a>\n</span>\n'; return __p } - /** @api private */ - var ThemeItemView = Marionette.ItemView.extend({ tagName: 'li', - template: template$B, + template: template$w, className: 'theme_item', mixins: [selectableView], selectionAttribute: 'theme', ui: { themeName: '.theme_name', @@ -7649,11 +7776,11 @@ inUse: function inUse() { return this.model.get('name') === this.options.themeInUse; } }); - function template$C(data) { + function template$x(data) { var __t, __p = ''; __p += '<div class="box">\n <div class="content">\n <div>\n <h2 class="themes_header">' + ((__t = ( I18n.t('pageflow.editor.templates.change_theme_dialog.header') )) == null ? '' : __t) + '</h2>\n <div class="themes_panel">\n </div>\n <div class="preview_panel">\n <h2 class="preview_header">' + ((__t = ( I18n.t('pageflow.editor.templates.change_theme_dialog.preview_header_prefix') )) == null ? '' : __t) + @@ -7664,11 +7791,11 @@ '\n </a>\n </div>\n</div>\n'; return __p } var ChangeThemeDialogView = Marionette.ItemView.extend({ - template: template$C, + template: template$x, className: 'change_theme dialog editor', mixins: [dialogView], ui: { content: '.content', themesPanel: '.themes_panel', @@ -7733,28 +7860,98 @@ }); app.dialogRegion.show(view.render()); }).promise(); }; - function template$D(data) { + function template$y(data) { var __p = ''; + __p += '\n'; + return __p + } + + var StaticThumbnailView = Marionette.ItemView.extend({ + template: template$y, + className: 'static_thumbnail', + modelEvents: { + 'change:configuration': 'update' + }, + onRender: function onRender() { + this.update(); + }, + update: function update() { + this.$el.css('background-image', 'url(' + this._imageUrl() + ')'); + }, + _imageUrl: function _imageUrl() { + return this.model.thumbnailUrl(); + } + }); + + /** + * Base thumbnail view for models supporting a `thumbnailFile` method. + * + * @class + */ + + var ModelThumbnailView = Marionette.View.extend({ + className: 'model_thumbnail', + modelEvents: { + 'change:configuration': 'update' + }, + render: function render() { + this.update(); + return this; + }, + update: function update() { + if (this.model) { + if (_$1.isFunction(this.model.thumbnailFile)) { + var file = this.model && this.model.thumbnailFile(); + + if (this.thumbnailView && this.currentFileThumbnail == file) { + return; + } + + this.currentFileThumbnail = file; + this.newThumbnailView = new FileThumbnailView({ + model: file, + className: 'thumbnail file_thumbnail', + imageUrlPropertyName: this.options.imageUrlPropertyName + }); + } else { + this.newThumbnailView = this.newThumbnailView || new StaticThumbnailView({ + model: this.model + }); + } + } + + if (this.thumbnailView) { + this.thumbnailView.close(); + } + + if (this.model) { + this.thumbnailView = this.subview(this.newThumbnailView); + this.$el.append(this.thumbnailView.el); + } + } + }); + + function template$z(data) { + var __p = ''; __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="title"></div>\n<button class="unset"></button>\n<button class="choose"></button>\n'; return __p } /** * Base class for input views that reference models. * * @class - * @memberof module:pageflow/editor */ var ReferenceInputView = Marionette.ItemView.extend( - /** @lends module:pageflow/editor.ReferenceInputView# */ + /** @lends ReferenceInputView.prototype */ { mixins: [inputView], - template: template$D, + template: template$z, className: 'reference_input', ui: { title: '.title', chooseButton: '.choose', unsetButton: '.unset', @@ -7849,11 +8046,11 @@ getTarget: function getTarget(themeName) { return this.options.themes.findByName(themeName); } }); - function template$E(data) { + function template$A(data) { var __t, __p = ''; __p += '<a class="back">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_meta_data.outline') )) == null ? '' : __t) + '</a>\n\n<div class="failure">\n <p>' + ((__t = ( I18n.t('pageflow.editor.templates.edit_meta_data.save_error') )) == null ? '' : __t) + @@ -7862,11 +8059,11 @@ '</a>\n</div>\n\n<div class="form_fields"></div>\n'; return __p } var EditMetaDataView = Marionette.Layout.extend({ - template: template$E, + template: template$A, className: 'edit_meta_data', mixins: [failureIndicatingView], regions: { formContainer: '.form_fields' }, @@ -7874,87 +8071,79 @@ 'click a.back': 'goBack' }, onRender: function onRender() { var entry = this.model; var configurationEditor = new ConfigurationEditorView({ - model: entry.configuration, + model: entry.metadata.configuration, tab: this.options.tab }); configurationEditor.tab('general', function () { this.input('title', TextInputView, { - placeholder: entry.get('entry_title') + placeholder: entry.get('entry_title'), + model: entry.metadata }); this.input('locale', SelectInputView, { values: state.config.availablePublicLocales, texts: _$1.map(state.config.availablePublicLocales, function (locale) { return I18n$1.t('pageflow.public._language', { locale: locale }); - }) + }), + model: entry.metadata }); - this.input('credits', TextAreaInputView); + this.input('credits', TextAreaInputView, { + model: entry.metadata + }); this.input('author', TextInputView, { - placeholder: state.config.defaultAuthorMetaTag + placeholder: state.config.defaultAuthorMetaTag, + model: entry.metadata }); this.input('publisher', TextInputView, { - placeholder: state.config.defaultPublisherMetaTag + placeholder: state.config.defaultPublisherMetaTag, + model: entry.metadata }); this.input('keywords', TextInputView, { - placeholder: state.config.defaultKeywordsMetaTag + placeholder: state.config.defaultKeywordsMetaTag, + model: entry.metadata }); }); configurationEditor.tab('widgets', function () { - var theme = entry.getTheme(); - this.input('manual_start', CheckBoxInputView); - this.input('emphasize_chapter_beginning', CheckBoxInputView); - this.input('emphasize_new_pages', CheckBoxInputView); - this.input('home_button_enabled', CheckBoxInputView, { - disabled: !theme.hasHomeButton(), - displayUncheckedIfDisabled: true - }); - this.input('overview_button_enabled', CheckBoxInputView, { - disabled: !theme.hasOverviewButton(), - displayUncheckedIfDisabled: true - }); - - if (theme.hasHomeButton()) { - this.input('home_url', TextInputView, { - placeholder: state.theming.get('pretty_url'), - visibleBinding: 'home_button_enabled' - }); - } - - this.view(EditWidgetsView, { + editor.entryType.appearanceInputs && editor.entryType.appearanceInputs(this, entry, state.theming); + entry.widgets && this.view(EditWidgetsView, { model: entry, widgetTypes: editor.widgetTypes }); - if (pageflow.features.isEnabled('selectable_themes') && state.themes.length > 1) { + if (pageflow.features && pageflow.features.isEnabled('selectable_themes') && state.themes.length > 1) { this.view(ThemeInputView, { themes: state.themes, propertyName: 'theme_name' }); } }); configurationEditor.tab('social', function () { this.input('share_image_id', FileInputView, { collection: state.imageFiles, - fileSelectionHandler: 'entryConfiguration' + fileSelectionHandler: 'entryMetadata', + model: entry.metadata }); this.input('summary', TextAreaInputView, { disableRichtext: true, - disableLinks: true + disableLinks: true, + model: entry.metadata }); this.input('share_url', TextInputView, { - placeholder: state.entry.get('pretty_url') + placeholder: state.entry.get('pretty_url'), + model: entry.metadata }); this.input('share_providers', CheckBoxGroupInputView, { values: state.config.availableShareProviders, - translationKeyPrefix: 'activerecord.values.pageflow/entry.share_providers' + translationKeyPrefix: 'activerecord.values.pageflow/entry.share_providers', + model: entry.metadata }); }); - this.listenTo(entry.configuration, 'change:theme_name', function () { + this.listenTo(entry.metadata, 'change:theme_name', function () { configurationEditor.refresh(); }); this.formContainer.show(configurationEditor); }, goBack: function goBack() { @@ -7962,22 +8151,22 @@ trigger: true }); } }); - function template$F(data) { + function template$B(data) { var __t, __p = ''; __p += '<a class="back">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_page_link.back') )) == null ? '' : __t) + '</a>\n<a class="destroy">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_page_link.destroy') )) == null ? '' : __t) + '</a>\n<div class="form_container"></div>\n'; return __p } var EditPageLinkView = Marionette.Layout.extend({ - template: template$F, + template: template$B, regions: { formContainer: '.form_container' }, ui: { backButton: 'a.back' @@ -8012,11 +8201,11 @@ trigger: true }); } }); - function template$G(data) { + function template$C(data) { var __t, __p = ''; __p += '<a class="back">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_page.outline') )) == null ? '' : __t) + '</a>\n<a class="destroy">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_page.destroy') )) == null ? '' : __t) + @@ -8027,11 +8216,11 @@ '</a>\n</div>\n\n<div class="page_type"></div>\n\n<div class="configuration_container"></div>'; return __p } var EditPageView = Marionette.Layout.extend({ - template: template$G, + template: template$C, className: 'edit_page', mixins: [failureIndicatingView], regions: { pageTypeContainer: '.page_type', configurationContainer: '.configuration_container' @@ -8081,17 +8270,10 @@ trigger: true }); } }); - /** - * Input view to reference a page. - * - * @class - * @memberof module:pageflow/editor - */ - var PageLinkInputView = ReferenceInputView.extend({ choose: function choose() { return editor.selectPage({ isAllowed: this.options.isAllowed }); @@ -8099,11 +8281,11 @@ getTarget: function getTarget(permaId) { return state.pages.getByPermaId(permaId); } }); - function template$H(data) { + function template$D(data) { var __t, __p = ''; __p += '<a class="back">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_storyline.outline') )) == null ? '' : __t) + '</a>\n<a class="destroy" data-tooltip-align="bottom right">\n ' + ((__t = ( I18n.t('pageflow.editor.templates.edit_storyline.destroy') )) == null ? '' : __t) + @@ -8114,11 +8296,11 @@ '</a>\n</div>\n\n<div class="form_container"></div>\n'; return __p } var EditStorylineView = Marionette.Layout.extend({ - template: template$H, + template: template$D, className: 'edit_storyline', mixins: [failureIndicatingView, tooltipContainer], regions: { formContainer: '.form_container' }, @@ -8197,20 +8379,20 @@ trigger: true }); } }); - function template$I(data) { + function template$E(data) { var __t, __p = ''; __p += '<a class="back">' + ((__t = ( I18n.t('pageflow.editor.templates.edit_widget.back') )) == null ? '' : __t) + '</a>\n'; return __p } var EditWidgetView = Marionette.ItemView.extend({ - template: template$I, + template: template$E, className: 'edit_widget', events: { 'click a.back': function clickABack() { editor.navigate('/meta_data/widgets', { trigger: true @@ -8231,19 +8413,26 @@ }); this.appendSubview(configurationEditor); } }); - function template$J(data) { + var loadable = modelLifecycleTrackingView({ + classNames: { + creating: 'creating', + destroying: 'destroying' + } + }); + + function template$F(data) { var __p = ''; __p += '<span class="file_thumbnail"></span>\n\n<span class="file_name"></span>\n'; return __p } var ExplorerFileItemView = Marionette.ItemView.extend({ tagName: 'li', - template: template$J, + template: template$F, mixins: [loadable, selectableView], selectionAttribute: 'file', ui: { fileName: '.file_name', thumbnail: '.file_thumbnail' @@ -8276,18 +8465,18 @@ isDisabled: function isDisabled() { return this.options.disabledIds && _$1.contains(this.options.disabledIds, this.model.get('id')); } }); - function template$K(data) { + function template$G(data) { var __p = ''; __p += '<a href="">\n <span class="title"></span>\n</a>\n'; return __p } var OtherEntryItemView = Marionette.ItemView.extend({ - template: template$K, + template: template$G, className: 'other_entry_item', tagName: 'li', mixins: [selectableView], ui: { title: '.title' @@ -8298,11 +8487,11 @@ onRender: function onRender() { this.ui.title.text(this.model.titleOrSlug()); } }); - function template$L(data) { + function template$H(data) { var __t, __p = ''; __p += ((__t = ( I18n.t('pageflow.editor.templates.other_entries_blank_slate.none_available') )) == null ? '' : __t) + '\n'; return __p @@ -8324,22 +8513,22 @@ itemViewConstructor: OtherEntryItemView, itemViewOptions: { selection: this.options.selection }, blankSlateViewConstructor: Marionette.ItemView.extend({ - template: template$L, + template: template$H, tagName: 'li', className: 'blank_slate' }), loadingViewConstructor: LoadingView })); this.otherEntries.fetch(); return this; } }); - function template$M(data) { + function template$I(data) { var __t, __p = ''; __p += '<div class="box">\n <h2>' + ((__t = ( I18n.t('pageflow.editor.templates.files_explorer.reuse_files') )) == null ? '' : __t) + '</h2>\n\n <div class="panels">\n <ul class="entries_panel">\n </ul>\n\n <div class="files_panel">\n </div>\n </div>\n\n <div class="footer">\n <button class="ok">' + ((__t = ( I18n.t('pageflow.editor.templates.files_explorer.ok') )) == null ? '' : __t) + @@ -8364,11 +8553,11 @@ '<li>\n'; return __p } var FilesExplorerView = Marionette.ItemView.extend({ - template: template$M, + template: template$I, className: 'files_explorer editor dialog', mixins: [dialogView], ui: { entriesPanel: '.entries_panel', filesPanel: '.files_panel', @@ -8451,19 +8640,19 @@ FilesExplorerView.open = function (options) { app.dialogRegion.show(new FilesExplorerView(options)); }; - function template$N(data) { + function template$J(data) { var __p = ''; __p += '<th></th>\n<td></td>'; return __p } var FileMetaDataItemView = Marionette.ItemView.extend({ tagName: 'tr', - template: template$N, + template: template$J, ui: { label: 'th', value: 'td' }, onRender: function onRender() { @@ -8473,28 +8662,28 @@ name: this.options.name }, this.options.valueViewOptions || {}))); this.ui.label.text(this.labelText()); }, labelText: function labelText() { - return attributeTranslation(this.options.name, 'label', { + return i18nUtils.attributeTranslation(this.options.name, 'label', { prefixes: ['pageflow.editor.files.attributes.' + this.model.fileType().collectionName, 'pageflow.editor.files.common_attributes'], fallbackPrefix: 'activerecord.attributes', fallbackModelI18nKey: this.model.i18nKey }); } }); - function template$O(data) { + function template$K(data) { var __p = ''; __p += '<p class="percent"></p>\n<p class="description"></p>\n<p class="error_message"></p>'; return __p } var FileStageItemView = Marionette.ItemView.extend({ tagName: 'li', className: 'file_stage_item', - template: template$O, + template: template$K, ui: { description: '.description', percent: '.percent', errorMessage: '.error_message' }, @@ -8531,11 +8720,11 @@ defaultValue: this.model.get('error_message') }); } }); - function template$P(data) { + function template$L(data) { var __t, __p = ''; __p += '<span class="file_thumbnail"></span>\n\n<span class="file_name"></span>\n<a class="select">' + ((__t = ( I18n.t('pageflow.editor.templates.file_item.select') )) == null ? '' : __t) + '</a>\n\n<div class="actions">\n <a class="settings" title="' + ((__t = ( I18n.t('pageflow.editor.templates.file_item.settings') )) == null ? '' : __t) + @@ -8555,11 +8744,11 @@ return __p } var FileItemView = Marionette.ItemView.extend({ tagName: 'li', - template: template$P, + template: template$L, mixins: [loadable], ui: { fileName: '.file_name', selectButton: '.select', settingsButton: '.settings', @@ -8670,11 +8859,11 @@ retry: function retry() { this.model.retry(); } }); - function template$Q(data) { + function template$M(data) { var __t, __p = ''; __p += '<div class="filtered_files-banner">\n <span class="filtered_files-banner_prefix">\n ' + ((__t = ( I18n.t('pageflow.editor.views.filtered_files_view.banner_prefix') )) == null ? '' : __t) + '\n </span>\n <span class="filtered_files-filter_name"></span>\n <a href=""\n class="filtered_files-reset_filter"\n title="' + ((__t = ( I18n.t('pageflow.editor.views.filtered_files_view.reset_filter') )) == null ? '' : __t) + @@ -8689,11 +8878,11 @@ '</li>\n'; return __p } var FilteredFilesView = Marionette.ItemView.extend({ - template: template$Q, + template: template$M, className: 'filtered_files', ui: { banner: '.filtered_files-banner', filterName: '.filtered_files-filter_name' }, @@ -8744,30 +8933,30 @@ this.ui.filterName.text(this.filterTranslation('name')); } }, filterTranslation: function filterTranslation(keyName, options) { var filterName = this.options.filterName; - return findTranslation(['pageflow.editor.files.filters.' + this.options.fileType.collectionName + '.' + filterName + '.' + keyName, 'pageflow.editor.files.common_filters.' + keyName], options); + return i18nUtils.findTranslation(['pageflow.editor.files.filters.' + this.options.fileType.collectionName + '.' + filterName + '.' + keyName, 'pageflow.editor.files.common_filters.' + keyName], options); }, onClose: function onClose() { if (this.filteredCollection) { this.filteredCollection.dispose(); } } }); - function template$R(data) { + function template$N(data) { var __t, __p = ''; __p += '<div class="box choose_importer_box">\n <h1 class="dialog-header">' + ((__t = ( I18n.t('pageflow.editor.views.files_view.importer.heading') )) == null ? '' : __t) + '</h1>\n\n <div class="content">\n <ul class="importers_panel">\n </ul>\n </div>\n\n <div class="footer">\n <button class="close">' + ((__t = ( I18n.t('pageflow.editor.templates.files_explorer.cancel') )) == null ? '' : __t) + '</button>\n </div>\n</div>\n'; return __p } - function template$S(data) { + function template$O(data) { var __t, __p = ''; __p += '<button class=\'importer\' data-key=\'' + ((__t = ( data.fileImporter.key )) == null ? '' : __t) + '\'>\n <div class="logo">\n <img alt="Free high resolution images" src="' + ((__t = ( data.fileImporter.logoSource )) == null ? '' : __t) + @@ -8778,11 +8967,11 @@ '</p>\n </div>\n</button>'; return __p } var ImporterSelectView = Marionette.ItemView.extend({ - template: template$S, + template: template$O, className: 'importer_select', tagName: 'li', events: { 'click .importer': function clickImporter(event) { this.options.parentView.importerSelected(this.options.importer); @@ -8795,11 +8984,11 @@ }; } }); var ChooseImporterView = Marionette.ItemView.extend({ - template: template$R, + template: template$N, className: 'choose_importer editor dialog', mixins: [dialogView], ui: { importersList: '.importers_panel', closeButton: '.close' @@ -8830,11 +9019,11 @@ ChooseImporterView.open = function (options) { app.dialogRegion.show(new ChooseImporterView(options).render()); }; - function template$T(data) { + function template$P(data) { var __t, __p = ''; __p += '<div class="box file_importer_box">\n <h1 class="dialog-header">' + ((__t = ( I18n.t('pageflow.editor.file_importers.'+data.importerKey+'.dialog_label') )) == null ? '' : __t) + '</h1>\n\n <div class="content_panel">\n \n </div>\n\n <div class="footer">\n <div class="disclaimer">\n <p>' + ((__t = ( I18n.t('pageflow.editor.file_importers.'+data.importerKey+'.disclaimer') )) == null ? '' : __t) + @@ -8844,11 +9033,11 @@ ((__t = ( I18n.t('pageflow.editor.templates.files_explorer.cancel') )) == null ? '' : __t) + '</button>\n </div>\n</div>\n'; return __p } - function template$U(data) { + function template$Q(data) { var __t, __p = ''; __p += '<div class="box">\n <h1 class="dialog-header">' + ((__t = ( I18n.t('pageflow.editor.templates.confirm_upload.header') )) == null ? '' : __t) + '</h1>\n <p class="dialog-hint">' + ((__t = ( I18n.t('pageflow.editor.templates.confirm_upload.hint') )) == null ? '' : __t) + @@ -8860,18 +9049,18 @@ ((__t = ( I18n.t('pageflow.editor.templates.confirm_upload.close') )) == null ? '' : __t) + '</button>\n </div>\n</div>\n'; return __p } - function template$V(data) { + function template$R(data) { var __p = ''; __p += '<h2></h2>\n'; return __p } var UploadableFilesView = Marionette.ItemView.extend({ - template: template$V, + template: template$R, className: 'uploadable_files', ui: { header: 'h2' }, initialize: function initialize() { @@ -8913,11 +9102,11 @@ }); } }); var ConfirmFileImportUploadView = Marionette.Layout.extend({ - template: template$U, + template: template$Q, className: 'confirm_upload editor dialog', mixins: [dialogView], regions: { selectedFileRegion: '.selected_file_region' }, @@ -8987,11 +9176,11 @@ ConfirmFileImportUploadView.open = function (options) { app.dialogRegion.show(new ConfirmFileImportUploadView(options)); }; var FilesImporterView = Marionette.ItemView.extend({ - template: template$T, + template: template$P, className: 'files_importer editor dialog', mixins: [dialogView], ui: { contentPanel: '.content_panel', spinner: '.lds-spinner', @@ -9074,20 +9263,20 @@ FilesImporterView.open = function (options) { app.dialogRegion.show(new FilesImporterView(options).render()); }; - function template$W(data) { + function template$S(data) { var __t, __p = ''; __p += '<button class="">\n <span class="label">' + ((__t = ( I18n.t('pageflow.editor.templates.select_button.select') )) == null ? '' : __t) + '</span>\n</button>\n\n<div class="dropdown">\n <ul class="dropdown-menu" role="menu">\n </ul>\n</div>\n'; return __p } var SelectButtonView = Marionette.ItemView.extend({ - template: template$W, + template: template$S, className: 'select_button', ui: { button: 'button', label: 'button .label', menu: '.dropdown-menu', @@ -9118,52 +9307,58 @@ addOption: function addOption(option, index) { this.ui.menu.append('<li><a href="#" data-index="' + index + '">' + option.label + '</a></li>'); } }); - function template$X(data) { + function template$T(data) { var __t, __p = ''; __p += '<a class="back">' + ((__t = ( I18n.t('pageflow.editor.templates.files.back') )) == null ? '' : __t) + '</a>\n'; return __p } var FilesView = Marionette.ItemView.extend({ - template: template$X, + template: template$T, className: 'manage_files', events: { 'click a.back': 'goBack', 'file-selected': 'updatePage' }, onRender: function onRender() { - this.addFileModel = new Backbone.Model({ - label: I18n$1.t('pageflow.editor.views.files_view.add'), - options: [{ - label: I18n$1.t('pageflow.editor.views.files_view.upload'), - handler: this.upload.bind(this) - }, { - label: I18n$1.t('pageflow.editor.views.files_view.reuse'), - handler: function handler() { - FilesExplorerView.open({ - callback: function callback(otherEntry, file) { - state.entry.reuseFile(otherEntry, file); - } - }); - } - }, { + var menuOptions = [{ + label: I18n$1.t('pageflow.editor.views.files_view.upload'), + handler: this.upload.bind(this) + }, { + label: I18n$1.t('pageflow.editor.views.files_view.reuse'), + handler: function handler() { + FilesExplorerView.open({ + callback: function callback(otherEntry, file) { + state.entry.reuseFile(otherEntry, file); + } + }); + } + }]; + + if (editor.fileImporters.keys().length > 0) { + menuOptions.push({ label: I18n$1.t('pageflow.editor.views.files_view.import'), handler: function handler() { ChooseImporterView.open({ callback: function callback(importer) { FilesImporterView.open({ importer: importer }); } }); } - }] + }); + } + + this.addFileModel = new Backbone.Model({ + label: I18n$1.t('pageflow.editor.views.files_view.add'), + options: menuOptions }); this.$el.append(this.subview(new SelectButtonView({ model: this.addFileModel })).el); this.tabsView = new TabsView({ @@ -9206,18 +9401,18 @@ upload: function upload() { app.trigger('request-upload'); } }); - function template$Y(data) { + function template$U(data) { var __p = ''; __p += '<div class="quota_state">\n</div>\n<div class="outlet">\n</div>\n<div class="exhausted_message">\n</div>\n'; return __p } var EntryPublicationQuotaDecoratorView = Marionette.Layout.extend({ - template: template$Y, + template: template$U, className: 'quota_decorator', regions: { outlet: '.outlet' }, ui: { @@ -9254,11 +9449,11 @@ } } } }); - function template$Z(data) { + function template$V(data) { var __t, __p = ''; __p += '<div class="files_pending notice">\n <p>' + ((__t = ( I18n.t('pageflow.editor.templates.publish_entry.files_pending_notice') )) == null ? '' : __t) + '</p>\n <p><a href="#files">' + ((__t = ( I18n.t('pageflow.editor.templates.publish_entry.show_files') )) == null ? '' : __t) + @@ -9301,11 +9496,11 @@ '</p>\n <p><a href="" target="_blank"></a></p>\n</div>\n'; return __p } var PublishEntryView = Marionette.ItemView.extend({ - template: template$Z, + template: template$V, className: 'publish_entry', ui: { publishUntilFields: '.publish_until_fields', publishUntilField: 'input[name=publish_until]', publishUntilTimeField: 'input[name=publish_until_time]', @@ -9709,138 +9904,22 @@ _isChapterView: function _isChapterView() { return !Backbone.history.getFragment(); } }); - function template$_(data) { + function template$W(data) { var __t, __p = ''; - __p += '<h2>' + - ((__t = ( I18n.t('pageflow.editor.blank_entry.header') )) == null ? '' : __t) + - '</h2>\n<p>' + - ((__t = ( I18n.t('pageflow.editor.blank_entry.intro') )) == null ? '' : __t) + - '</p>\n<ol>\n <li>' + - ((__t = ( I18n.t('pageflow.editor.blank_entry.create_chapter') )) == null ? '' : __t) + - '</li>\n <li>' + - ((__t = ( I18n.t('pageflow.editor.blank_entry.create_page') )) == null ? '' : __t) + - '</li>\n <li>' + - ((__t = ( I18n.t('pageflow.editor.blank_entry.edit_page') )) == null ? '' : __t) + - '</li>\n</ol>\n<p>' + - ((__t = ( I18n.t('pageflow.editor.blank_entry.outro') )) == null ? '' : __t) + - '</p>\n'; - return __p - } - - var BlankEntryView = Marionette.ItemView.extend({ - template: template$_, - className: 'blank_entry' - }); - - var PagePreviewView = Marionette.View.extend({ - tagName: 'section', - className: 'page', - modelEvents: { - 'change:template': 'updateTemplate', - 'change:configuration': 'update', - 'change:position': 'updatePositionClassNames', - 'change:id': function changeId() { - this.$el.attr('data-id', this.model.id); - this.$el.attr('data-perma-id', this.model.get('perma_id')); - this.$el.attr('id', this.model.get('perma_id')); - } - }, - events: { - pageactivate: function pageactivate() { - this.model.set('active', true); - }, - pagedeactivate: function pagedeactivate() { - this.model.set('active', false); - } - }, - render: function render() { - this.$el.html(this.pageTemplate()); - this.$el.attr('data-id', this.model.id); - this.$el.attr('data-perma-id', this.model.get('perma_id')); - this.$el.attr('id', this.model.get('perma_id')); - this.$el.attr('data-chapter-id', this.model.get('chapter_id')); - this.$el.data('template', this.model.get('template')); - this.$el.data('configuration', this.model.get('configuration')); - this.$el.on('pageenhanced', _$1.bind(function () { - this.update(); - this.initEmbeddedViews(); - this.$el.page('reactivate'); - }, this)); - return this; - }, - onClose: function onClose() { - this.$el.page('cleanup'); - }, - updateTemplate: function updateTemplate() { - this.$el.page('cleanup'); - this.$el.html(this.pageTemplate()); - this.$el.data('template', this.model.get('template')); - setTimeout(_$1.bind(function () { - this.$el.page('reinit'); - }, this), 0); - }, - update: function update() { - this.$el.page('update', this.model.configuration); - pageflow.events.trigger('page:update', this.model); - this.refreshScroller(); - this.updatePositionClassNames(); - }, - updatePositionClassNames: function updatePositionClassNames() { - this.$el.toggleClass('chapter_beginning', this.model.isChapterBeginning()); - this.$el.toggleClass('first_page', this.model.isFirstPage()); - }, - pageTypeHooks: function pageTypeHooks() { - return pageflow.pageType.get(this.model.get('template')); - }, - pageTemplate: function pageTemplate() { - return this._unescape($('script[data-template="' + this.model.get('template') + '_page"]').html()); - }, - refreshScroller: function refreshScroller() { - this.$el.page('refreshScroller'); - }, - initEmbeddedViews: function initEmbeddedViews() { - var view = this; - - if (view.embeddedViews) { - view.embeddedViews.call('close'); - } - - view.embeddedViews = new ChildViewContainer(); - - _$1.each(view.embeddedViewDefinitions(), function (item, selector) { - view.$(selector).each(function () { - view.embeddedViews.add(new item.view(_$1.extend(item.options || {}, { - el: this, - model: view.model.configuration, - container: view - })).render()); - }); - }); - }, - embeddedViewDefinitions: function embeddedViewDefinitions() { - return _$1.extend({}, this.pageTypeHooks().embeddedEditorViews() || {}, this.model.pageType().embeddedViews()); - }, - _unescape: function _unescape(text) { - return $('<div/>').html(text).text(); - } - }); - - function template$$(data) { - var __t, __p = ''; __p += '<div class="box">\n <h2>' + ((__t = ( I18n.t('pageflow.editor.templates.help.title') )) == null ? '' : __t) + '</h2>\n\n <div class="placeholder"></div>\n\n <div class="footer">\n <a class="close" href="">' + ((__t = ( I18n.t('pageflow.editor.templates.help.close') )) == null ? '' : __t) + '</a>\n </div>\n</div>\n'; return __p } var HelpView = Marionette.ItemView.extend({ - template: template$$, + template: template$W, className: 'help', ui: { placeholder: '.placeholder', sections: 'section', menuItems: 'li' @@ -9909,22 +9988,26 @@ section.toggle(section.data('name') === name); }); } }); - function template$10(data) { + var PageThumbnailView = ModelThumbnailView.extend({ + className: 'model_thumbnail page_thumbnail' + }); + + function template$X(data) { var __t, __p = ''; __p += '<div>\n <span class="missing_page_thumbnail"></span>\n <span class="page_thumbnail"></span>\n <div class="title"></div>\n <div class="label"></div>\n <a class="remove" title="' + ((__t = ( I18n.t('pageflow.editor.templates.page_link_item.remove') )) == null ? '' : __t) + '"></a>\n <a class="edit" title="' + ((__t = ( I18n.t('pageflow.editor.templates.page_link_item.edit') )) == null ? '' : __t) + '"></a>\n<div>\n'; return __p } var PageLinkItemView = Marionette.ItemView.extend({ - template: template$10, + template: template$X, tagName: 'li', className: 'page_link', ui: { thumbnail: '.page_thumbnail', title: '.title', @@ -9970,22 +10053,22 @@ this.ui.editButton.toggle(!!this.model.editPath()); this.$el.toggleClass('dangling', !page); } }); - function template$11(data) { + function template$Y(data) { var __t, __p = ''; __p += '<label>\n <span class="name">' + ((__t = ( I18n.t('pageflow.editor.templates.page_links.label') )) == null ? '' : __t) + '</span>\n</label>\n<ul class="links outline"></ul>\n\n<a href="" class="add_link">' + ((__t = ( I18n.t('pageflow.editor.templates.page_links.add') )) == null ? '' : __t) + '</a>\n'; return __p } var PageLinksView = Marionette.ItemView.extend({ - template: template$11, + template: template$Y, className: 'page_links', ui: { links: 'ul.links', addButton: '.add_link' }, @@ -10017,11 +10100,11 @@ updateAddButton: function updateAddButton(pageLinks) { this.ui.addButton.css('display', pageLinks.canAddLink() ? 'inline-block' : 'none'); } }); - function template$12(data) { + function template$Z(data) { var __t, __p = ''; __p += '<div class="emulation_mode_button-menu">\n <div class="emulation_mode_button-menu_header">\n ' + ((__t = ( I18n.t('pageflow.editor.templates.emulation_mode_button.header') )) == null ? '' : __t) + '\n </div>\n <ul>\n <li class="emulation_mode_button-menu_item emulation_mode_button-desktop">\n <a class="emulation_mode_button-menu_link">\n ' + ((__t = ( I18n.t('pageflow.editor.templates.emulation_mode_button.desktop') )) == null ? '' : __t) + @@ -10036,11 +10119,11 @@ '\n</div>\n'; return __p } var EmulationModeButtonView = Marionette.ItemView.extend({ - template: template$12, + template: template$Z, className: 'emulation_mode_button', ui: { phoneItem: '.emulation_mode_button-phone', desktopItem: '.emulation_mode_button-desktop', phoneDisplay: '.emulation_mode_button-display.emulation_mode_button-phone', @@ -10049,41 +10132,41 @@ events: { 'click .emulation_mode_button-desktop a': function clickEmulation_mode_buttonDesktopA() { this.model.unset('emulation_mode'); }, 'click .emulation_mode_button-phone a': function clickEmulation_mode_buttonPhoneA() { - if (!this.model.get('current_page_supports_emulation_mode')) { + if (this.model.get('emulation_mode_disabled')) { return; } this.model.set('emulation_mode', 'phone'); } }, modelEvents: { - 'change:emulation_mode change:current_page_supports_emulation_mode': 'update' + 'change:emulation_mode change:emulation_mode_disabled': 'update' }, onRender: function onRender() { this.update(); }, update: function update() { - this.ui.phoneItem.toggleClass('disabled', !this.model.get('current_page_supports_emulation_mode')); + this.ui.phoneItem.toggleClass('disabled', !!this.model.get('emulation_mode_disabled')); this.ui.phoneItem.toggleClass('active', this.model.has('emulation_mode')); this.ui.desktopItem.toggleClass('active', !this.model.has('emulation_mode')); this.ui.phoneDisplay.toggleClass('active', this.model.has('emulation_mode')); this.ui.desktopDisplay.toggleClass('active', !this.model.has('emulation_mode')); } }); - function template$13(data) { + function template$_(data) { var __t, __p = ''; __p += ((__t = ( I18n.t('pageflow.editor.templates.help_button.open_help') )) == null ? '' : __t); return __p } var HelpButtonView = Marionette.ItemView.extend({ - template: template$13, + template: template$_, className: 'help_button', events: { 'click': function click() { app.trigger('toggle-help'); } @@ -10091,181 +10174,21 @@ }); var SidebarFooterView = Marionette.View.extend({ className: 'sidebar_footer', render: function render() { - if (pageflow.features.isEnabled('editor_emulation_mode')) { + if (this.model.supportsPhoneEmulation()) { this.appendSubview(new EmulationModeButtonView({ model: this.model })); } this.appendSubview(new HelpButtonView()); return this; } }); - function template$14(data) { - var __t, __p = ''; - __p += '<div class="container">\n <div class="header"></div>\n <div class="overview"></div>\n\n <div class="entry"></div>\n</div>\n<div class="navigation_disabled_hint">\n ' + - ((__t = ( I18n.t('pageflow.editor.templates.entry_preview.navigation_disabled_hint') )) == null ? '' : __t) + - '\n</div>\n'; - return __p - } - - var EntryPreviewView = Marionette.ItemView.extend({ - template: template$14, - className: 'entry_preview', - ui: { - container: '> .container', - header: '> .container > .header', - entry: '> .container > .entry', - overview: '> .container > .overview', - navigationDisabledHint: '.navigation_disabled_hint' - }, - initialize: function initialize() { - this.pages = this.model.pages.persisted(); - this.widgets = $(); - this.debouncedFetchWidgets = _$1.debounce(this.fetchWidgets, 200); - }, - onRender: function onRender() { - this.pageViews = this.subview(new CollectionView({ - el: this.ui.entry, - collection: this.pages, - itemViewConstructor: PagePreviewView, - blankSlateViewConstructor: BlankEntryView - })); - this.ui.entry.append($('#indicators_seed > *')); - this.update(); - this.listenTo(state.entry, 'sync:order sync:widgets', this.update); - this.listenTo(state.entry, 'change:configuration', function () { - state.entry.once('sync', this.update, this); - }); - this.listenTo(state.entry, 'change:emulation_mode', this.updateEmulationMode); - this.listenTo(state.storylines, 'sync', this.update); - this.listenTo(state.chapters, 'sync', this.update); - this.listenTo(state.pages, 'sync', this.update); - this.listenTo(state.audioFiles, 'sync', this.update); - this.listenTo(state.imageFiles, 'sync', this.update); - this.listenTo(state.videoFiles, 'sync', this.update); - this.listenTo(pageflow.events, 'page:changing', function (event) { - if (state.entry.get('emulation_mode')) { - this.ui.navigationDisabledHint.css('opacity', 1); - clearTimeout(this.navigationDisabledHintTimeout); - this.navigationDisabledHintTimeout = setTimeout(_$1.bind(function () { - this.ui.navigationDisabledHint.css('opacity', 0); - }, this), 2000); - event.cancel(); - } - }); - this.listenTo(pageflow.events, 'page:change', function (page) { - this.updateEmulationModeSupport(page.getPermaId()); - }); - }, - onShow: function onShow() { - var slideshow = pageflow.Slideshow.setup({ - element: this.ui.entry, - enabledFeatureNames: state.entry.get('enabled_feature_names'), - simulateHistory: true - }); - pageflow.delayedStart.perform(); - this.listenTo(this.pages, 'add', function () { - slideshow.update(); - }); - this.listenTo(this.pages, 'remove', function () { - slideshow.update(); - }); - this.listenTo(this.pages, 'edit', function (model) { - if (this.lastEditedPage != model) { - state.entry.unset('emulation_mode'); - } - - this.lastEditedPage = model; - slideshow.goTo(this.pageViews.itemViews.findByModel(model).$el); - }); - this.listenTo(app, 'resize', function () { - slideshow.triggerResizeHooks(); - this.updateSimulatedMediaQueryClasses(); - }); - this.listenTo(state.pages, 'change:template', function () { - this.updateEmulationModeSupport(slideshow.currentPagePermaId()); - }); - this.updateSimulatedMediaQueryClasses(); - }, - updateEmulationModeSupport: function updateEmulationModeSupport(permaId) { - var model = state.pages.getByPermaId(permaId); - state.entry.set('current_page_supports_emulation_mode', model && model.pageType().supportsPhoneEmulation()); - }, - updateSimulatedMediaQueryClasses: function updateSimulatedMediaQueryClasses() { - var width = this.ui.container.width(); - var height = this.ui.container.height(); - var portrait = width < height; - $('html').toggleClass('simulate_mobile', width <= 900).toggleClass('simulate_phone', portrait && width <= 500 || !portrait && height <= 500).toggleClass('simulate_desktop', portrait && width > 500 || !portrait && height > 500).toggleClass('simulate_narrow_desktop', width <= 1200).toggleClass('simulate_wide_desktop', width > 1600).toggleClass('simulate_pad_portrait', width <= 768 && portrait).toggleClass('simulate_phone_portrait', width <= 500 && portrait); - }, - update: function update() { - this.debouncedFetchWidgets(); - this.$el.toggleClass('emphasize_chapter_beginning', !!this.model.configuration.get('emphasize_chapter_beginning')); - }, - fetchWidgets: function fetchWidgets() { - var view = this; - $.ajax(this.model.url() + '/partials').success(function (response) { - var partials = $('<div />').html(response); - view.ui.header.replaceWith(partials.find('> .header')); - view.ui.overview.replaceWith(partials.find('> .overview')); - view.bindUIElements(); - view.updateWidgets(partials); - view.ui.header.header({ - slideshow: pageflow.slides - }); - view.ui.overview.overview(); - }); - }, - updateWidgets: function updateWidgets(partials) { - var widgets = partials.find('[data-widget]'); - this.updatePresentWidgetsCssClasses(widgets); - this.widgets.remove(); - this.widgets = widgets; - this.ui.entry.before(this.widgets); - pageflow.widgetTypes.enhance(this.$el); - }, - updatePresentWidgetsCssClasses: function updatePresentWidgetsCssClasses(newWidgets) { - var previousClasses = this.widgetNames(this.widgets); - var newClasses = this.widgetNames(newWidgets); - - var removedClasses = _$1.difference(previousClasses, newClasses); - - var addedClasses = _$1.difference(newClasses, previousClasses); - - this.$el.addClass('widgets_present'); - this.$el.removeClass(removedClasses.join(' ')); - this.$el.addClass(addedClasses.join(' ')); - - if (removedClasses.length || addedClasses.length) { - pageflow.events.trigger('widgets:update'); - } - }, - widgetNames: function widgetNames(widgets) { - return widgets.map(function () { - return 'widget_' + $(this).data('widget') + '_present'; - }).get(); - }, - updateEmulationMode: function updateEmulationMode() { - if (state.entry.previous('emulation_mode')) { - this.$el.removeClass(this.emulationModeClassName(state.entry.previous('emulation_mode'))); - } - - if (state.entry.get('emulation_mode')) { - this.$el.addClass(this.emulationModeClassName(state.entry.get('emulation_mode'))); - } - - app.trigger('resize'); - }, - emulationModeClassName: function emulationModeClassName(mode) { - return 'emulation_mode_' + mode; - } - }); - var HelpImageView = Marionette.View.extend({ tagName: 'img', className: 'help_image', render: function render() { this.$el.attr('src', state.editorAssetUrls.help[this.options.imageName]); @@ -10279,77 +10202,22 @@ this.$el.html(this.options.text); return this; } }); - function template$15(data) { + function template$$(data) { var __t, __p = ''; - __p += '<div class="box">\n <h2>' + - ((__t = ( I18n.t('pageflow.editor.templates.page_selection.title') )) == null ? '' : __t) + - '</h2>\n\n <div class="wrapper editor">\n <div class="storyline_picker">\n </div>\n </div>\n\n <div class="footer">\n <a href="" class="close">' + - ((__t = ( I18n.t('pageflow.editor.templates.page_selection.cancel') )) == null ? '' : __t) + - '</a>\n </div>\n</div>\n'; - return __p - } - - var PageSelectionView = Marionette.ItemView.extend({ - template: template$15, - className: 'page_selection dialog', - mixins: [dialogView], - ui: { - storylines: '.storyline_picker', - chapters: '.chapters' - }, - events: { - 'click ul.pages li': function clickUlPagesLi(event) { - this.options.onSelect(state.pages.get($(event.currentTarget).data('id'))); - this.close(); - } - }, - onRender: function onRender() { - var options = this.options; - this.subview(new StorylinePickerView({ - el: this.ui.storylines, - pageItemViewOptions: { - isDisabled: function isDisabled(page) { - return options.isAllowed && !options.isAllowed(page); - } - } - })); - } - }); - - PageSelectionView.selectPage = function (options) { - return $.Deferred(function (deferred) { - var view = new PageSelectionView({ - model: state.entry, - onSelect: deferred.resolve, - isAllowed: options && options.isAllowed - }); - view.on('close', function () { - deferred.reject(); - }); - app.dialogRegion.show(view.render()); - }).promise(); - }; - - editor.pageSelectionView = PageSelectionView; - - function template$16(data) { - var __t, __p = ''; __p += '<span class="list_item_thumbnail"></span>\n<span class="list_item_missing_thumbnail"></span>\n<span class="list_item_type_pictogram type_pictogram"></span>\n\n<div class="list_item_title"></div>\n<div class="list_item_description"></div>\n\n<div class="list_item_buttons">\n <a class="list_item_edit_button" title="' + ((__t = ( I18n.t('pageflow.editor.templates.list_item.edit') )) == null ? '' : __t) + '"></a>\n <a class="list_item_remove_button" title="' + ((__t = ( I18n.t('pageflow.editor.templates.list_item.remove') )) == null ? '' : __t) + '"></a>\n</div>\n'; return __p } - /** @api private */ - var ListItemView = Marionette.ItemView.extend({ - template: template$16, + template: template$$, tagName: 'li', className: 'list_item', ui: { thumbnail: '.list_item_thumbnail', typePictogram: '.list_item_type_pictogram', @@ -10419,11 +10287,11 @@ getOptionResult: function getOptionResult(name) { return typeof this.options[name] === 'function' ? this.options[name](this.model) : this.options[name]; } }); - function template$17(data) { + function template$10(data) { var __t, __p = ''; __p += '<div class="checking notice">\n <p>' + ((__t = ( I18n.t('pageflow.editor.templates.locked.loading') )) == null ? '' : __t) + '</p>\n\n <a class="close" href="#">' + ((__t = ( I18n.t('pageflow.editor.templates.locked.close') )) == null ? '' : __t) + @@ -10434,11 +10302,11 @@ '</a>\n</div>\n\n'; return __p } var LockedView = Marionette.ItemView.extend({ - template: template$17, + template: template$10, className: 'locked checking', ui: { breakButton: '.break', message: '.error .message' }, @@ -10511,10 +10379,11 @@ togglerTip_closed: I18n$1.t('pageflow.editor.views.editor_views.show_editor'), togglerTip_open: I18n$1.t('pageflow.editor.views.editor_views.hide_editor'), resizerTip: I18n$1.t('pageflow.editor.views.editor_views.resize_editor'), enableCursorHotkey: false, fxName: 'none', + maskIframesOnResize: true, onresize: function onresize() { app.trigger('resize'); } }); new UploaderView().render(); @@ -10626,11 +10495,11 @@ } } } }); - function template$18(data) { + function template$11(data) { var __t, __p = ''; __p += '<li class="uploading"><span class="count">0</span>' + ((__t = ( I18n.t('pageflow.editor.templates.notification.upload_pending') )) == null ? '' : __t) + '</li>\n<li class="failed"><span class="count">0</span> <span class="description">' + ((__t = ( I18n.t('pageflow.editor.templates.notification.save_error') )) == null ? '' : __t) + @@ -10649,11 +10518,11 @@ } var NotificationsView = Marionette.ItemView.extend({ className: 'notifications', tagName: 'ul', - template: template$18, + template: template$11, ui: { failedCount: '.failed .count', uploadingCount: '.uploading .count', confirmableFilesCount: '.confirmable_files .count' }, @@ -10663,20 +10532,20 @@ } }, onRender: function onRender() { this.listenTo(state.entry, 'change:uploading_files_count', this.notifyUploadCount); this.listenTo(state.entry, 'change:confirmable_files_count', this.notifyConfirmableFilesCount); - this.listenTo(state.savingRecords, 'add', this.update); - this.listenTo(state.savingRecords, 'remove', this.update); + this.listenTo(editor.savingRecords, 'add', this.update); + this.listenTo(editor.savingRecords, 'remove', this.update); this.listenTo(editor.failures, 'add', this.update); this.listenTo(editor.failures, 'remove', this.update); this.update(); this.notifyConfirmableFilesCount(); }, update: function update() { this.$el.toggleClass('failed', !editor.failures.isEmpty()); - this.$el.toggleClass('saving', !state.savingRecords.isEmpty()); + this.$el.toggleClass('saving', !editor.savingRecords.isEmpty()); this.ui.failedCount.text(editor.failures.count()); }, notifyUploadCount: function notifyUploadCount(model, uploadCount) { this.$el.toggleClass('uploading', uploadCount > 0); this.ui.uploadingCount.text(uploadCount); @@ -10736,18 +10605,18 @@ _getFile: function _getFile() { return this.model.getReference(this.options.propertyName, this.options.collection); } }); - function template$19(data) { + function template$12(data) { var __p = ''; __p += '<h2></h2>\n'; return __p } var NestedFilesView = Marionette.ItemView.extend({ - template: template$19, + template: template$12, className: 'nested_files', ui: { header: 'h2' }, initialize: function initialize() { @@ -10819,22 +10688,22 @@ this.options.selection.set('nextFile', undefined); } }, 200) }); - function template$1a(data) { + function template$13(data) { var __t, __p = ''; __p += '<div class="text_tracks_container">\n <div class="files_upload_panel">\n <div class="files_panel">\n </div>\n <a class="upload" href="">' + ((__t = ( I18n.t('pageflow.editor.templates.text_tracks.upload') )) == null ? '' : __t) + '</a>\n </div>\n\n <div class="selected_file_panel">\n <h2 class="selected_file_header">' + ((__t = ( I18n.t('pageflow.editor.templates.text_tracks.edit_file_header') )) == null ? '' : __t) + '</h2>\n <div class="selected_file_region">\n </div>\n </div>\n</div>\n'; return __p } var TextTracksView = Marionette.Layout.extend({ - template: template$1a, + template: template$13, className: 'text_tracks', regions: { selectedFileRegion: '.selected_file_region' }, ui: { @@ -10907,19 +10776,19 @@ this.$el.show(); }); this.listenTo(pageflow.events, 'atmo:enabled', function () { this.$el.hide(); }); - this.$el.toggle(pageflow.atmo.disabled); + this.$el.toggle(!!pageflow.atmo && pageflow.atmo.disabled); }, render: function render() { this.$el.attr('title', I18n$1.t('pageflow.editor.atmo.disabled')); return this; } }); - function template$1b(data) { + function template$14(data) { var __p = ''; __p += '<label>\n <span class="list_label"></span>\n</label>\n\n<ul class="list_items"></ul>\n'; return __p } @@ -10937,10 +10806,12 @@ * * Models inside the collection must implement the following methods: * * @param {Backbone.Collection} options.collection * + * @param {Object} options + * * @param {string} options.label * Text of the label to display above the list. * * @param {boolean} [options.highlight=false] * @@ -10957,15 +10828,14 @@ * @param {function} [options.onEdit] * * @param {function} [options.onRemove] * * @class - * @memberof module:pageflow/editor */ var ListView = Marionette.ItemView.extend({ - template: template$1b, + template: template$14, className: 'list', ui: { label: '.list_label', items: '.list_items' }, @@ -10991,11 +10861,11 @@ this.$el.toggleClass('with_type_pictogram', !!this.options.itemTypeName); } }); var ConfirmUploadView = Marionette.Layout.extend({ - template: template$U, + template: template$Q, className: 'confirm_upload editor dialog', mixins: [dialogView], regions: { selectedFileRegion: '.selected_file_region' }, @@ -11050,10 +10920,104 @@ ConfirmUploadView.open = function (options) { app.dialogRegion.show(new ConfirmUploadView(options)); }; + /** + * Base view to edit configuration container models. Extend and + * override the `configure` method which receives a {@link + * ConfigurationEditorView} to define the tabs and inputs that shall + * be displayed. + * + * Add a `translationKeyPrefix` property to the prototype and define + * the following translations: + * + * * `<translationKeyPrefix>.tabs`: used as `tabTranslationKeyPrefix` + * of the `ConfigurationEditorView`. + * + * * `<translationKeyPrefix>.attributes`: used as one of the + * `attributeTranslationKeyPrefixes` of the + * `ConfigurationEditorView`. + * + * * `<translationKeyPrefix>.back` (optional): Back button label. + * + * * `<translationKeyPrefix>.destroy` (optional): Destroy button + * label. + * + * * `<translationKeyPrefix>.confirm_destroy` (optional): Confirm + * message displayed before destroying. + * + * * `<translationKeyPrefix>.save_error` (optional): Header of the + * failure message that is displayed if the model cannot be saved. + * + * * `<translationKeyPrefix>.retry` (optional): Label of the retry + * button of the failure message. + * + * @param {Object} options + * @param {Backbone.Model} options.model - + * Model including the {@link configurationContainer}, + * {@link failureTracking} and {@link delayedDestroying} mixins. + * + * @since 15.1 + */ + + var EditConfigurationView = Marionette.Layout.extend({ + template: function template(_ref) { + var t = _ref.t; + 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 "); + }, + serializeData: function serializeData() { + var _this = this; + + return { + t: function t(key) { + return _this.t(key); + } + }; + }, + mixins: [failureIndicatingView], + regions: { + configurationContainer: '.configuration_container' + }, + events: { + 'click a.back': 'goBack', + 'click a.destroy': 'destroy' + }, + onRender: function onRender() { + var translationKeyPrefix = _$1.result(this, 'translationKeyPrefix'); + + this.configurationEditor = new ConfigurationEditorView({ + tabTranslationKeyPrefix: "".concat(translationKeyPrefix, ".tabs"), + attributeTranslationKeyPrefixes: ["".concat(translationKeyPrefix, ".attributes")], + model: this.model.configuration + }); + this.configure(this.configurationEditor); + this.configurationContainer.show(this.configurationEditor); + }, + onShow: function onShow() { + this.configurationEditor.refreshScroller(); + }, + destroy: function destroy() { + if (window.confirm(this.t('confirm_destroy'))) { + this.model.destroyWithDelay(); + this.goBack(); + } + }, + goBack: function goBack() { + editor.navigate('/', { + trigger: true + }); + }, + t: function t(suffix) { + var translationKeyPrefix = _$1.result(this, 'translationKeyPrefix'); + + return I18n$1.t("".concat(translationKeyPrefix, ".").concat(suffix), { + defaultValue: I18n$1.t("pageflow.editor.views.edit_configuration.".concat(suffix)) + }); + } + }); + ConfigurationEditorView.register('audio', { configure: function configure() { this.tab('general', function () { this.group('general', { supportsTextPositionCenter: true @@ -11353,11 +11317,11 @@ this.tab('loading_spinner', function () { this.view(InfoBoxView, { text: I18n$1.t('pageflow.editor.title_loading_spinner.widget_type_info_box_text') }); this.input('title', TextInputView, { - placeholder: state.entry.configuration.get('title') || state.entry.get('entry_title') + placeholder: state.entry.metadata.get('title') || state.entry.get('entry_title') }); this.input('subtitle', TextInputView); this.input('custom_background_image_id', FileInputView, { collection: 'image_files', fileSelectionHandler: 'widgetConfiguration' @@ -11531,11 +11495,11 @@ }); state.themes = new ThemesCollection(options.themes); state.pages = new PagesCollection(options.pages); state.chapters = new ChaptersCollection(options.chapters); state.storylines = new StorylinesCollection(options.storylines); - state.entry = new Entry(options.entry, { + state.entry = editor.createEntryModel(options, { widgets: widgets }); state.theming = new Theming(options.theming); state.account = new Backbone.Model(options.account); widgets.subject = state.entry; @@ -11555,13 +11519,12 @@ state.storylines.saveOrder(); }, 100)); editor.failures.watch(state.entry); editor.failures.watch(state.pages); editor.failures.watch(state.chapters); - state.savingRecords = new SavingRecordsCollection(); - state.savingRecords.watch(state.pages); - state.savingRecords.watch(state.chapters); + editor.savingRecords.watch(state.pages); + editor.savingRecords.watch(state.chapters); pageflow.events.trigger('seed:loaded'); }); app.addInitializer(function (options) { state.fileUploader = new FileUploader({ @@ -11614,11 +11577,11 @@ } }); state.entry.on('use:files', function () { pageflow.stylesheet.reload('entry'); }); - state.entry.configuration.on('change:theme_name', function () { + state.entry.metadata.on('change:theme_name', function () { var theme = state.entry.getTheme(); pageflow.stylesheet.update('theme', theme.get('stylesheet_path')); }); }); @@ -11671,11 +11634,11 @@ }).render(); new ScrollingView({ el: $('sidebar .scrolling'), region: app.sidebarRegion }).render(); - app.previewRegion.show(new EntryPreviewView({ + app.previewRegion.show(new editor.entryType.previewView({ model: state.entry })); app.indicatorsRegion.show(new DisabledAtmoIndicatorView()); app.notificationsRegion.show(new NotificationsView()); app.sidebarFooterRegion.show(new SidebarFooterView({ @@ -11699,15 +11662,13 @@ exports.BackButtonDecoratorView = BackButtonDecoratorView; exports.BackgroundImageEmbeddedView = BackgroundImageEmbeddedView; exports.BackgroundPositioningPreviewView = BackgroundPositioningPreviewView; exports.BackgroundPositioningSlidersView = BackgroundPositioningSlidersView; exports.BackgroundPositioningView = BackgroundPositioningView; - exports.BlankEntryView = BlankEntryView; exports.ChangeThemeDialogView = ChangeThemeDialogView; exports.Chapter = Chapter; exports.ChapterConfiguration = ChapterConfiguration; - exports.ChapterItemView = ChapterItemView; exports.ChapterPagesCollection = ChapterPagesCollection; exports.ChapterScaffold = ChapterScaffold; exports.ChaptersCollection = ChaptersCollection; exports.CheckBoxGroupInputView = CheckBoxGroupInputView; exports.CheckBoxInputView = CheckBoxInputView; @@ -11725,10 +11686,11 @@ exports.DisabledAtmoIndicatorView = DisabledAtmoIndicatorView; exports.DropDownButtonItemListView = DropDownButtonItemListView; exports.DropDownButtonItemView = DropDownButtonItemView; exports.DropDownButtonView = DropDownButtonView; exports.EditChapterView = EditChapterView; + exports.EditConfigurationView = EditConfigurationView; exports.EditEntryView = EditEntryView; exports.EditFileView = EditFileView; exports.EditLock = EditLock; exports.EditLockContainer = EditLockContainer; exports.EditMetaDataView = EditMetaDataView; @@ -11741,18 +11703,18 @@ exports.EditorView = EditorView; exports.EmulationModeButtonView = EmulationModeButtonView; exports.EncodedFile = EncodedFile; exports.EncodingConfirmation = EncodingConfirmation; exports.Entry = Entry; - exports.EntryConfiguration = EntryConfiguration; - exports.EntryConfigurationFileSelectionHandler = EntryConfigurationFileSelectionHandler; - exports.EntryPreviewView = EntryPreviewView; + exports.EntryMetadata = EntryMetadata; + exports.EntryMetadataFileSelectionHandler = EntryMetadataFileSelectionHandler; exports.EntryPublication = EntryPublication; exports.EntryPublicationQuotaDecoratorView = EntryPublicationQuotaDecoratorView; exports.EnumTableCellView = EnumTableCellView; exports.ExplorerFileItemView = ExplorerFileItemView; exports.ExtendedSelectInputView = ExtendedSelectInputView; + exports.Failure = Failure; exports.FileConfiguration = FileConfiguration; exports.FileImport = FileImport; exports.FileInputView = FileInputView; exports.FileItemView = FileItemView; exports.FileMetaDataItemValueView = FileMetaDataItemValueView; @@ -11761,17 +11723,19 @@ exports.FileReuse = FileReuse; exports.FileSettingsDialogView = FileSettingsDialogView; exports.FileStage = FileStage; exports.FileStageItemView = FileStageItemView; exports.FileThumbnailView = FileThumbnailView; + exports.FileTypes = FileTypes; exports.FileTypesCollection = FileTypesCollection; exports.FileUploader = FileUploader; exports.FilesCollection = FilesCollection; exports.FilesExplorerView = FilesExplorerView; exports.FilesImporterView = FilesImporterView; exports.FilesView = FilesView; exports.FilteredFilesView = FilteredFilesView; + exports.ForeignKeySubsetCollection = ForeignKeySubsetCollection; exports.HelpButtonView = HelpButtonView; exports.HelpImageView = HelpImageView; exports.HelpView = HelpView; exports.IconTableCellView = IconTableCellView; exports.ImageFile = ImageFile; @@ -11782,13 +11746,10 @@ exports.ListItemView = ListItemView; exports.ListView = ListView; exports.LoadingView = LoadingView; exports.LockedView = LockedView; exports.ModelThumbnailView = ModelThumbnailView; - exports.MultiCollection = MultiCollection; - exports.NavigatableChapterItemView = NavigatableChapterItemView; - exports.NavigatablePageItemView = NavigatablePageItemView; exports.NestedFilesCollection = NestedFilesCollection; exports.NestedFilesView = NestedFilesView; exports.NestedTypeError = NestedTypeError; exports.NotificationsView = NotificationsView; exports.Object = BaseObject; @@ -11797,29 +11758,25 @@ exports.OtherEntriesCollectionView = OtherEntriesCollectionView; exports.OtherEntry = OtherEntry; exports.OtherEntryItemView = OtherEntryItemView; exports.Page = Page; exports.PageConfigurationFileSelectionHandler = PageConfigurationFileSelectionHandler; - exports.PageItemView = PageItemView; exports.PageLink = PageLink; exports.PageLinkConfigurationEditorView = PageLinkConfigurationEditorView; exports.PageLinkFileSelectionHandler = PageLinkFileSelectionHandler; exports.PageLinkInputView = PageLinkInputView; exports.PageLinkItemView = PageLinkItemView; exports.PageLinksCollection = PageLinksCollection; exports.PageLinksView = PageLinksView; - exports.PagePreviewView = PagePreviewView; - exports.PageSelectionView = PageSelectionView; exports.PageThumbnailView = PageThumbnailView; exports.PagesCollection = PagesCollection; exports.PresenceTableCellView = PresenceTableCellView; exports.PreviewEntryData = PreviewEntryData; exports.ProxyUrlInputView = ProxyUrlInputView; exports.PublishEntryView = PublishEntryView; exports.ReferenceInputView = ReferenceInputView; exports.ReusableFile = ReusableFile; - exports.SavingRecordsCollection = SavingRecordsCollection; exports.Scaffold = Scaffold; exports.ScrollingView = ScrollingView; exports.SelectButtonView = SelectButtonView; exports.SelectInputView = SelectInputView; exports.SidebarController = SidebarController; @@ -11830,12 +11787,10 @@ exports.StaticThumbnailView = StaticThumbnailView; exports.Storyline = Storyline; exports.StorylineChaptersCollection = StorylineChaptersCollection; exports.StorylineConfiguration = StorylineConfiguration; exports.StorylineOrdering = StorylineOrdering; - exports.StorylineOutlineView = StorylineOutlineView; - exports.StorylinePickerView = StorylinePickerView; exports.StorylineScaffold = StorylineScaffold; exports.StorylineTransitiveChildPages = StorylineTransitiveChildPages; exports.StorylinesCollection = StorylinesCollection; exports.SubsetCollection = SubsetCollection; exports.TableCellView = TableCellView; @@ -11866,25 +11821,30 @@ exports.VideoFile = VideoFile; exports.Widget = Widget; exports.WidgetConfiguration = WidgetConfiguration; exports.WidgetConfigurationFileSelectionHandler = WidgetConfigurationFileSelectionHandler; exports.WidgetItemView = WidgetItemView; + exports.WidgetTypes = WidgetTypes; exports.WidgetsCollection = WidgetsCollection; exports.addAndReturnModel = addAndReturnModel; exports.app = app; exports.authenticationProvider = authenticationProvider; + exports.configurationContainer = configurationContainer; + exports.cssModulesUtils = cssModulesUtils; exports.delayedDestroying = delayedDestroying; exports.dialogView = dialogView; exports.editor = editor; + exports.entryTypeEditorControllerUrls = entryTypeEditorControllerUrls; exports.failureIndicatingView = failureIndicatingView; exports.failureTracking = failureTracking; exports.fileWithType = fileWithType; exports.filesCountWatcher = filesCountWatcher; exports.formDataUtils = formDataUtils; exports.i18nUtils = i18nUtils; exports.inputView = inputView; exports.inputWithPlaceholderText = inputWithPlaceholderText; exports.loadable = loadable; + exports.modelLifecycleTrackingView = modelLifecycleTrackingView; exports.orderedCollection = orderedCollection; exports.persistedPromise = persistedPromise; exports.polling = polling; exports.retryable = retryable; exports.selectableView = selectableView;