Files

566 lines
25 KiB
JavaScript

/**
* Cause gallery shortcode is hardcoded into WP core and has a lot of relations with Media Manager and other parts
* had to copy related code and adjust to the DG needs.
* Since WP core make use of shortcode tag to get (build) needed function name while handling shortcode (especially in visual editor),
* named all DG related variables and functions using prefix or suffix (depending on context) same as shortcode tag - dg
* As long as WP core has strange conditional statements for order and orderby attributes had to rename them by adding dg prefix.
*
*/
( function ($, _) {
// Check if environment is suitable for the code to be executed
if ( typeof wp.media.collection !== 'function' ) return;
var l10n,
media = wp.media;
// Link any localized strings.
l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;
jQuery.extend(l10n, DGl10n);
// Based on code from /wp-includes/js/media-views.js WP 4.1
/**
* wp.media.controller.dgEdit
*
* A state for editing a dg's images and settings.
*
* @class
* @augments wp.media.controller.Library
* @augments wp.media.controller.State
* @augments Backbone.Model
*
* @param {object} [attributes] The attributes hash passed to the state.
* @param {string} [attributes.id=dg-edit] Unique identifier.
* @param {string} [attributes.title=Edit Document Gallery] Title for the state. Displays in the frame's title region.
* @param {wp.media.model.Attachments} [attributes.library] The collection of attachments in the dg.
* If one is not supplied, an empty media.model.Selection collection is created.
* @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
* @param {boolean} [attributes.searchable=false] Whether the library is searchable.
* @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
* @param {string|false} [attributes.content=browse] Initial mode for the content region.
* @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region.
* @param {boolean} [attributes.describe=true] Whether to offer UI to describe attachments - e.g. captioning images in a dg.
* @param {boolean} [attributes.displaySettings=true] Whether to show the attachment display settings interface.
* @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable.
* @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
* @param {boolean} [attributes.editing=false] Whether the dg is being created, or editing an existing instance.
* @param {int} [attributes.priority=60] The priority for the state link in the media menu.
* @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
* Defaults to false for this state, because the library passed in *is* the selection.
* @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`.
* If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
*/
media.controller.dgEdit = media.controller.Library.extend({
defaults: {
id: 'dg-edit',
title: l10n.editdgTitle,
multiple: false,
searchable: false,
date: false,
sortable: true,
display: false,
content: 'browse',
toolbar: 'dg-edit',
describe: true,
displaySettings: true,
dragInfo: true,
idealColumnWidth: 170,
editing: false,
priority: 60,
syncSelection: false
},
/**
* @since 3.5.0
*/
initialize: function () {
// If we haven't been provided a `library`, create a `Selection`.
if (!this.get('library'))
this.set('library', new media.model.Selection());
// The single `Attachment` view to be used in the `Attachments` view.
if (!this.get('AttachmentView'))
this.set('AttachmentView', media.view.Attachment.EditLibrary);
media.controller.Library.prototype.initialize.apply(this, arguments);
},
/**
* @since 3.5.0
*/
activate: function () {
var library = this.get('library');
// Limit the library to images only.
//library.props.set( 'type', 'image' );
// Watch for uploaded attachments.
this.get('library').observe(wp.Uploader.queue);
this.frame.on('content:render:browse', this.dgSettings, this);
media.controller.Library.prototype.activate.apply(this, arguments);
},
/**
* @since 3.5.0
*/
deactivate: function () {
// Stop watching for uploaded attachments.
this.get('library').unobserve(wp.Uploader.queue);
this.frame.off('content:render:browse', this.dgSettings, this);
media.controller.Library.prototype.deactivate.apply(this, arguments);
},
/**
* @since 3.5.0
*
* @param browser
*/
dgSettings: function (browser) {
if (!this.get('displaySettings')) {
return;
}
var library = this.get('library');
if (!library || !browser) {
return;
}
library.dg = library.dg || new Backbone.Model();
browser.sidebar.set({
dg: new media.view.Settings.dg({
controller: this,
model: library.dg,
priority: 40
})
});
browser.toolbar.set('reverse', {
text: l10n.reverseOrder,
priority: 80,
click: function () {
library.reset(library.toArray().reverse());
}
});
}
});
/**
* A state for selecting more images to add to a dg.
*
* @class
* @augments wp.media.controller.Library
* @augments wp.media.controller.State
* @augments Backbone.Model
*
* @param {object} [attributes] The attributes hash passed to the state.
* @param {string} [attributes.id=dg-library] Unique identifier.
* @param {string} [attributes.title=Add to Document Gallery] Title for the state. Displays in the frame's title region.
* @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
* @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
* If one is not supplied, a collection of all images will be created.
* @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
* Accepts 'all', 'uploaded', or 'unattached'.
* @param {string} [attributes.menu=dg] Initial mode for the menu region.
* @param {string} [attributes.content=upload] Initial mode for the content region.
* Overridden by persistent user setting if 'contentUserSetting' is true.
* @param {string} [attributes.router=browse] Initial mode for the router region.
* @param {string} [attributes.toolbar=dg-add] Initial mode for the toolbar region.
* @param {boolean} [attributes.searchable=true] Whether the library is searchable.
* @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
* @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
* @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
* @param {int} [attributes.priority=100] The priority for the state link in the media menu.
* @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
* Defaults to false because for this state, because the library of the Edit Document Gallery state is the selection.
*/
media.controller.dgAdd = media.controller.Library.extend({
defaults: _.defaults({
id: 'dg-library',
title: l10n.addTodgTitle,
multiple: 'add',
filterable: 'uploaded',
menu: 'dg',
toolbar: 'dg-add',
priority: 100,
syncSelection: false
}, media.controller.Library.prototype.defaults),
/**
* @since 3.5.0
*/
initialize: function () {
// If a library wasn't supplied, create a library of images.
if (!this.get('library'))
this.set('library', media.query());
media.controller.Library.prototype.initialize.apply(this, arguments);
},
/**
* @since 3.5.0
*/
activate: function () {
var library = this.get('library'),
edit = this.frame.state('dg-edit').get('library');
if (this.editLibrary && this.editLibrary !== edit)
library.unobserve(this.editLibrary);
// Accepts attachments that exist in the original library and
// that do not exist in dg's library.
library.validator = function (attachment) {
return !!this.mirroring.get(attachment.cid) && !edit.get(attachment.cid) && media.model.Selection.prototype.validator.apply(this, arguments);
};
// Reset the library to ensure that all attachments are re-added
// to the collection. Do so silently, as calling `observe` will
// trigger the `reset` event.
library.reset(library.mirroring.models, {silent: true});
library.observe(edit);
this.editLibrary = edit;
media.controller.Library.prototype.activate.apply(this, arguments);
}
});
/**
* wp.media.view.Settings.dg
*
* @class
* @augments wp.media.view.Settings
* @augments wp.media.View
* @augments wp.Backbone.View
* @augments Backbone.View
*/
media.view.Settings.dg = media.view.Settings.extend({
update: function( key ) {
var value = this.model.get( key ),
$setting = this.$('[data-setting="' + key + '"]'),
$buttons, $value;
// Bail if we didn't find a matching setting.
if ( ! $setting.length ) {
return;
}
// Attempt to determine how the setting is rendered and update
// the selected value.
// Handle dropdowns.
if ( $setting.is('select') ) {
$value = $setting.find('[value="' + value + '"]');
if ( $value.length ) {
$setting.find('option').prop( 'selected', false );
$value.prop( 'selected', true );
} else {
// If we can't find the desired value, record what *is* selected.
this.model.set( key, $setting.find(':selected').val() );
}
// Handle button groups.
} else if ( $setting.hasClass('button-group') ) {
$buttons = $setting.find('button').removeClass('active');
$buttons.filter( '[value="' + value + '"]' ).addClass('active');
// Handle text inputs and textareas.
} else if ( $setting.is('input[type="text"], input[type="number"], textarea') ) {
if ( ! $setting.is(':focus') ) {
$setting.val( value );
}
// Handle checkboxes.
} else if ( $setting.is('input[type="checkbox"]') ) {
$setting.prop( 'checked', !! value && 'false' !== value );
}
},
className: 'collection-settings dg-settings',
template: media.template('dg-settings')
});
// supersede the default MediaFrame.Post view
// hint from https://gist.github.com/Fab1en/4586865
var wpMediaFramePost = wp.media.view.MediaFrame.Post;
wp.media.view.MediaFrame.Post = wpMediaFramePost.extend(
{
initialize: function () {
wpMediaFramePost.prototype.initialize.apply(this, arguments);
this.states.add([
new media.controller.Library({
id: 'dg',
title: l10n.dgMenuTitle,
priority: 50,
toolbar: 'main-dg',
filterable: 'all',
multiple: 'add',
editable: false,
library: media.query(this.options.library)
}),
// Document Gallery states.
new media.controller.dgEdit({
library: this.options.selection,
editing: this.options.editing,
menu: 'dg'
}),
new media.controller.dgAdd()
]);
this.on('menu:create:dg', this.createMenu, this);
this.on('toolbar:create:main-dg', this.createToolbar, this);
this.on('menu:render:dg', this.dgMenu, this);
this.on('toolbar:render:main-dg', this.maindgToolbar, this);
this.on('toolbar:render:dg-edit', this.dgEditToolbar, this);
this.on('toolbar:render:dg-add', this.dgAddToolbar, this);
},
/**
* @param {wp.Backbone.View} view
*/
dgMenu: function (view) {
var lastState = this.lastState(),
previous = lastState && lastState.id,
frame = this;
view.set({
cancel: {
text: l10n.canceldgTitle,
priority: 20,
click: function () {
if (previous) {
frame.setState(previous);
} else {
frame.close();
}
// Keep focus inside media modal
// after canceling a dg
this.controller.modal.focusManager.focus();
}
},
separateCancel: new media.View({
className: 'separator',
priority: 40
})
});
},
/**
* @param {wp.Backbone.View} view
*/
maindgToolbar: function (view) {
var controller = this;
this.selectionStatusToolbar(view);
view.set('dg', {
style: 'primary',
text: l10n.dgButton,
priority: 60,
requires: {selection: true},
click: function () {
var selection = controller.state().get('selection'),
edit = controller.state('dg-edit'),
models = selection.models;
edit.set('library', new media.model.Selection(models, {
props: selection.props.toJSON(),
multiple: true
}));
this.controller.setState('dg-edit');
// Keep focus inside media modal
// after jumping to dg view
this.controller.modal.focusManager.focus();
}
});
},
dgEditToolbar: function () {
var editing = this.state().get('editing');
this.toolbar.set(new media.view.Toolbar({
controller: this,
items: {
insert: {
style: 'primary',
text: editing ? l10n.updatedg : l10n.insertdg,
priority: 80,
requires: {library: true},
/**
* @fires wp.media.controller.State#update
*/
click: function () {
var controller = this.controller,
state = controller.state();
controller.close();
if ( editing ) {
state.trigger('update', state.get('library')); // calling for workflow.state update, so just execute its contents
} else {
wp.media.editor.insert(wp.media.dg.shortcode(state.get('library')).string().replace(/\sdgorder=/ig, ' order=').replace(/\sdgorderby=/ig, ' orderby='));
}
// Restore and reset the default state.
controller.setState(controller.options.state);
controller.reset();
if (typeof tinyMCE != 'undefined' && tinyMCE.activeEditor !== null) {
tinyMCE.activeEditor.fire('update.dg');
}
}
}
}
}));
},
dgAddToolbar: function () {
this.toolbar.set(new media.view.Toolbar({
controller: this,
items: {
insert: {
style: 'primary',
text: l10n.addTodg,
priority: 80,
requires: {selection: true},
/**
* @fires wp.media.controller.State#reset
*/
click: function () {
var controller = this.controller,
state = controller.state(),
edit = controller.state('dg-edit');
edit.get('library').add(state.get('selection').models);
state.trigger('reset');
controller.setState('dg-edit');
}
}
}
}));
}
});
// Based on code from /wp-includes/js/media-editor.js WP 4.1
wp.media._dgDefaults = {
id: wp.media.view.settings.post && wp.media.view.settings.post.id,
columns: dgDefaults.columns,
fancy: dgDefaults.fancy,
relation: dgDefaults.relation,
limit: dgDefaults.limit,
mime_types: dgDefaults.mime_types,
post_status: dgDefaults.post_status,
post_type: dgDefaults.post_type,
attachment_pg: dgDefaults.attachment_pg,
descriptions: dgDefaults.descriptions,
new_window: dgDefaults.new_window,
paginate: dgDefaults.paginate,
dgorder: dgDefaults.order,
dgorderby: dgDefaults.orderby
};
if (wp.media.view.settings.dgDefaults) {
wp.media.dgDefaults = _.extend({}, wp.media._dgDefaults, wp.media.view.settings.dgDefaults);
} else {
wp.media.dgDefaults = wp.media._dgDefaults;
}
wp.media.dg = new wp.media.collection({
tag: 'dg',
//type : 'image',
editTitle: wp.media.view.l10n.editdgTitle,
defaults: wp.media.dgDefaults,
setDefaults: function (attrs) {
var self = this, changed = !_.isEqual(wp.media.dgDefaults, wp.media._dgDefaults);
_.each(this.defaults, function (value, key) {
attrs[key] = self.coerce(attrs, key);
if (value === attrs[key] && ( !changed || value === wp.media._dgDefaults[key] )) {
delete attrs[key];
}
});
return attrs;
}
});
}(jQuery, _));
// Check if environment is suitable for the code to be executed
if ( typeof window.wp.mce !== 'undefined' && typeof window.wp.mce.views !== 'undefined' ) {
// Based on code from /wp-includes/js/mce-view.js WP 4.2
/*
* The WordPress core TinyMCE views.
* View for the dg shortcode.
*/
(function (window, views, media, $) {
var dg;
base = {
state: [],
edit: function (text, update) {
// currently the shortcode *must* include ids attribute and may include any of the listed attributes to be editable
if (text.search(/\sids\s*=/gi) == -1 || text.search(/\s(?!(?:ids|attachment_pg|columns|new_window|descriptions|fancy|orderby|order|paginate|limit)\s*=)[\w\-]+\s*=/gi) > -1) {
tinyMCE.activeEditor.windowManager.alert(DGl10n.unfitSCalert);
} else {
var type = this.type,
frame = media[type].edit(text.replace(/\sorder\s*=/ig, ' dgorder=').replace(/\sorderby\s*=/ig, ' dgorderby='));
this.pausePlayers && this.pausePlayers();
_.each(this.state, function (state) {
frame.state(state).on('update', function (selection) {
update(media[type].shortcode(selection).string().replace(/\sdgorder=/ig, ' order=').replace(/\sdgorderby=/ig, ' orderby='), type === 'dg');
});
});
frame.on('close', function () {
frame.detach();
});
frame.open();
}
}
};
dg = _.extend({}, base, {
state: ['dg-edit'],
template: media.template('editor-dg'),
initialize: function () {
var attachments = media.dg.attachments(this.shortcode, media.view.settings.post.id),
attrs = this.shortcode.attrs.named,
sc = this.text,
atts = {},
self = this;
for (prop in attrs) {
if (sc.indexOf(' ' + prop + '=') > -1) {
atts[prop] = attrs[prop];
}
}
if (sc.indexOf(' dgorderby=') > -1) {
atts['orderby'] = attrs['dgorderby'];
}
if (sc.indexOf(' dgorder=') > -1) {
atts['order'] = attrs['dgorder'];
}
self.render('<div data-shortcode="' +
encodeURIComponent(JSON.stringify(atts)) +
'"><div class="loading-placeholder"><div class="dashicons dashicons-admin-media"></div><div class="wpview-loading"><ins></ins></div></div></div>');
}
});
views.register('dg', _.extend({}, dg));
})(window, window.wp.mce.views, window.wp.media, window.jQuery);
}