diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a088b6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +bower_components diff --git a/README.md b/README.md index 758ca9a..3a35a51 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,17 @@ The `from` attribute is the name of the model in the current scope you want to slugify. The `to` attribute is the name of the model you want to be automatically updated with the slugification whenever `from` changes. +## Configuration + +The `SlugProvider` provider enables you to override mappings. + +```js +angular.module("myApp") + .config(function(SlugProvider) { + SlugProvider.charmap['å'] = 'aa'; // default: 'a' + }); +``` + ## Tests ```bash diff --git a/angular-slugify.js b/angular-slugify.js index 69195e2..aed8983 100644 --- a/angular-slugify.js +++ b/angular-slugify.js @@ -29,248 +29,253 @@ // Unicode (non-control) characters in the Latin-1 Supplement and Latin // Extended-A blocks, transliterated into ASCII characters. - var charmap = { - ' ': " ", - '¡': "!", - '¢': "c", - '£': "lb", - '¥': "yen", - '¦': "|", - '§': "SS", - '¨': "\"", - '©': "(c)", - 'ª': "a", - '«': "<<", - '¬': "not", - '­': "-", - '®': "(R)", - '°': "^0", - '±': "+/-", - '²': "^2", - '³': "^3", - '´': "'", - 'µ': "u", - '¶': "P", - '·': ".", - '¸': ",", - '¹': "^1", - 'º': "o", - '»': ">>", - '¼': " 1/4 ", - '½': " 1/2 ", - '¾': " 3/4 ", - '¿': "?", - 'À': "`A", - 'Á': "'A", - 'Â': "^A", - 'Ã': "~A", - 'Ä': '"A', - 'Å': "A", - 'Æ': "AE", - 'Ç': "C", - 'È': "`E", - 'É': "'E", - 'Ê': "^E", - 'Ë': '"E', - 'Ì': "`I", - 'Í': "'I", - 'Î': "^I", - 'Ï': '"I', - 'Ð': "D", - 'Ñ': "~N", - 'Ò': "`O", - 'Ó': "'O", - 'Ô': "^O", - 'Õ': "~O", - 'Ö': '"O', - '×': "x", - 'Ø': "O", - 'Ù': "`U", - 'Ú': "'U", - 'Û': "^U", - 'Ü': '"U', - 'Ý': "'Y", - 'Þ': "Th", - 'ß': "ss", - 'à': "`a", - 'á': "'a", - 'â': "^a", - 'ã': "~a", - 'ä': '"a', - 'å': "a", - 'æ': "ae", - 'ç': "c", - 'è': "`e", - 'é': "'e", - 'ê': "^e", - 'ë': '"e', - 'ì': "`i", - 'í': "'i", - 'î': "^i", - 'ï': '"i', - 'ð': "d", - 'ñ': "~n", - 'ò': "`o", - 'ó': "'o", - 'ô': "^o", - 'õ': "~o", - 'ö': '"o', - '÷': ":", - 'ø': "o", - 'ù': "`u", - 'ú': "'u", - 'û': "^u", - 'ü': '"u', - 'ý': "'y", - 'þ': "th", - 'ÿ': '"y', - 'Ā': "A", - 'ā': "a", - 'Ă': "A", - 'ă': "a", - 'Ą': "A", - 'ą': "a", - 'Ć': "'C", - 'ć': "'c", - 'Ĉ': "^C", - 'ĉ': "^c", - 'Ċ': "C", - 'ċ': "c", - 'Č': "C", - 'č': "c", - 'Ď': "D", - 'ď': "d", - 'Đ': "D", - 'đ': "d", - 'Ē': "E", - 'ē': "e", - 'Ĕ': "E", - 'ĕ': "e", - 'Ė': "E", - 'ė': "e", - 'Ę': "E", - 'ę': "e", - 'Ě': "E", - 'ě': "e", - 'Ĝ': "^G", - 'ĝ': "^g", - 'Ğ': "G", - 'ğ': "g", - 'Ġ': "G", - 'ġ': "g", - 'Ģ': "G", - 'ģ': "g", - 'Ĥ': "^H", - 'ĥ': "^h", - 'Ħ': "H", - 'ħ': "h", - 'Ĩ': "~I", - 'ĩ': "~i", - 'Ī': "I", - 'ī': "i", - 'Ĭ': "I", - 'ĭ': "i", - 'Į': "I", - 'į': "i", - 'İ': "I", - 'ı': "i", - 'IJ': "IJ", - 'ij': "ij", - 'Ĵ': "^J", - 'ĵ': "^j", - 'Ķ': "K", - 'ķ': "k", - 'Ĺ': "L", - 'ĺ': "l", - 'Ļ': "L", - 'ļ': "l", - 'Ľ': "L", - 'ľ': "l", - 'Ŀ': "L", - 'ŀ': "l", - 'Ł': "L", - 'ł': "l", - 'Ń': "'N", - 'ń': "'n", - 'Ņ': "N", - 'ņ': "n", - 'Ň': "N", - 'ň': "n", - 'ʼn': "'n", - 'Ō': "O", - 'ō': "o", - 'Ŏ': "O", - 'ŏ': "o", - 'Ő': '"O', - 'ő': '"o', - 'Œ': "OE", - 'œ': "oe", - 'Ŕ': "'R", - 'ŕ': "'r", - 'Ŗ': "R", - 'ŗ': "r", - 'Ř': "R", - 'ř': "r", - 'Ś': "'S", - 'ś': "'s", - 'Ŝ': "^S", - 'ŝ': "^s", - 'Ş': "S", - 'ş': "s", - 'Š': "S", - 'š': "s", - 'Ţ': "T", - 'ţ': "t", - 'Ť': "T", - 'ť': "t", - 'Ŧ': "T", - 'ŧ': "t", - 'Ũ': "~U", - 'ũ': "~u", - 'Ū': "U", - 'ū': "u", - 'Ŭ': "U", - 'ŭ': "u", - 'Ů': "U", - 'ů': "u", - 'Ű': '"U', - 'ű': '"u', - 'Ų': "U", - 'ų': "u", - 'Ŵ': "^W", - 'ŵ': "^w", - 'Ŷ': "^Y", - 'ŷ': "^y", - 'Ÿ': '"Y', - 'Ź': "'Z", - 'ź': "'z", - 'Ż': "Z", - 'ż': "z", - 'Ž': "Z", - 'ž': "z", - 'ſ': "s" - }; - - function _slugify(s) { - if (!s) return ""; - var ascii = []; - var ch, cp; - for (var i = 0; i < s.length; i++) { - if ((cp = s.charCodeAt(i)) < 0x180) { - ch = String.fromCharCode(cp); - ascii.push(charmap[ch] || ch); + mod.provider('Slug', [function () { + var self = { + charmap: { + ' ': " ", + '¡': "!", + '¢': "c", + '£': "lb", + '¥': "yen", + '¦': "|", + '§': "SS", + '¨': "\"", + '©': "(c)", + 'ª': "a", + '«': "<<", + '¬': "not", + '­': "-", + '®': "(R)", + '°': "^0", + '±': "+/-", + '²': "^2", + '³': "^3", + '´': "'", + 'µ': "u", + '¶': "P", + '·': ".", + '¸': ",", + '¹': "^1", + 'º': "o", + '»': ">>", + '¼': " 1/4 ", + '½': " 1/2 ", + '¾': " 3/4 ", + '¿': "?", + 'À': "`A", + 'Á': "'A", + 'Â': "^A", + 'Ã': "~A", + 'Ä': '"A', + 'Å': "A", + 'Æ': "AE", + 'Ç': "C", + 'È': "`E", + 'É': "'E", + 'Ê': "^E", + 'Ë': '"E', + 'Ì': "`I", + 'Í': "'I", + 'Î': "^I", + 'Ï': '"I', + 'Ð': "D", + 'Ñ': "~N", + 'Ò': "`O", + 'Ó': "'O", + 'Ô': "^O", + 'Õ': "~O", + 'Ö': '"O', + '×': "x", + 'Ø': "O", + 'Ù': "`U", + 'Ú': "'U", + 'Û': "^U", + 'Ü': '"U', + 'Ý': "'Y", + 'Þ': "Th", + 'ß': "ss", + 'à': "`a", + 'á': "'a", + 'â': "^a", + 'ã': "~a", + 'ä': '"a', + 'å': "a", + 'æ': "ae", + 'ç': "c", + 'è': "`e", + 'é': "'e", + 'ê': "^e", + 'ë': '"e', + 'ì': "`i", + 'í': "'i", + 'î': "^i", + 'ï': '"i', + 'ð': "d", + 'ñ': "~n", + 'ò': "`o", + 'ó': "'o", + 'ô': "^o", + 'õ': "~o", + 'ö': '"o', + '÷': ":", + 'ø': "o", + 'ù': "`u", + 'ú': "'u", + 'û': "^u", + 'ü': '"u', + 'ý': "'y", + 'þ': "th", + 'ÿ': '"y', + 'Ā': "A", + 'ā': "a", + 'Ă': "A", + 'ă': "a", + 'Ą': "A", + 'ą': "a", + 'Ć': "'C", + 'ć': "'c", + 'Ĉ': "^C", + 'ĉ': "^c", + 'Ċ': "C", + 'ċ': "c", + 'Č': "C", + 'č': "c", + 'Ď': "D", + 'ď': "d", + 'Đ': "D", + 'đ': "d", + 'Ē': "E", + 'ē': "e", + 'Ĕ': "E", + 'ĕ': "e", + 'Ė': "E", + 'ė': "e", + 'Ę': "E", + 'ę': "e", + 'Ě': "E", + 'ě': "e", + 'Ĝ': "^G", + 'ĝ': "^g", + 'Ğ': "G", + 'ğ': "g", + 'Ġ': "G", + 'ġ': "g", + 'Ģ': "G", + 'ģ': "g", + 'Ĥ': "^H", + 'ĥ': "^h", + 'Ħ': "H", + 'ħ': "h", + 'Ĩ': "~I", + 'ĩ': "~i", + 'Ī': "I", + 'ī': "i", + 'Ĭ': "I", + 'ĭ': "i", + 'Į': "I", + 'į': "i", + 'İ': "I", + 'ı': "i", + 'IJ': "IJ", + 'ij': "ij", + 'Ĵ': "^J", + 'ĵ': "^j", + 'Ķ': "K", + 'ķ': "k", + 'Ĺ': "L", + 'ĺ': "l", + 'Ļ': "L", + 'ļ': "l", + 'Ľ': "L", + 'ľ': "l", + 'Ŀ': "L", + 'ŀ': "l", + 'Ł': "L", + 'ł': "l", + 'Ń': "'N", + 'ń': "'n", + 'Ņ': "N", + 'ņ': "n", + 'Ň': "N", + 'ň': "n", + 'ʼn': "'n", + 'Ō': "O", + 'ō': "o", + 'Ŏ': "O", + 'ŏ': "o", + 'Ő': '"O', + 'ő': '"o', + 'Œ': "OE", + 'œ': "oe", + 'Ŕ': "'R", + 'ŕ': "'r", + 'Ŗ': "R", + 'ŗ': "r", + 'Ř': "R", + 'ř': "r", + 'Ś': "'S", + 'ś': "'s", + 'Ŝ': "^S", + 'ŝ': "^s", + 'Ş': "S", + 'ş': "s", + 'Š': "S", + 'š': "s", + 'Ţ': "T", + 'ţ': "t", + 'Ť': "T", + 'ť': "t", + 'Ŧ': "T", + 'ŧ': "t", + 'Ũ': "~U", + 'ũ': "~u", + 'Ū': "U", + 'ū': "u", + 'Ŭ': "U", + 'ŭ': "u", + 'Ů': "U", + 'ů': "u", + 'Ű': '"U', + 'ű': '"u', + 'Ų': "U", + 'ų': "u", + 'Ŵ': "^W", + 'ŵ': "^w", + 'Ŷ': "^Y", + 'ŷ': "^y", + 'Ÿ': '"Y', + 'Ź': "'Z", + 'ź': "'z", + 'Ż': "Z", + 'ż': "z", + 'Ž': "Z", + 'ž': "z", + 'ſ': "s" + }, + $get: function () { + return { + slugify: _slugify + } } } - s = ascii.join(""); - s = s.replace(/[^\w\s-]/g, "").trim().toLowerCase(); - return s.replace(/[-\s]+/g, "-"); - } - mod.factory("Slug", function() { - return { - slugify: _slugify - }; - }); + return self; + + function _slugify(s) { + if (!s) return ""; + var ascii = []; + var ch, cp; + for (var i = 0; i < s.length; i++) { + if ((cp = s.charCodeAt(i)) < 0x180) { + ch = String.fromCharCode(cp); + ascii.push(self.charmap[ch] || ch); + } + } + s = ascii.join(""); + s = s.replace(/[^\w\s-]/g, "").trim().toLowerCase(); + return s.replace(/[-\s]+/g, "-"); + } + }]); mod.directive("slug", ["Slug", function(Slug) { return { diff --git a/test/unit/overrideCharmapSpec.js b/test/unit/overrideCharmapSpec.js new file mode 100644 index 0000000..85fa2fe --- /dev/null +++ b/test/unit/overrideCharmapSpec.js @@ -0,0 +1,34 @@ +"use strict"; + +describe("slugify-charmap-override", function() { + var Slug, originalCharmap; + + var slugTestCases = [ + ["hello world", "hillo-world"] + ]; + + beforeEach(module("slugifier")); + beforeEach(function () { + module(function (SlugProvider) { + originalCharmap = originalCharmap || SlugProvider.charmap; + SlugProvider.charmap["e"] = "i"; + }); + }); + + afterAll(function () { + module(function (SlugProvider) { + SlugProvider.charmap = originalCharmap; + }); + }); + + describe("slugify", function() { + it("can override a letter", inject(function(Slug) { + var input, expected; + for (var i = 0; i < slugTestCases.length; i++) { + input = slugTestCases[i][0]; + expected = slugTestCases[i][1]; + expect(Slug.slugify(input)).toEqual(expected); + } + })); + }); +}); diff --git a/test/unit/servicesSpec.js b/test/unit/servicesSpec.js index 46febdf..0d67a4c 100644 --- a/test/unit/servicesSpec.js +++ b/test/unit/servicesSpec.js @@ -1,16 +1,16 @@ "use strict"; -var slugTestCases = [ - ["", ""] - ,[" Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/", - "jack-jill-like-numbers-123-and-4-and-silly-characters"] - ,["Un éléphant à l'orée du bois", "un-elephant-a-loree-du-bois"] - ,["Iñtërnâtiônàlizætiøn", "internationalizaetion"] -]; - describe("slugify-service", function() { beforeEach(module("slugifier")); + var slugTestCases = [ + ["", ""] + ,[" Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/", + "jack-jill-like-numbers-123-and-4-and-silly-characters"] + ,["Un éléphant à l'orée du bois", "un-elephant-a-loree-du-bois"] + ,["Iñtërnâtiônàlizætiøn", "internationalizaetion"] + ]; + describe("slugify", function() { it("should produce a correct slug from string input", inject(function(Slug) { var input, expected;