Permalink
Browse files

Add tab for text tracks

- In setup_file_types, add to VideoFile:
  - settingsDialogTab 'text_tracks' to VideoFile,
  - nestedFileType 'text_track_files',
  - configurationEditorInput for alt_text.
- In setup_file_types, add to TextTrackFile:
  - skipUploadConfirmation,
  - configurationEditorInputs for configuration attributes label, kind
    and srclang,
  - nestedFileTableColumns for configuration attributes label, kind and
    srclang.
- Add templates and/or views as well as stylesheets for:
  - TableCell,
    - DeleteRowTableCell,
    - IconTableCell,
  - NestedFiles,
  - TextTracks,
  - NestedTextTrackItem.
- Add available_text_track_kinds configuration parameter and make it
  accessible in the frontend
  • Loading branch information...
1 parent cb4a3db commit 33e2514b0bd6fc08377b3e0810bcb5c93ca8d07f @aviav aviav committed Sep 19, 2016
Showing with 807 additions and 30 deletions.
  1. +1 −0 app/assets/javascripts/pageflow/editor/api/file_type.js
  2. +47 −1 app/assets/javascripts/pageflow/editor/initializers/setup_file_types.js
  3. +1 −5 app/assets/javascripts/pageflow/editor/models/text_track_file.js
  4. +1 −0 app/assets/javascripts/pageflow/editor/templates/delete_row_table_cell.jst.ejs
  5. +1 −0 app/assets/javascripts/pageflow/editor/templates/nested_files.jst.ejs
  6. 0 app/assets/javascripts/pageflow/editor/templates/table_cell.jst.ejs
  7. +11 −0 app/assets/javascripts/pageflow/editor/templates/text_tracks.jst.ejs
  8. +89 −0 app/assets/javascripts/pageflow/editor/views/nested_files_view.js
  9. +57 −0 app/assets/javascripts/pageflow/editor/views/text_tracks_view.js
  10. +4 −4 app/assets/javascripts/pageflow/ui/views/mixins/input_view.js
  11. +63 −0 app/assets/javascripts/pageflow/ui/views/table_cells/delete_row_table_cell_view.js
  12. +3 −3 app/assets/javascripts/pageflow/ui/views/table_cells/enum_table_cell_view.js
  13. +40 −0 app/assets/javascripts/pageflow/ui/views/table_cells/icon_table_cell_view.js
  14. +2 −2 app/assets/javascripts/pageflow/ui/views/table_cells/presence_table_cell_view.js
  15. +21 −5 app/assets/javascripts/pageflow/ui/views/table_cells/table_cell_view.js
  16. +35 −2 app/assets/javascripts/pageflow/ui/views/table_cells/text_table_cell_view.js
  17. +6 −0 app/assets/stylesheets/pageflow/editor/text_tracks.scss
  18. +3 −1 app/assets/stylesheets/pageflow/ui.scss
  19. +6 −0 app/assets/stylesheets/pageflow/ui/table_cells/delete_row_table_cell.scss
  20. +16 −0 app/assets/stylesheets/pageflow/ui/table_cells/icon_table_cell.scss
  21. +15 −2 app/views/pageflow/config/_editor_seeds.json.jbuilder
  22. +8 −1 app/views/pageflow/editor/files/_file.json.jbuilder
  23. +6 −0 lib/pageflow/configuration.rb
  24. +21 −0 spec/javascripts/models/text_track_file_spec.js
  25. +135 −0 spec/javascripts/pageflow/ui/views/table_cells/delete_row_table_cell_view_spec.js
  26. +75 −0 spec/javascripts/pageflow/ui/views/table_cells/icon_table_cell_view_spec.js
  27. +59 −1 spec/javascripts/pageflow/ui/views/table_cells/table_cell_view_spec.js
  28. +57 −3 spec/javascripts/pageflow/ui/views/table_cells/text_table_cell_view_spec.js
  29. +24 −0 spec/javascripts/views/nested_files_view_spec.js
