From 716a1c21b989dfb6eb19287d55f50b9f2d194022 Mon Sep 17 00:00:00 2001 From: Goran Miric Date: Fri, 15 Mar 2019 12:59:30 +0100 Subject: [PATCH 1/2] 48: Accessibility - Count down remaining characters/text. --- textcounter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/textcounter.js b/textcounter.js index beb0afe..6ea021b 100644 --- a/textcounter.js +++ b/textcounter.js @@ -25,6 +25,7 @@ var counterText = base.options.countDown ? base.options.countDownText : base.options.counterText, counterNum = base.options.countDown ? base.options.max : 0, $formatted_counter_text = $('
').addClass(base.options.textCountMessageClass) + .attr('aria-live', 'assertive').attr('aria-atomic', 'true') .html(counterText.replace('%d', '' + counterNum + '')), $count_overflow_text = $('
').addClass(base.options.countOverflowContainerClass); From 5ccb0b1496218c7646ef2b333b959775d96e2f00 Mon Sep 17 00:00:00 2001 From: ractoon Date: Mon, 24 Feb 2020 20:36:37 -0700 Subject: [PATCH 2/2] Merge a11ty improvements from @goranmiric --- README.md | 1 + bower.json | 2 +- package.json | 2 +- textcounter.jquery.json | 2 +- textcounter.js | 10 +++++----- textcounter.min.js | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 716a141..f296fe6 100644 --- a/README.md +++ b/README.md @@ -169,3 +169,4 @@ init : function(el){} // Callback: func - [diptopol](https://github.com/diptopol) - `stopInputAtMaximum` with `twoCharCarriageReturn` count fix, trimmed newline calculation fix, maximum text reached condition fix, text count overflow notification - [trevorloflin](https://github.com/trevorloflin) - `minDisplayCutoff` and `maxDisplayCutoff` options - [t3mujin](https://github.com/t3mujin) - autocustom support (maxlength workaround), text fixes +- [goranmiric ](https://github.com/goranmiric) - accessibility enhancements diff --git a/bower.json b/bower.json index e4a1e27..6dcf984 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "jquery-text-counter", - "version": "0.8.0", + "version": "0.9.0", "main": "textcounter.js", "license": "MIT", "ignore": [ diff --git a/package.json b/package.json index 1fb5004..3405281 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "minimum", "maximum" ], - "version": "0.8.0", + "version": "0.9.0", "author": { "name": "ractoon", "url": "https://www.ractoon.com" diff --git a/textcounter.jquery.json b/textcounter.jquery.json index ea542f7..ffe30e4 100644 --- a/textcounter.jquery.json +++ b/textcounter.jquery.json @@ -9,7 +9,7 @@ "minimum", "maximum" ], - "version": "0.8.0", + "version": "0.9.0", "author": { "name": "ractoon", "url": "https://www.ractoon.com" diff --git a/textcounter.js b/textcounter.js index 6ea021b..52b3147 100644 --- a/textcounter.js +++ b/textcounter.js @@ -1,5 +1,5 @@ /*! -* jQuery Text Counter Plugin v0.8.0 +* jQuery Text Counter Plugin v0.9.0 * https://github.com/ractoon/jQuery-Text-Counter * * Copyright 2014 ractoon @@ -71,7 +71,7 @@ } else if (base.options.max == 'autocustom') { var max = base.$el.attr(base.options.autoCustomAttr); - + if (typeof max !== 'undefined' && max !== false) { base.options.max = max; } @@ -162,11 +162,11 @@ base.clearErrors('max'); } } - + // hide the counter if it doesn't meet either the minimum or maximum display cutoff if (base.options.minDisplayCutoff == -1 && base.options.maxDisplayCutoff == -1) { - base.$container.show(); - } else if (textCount <= base.options.min + base.options.minDisplayCutoff) { + base.$container.show(); + } else if (textCount <= base.options.min + base.options.minDisplayCutoff) { base.$container.show(); } else if (base.options.max !== -1 && textCount >= base.options.max - base.options.maxDisplayCutoff) { base.$container.show(); diff --git a/textcounter.min.js b/textcounter.min.js index c5d0439..33267f8 100644 --- a/textcounter.min.js +++ b/textcounter.min.js @@ -1,8 +1,8 @@ /*! -* jQuery Text Counter Plugin v0.8.0 +* jQuery Text Counter Plugin v0.9.0 * https://github.com/ractoon/jQuery-Text-Counter * * Copyright 2014 ractoon * Released under the MIT license */ -!function(t){t.textcounter=function(o,n){var e=this;e.$el=t(o),e.el=o,e.$el.data("textcounter",e),e.init=function(){e.options=t.extend({},t.textcounter.defaultOptions,n);var o=e.options.countDown?e.options.countDownText:e.options.counterText,r=e.options.countDown?e.options.max:0,s=t("
").addClass(e.options.textCountMessageClass).html(o.replace("%d",''+r+"")),i=t("
").addClass(e.options.countOverflowContainerClass);e.hideMessage(i),e.$container=t("<"+e.options.countContainerElement+"/>").addClass(e.options.countContainerClass).append(s).append(i),e.$text_counter=e.$container.find("span"),e.$el.after(e.$container),e.$el.bind("keyup.textcounter click.textcounter blur.textcounter focus.textcounter change.textcounter paste.textcounter",e.checkLimits).trigger("click.textcounter"),e.options.init(e.el)},e.checkLimits=function(o){var n=e.$el,r=(e.$container,n.val()),s=0,i=0,a=void 0!==o.originalEvent;if(t.isEmptyObject(r)||(s=e.textCount(r)),"auto"==e.options.max)void 0!==(u=e.$el.attr("maxlength"))&&!1!==u?e.options.max=u:e.$container.text("error: [maxlength] attribute not set");else if("autocustom"==e.options.max){var u=e.$el.attr(e.options.autoCustomAttr);void 0!==u&&!1!==u?e.options.max=u:e.$container.text("error: ["+e.options.autoCustomAttr+"] attribute not set")}if(i=e.options.countDown?e.options.max-s:s,e.setCount(i),e.options.min>0&&a&&(s=e.options.min&&(e.options.mincount(e.el),e.clearErrors("min"))),-1!==e.options.max)if(s===e.options.max&&0!==e.options.max)e.options.maxcount(e.el),e.clearErrors("max");else if(s>e.options.max&&0!==e.options.max)if(e.options.stopInputAtMaximum){var c="";if("word"==e.options.type)for(var p=r.split(/[^\S\n]/g),l=0;l=e.options.max);)void 0!==p[l]&&(c+=p[l]+" ",l++);else{var m=e.options.twoCharCarriageReturn?e.options.max-e.twoCharCarriageReturnCount(r):e.options.max;if(e.options.countSpaces)c=r.substring(0,m);else for(var x=r.split(""),C=x.length,f=0,l=0;f=e.options.max-e.options.maxDisplayCutoff?e.$container.show():e.$container.hide()},e.textCount=function(t){return"word"==e.options.type?e.wordCount(t):e.characterCount(t)},e.wordCount=function(t){return t.trim().replace(/\s+/gi," ").split(" ").length},e.characterCount=function(t){var o=0,n=0;if(e.options.twoCharCarriageReturn&&(n=e.twoCharCarriageReturnCount(t)),o=e.options.countSpaces?t.replace(/[^\S\n|\r|\r\n]/g," ").length:t.replace(/\s/g,"").length,e.options.countExtendedCharacters){var r=t.match(/[^\x00-\xff]/gi);o=null==r?t.length:t.length+r.length}return e.options.twoCharCarriageReturn&&(o+=n),o},e.twoCharCarriageReturnCount=function(t){var o=t.match(/(\r\n|\n|\r)/g),n=0;return null!==o&&(n=o.length),n},e.setCount=function(t){e.$text_counter.text(t)},e.setErrors=function(t){var o=e.$el,n=e.$container,r="";switch(o.addClass(e.options.inputErrorClass),n.addClass(e.options.counterErrorClass),t){case"min":r=e.options.minimumErrorText;break;case"max":r=e.options.maximumErrorText,e.options.countOverflow&&e.setOverflowMessage()}e.options.displayErrorText&&(n.children(".error-text-"+t).length||n.append("<"+e.options.errorTextElement+' class="error-text error-text-'+t+'">'+r+""))},e.setOverflowMessage=function(){e.hideMessage(e.$container.find("."+e.options.textCountMessageClass)),e.removeOverflowMessage();var t=e.options.countOverflowText.replace("%d",e.textCount(e.$el.val())-e.options.max).replace("%type",e.options.type+"s"),o=e.$container.find("."+e.options.countOverflowContainerClass).append(t);e.showMessage(o)},e.removeOverflowMessage=function(){e.$container.find("."+e.options.countOverflowContainerClass).empty()},e.showMessage=function(t){t.css("display","inline")},e.hideMessage=function(t){t.css("display","none")},e.clearErrors=function(t){var o=e.$el,n=e.$container;n.children(".error-text-"+t).remove(),0==n.children(".error-text").length&&(e.removeOverflowMessage(),e.showMessage(e.$container.find("."+e.options.textCountMessageClass)),o.removeClass(e.options.inputErrorClass),n.removeClass(e.options.counterErrorClass))},e.init()},t.textcounter.defaultOptions={type:"character",min:0,max:200,autoCustomAttr:"counterlimit",countContainerElement:"div",countContainerClass:"text-count-wrapper",textCountMessageClass:"text-count-message",textCountClass:"text-count",inputErrorClass:"error",counterErrorClass:"error",counterText:"Total Count: %d",errorTextElement:"div",minimumErrorText:"Minimum not met",maximumErrorText:"Maximum exceeded",displayErrorText:!0,stopInputAtMaximum:!0,countSpaces:!1,countDown:!1,countDownText:"Remaining: %d",countExtendedCharacters:!1,twoCharCarriageReturn:!1,countOverflow:!1,countOverflowText:"Maximum %type exceeded by %d",countOverflowContainerClass:"text-count-overflow-wrapper",minDisplayCutoff:-1,maxDisplayCutoff:-1,maxunder:function(t){},minunder:function(t){},maxcount:function(t){},mincount:function(t){},init:function(t){}},t.fn.textcounter=function(o){return this.each(function(){new t.textcounter(this,o)})}}(jQuery); \ No newline at end of file +; (function ($) { $.textcounter = function (el, options) { var base = this; base.$el = $(el); base.el = el; base.$el.data('textcounter', base); base.init = function () { base.options = $.extend({}, $.textcounter.defaultOptions, options); var counterText = base.options.countDown ? base.options.countDownText : base.options.counterText, counterNum = base.options.countDown ? base.options.max : 0, $formatted_counter_text = $('
').addClass(base.options.textCountMessageClass).attr('aria-live', 'assertive').attr('aria-atomic', 'true').html(counterText.replace('%d', '' + counterNum + '')), $count_overflow_text = $('
').addClass(base.options.countOverflowContainerClass); base.hideMessage($count_overflow_text); base.$container = $('<' + base.options.countContainerElement + '/>').addClass(base.options.countContainerClass).append($formatted_counter_text).append($count_overflow_text); base.$text_counter = base.$container.find('span'); base.$el.after(base.$container); base.$el.bind('keyup.textcounter click.textcounter blur.textcounter focus.textcounter change.textcounter paste.textcounter', base.checkLimits).trigger('click.textcounter'); base.options.init(base.el) }; base.checkLimits = function (e) { var $this = base.$el, $countEl = base.$container, $text = $this.val(), textCount = 0, textTotalCount = 0, eventTriggered = e.originalEvent === undefined ? false : true; if (!$.isEmptyObject($text)) { textCount = base.textCount($text) } if (base.options.max == 'auto') { var max = base.$el.attr('maxlength'); if (typeof max !== 'undefined' && max !== false) { base.options.max = max } else { base.$container.text('error: [maxlength] attribute not set') } } else if (base.options.max == 'autocustom') { var max = base.$el.attr(base.options.autoCustomAttr); if (typeof max !== 'undefined' && max !== false) { base.options.max = max } else { base.$container.text('error: [' + base.options.autoCustomAttr + '] attribute not set') } } textTotalCount = base.options.countDown ? base.options.max - textCount : textCount; base.setCount(textTotalCount); if (base.options.min > 0 && eventTriggered) { if (textCount < base.options.min) { base.setErrors('min'); base.options.minunder(base.el) } else if (textCount >= base.options.min) { base.options.mincount(base.el); base.clearErrors('min') } } if (base.options.max !== -1) { if (textCount === base.options.max && base.options.max !== 0) { base.options.maxcount(base.el); base.clearErrors('max') } else if (textCount > base.options.max && base.options.max !== 0) { if (base.options.stopInputAtMaximum) { var trimmedString = ''; if (base.options.type == "word") { var wordArray = $text.split(/[^\S\n]/g); var i = 0; while (i < wordArray.length) { if (i >= base.options.max) { break } if (wordArray[i] !== undefined) { trimmedString += wordArray[i] + ' '; i += 1 } } } else { var maxLimit = (base.options.twoCharCarriageReturn) ? base.options.max - base.twoCharCarriageReturnCount($text) : base.options.max; if (base.options.countSpaces) { trimmedString = $text.substring(0, maxLimit) } else { var charArray = $text.split(''), totalCharacters = charArray.length, charCount = 0, i = 0; while (charCount < maxLimit && i < totalCharacters) { if (charArray[i] !== ' ') { charCount += 1 } trimmedString += charArray[i++] } } } $this.val(trimmedString.trim()); textCount = base.textCount($this.val()); textTotalCount = base.options.countDown ? base.options.max - textCount : textCount; base.setCount(textTotalCount) } else { base.setErrors('max') } } else { base.options.maxunder(base.el); base.clearErrors('max') } } if (base.options.minDisplayCutoff == -1 && base.options.maxDisplayCutoff == -1) { base.$container.show() } else if (textCount <= base.options.min + base.options.minDisplayCutoff) { base.$container.show() } else if (base.options.max !== -1 && textCount >= base.options.max - base.options.maxDisplayCutoff) { base.$container.show() } else { base.$container.hide() } }; base.textCount = function (text) { var textCount = 0; if (base.options.type == "word") { textCount = base.wordCount(text) } else { textCount = base.characterCount(text) } return textCount }; base.wordCount = function (text) { return text.trim().replace(/\s+/gi, ' ').split(' ').length }; base.characterCount = function (text) { var textCount = 0, carriageReturnsCount = 0; if (base.options.twoCharCarriageReturn) { carriageReturnsCount = base.twoCharCarriageReturnCount(text) } if (base.options.countSpaces) { textCount = text.replace(/[^\S\n|\r|\r\n]/g, ' ').length } else { textCount = text.replace(/\s/g, '').length } if (base.options.countExtendedCharacters) { var extended = text.match(/[^\x00-\xff]/gi); if (extended == null) { textCount = text.length } else { textCount = text.length + extended.length } } if (base.options.twoCharCarriageReturn) { textCount += carriageReturnsCount } return textCount }; base.twoCharCarriageReturnCount = function (text) { var carriageReturns = text.match(/(\r\n|\n|\r)/g), carriageReturnsCount = 0; if (carriageReturns !== null) { carriageReturnsCount = carriageReturns.length } return carriageReturnsCount }; base.setCount = function (count) { base.$text_counter.text(count) }; base.setErrors = function (type) { var $this = base.$el, $countEl = base.$container, errorText = ''; $this.addClass(base.options.inputErrorClass); $countEl.addClass(base.options.counterErrorClass); switch (type) { case 'min': errorText = base.options.minimumErrorText; break; case 'max': errorText = base.options.maximumErrorText; if (base.options.countOverflow) { base.setOverflowMessage() } break }if (base.options.displayErrorText) { if (!$countEl.children('.error-text-' + type).length) { $countEl.append('<' + base.options.errorTextElement + ' class="error-text error-text-' + type + '">' + errorText + '') } } }; base.setOverflowMessage = function () { base.hideMessage(base.$container.find('.' + base.options.textCountMessageClass)); base.removeOverflowMessage(); var overflowText = base.options.countOverflowText.replace('%d', base.textCount(base.$el.val()) - base.options.max).replace('%type', base.options.type + 's'); var overflowDiv = base.$container.find('.' + base.options.countOverflowContainerClass).append(overflowText); base.showMessage(overflowDiv) }, base.removeOverflowMessage = function () { base.$container.find('.' + base.options.countOverflowContainerClass).empty() }, base.showMessage = function ($selector) { $selector.css('display', 'inline') }, base.hideMessage = function ($selector) { $selector.css('display', 'none') }, base.clearErrors = function (type) { var $this = base.$el, $countEl = base.$container; $countEl.children('.error-text-' + type).remove(); if ($countEl.children('.error-text').length == 0) { base.removeOverflowMessage(); base.showMessage(base.$container.find('.' + base.options.textCountMessageClass)); $this.removeClass(base.options.inputErrorClass); $countEl.removeClass(base.options.counterErrorClass) } }; base.init() }; $.textcounter.defaultOptions = { 'type': "character", 'min': 0, 'max': 200, 'autoCustomAttr': "counterlimit", 'countContainerElement': "div", 'countContainerClass': "text-count-wrapper", 'textCountMessageClass': "text-count-message", 'textCountClass': "text-count", 'inputErrorClass': "error", 'counterErrorClass': "error", 'counterText': "Total Count: %d", 'errorTextElement': "div", 'minimumErrorText': "Minimum not met", 'maximumErrorText': "Maximum exceeded", 'displayErrorText': true, 'stopInputAtMaximum': true, 'countSpaces': false, 'countDown': false, 'countDownText': "Remaining: %d", 'countExtendedCharacters': false, 'twoCharCarriageReturn': false, 'countOverflow': false, 'countOverflowText': "Maximum %type exceeded by %d", 'countOverflowContainerClass': "text-count-overflow-wrapper", 'minDisplayCutoff': -1, 'maxDisplayCutoff': -1, 'maxunder': function (el) { }, 'minunder': function (el) { }, 'maxcount': function (el) { }, 'mincount': function (el) { }, 'init': function (el) { } }; $.fn.textcounter = function (options) { return this.each(function () { new $.textcounter(this, options) }) } })(jQuery); \ No newline at end of file