├── package.json ├── examples └── basic-jquery.html ├── LICENSE.txt ├── readme.md ├── sms_counter.js.coffee ├── sms_counter.min.js └── sms_counter.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sms-counter", 3 | "version": "1.0.0", 4 | "description": "Character counter for SMS messages.", 5 | "main": "sms_counter.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+ssh://git@github.com/danxexe/sms-counter.git" 15 | }, 16 | "keywords": [ 17 | "sms", 18 | "sms-counter" 19 | ], 20 | "author": "Danilo R. Gonçalves", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/danxexe/sms-counter/issues" 24 | }, 25 | "homepage": "https://github.com/danxexe/sms-counter#readme" 26 | } 27 | -------------------------------------------------------------------------------- /examples/basic-jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | danxexe/sms-counter sample 6 | 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 danxexe 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | SMS Counter 2 | ============================= 3 | 4 | Character counter for SMS messages. 5 | 6 | 7 | Usage 8 | ---------- 9 | 10 | ```javascript 11 | SmsCounter.count('content of the SMS') 12 | ``` 13 | 14 | This will return the following object: 15 | 16 | ```javascript 17 | { 18 | encoding: "GSM_7BIT", 19 | length: 18, 20 | messages: 1, 21 | per_message: 160, 22 | remaining: 142 23 | } 24 | ``` 25 | 26 | jQuery Plugin 27 | ---------- 28 | 29 | Given the following markup: 30 | 31 | ```html 32 | 33 | 40 | ``` 41 | 42 | You can use the `countSms` jQuery extension to update the count on keyup: 43 | 44 | ```javascript 45 | $('#message').countSms('#sms-counter') 46 | ``` 47 | 48 | 49 | TODO 50 | ---- 51 | 52 | - Better docs 53 | 54 | 55 | Known Issue 56 | ---- 57 | 58 | (none) 59 | 60 | 61 | Other Languages 62 | ---- 63 | 64 | - PHP: [https://github.com/acpmasquerade/sms-counter-php](https://github.com/acpmasquerade/sms-counter-php) 65 | - C#: [https://github.com/troll31/sms-counter-csharp](https://github.com/troll31/sms-counter-csharp) 66 | 67 | 68 | ## License 69 | 70 | SMS Counter is released under the [MIT License](LICENSE.txt). -------------------------------------------------------------------------------- /sms_counter.js.coffee: -------------------------------------------------------------------------------- 1 | window.SmsCounter = class SmsCounter 2 | 3 | @gsm7bitChars = "@£$¥èéùìòÇ\\nØø\\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\\\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà" 4 | @gsm7bitExChar = "\\^{}\\\\\\[~\\]|€" 5 | 6 | @gsm7bitRegExp = RegExp("^[#{(@gsm7bitChars)}]*$") 7 | @gsm7bitExRegExp = RegExp("^[#{ (@gsm7bitChars)}#{(@gsm7bitExChar)}]*$") 8 | @gsm7bitExOnlyRegExp = RegExp("^[\\#{(@gsm7bitExChar)}]*$") 9 | 10 | @GSM_7BIT = 'GSM_7BIT' 11 | @GSM_7BIT_EX = 'GSM_7BIT_EX' 12 | @UTF16 = 'UTF16' 13 | 14 | @messageLength = 15 | GSM_7BIT: 160 16 | GSM_7BIT_EX: 160 17 | UTF16: 70 18 | 19 | @multiMessageLength = 20 | GSM_7BIT: 153 21 | GSM_7BIT_EX: 153 22 | UTF16: 67 23 | 24 | @count: (text) -> 25 | encoding = @detectEncoding(text) 26 | 27 | length = text.length 28 | length += @countGsm7bitEx(text) if encoding == @GSM_7BIT_EX 29 | 30 | per_message = @messageLength[encoding] 31 | per_message = @multiMessageLength[encoding] if length > per_message 32 | 33 | messages = Math.ceil(length / per_message) 34 | remaining = (per_message * messages) - length 35 | remaining = per_message if (remaining == 0 && messages == 0) 36 | 37 | count = 38 | encoding: encoding 39 | length: length 40 | per_message: per_message 41 | remaining: remaining 42 | messages: messages 43 | 44 | @detectEncoding: (text) -> 45 | switch 46 | when text.match(@gsm7bitRegExp)? then @GSM_7BIT 47 | when text.match(@gsm7bitExRegExp)? then @GSM_7BIT_EX 48 | else @UTF16 49 | 50 | @countGsm7bitEx: (text) -> 51 | chars = (char2 for char2 in text when char2.match(@gsm7bitExOnlyRegExp)?) 52 | chars.length 53 | 54 | if jQuery? 55 | $ = jQuery 56 | $.fn.countSms = (target) -> 57 | input = @ 58 | target = $(target) 59 | count_sms = -> 60 | count = SmsCounter.count(input.val()) 61 | for k, v of count 62 | target.find(".#{k}").text(v) 63 | 64 | @.on 'keyup', count_sms 65 | count_sms() 66 | -------------------------------------------------------------------------------- /sms_counter.min.js: -------------------------------------------------------------------------------- 1 | !function(){var $,SmsCounter;window.SmsCounter=SmsCounter=function(){function SmsCounter(){}SmsCounter.gsm7bitChars="@£$¥èéùìòÇ\\nØø\\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\\\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà";SmsCounter.gsm7bitExChar="\\^{}\\\\\\[~\\]|€";SmsCounter.gsm7bitRegExp=RegExp("^["+SmsCounter.gsm7bitChars+"]*$");SmsCounter.gsm7bitExRegExp=RegExp("^["+SmsCounter.gsm7bitChars+SmsCounter.gsm7bitExChar+"]*$");SmsCounter.gsm7bitExOnlyRegExp=RegExp("^[\\"+SmsCounter.gsm7bitExChar+"]*$");SmsCounter.GSM_7BIT="GSM_7BIT";SmsCounter.GSM_7BIT_EX="GSM_7BIT_EX";SmsCounter.UTF16="UTF16";SmsCounter.messageLength={GSM_7BIT:160,GSM_7BIT_EX:160,UTF16:70};SmsCounter.multiMessageLength={GSM_7BIT:153,GSM_7BIT_EX:153,UTF16:67};SmsCounter.count=function(text){var count,encoding,length,messages,per_message,remaining;encoding=this.detectEncoding(text);length=text.length;if(encoding===this.GSM_7BIT_EX){length+=this.countGsm7bitEx(text)}per_message=this.messageLength[encoding];if(length>per_message){per_message=this.multiMessageLength[encoding]}messages=Math.ceil(length/per_message);remaining=per_message*messages-length;if(remaining == 0 && messages == 0){remaining = per_message; }return count={encoding:encoding,length:length,per_message:per_message,remaining:remaining,messages:messages}};SmsCounter.detectEncoding=function(text){switch(false){case text.match(this.gsm7bitRegExp)==null:return this.GSM_7BIT;case text.match(this.gsm7bitExRegExp)==null:return this.GSM_7BIT_EX;default:return this.UTF16}};SmsCounter.countGsm7bitEx=function(text){var char2,chars;chars=function(){var _i,_len,_results;_results=[];for(_i=0,_len=text.length;_i<_len;_i++){char2=text[_i];if(char2.match(this.gsm7bitExOnlyRegExp)!=null){_results.push(char2)}}return _results}.call(this);return chars.length};return SmsCounter}();if(typeof jQuery!=="undefined"&&jQuery!==null){$=jQuery;$.fn.countSms=function(target){var count_sms,input;input=this;target=$(target);count_sms=function(){var count,k,v,_results;count=SmsCounter.count(input.val());_results=[];for(k in count){v=count[k];_results.push(target.find("."+k).text(v))}return _results};this.on("keyup",count_sms);return count_sms()}}}.call(this); -------------------------------------------------------------------------------- /sms_counter.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var $, SmsCounter; 3 | 4 | window.SmsCounter = SmsCounter = (function() { 5 | function SmsCounter() {} 6 | 7 | SmsCounter.gsm7bitChars = "@£$¥èéùìòÇ\\nØø\\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\\\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà"; 8 | 9 | SmsCounter.gsm7bitExChar = "\\^{}\\\\\\[~\\]|€"; 10 | 11 | SmsCounter.gsm7bitRegExp = RegExp("^[" + SmsCounter.gsm7bitChars + "]*$"); 12 | 13 | SmsCounter.gsm7bitExRegExp = RegExp("^[" + SmsCounter.gsm7bitChars + SmsCounter.gsm7bitExChar + "]*$"); 14 | 15 | SmsCounter.gsm7bitExOnlyRegExp = RegExp("^[\\" + SmsCounter.gsm7bitExChar + "]*$"); 16 | 17 | SmsCounter.GSM_7BIT = 'GSM_7BIT'; 18 | 19 | SmsCounter.GSM_7BIT_EX = 'GSM_7BIT_EX'; 20 | 21 | SmsCounter.UTF16 = 'UTF16'; 22 | 23 | SmsCounter.messageLength = { 24 | GSM_7BIT: 160, 25 | GSM_7BIT_EX: 160, 26 | UTF16: 70 27 | }; 28 | 29 | SmsCounter.multiMessageLength = { 30 | GSM_7BIT: 153, 31 | GSM_7BIT_EX: 153, 32 | UTF16: 67 33 | }; 34 | 35 | SmsCounter.count = function(text) { 36 | var count, encoding, length, messages, per_message, remaining; 37 | encoding = this.detectEncoding(text); 38 | length = text.length; 39 | if (encoding === this.GSM_7BIT_EX) { 40 | length += this.countGsm7bitEx(text); 41 | } 42 | per_message = this.messageLength[encoding]; 43 | if (length > per_message) { 44 | per_message = this.multiMessageLength[encoding]; 45 | } 46 | messages = Math.ceil(length / per_message); 47 | remaining = (per_message * messages) - length; 48 | if(remaining == 0 && messages == 0){ 49 | remaining = per_message; 50 | } 51 | return count = { 52 | encoding: encoding, 53 | length: length, 54 | per_message: per_message, 55 | remaining: remaining, 56 | messages: messages 57 | }; 58 | }; 59 | 60 | SmsCounter.detectEncoding = function(text) { 61 | switch (false) { 62 | case text.match(this.gsm7bitRegExp) == null: 63 | return this.GSM_7BIT; 64 | case text.match(this.gsm7bitExRegExp) == null: 65 | return this.GSM_7BIT_EX; 66 | default: 67 | return this.UTF16; 68 | } 69 | }; 70 | 71 | SmsCounter.countGsm7bitEx = function(text) { 72 | var char2, chars; 73 | chars = (function() { 74 | var _i, _len, _results; 75 | _results = []; 76 | for (_i = 0, _len = text.length; _i < _len; _i++) { 77 | char2 = text[_i]; 78 | if (char2.match(this.gsm7bitExOnlyRegExp) != null) { 79 | _results.push(char2); 80 | } 81 | } 82 | return _results; 83 | }).call(this); 84 | return chars.length; 85 | }; 86 | 87 | return SmsCounter; 88 | 89 | })(); 90 | 91 | if (typeof jQuery !== "undefined" && jQuery !== null) { 92 | $ = jQuery; 93 | $.fn.countSms = function(target) { 94 | var count_sms, input; 95 | input = this; 96 | target = $(target); 97 | count_sms = function() { 98 | var count, k, v, _results; 99 | count = SmsCounter.count(input.val()); 100 | _results = []; 101 | for (k in count) { 102 | v = count[k]; 103 | _results.push(target.find("." + k).text(v)); 104 | } 105 | return _results; 106 | }; 107 | this.on('keyup', count_sms); 108 | return count_sms(); 109 | }; 110 | } 111 | 112 | }).call(this); 113 | --------------------------------------------------------------------------------