PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.14.0
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.14.0
2.14.0 2.13.0 2.13.1 2.12.0 2.11.1 2.11.0 2.10.0 2.9.0 2.7.4 2.7.5 2.7.6 2.7.7 2.8.0 2.8.1 2.9.1 trunk 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 All 81 releases
ablocks / assets / js / media-alt-guard.js

media-alt-guard.js in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.14.0, at assets/js/media-alt-guard.js

162 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Require alt text before an image can be used.
3 *
4 * Alt text cannot be demanded while a file is uploading — it is post meta
5 * written after the attachment exists, and the field to type it into does not
6 * render until the upload has already finished. So the gate is on *use*: the
7 * modal's insert button stays disabled while a selected image has no alt text.
8 *
9 * This is a convenience, not the enforcement. Anything that bypasses the modal —
10 * a pasted block, an import, a REST call — bypasses this too, which is why the
11 * server also refuses to publish a post containing images without alt text. That
12 * check is the one that actually holds.
13 */
14 ( function ( wp ) {
15 'use strict';
16
17 if ( ! wp || ! wp.media || ! wp.media.view ) {
18 return;
19 }
20
21 var strings = window.aBlocksAltGuard || {};
22 var MESSAGE = strings.message || 'Add alt text before using this image.';
23 var HINT = strings.hint || '';
24
25 /**
26 * Does this attachment still need alt text?
27 *
28 * Only images: audio, video and documents have no alt text to give, and
29 * blocking them would be nonsense.
30 *
31 * @param {Object} model Attachment model.
32 * @return {boolean} True when alt text is missing.
33 */
34 function needsAlt( model ) {
35 if ( ! model || 'image' !== model.get( 'type' ) ) {
36 return false;
37 }
38 var alt = model.get( 'alt' );
39 return ! alt || ! String( alt ).trim();
40 }
41
42 /**
43 * Which of the selected attachments are missing alt text.
44 *
45 * @param {Object} selection Backbone collection.
46 * @return {Array} Offending models.
47 */
48 function offenders( selection ) {
49 if ( ! selection || ! selection.models ) {
50 return [];
51 }
52 return selection.models.filter( needsAlt );
53 }
54
55 /**
56 * Enable or disable a toolbar's primary button to match the selection.
57 *
58 * @param {Object} toolbar Media toolbar view.
59 */
60 function syncToolbar( toolbar ) {
61 if ( ! toolbar || ! toolbar.get ) {
62 return;
63 }
64
65 var controller = toolbar.controller;
66 var state = controller && controller.state && controller.state();
67 var selection = state && state.get && state.get( 'selection' );
68 var blocked = offenders( selection );
69
70 [ 'select', 'insert' ].forEach( function ( id ) {
71 var button = toolbar.get( id );
72 if ( ! button || ! button.$el ) {
73 return;
74 }
75 button.model.set( 'disabled', blocked.length > 0 );
76 button.$el.attr(
77 'title',
78 blocked.length > 0 ? MESSAGE : ''
79 );
80 } );
81
82 var $notice = toolbar.$el.find( '.ablocks-alt-required' );
83 if ( blocked.length > 0 ) {
84 if ( ! $notice.length ) {
85 toolbar.$el.prepend(
86 '<div class="ablocks-alt-required" style="flex:1 1 100%;color:#8a1f1f;font-size:12px;padding:4px 0;">' +
87 MESSAGE +
88 '</div>'
89 );
90 }
91 } else {
92 $notice.remove();
93 }
94 }
95
96 // The toolbar is rebuilt whenever the state changes, so the hook is on the
97 // view rather than on a one-time DOM query.
98 var Toolbar = wp.media.view.Toolbar;
99 wp.media.view.Toolbar = Toolbar.extend( {
100 initialize: function () {
101 Toolbar.prototype.initialize.apply( this, arguments );
102
103 var self = this;
104 var controller = this.controller;
105 if ( ! controller || ! controller.state ) {
106 return;
107 }
108
109 var state = controller.state();
110 var selection = state && state.get && state.get( 'selection' );
111
112 if ( selection ) {
113 // 'change:alt' fires as soon as the field is edited, so the
114 // button unlocks while the user is still looking at it.
115 this.listenTo( selection, 'add remove reset change:alt', function () {
116 syncToolbar( self );
117 } );
118 }
119
120 this.listenTo( controller, 'content:render', function () {
121 syncToolbar( self );
122 } );
123
124 window.setTimeout( function () {
125 syncToolbar( self );
126 }, 0 );
127 },
128 } );
129
130 // Mark the field itself, so the reason is visible where the fix happens
131 // rather than only at the button that refuses.
132 if ( wp.media.view.Attachment && wp.media.view.Attachment.Details ) {
133 var Details = wp.media.view.Attachment.Details;
134 wp.media.view.Attachment.Details = Details.extend( {
135 render: function () {
136 Details.prototype.render.apply( this, arguments );
137
138 if ( 'image' !== this.model.get( 'type' ) ) {
139 return this;
140 }
141
142 var $alt = this.$el.find( '.setting[data-setting="alt"]' );
143 if ( ! $alt.length ) {
144 return this;
145 }
146
147 $alt.find( 'input' ).attr( 'required', 'required' );
148
149 if ( HINT && ! $alt.next( '.ablocks-alt-hint' ).length ) {
150 $alt.after(
151 '<span class="ablocks-alt-hint" style="display:block;padding:2px 0 8px;font-size:11px;color:#6c7781;">' +
152 HINT +
153 '</span>'
154 );
155 }
156
157 return this;
158 },
159 } );
160 }
161 } )( window.wp );
162