| 1 |
/** |
| 2 |
* Used for character counters |
| 3 |
*/ |
| 4 |
window.CMB2 = window.CMB2 || {}; |
| 5 |
window.CMB2.charcounter = window.CMB2.charcounter || {}; |
| 6 |
|
| 7 |
( function(window, document, $, cmb, counter ) { |
| 8 |
'use strict'; |
| 9 |
|
| 10 |
if ( ! wp.utils || ! wp.utils.WordCounter ) { |
| 11 |
return cmb.log( 'Cannot find wp.utils!' ); |
| 12 |
} |
| 13 |
|
| 14 |
// Private variables |
| 15 |
counter.counters = {}; |
| 16 |
var counters = counter.counters; |
| 17 |
var wpCounter = new wp.utils.WordCounter(); |
| 18 |
|
| 19 |
/** |
| 20 |
* Update a field's character counter |
| 21 |
* |
| 22 |
* @since 2.7.0 |
| 23 |
* |
| 24 |
* @param {string} field_id |
| 25 |
* |
| 26 |
* @return {int} |
| 27 |
*/ |
| 28 |
counter.updateCounter = function( field_id ) { |
| 29 |
// No counter? |
| 30 |
if ( ! counters.hasOwnProperty( field_id ) ) { |
| 31 |
return null; |
| 32 |
} |
| 33 |
|
| 34 |
var instance = counters[ field_id ]; |
| 35 |
var wysiwyg = instance.editor && ! instance.editor.isHidden(); |
| 36 |
|
| 37 |
// Are we dealing with WYSIWYG visual editor, or textarea / WYSIWYG textarea? |
| 38 |
var text = wysiwyg ? instance.editor.getContent( { format: 'raw' } ) : cmb.$id( field_id ).val().trim(); |
| 39 |
var count = wpCounter.count( text, instance.type ); |
| 40 |
var exceeded = instance.max && count > instance.max; |
| 41 |
|
| 42 |
// Number remaining when max is defined |
| 43 |
var val = instance.max ? instance.max - count : count; |
| 44 |
|
| 45 |
// Over maximum? |
| 46 |
instance.$el.parents( '.cmb2-char-counter-wrap' )[ exceeded ? 'addClass' : 'removeClass' ]( 'cmb2-max-exceeded' ); |
| 47 |
|
| 48 |
// Update counter, and update counter input width. |
| 49 |
instance.$el.val( val ).outerWidth( ( ( 8 * String( val ).length ) + 15 ) + 'px' ); |
| 50 |
|
| 51 |
return count; |
| 52 |
}; |
| 53 |
|
| 54 |
counter.instantiate = function( $el ) { |
| 55 |
var data = $el.data(); |
| 56 |
|
| 57 |
// Add counter details if not already done |
| 58 |
if ( ! ( data.fieldId in counters ) ) { |
| 59 |
|
| 60 |
var instance = { |
| 61 |
$el : $el, |
| 62 |
max : data.max, |
| 63 |
type : 'words' === data.counterType ? 'words' : 'characters_including_spaces', |
| 64 |
editor : false, |
| 65 |
}; |
| 66 |
|
| 67 |
counters[ data.fieldId ] = instance; |
| 68 |
|
| 69 |
// Initialise counter |
| 70 |
counter.updateCounter( data.fieldId ); |
| 71 |
} |
| 72 |
}; |
| 73 |
|
| 74 |
/** |
| 75 |
* Initializes all character counters. Hooked to cmb_init. |
| 76 |
* |
| 77 |
* @since 2.7.0 |
| 78 |
* |
| 79 |
* @param {bool} init First init? |
| 80 |
* |
| 81 |
* @return {void} |
| 82 |
*/ |
| 83 |
counter.initAll = function() { |
| 84 |
|
| 85 |
// Gather counters and initialise |
| 86 |
$( '.cmb2-char-counter' ).each( function() { |
| 87 |
counter.instantiate( $( this ) ); |
| 88 |
}); |
| 89 |
}; |
| 90 |
|
| 91 |
/** |
| 92 |
* Initializes WYSIWYG editors. Hooked to tinymce-editor-init |
| 93 |
* |
| 94 |
* @since 2.7.0 |
| 95 |
* |
| 96 |
* @param {object} evt |
| 97 |
* @param {object} editor |
| 98 |
* |
| 99 |
* @return {void} |
| 100 |
*/ |
| 101 |
counter.initWysiwyg = function( evt, editor ) { |
| 102 |
|
| 103 |
// Check if it's one of our WYSIWYGs |
| 104 |
// Should have already been registered in counters via hidden textarea |
| 105 |
if ( editor.id in counters ) { |
| 106 |
|
| 107 |
// Add editor to counter |
| 108 |
counters[ editor.id ].editor = editor; |
| 109 |
|
| 110 |
// Add nodechange event |
| 111 |
editor.on( 'nodechange keyup', counter.countWysiwyg ); |
| 112 |
} |
| 113 |
}; |
| 114 |
|
| 115 |
/** |
| 116 |
* Initializes after a new repeatable row has been added. Hooked to cmb2_add_row |
| 117 |
* |
| 118 |
* @since 2.7.0 |
| 119 |
* |
| 120 |
* @param {object} evt A jQuery-normalized event object. |
| 121 |
* @param {object} $row A jQuery dom element object for the group row. |
| 122 |
* |
| 123 |
* @return {void} |
| 124 |
*/ |
| 125 |
counter.addRow = function( evt, $row ) { |
| 126 |
|
| 127 |
// Character counters in row? |
| 128 |
$row.find( '.cmb2-char-counter' ).each( function() { |
| 129 |
|
| 130 |
// Update attributes |
| 131 |
var $this = $( this ); |
| 132 |
var id = $this.attr( 'id' ); |
| 133 |
var field_id = id.replace( /^char-counter-/, '' ); |
| 134 |
$this.attr( 'data-field-id', field_id ).data( 'field-id', field_id ); |
| 135 |
|
| 136 |
counter.instantiate( $this ); |
| 137 |
}); |
| 138 |
}; |
| 139 |
|
| 140 |
/** |
| 141 |
* Clean the counters array. |
| 142 |
* Removes counters after a repeatable row has been removed. Hooked to cmb2_remove_row. |
| 143 |
* |
| 144 |
* @since 2.7.0 |
| 145 |
* |
| 146 |
* @return {void} |
| 147 |
*/ |
| 148 |
counter.cleanCounters = function() { |
| 149 |
var field_id, remove = []; |
| 150 |
|
| 151 |
// Got through counters |
| 152 |
for ( field_id in counters ) { |
| 153 |
// Check for element, gather for removal |
| 154 |
if ( ! document.getElementById( field_id ) ) { |
| 155 |
remove.push( field_id ); |
| 156 |
} |
| 157 |
} |
| 158 |
|
| 159 |
// Anything to remove? |
| 160 |
if ( remove.length ) { |
| 161 |
_.each( remove, function( field_id ) { |
| 162 |
delete counters[ field_id ]; |
| 163 |
}); |
| 164 |
} |
| 165 |
}; |
| 166 |
|
| 167 |
/** |
| 168 |
* Counts the value of wysiwyg on the keyup event. |
| 169 |
* |
| 170 |
* @since 2.7.0 |
| 171 |
* |
| 172 |
* @param {object} evt |
| 173 |
* |
| 174 |
* @return {void} |
| 175 |
*/ |
| 176 |
counter.countWysiwyg = _.throttle( function( evt ) { |
| 177 |
|
| 178 |
// Init event |
| 179 |
if ( evt.hasOwnProperty( 'element' ) ) { |
| 180 |
return counter.updateCounter( $( evt.element ).data( 'id' ) ); |
| 181 |
} |
| 182 |
|
| 183 |
// Nodechange event |
| 184 |
if ( evt.hasOwnProperty( 'currentTarget' ) ) { |
| 185 |
return counter.updateCounter( $( evt.currentTarget ).data( 'id' ) ); |
| 186 |
} |
| 187 |
|
| 188 |
} ); |
| 189 |
|
| 190 |
/** |
| 191 |
* Counts the value of textarea on the keyup event. |
| 192 |
* |
| 193 |
* @since 2.7.0 |
| 194 |
* |
| 195 |
* @param {object} evt |
| 196 |
* |
| 197 |
* @return {void} |
| 198 |
*/ |
| 199 |
counter.countTextarea = _.throttle( function(evt) { |
| 200 |
counter.updateCounter( evt.currentTarget.id ); |
| 201 |
}, 400 ); |
| 202 |
|
| 203 |
// Hook in our event callbacks. |
| 204 |
$( document ) |
| 205 |
.on( 'cmb_init', counter.initAll ) |
| 206 |
.on( 'tinymce-editor-init', counter.initWysiwyg ) |
| 207 |
.on( 'cmb2_add_row', counter.addRow ) |
| 208 |
.on( 'cmb2_remove_row', counter.cleanCounters ) |
| 209 |
.on( 'input keyup', '.cmb2-count-chars', counter.countTextarea ); |
| 210 |
|
| 211 |
} )( window, document, jQuery, window.CMB2, window.CMB2.charcounter ); |
| 212 |
|