You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

384 lines
17 KiB

3 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
  1. (function($) {
  2. "use strict";
  3. if (!$.event.special.destroyed) {
  4. $.event.special.destroyed = {
  5. remove: function(o) {
  6. if (o.handler) {
  7. o.handler();
  8. }
  9. }
  10. };
  11. }
  12. $.fn.extend({
  13. maxlength: function(options, callback) {
  14. var documentBody = $("body"), defaults = {
  15. showOnReady: false,
  16. alwaysShow: true,
  17. threshold: 0,
  18. warningClass: "small form-text text-muted",
  19. limitReachedClass: "small form-text text-danger",
  20. limitExceededClass: "",
  21. separator: " / ",
  22. preText: "",
  23. postText: "",
  24. showMaxLength: true,
  25. placement: "bottom-right-inside",
  26. message: null,
  27. showCharsTyped: true,
  28. validate: false,
  29. utf8: false,
  30. appendToParent: false,
  31. twoCharLinebreak: true,
  32. customMaxAttribute: null,
  33. customMaxClass: "overmax",
  34. allowOverMax: false,
  35. zIndex: 1099
  36. };
  37. if ($.isFunction(options) && !callback) {
  38. callback = options;
  39. options = {};
  40. }
  41. options = $.extend(defaults, options);
  42. function utf8CharByteCount(character) {
  43. var c = character.charCodeAt();
  44. return !c ? 0 : c < 128 ? 1 : c < 2048 ? 2 : 3;
  45. }
  46. function utf8Length(string) {
  47. return string.split("").map(utf8CharByteCount).concat(0).reduce((function(sum, val) {
  48. return sum + val;
  49. }));
  50. }
  51. function inputLength(input) {
  52. var text = input.val();
  53. if (options.twoCharLinebreak) {
  54. text = text.replace(/\r(?!\n)|\n(?!\r)/g, "\r\n");
  55. } else {
  56. text = text.replace(/(?:\r\n|\r|\n)/g, "\n");
  57. }
  58. var currentLength = 0;
  59. if (options.utf8) {
  60. currentLength = utf8Length(text);
  61. } else {
  62. currentLength = text.length;
  63. }
  64. if (input.prop("type") === "file" && input.val() !== "") {
  65. currentLength -= 12;
  66. }
  67. return currentLength;
  68. }
  69. function truncateChars(input, maxlength) {
  70. var text = input.val();
  71. if (options.twoCharLinebreak) {
  72. text = text.replace(/\r(?!\n)|\n(?!\r)/g, "\r\n");
  73. if (text[text.length - 1] === "\n") {
  74. maxlength -= text.length % 2;
  75. }
  76. }
  77. if (options.utf8) {
  78. var indexedSize = text.split("").map(utf8CharByteCount);
  79. for (var removedBytes = 0, bytesPastMax = utf8Length(text) - maxlength; removedBytes < bytesPastMax; removedBytes += indexedSize.pop()) ;
  80. maxlength -= maxlength - indexedSize.length;
  81. }
  82. input.val(text.substr(0, maxlength));
  83. }
  84. function charsLeftThreshold(input, threshold, maxlength) {
  85. var output = true;
  86. if (!options.alwaysShow && maxlength - inputLength(input) > threshold) {
  87. output = false;
  88. }
  89. return output;
  90. }
  91. function remainingChars(input, maxlength) {
  92. var length = maxlength - inputLength(input);
  93. return length;
  94. }
  95. function showRemaining(currentInput, indicator) {
  96. indicator.css({
  97. display: "block"
  98. });
  99. currentInput.trigger("maxlength.shown");
  100. }
  101. function hideRemaining(currentInput, indicator) {
  102. if (options.alwaysShow) {
  103. return;
  104. }
  105. indicator.css({
  106. display: "none"
  107. });
  108. currentInput.trigger("maxlength.hidden");
  109. }
  110. function updateMaxLengthHTML(currentInputText, maxLengthThisInput, typedChars) {
  111. var output = "";
  112. if (options.message) {
  113. if (typeof options.message === "function") {
  114. output = options.message(currentInputText, maxLengthThisInput);
  115. } else {
  116. output = options.message.replace("%charsTyped%", typedChars).replace("%charsRemaining%", maxLengthThisInput - typedChars).replace("%charsTotal%", maxLengthThisInput);
  117. }
  118. } else {
  119. if (options.preText) {
  120. output += options.preText;
  121. }
  122. if (!options.showCharsTyped) {
  123. output += maxLengthThisInput - typedChars;
  124. } else {
  125. output += typedChars;
  126. }
  127. if (options.showMaxLength) {
  128. output += options.separator + maxLengthThisInput;
  129. }
  130. if (options.postText) {
  131. output += options.postText;
  132. }
  133. }
  134. return output;
  135. }
  136. function manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator) {
  137. if (maxLengthIndicator) {
  138. maxLengthIndicator.html(updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, maxLengthCurrentInput - remaining));
  139. if (remaining > 0) {
  140. if (charsLeftThreshold(currentInput, options.threshold, maxLengthCurrentInput)) {
  141. showRemaining(currentInput, maxLengthIndicator.removeClass(options.limitReachedClass + " " + options.limitExceededClass).addClass(options.warningClass));
  142. } else {
  143. hideRemaining(currentInput, maxLengthIndicator);
  144. }
  145. } else {
  146. if (!options.limitExceededClass) {
  147. showRemaining(currentInput, maxLengthIndicator.removeClass(options.warningClass).addClass(options.limitReachedClass));
  148. } else {
  149. if (remaining === 0) {
  150. showRemaining(currentInput, maxLengthIndicator.removeClass(options.warningClass + " " + options.limitExceededClass).addClass(options.limitReachedClass));
  151. } else {
  152. showRemaining(currentInput, maxLengthIndicator.removeClass(options.warningClass + " " + options.limitReachedClass).addClass(options.limitExceededClass));
  153. }
  154. }
  155. }
  156. }
  157. if (options.customMaxAttribute) {
  158. if (remaining < 0) {
  159. currentInput.addClass(options.customMaxClass);
  160. } else {
  161. currentInput.removeClass(options.customMaxClass);
  162. }
  163. }
  164. }
  165. function getPosition(currentInput) {
  166. var el = currentInput[0];
  167. return $.extend({}, typeof el.getBoundingClientRect === "function" ? el.getBoundingClientRect() : {
  168. width: el.offsetWidth,
  169. height: el.offsetHeight
  170. }, currentInput.offset());
  171. }
  172. function placeWithCSS(placement, maxLengthIndicator) {
  173. if (!placement || !maxLengthIndicator) {
  174. return;
  175. }
  176. var POSITION_KEYS = [ "top", "bottom", "left", "right", "position" ];
  177. var cssPos = {};
  178. $.each(POSITION_KEYS, (function(i, key) {
  179. var val = options.placement[key];
  180. if (typeof val !== "undefined") {
  181. cssPos[key] = val;
  182. }
  183. }));
  184. maxLengthIndicator.css(cssPos);
  185. return;
  186. }
  187. function place(currentInput, maxLengthIndicator) {
  188. var pos = getPosition(currentInput);
  189. if ($.type(options.placement) === "function") {
  190. options.placement(currentInput, maxLengthIndicator, pos);
  191. return;
  192. }
  193. if ($.isPlainObject(options.placement)) {
  194. placeWithCSS(options.placement, maxLengthIndicator);
  195. return;
  196. }
  197. var inputOuter = currentInput.outerWidth(), outerWidth = maxLengthIndicator.outerWidth(), actualWidth = maxLengthIndicator.width(), actualHeight = maxLengthIndicator.height();
  198. if (options.appendToParent) {
  199. pos.top -= currentInput.parent().offset().top;
  200. pos.left -= currentInput.parent().offset().left;
  201. }
  202. switch (options.placement) {
  203. case "bottom":
  204. maxLengthIndicator.css({
  205. top: pos.top + pos.height,
  206. left: pos.left + pos.width / 2 - actualWidth / 2
  207. });
  208. break;
  209. case "top":
  210. maxLengthIndicator.css({
  211. top: pos.top - actualHeight,
  212. left: pos.left + pos.width / 2 - actualWidth / 2
  213. });
  214. break;
  215. case "left":
  216. maxLengthIndicator.css({
  217. top: pos.top + pos.height / 2 - actualHeight / 2,
  218. left: pos.left - actualWidth
  219. });
  220. break;
  221. case "right":
  222. maxLengthIndicator.css({
  223. top: pos.top + pos.height / 2 - actualHeight / 2,
  224. left: pos.left + pos.width
  225. });
  226. break;
  227. case "bottom-right":
  228. maxLengthIndicator.css({
  229. top: pos.top + pos.height,
  230. left: pos.left + pos.width
  231. });
  232. break;
  233. case "top-right":
  234. maxLengthIndicator.css({
  235. top: pos.top - actualHeight,
  236. left: pos.left + inputOuter
  237. });
  238. break;
  239. case "top-left":
  240. maxLengthIndicator.css({
  241. top: pos.top - actualHeight,
  242. left: pos.left - outerWidth
  243. });
  244. break;
  245. case "bottom-left":
  246. maxLengthIndicator.css({
  247. top: pos.top + currentInput.outerHeight(),
  248. left: pos.left - outerWidth
  249. });
  250. break;
  251. case "centered-right":
  252. maxLengthIndicator.css({
  253. top: pos.top + actualHeight / 2,
  254. left: pos.left + inputOuter - outerWidth - 3
  255. });
  256. break;
  257. case "bottom-right-inside":
  258. maxLengthIndicator.css({
  259. top: pos.top + pos.height,
  260. left: pos.left + pos.width - outerWidth
  261. });
  262. break;
  263. case "top-right-inside":
  264. maxLengthIndicator.css({
  265. top: pos.top - actualHeight,
  266. left: pos.left + inputOuter - outerWidth
  267. });
  268. break;
  269. case "top-left-inside":
  270. maxLengthIndicator.css({
  271. top: pos.top - actualHeight,
  272. left: pos.left
  273. });
  274. break;
  275. case "bottom-left-inside":
  276. maxLengthIndicator.css({
  277. top: pos.top + currentInput.outerHeight(),
  278. left: pos.left
  279. });
  280. break;
  281. }
  282. }
  283. function isPlacementMutable() {
  284. return options.placement === "bottom-right-inside" || options.placement === "top-right-inside" || typeof options.placement === "function" || options.message && typeof options.message === "function";
  285. }
  286. function getMaxLength(currentInput) {
  287. var max = currentInput.attr("maxlength") || options.customMaxAttribute;
  288. if (options.customMaxAttribute && !options.allowOverMax) {
  289. var custom = currentInput.attr(options.customMaxAttribute);
  290. if (!max || custom < max) {
  291. max = custom;
  292. }
  293. }
  294. if (!max) {
  295. max = currentInput.attr("size");
  296. }
  297. return max;
  298. }
  299. return this.each((function() {
  300. var currentInput = $(this), maxLengthCurrentInput, maxLengthIndicator;
  301. $(window).resize((function() {
  302. if (maxLengthIndicator) {
  303. place(currentInput, maxLengthIndicator);
  304. }
  305. }));
  306. function firstInit() {
  307. var maxlengthContent = updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, "0");
  308. maxLengthCurrentInput = getMaxLength(currentInput);
  309. if (!maxLengthIndicator) {
  310. maxLengthIndicator = $('<span class="bootstrap-maxlength"></span>').css({
  311. display: "none",
  312. position: "absolute",
  313. whiteSpace: "nowrap",
  314. zIndex: options.zIndex
  315. }).html(maxlengthContent);
  316. }
  317. if (currentInput.is("textarea")) {
  318. currentInput.data("maxlenghtsizex", currentInput.outerWidth());
  319. currentInput.data("maxlenghtsizey", currentInput.outerHeight());
  320. currentInput.mouseup((function() {
  321. if (currentInput.outerWidth() !== currentInput.data("maxlenghtsizex") || currentInput.outerHeight() !== currentInput.data("maxlenghtsizey")) {
  322. place(currentInput, maxLengthIndicator);
  323. }
  324. currentInput.data("maxlenghtsizex", currentInput.outerWidth());
  325. currentInput.data("maxlenghtsizey", currentInput.outerHeight());
  326. }));
  327. }
  328. if (options.appendToParent) {
  329. currentInput.parent().append(maxLengthIndicator);
  330. currentInput.parent().css("position", "relative");
  331. } else {
  332. documentBody.append(maxLengthIndicator);
  333. }
  334. var remaining = remainingChars(currentInput, getMaxLength(currentInput));
  335. manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);
  336. place(currentInput, maxLengthIndicator);
  337. }
  338. if (options.showOnReady) {
  339. currentInput.ready((function() {
  340. firstInit();
  341. }));
  342. } else {
  343. currentInput.focus((function() {
  344. firstInit();
  345. }));
  346. }
  347. currentInput.on("maxlength.reposition", (function() {
  348. place(currentInput, maxLengthIndicator);
  349. }));
  350. currentInput.on("destroyed", (function() {
  351. if (maxLengthIndicator) {
  352. maxLengthIndicator.remove();
  353. }
  354. }));
  355. currentInput.on("blur", (function() {
  356. if (maxLengthIndicator && !options.showOnReady) {
  357. maxLengthIndicator.remove();
  358. }
  359. }));
  360. currentInput.on("input", (function() {
  361. var maxlength = getMaxLength(currentInput), remaining = remainingChars(currentInput, maxlength), output = true;
  362. if (options.validate && remaining < 0) {
  363. truncateChars(currentInput, maxlength);
  364. output = false;
  365. } else {
  366. manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);
  367. }
  368. return output;
  369. }));
  370. }));
  371. }
  372. });
  373. })(jQuery);