@@ -6,6 +6,7 @@ pageflow.FileType = pageflow.Object.extend({
this.topLevelType = options.topLevelType;
this.paramKey = options.paramKey;
this.i18nKey = options.i18nKey;
+ this.nestedFileTypes = [];
this.confirmUploadTableColumns = options.confirmUploadTableColumns || [];
this.configurationEditorInputs = [].concat(options.configurationEditorInputs || []);
this.configurationUpdaters = options.configurationUpdaters || [];
@@ -30,7 +30,53 @@ pageflow.app.addInitializer(function(options) {
pageflow.editor.fileTypes.register('text_track_files', {
model: pageflow.TextTrackFile,
- matchUpload: /vtt$/
+ matchUpload: /vtt$/,
+ skipUploadConfirmation: true,
+ configurationEditorInputs: [
+ {
+ name: 'label',
+ inputView: pageflow.TextInputView
+ },
+ {
+ name: 'kind',
+ inputView: pageflow.SelectInputView,
+ inputViewOptions: {
+ values: pageflow.config.availableTextTrackKinds,
+ translationKeyPrefix: 'pageflow.config.text_track_kind'
+ }
+ },
+ {
+ name: 'srclang',
+ inputView: pageflow.TextInputView
+ }
+ ],
+ nestedFileTableColumns: [
+ {
+ name: 'label',
+ cellView: pageflow.TextTableCellView,
+ default: function(options) {
+ if (!!options.model.get(options.contentBinding)) {
+ return I18n.t('pageflow.languages.' + options.model.get(options.contentBinding),
+ {defaultValue: I18n.t('pageflow.languages.unknown')});
+ }
+ else {
+ return I18n.t('pageflow.editor.nested_files.text_track_files.label.missing');
+ }
+ },
+ contentBinding: 'srclang'
+ },
+ {
+ name: 'srclang',
+ cellView: pageflow.PresenceTableCellView
+ },
+ {
+ name: 'kind',
+ cellView: pageflow.IconTableCellView,
+ cellViewOptions: {
+ icons: pageflow.config.availableTextTrackKinds
+ }
+ },
+ ]
});
pageflow.editor.fileTypes.setup(options.config.fileTypes);
@@ -7,12 +7,8 @@ pageflow.TextTrackFile = pageflow.HostedFile.extend({
initialize: function(attributes, options) {
pageflow.UploadedFile.prototype.initialize.apply(this, arguments);
- if(this.isNew()) {
+ if (this.isNew()) {
this.configuration.set('srclang', this.extractLanguageCodeFromFilename());
- if(this.configuration.get('srclang' !== 'null')){
- this.configuration.set('label',
- I18n.t('pageflow.languages.' + this.configuration.get('srclang')));
- }
}
},
@@ -0,0 +1 @@
+<a class="remove" title="<%= I18n.t('pageflow.editor.templates.row.destroy') %>"></a>
@@ -0,0 +1,11 @@
+<div>
+ <div class="files_panel">
+ <a class="upload" href=""><%= I18n.t('pageflow.editor.templates.text_tracks.upload') %></a>
+ </div>
+
+ <div class="selected_file_panel">
+ <h2><%= I18n.t('pageflow.editor.templates.text_tracks.edit_file_header') %></h2>
+ <div class="selected_file_region">
+ </div>
+ </div>
+</div>
@@ -0,0 +1,89 @@
+pageflow.NestedFilesView = Backbone.Marionette.ItemView.extend({
+ template: 'templates/nested_files',
+
+ className: 'nested_files',
+
+ ui: {
+ header: 'h2'
+ },
+
+ initialize: function() {
+ if (!this.options.selection.has('file')) {
+ this.options.selection.set('file', this.collection.first());
+ this.options.selection.set('nextFile', this.collection.at(1));
+ }
+
+ this.listenTo(this.collection, 'add', this.selectNewFile);
+ this.listenTo(this.collection, 'remove', this.selectNextFileIfSelectionDeleted);
+ this.listenTo(this.options.selection, 'change', this.setNextFile);
+ },
+
+ onRender: function() {
+ this.ui.header.text(
+ this.collection.parentModel.get('file_name')
+ );
+
+ this.appendSubview(new pageflow.TableView({
+ collection: this.collection,
+ attributeTranslationKeyPrefixes: [
+ 'pageflow.editor.nested_files.' + this.options.fileType.collectionName
+ ],
+ columns: this.columns(this.options.fileType),
+ selection: this.options.selection,
+ selectionAttribute: 'file'
+ }));
+
+ this.update();
+ },
+
+ update: function() {
+ this.$el.toggleClass('is_empty', this.collection.length === 0);
+ },
+
+ columns: function(fileType) {
+ var nestedFilesColumns = _(fileType.nestedFileTableColumns).map(function(column) {
+ return _.extend({}, column, {
+ configurationAttribute: true
+ });
+ });
+
+ nestedFilesColumns.push({
+ name: 'delete',
+ cellView: pageflow.DeleteRowTableCellView,
+ cellViewOptions: {
+ toggleDeleteButton: 'isUploading',
+ invertToggleDeleteButton: true
+ }
+ });
+
+ return nestedFilesColumns;
+ },
+
+ selectNewFile: function(file) {
+ this.options.selection.set('file', file);
+ this.setNextFile();
+ },
+
+ selectNextFileIfSelectionDeleted: function() {
+ var fileIndex = this.collection.indexOf(this.options.selection.get('file'));
+ if (fileIndex === -1)
+ {
+ var nextFile = this.options.selection.get('nextFile');
+ this.options.selection.set('file', nextFile);
+ }
+ },
+
+ setNextFile: _.debounce(function() {
+ var fileIndex = this.collection.indexOf(this.options.selection.get('file'));
+
+ if (typeof(this.collection.at(fileIndex + 1)) !== 'undefined') {
+ this.options.selection.set('nextFile', this.collection.at(fileIndex + 1));
+ }
+ else if (typeof(this.collection.at(fileIndex - 1)) !== 'undefined') {
+ this.options.selection.set('nextFile', this.collection.at(fileIndex - 1));
+ }
+ else {
+ this.options.selection.set('nextFile', undefined);
+ }
+ }, 200)
+});
@@ -0,0 +1,57 @@
+pageflow.TextTracksView = Backbone.Marionette.Layout.extend({
+ template: 'templates/text_tracks',
+ className: 'text_tracks',
+
+ regions: {
+ selectedFileRegion: '.selected_file_region'
+ },
+
+ ui: {
+ filesPanel: '.files_panel'
+ },
+
+ events: {
+ 'click a.upload': 'upload'
+ },
+
+ initialize: function(options) {
+ this.options = options || {};
+ this.selection = new Backbone.Model();
+ this.listenTo(this.selection, 'change', this.update);
+ },
+
+ onRender: function() {
+ this.nestedFilesView = new pageflow.NestedFilesView({
+ collection: this.model.nestedFiles(this.options.supersetCollection),
+ fileType: pageflow.editor.fileTypes.findByCollectionName('text_track_files'),
+ selection: this.selection,
+ model: this.model
+ });
+
+ this.ui.filesPanel.append(this.subview(this.nestedFilesView).el);
+
+ this.update();
+
+ pageflow.editor.setUploadTargetFile(this.model);
+ },
+
+ onClose: function() {
+ pageflow.editor.setUploadTargetFile(undefined);
+ },
+
+ update: function() {
+ var selectedFile = this.selection.get('file');
+ if (selectedFile) {
+ this.selectedFileRegion.show(new pageflow.EditFileView({
+ model: selectedFile
+ }));
+ }
+ else {
+ this.selectedFileRegion.close();
+ }
+ },
+
+ upload: function() {
+ pageflow.app.trigger('request-upload');
+ }
+});
@@ -80,10 +80,10 @@
* @param {boolean} [options.disabled]
* Render input as disabled.
*
- * @param {string} [options.visibleBinding]
- * Name of an attribute to control whether the input is visible. If
- * the `visible` and `visibleBindingValue` options are not set,
- * input will be visible whenever this attribute as a truthy value.
+ * @param {string} [options.visibleBinding] Name of an attribute to
+ * control whether the input is visible. If the `visible` and
+ * `visibleBindingValue` options are not set, input will be visible
+ * whenever this attribute has a truthy value.
*
* @param {function|boolean} [options.visible]
* A Function taking the value of the `visibleBinding` attribute as
@@ -0,0 +1,63 @@
+/**
+ * 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 {function} [options.toggleDeleteButton]
+ * A function with boolean return value to be called on
+ * this.getModel(). Delete button will be visible only if the
+ * function returns a truthy value.
+ *
+ * @param {boolean} [options.invertToggleDeleteButton]
+ * Invert the return value of `toggleDeleteButton`?
+ *
+ * @since edge
+ */
+pageflow.DeleteRowTableCellView = pageflow.TableCellView.extend({
+ className: 'delete_row_table_cell',
+ template: 'templates/delete_row_table_cell',
+
+ ui: {
+ removeButton: '.remove'
+ },
+
+ events: {
+ 'click .remove': 'destroy',
+
+ 'click': function() {
+ return false;
+ }
+ },
+
+ showButton: function() {
+ if (this.options.toggleDeleteButton) {
+ var context = this.getModel();
+ var toggle = context[this.options.toggleDeleteButton].apply(context);
+
+ if (this.options.invertToggleDeleteButton) {
+ return !toggle;
+ }
+ else {
+ return !!toggle;
+ }
+ }
+ else {
+ return true;
+ }
+ },
+
+ update: function() {
+ this.ui.removeButton.toggleClass('remove', this.showButton());
+
+ this.ui.removeButton.attr('title', this.attributeTranslation('cell_title'));
+ },
+
+ destroy: function() {
+ this.getModel().destroy();
+ }
+});
@@ -6,8 +6,8 @@
*
* The following attribute translations are used:
*
- * - `.cell_text.<attribute_value>` - Useas as cell content.
- * - `.cell_text.blank` - Useas as cell content if attribute is blank.
+ * - `.cell_text.<attribute_value>` - Used as cell content.
+ * - `.cell_text.blank` - Used as cell content if attribute is blank.
* - `.cell_title.<attribute_value>` - Used as title attribute.
* - `.cell_title.blank` - Used as title attribute if attribute is blank.
*
@@ -23,4 +23,4 @@ pageflow.EnumTableCellView = pageflow.TableCellView.extend({
defaultValue: ''
}));
}
-});
+});
@@ -0,0 +1,40 @@
+/**
+ * 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 {string[]} [options.icons]
+ * An array of all possible attribute values to be mapped to HTML
+ * classes of the same name. A global mapping from those classes to
+ * icon mixins is provided in
+ * pageflow/ui/table_cells/icon_table_cell.scss.
+ *
+ * @since edge
+ */
+pageflow.IconTableCellView = pageflow.TableCellView.extend({
+ className: 'icon_table_cell',
+
+ update: function() {
+ var icon = this.attributeValue();
+ var isPresent = !!this.attributeValue();
+
+ this.removeExistingIcons();
+
+ this.$el.attr('title',
+ isPresent ?
+ this.attributeTranslation('cell_title.' + icon, {
+ value: this.attributeValue()
+ }) :
+ this.attributeTranslation('cell_title.blank'));
+ this.$el.addClass(icon);
+ },
+
+ removeExistingIcons: function() {
+ this.$el.removeClass(this.options.icons.join(' '));
+ }
+});
@@ -9,7 +9,7 @@
* - `.cell_title.present` - Used as title attribute if the attribute
* is present. The current attribute value is provided as
* interpolation `%{value}`.
- * - `.cell_title.not_present` - Used as title attribute if the
+ * - `.cell_title.blank` - Used as title attribute if the
* attribute is blank.
*
* @since edge
@@ -28,4 +28,4 @@ pageflow.PresenceTableCellView = pageflow.TableCellView.extend({
this.attributeTranslation('cell_title.blank'));
this.$el.toggleClass('is_present', isPresent);
}
-});
+});
Oops, something went wrong.

0 comments on commit 33e2514

Please sign in to comment.