From 36e6fe5988540f0bc3480be7c70ad216f3b5c2b8 Mon Sep 17 00:00:00 2001
From: Anthony Cameron <anthony.cameron@jadedpixel.com>
Date: Mon, 3 Feb 2014 23:10:54 -0500
Subject: [PATCH] added testing with testem, added ability to emojify a string

---
 .bowerrc                 |  4 +++
 .gitignore               |  2 ++
 Gruntfile.js             |  1 -
 emojify.js               | 81 ++++++++++++++++++++++++++++++------------------
 package.json             |  3 ++
 testem.json              | 21 +++++++++++++
 tests/dom_test.coffee    | 51 ++++++++++++++++++++++++++++++
 tests/setup.js           |  5 +++
 tests/string_test.coffee | 40 ++++++++++++++++++++++++
 tests/tests.coffee       |  2 ++
 10 files changed, 179 insertions(+), 31 deletions(-)
 create mode 100644 .bowerrc
 create mode 100644 testem.json
 create mode 100644 tests/dom_test.coffee
 create mode 100644 tests/setup.js
 create mode 100644 tests/string_test.coffee
 create mode 100644 tests/tests.coffee

diff --git a/.bowerrc b/.bowerrc
new file mode 100644
index 0000000..eb65641
--- /dev/null
+++ b/.bowerrc
@@ -0,0 +1,4 @@
+{
+  "directory": "/vendor/",
+  "json" : "bower.json"
+}
diff --git a/.gitignore b/.gitignore
index cbadd1d..c6a826f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
 node_modules
 css
+vendor
+tests.browserified.js
diff --git a/Gruntfile.js b/Gruntfile.js
index 4dc5bd0..1ac6cbc 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -27,7 +27,6 @@ module.exports = function (grunt) {
 
     grunt.loadNpmTasks('grunt-contrib-jshint');
     grunt.loadNpmTasks('grunt-contrib-uglify');
-    grunt.loadNpmTasks('grunt-contrib-clean');
 
     grunt.registerTask(
         'default',
diff --git a/emojify.js b/emojify.js
index 04f96d7..2558214 100644
--- a/emojify.js
+++ b/emojify.js
@@ -105,42 +105,63 @@
                 }
             }
 
-            function run(el) {
-                function emojiValidator(match) {
-                    /* Validator */
-                    var emojiName = getEmojiNameForMatch(match);
-                    if(!emojiName) { return; }
-
-                    var m = match[0];
-                    var index = match.index;
-                    var input = match.input;
-
-                    function success() {
-                        lastEmojiTerminatedAt = m.length + index;
-                        return true;
+            function emojifyString (htmlString) {
+                var match, emojiName, img;
+
+                while (match = emojiMegaRe.exec(htmlString)) {
+                    if(emojiName = emojiValidator(match)) {
+                        // use &58; instead of `:` character so we don't have an infinite replacement loop
+                        img = "<img title='&58;" + emojiName + "&58;' class='emoji' src='" + defaultConfig.img_dir + '/' + emojiName + ".png' align='absmiddle' />";
+                        htmlString = htmlString.replace(match[0], img);
                     }
+                }
+
+                return htmlString;
+            }
 
-                    /* Any smiley thats 3 chars long is probably a smiley */
-                    if(m.length > 2) { return success(); }
+            function emojiValidator(match) {
+                /* Validator */
+                var lastEmojiTerminatedAt = -1;
+                var emojiName = getEmojiNameForMatch(match);
+                if(!emojiName) { return; }
+
+                var m = match[0];
+                var index = match.index;
+                var input = match.input;
 
-                    /* At the beginning? */
-                    if(index === 0) { return success(); }
+                function success() {
+                    lastEmojiTerminatedAt = m.length + index;
+                    return emojiName;
+                }
 
-                    /* At the end? */
-                    if(input.length === m.length + index) { return success(); }
+                /* Any smiley thats 3 chars long is probably a smiley */
+                if(m.length > 2) { return success(); }
 
-                    /* Has a whitespace before? */
-                    if(isWhitespace(input.charAt(index - 1))) { return success(); }
+                /* At the beginning? */
+                if(index === 0) { return success(); }
 
-                    /* Has a whitespace after? */
-                    if(isWhitespace(input.charAt(m.length + index))) { return success(); }
+                /* At the end? */
+                if(input.length === m.length + index) { return success(); }
 
-                    /* Has an emoji before? */
-                    if(lastEmojiTerminatedAt === index) { return success(); }
+                /* Has a whitespace before? */
+                if(isWhitespace(input.charAt(index - 1))) { return success(); }
 
-                    return false;
-                }
+                /* Has a whitespace after? */
+                if(isWhitespace(input.charAt(m.length + index))) { return success(); }
 
+                /* Has a > before? */
+                if(input.charAt(index - 1) === ">") { return success(); }
+
+                /* Has a < after? */
+                if(input.charAt(m.length + index) === "<") { return success(); }
+
+                /* Has an emoji before? */
+                if(lastEmojiTerminatedAt === index) { return success(); }
+
+                return false;
+            }
+
+            function run(el) {
                 // Check if an element was not passed.
                 if(typeof el === 'undefined'){
                     // Check if an element was configured. If not, default to the body.
@@ -149,10 +170,10 @@
                     } else {
                         el = document.body;
                     }
+                } else if (typeof el === 'string') {
+                    return emojifyString(el);
                 }
 
-                var lastEmojiTerminatedAt = -1;
-
                 var ignoredTags = defaultConfig.ignored_tags;
 
                 var treeWalker = document.createTreeWalker(
@@ -211,4 +232,4 @@
 
         global.emojify = emojify;
 
-    })(this);
\ No newline at end of file
+    })(this);
diff --git a/package.json b/package.json
index c72dc5d..5402303 100644
--- a/package.json
+++ b/package.json
@@ -24,5 +24,8 @@
     "grunt": "~0.4.1",
     "grunt-contrib-jshint": "~0.6.4",
     "grunt-contrib-uglify": "~0.2.4"
+  },
+  "dependencies": {
+    "coffeeify": "~0.6.0"
   }
 }
diff --git a/testem.json b/testem.json
new file mode 100644
index 0000000..7873683
--- /dev/null
+++ b/testem.json
@@ -0,0 +1,21 @@
+{
+  "framework": "jasmine",
+  "before_tests": "browserify tests/tests.coffee -o tests.browserified.js -t coffeeify",
+  "src_files": [
+    "emojify.js",
+    "tests/**/*.coffee",
+    "tests/**/*.js"
+  ],
+  "serve_files": [
+    "emojify.js",
+    "vendor/sinon/lib/sinon.js",
+    "vendor/sinon/lib/sinon/match.js",
+    "vendor/sinon/lib/sinon/call.js",
+    "vendor/sinon/lib/sinon/spy.js",
+    "vendor/sinon/lib/sinon/mock.js",
+    "vendor/sinon/lib/sinon/stub.js",
+    "vendor/chai/chai.js",
+    "tests/setup.js",
+    "tests.browserified.js"
+  ]
+}
diff --git a/tests/dom_test.coffee b/tests/dom_test.coffee
new file mode 100644
index 0000000..a14e362
--- /dev/null
+++ b/tests/dom_test.coffee
@@ -0,0 +1,51 @@
+describe 'emojify on DOM nodes', ->
+  beforeEach ->
+    @el = document.createElement("DIV")
+
+  describe 'with variations of spacing around 2char smileys', ->
+    it 'works with no spacing around :)', ->
+      @el.innerHTML = ":)"
+      result = emojify.run(@el)
+      assert.equal '<img title=":blush:" class="emoji" src="images/emoji/blush.png" align="absmiddle"></img>', @el.innerHTML
+
+    it 'works with spacing before :)', ->
+      @el.innerHTML = " :)"
+      result = emojify.run(@el)
+      assert.equal ' <img title=":blush:" class="emoji" src="images/emoji/blush.png" align="absmiddle"></img>', @el.innerHTML
+
+    it 'works with spacing after :)', ->
+      @el.innerHTML = ":) "
+      result = emojify.run(@el)
+      assert.equal '<img title=":blush:" class="emoji" src="images/emoji/blush.png" align="absmiddle"></img> ', @el.innerHTML
+
+    it 'works with spacing before and after :)', ->
+      @el.innerHTML = " :) "
+      result = emojify.run(@el)
+      assert.equal ' <img title=":blush:" class="emoji" src="images/emoji/blush.png" align="absmiddle"></img> ', @el.innerHTML
+
+  describe 'with multiple emoji beside each other', ->
+    it 'works with multiple :emoji: style', ->
+      @el.innerHTML = ":railway_car::railway_car:"
+      emojify.run(@el)
+      assert.equal '<img title=":railway_car:" class="emoji" src="images/emoji/railway_car.png" align="absmiddle"></img><img title=":railway_car:" class="emoji" src="images/emoji/railway_car.png" align="absmiddle"></img>', @el.innerHTML
+
+  describe 'isolated cases', ->
+    it ':)', ->
+      @el.innerHTML = ":)"
+      emojify.run(@el)
+      assert.equal '<img title=":blush:" class="emoji" src="images/emoji/blush.png" align="absmiddle"></img>', @el.innerHTML
+
+    it ':D', ->
+      @el.innerHTML = ":D"
+      emojify.run(@el)
+      assert.equal '<img title=":smiley:" class="emoji" src="images/emoji/smiley.png" align="absmiddle"></img>', @el.innerHTML
+
+    it ':P', ->
+      @el.innerHTML = ":P"
+      emojify.run(@el)
+      assert.equal '<img title=":stuck_out_tongue_winking_eye:" class="emoji" src="images/emoji/stuck_out_tongue_winking_eye.png" align="absmiddle"></img>', @el.innerHTML
+
+    it '>:P', ->
+      @el.innerHTML = ">:P"
+      emojify.run(@el)
+      assert.equal '&gt;<img title=":stuck_out_tongue_winking_eye:" class="emoji" src="images/emoji/stuck_out_tongue_winking_eye.png" align="absmiddle"></img>', @el.innerHTML
diff --git a/tests/setup.js b/tests/setup.js
new file mode 100644
index 0000000..5ff8262
--- /dev/null
+++ b/tests/setup.js
@@ -0,0 +1,5 @@
+expect = chai.expect;
+assert = chai.assert;
+spy = sinon.spy;
+mock = sinon.mock;
+stub = sinon.stub;
diff --git a/tests/string_test.coffee b/tests/string_test.coffee
new file mode 100644
index 0000000..e60fd3e
--- /dev/null
+++ b/tests/string_test.coffee
@@ -0,0 +1,40 @@
+describe 'emojify used with flat strings', ->
+
+  describe 'with variations of spacing around 2char smileys', ->
+    it 'works with no spacing around :)', ->
+      el = "<div>:)</div>"
+      result = emojify.run(el)
+      assert.equal '<div><img title=\'&58;blush&58;\' class=\'emoji\' src=\'images/emoji/blush.png\' align=\'absmiddle\' /></div>', result
+
+    it 'works with spacing before :)', ->
+      el = "<div> :)</div>"
+      result = emojify.run(el)
+      assert.equal '<div> <img title=\'&58;blush&58;\' class=\'emoji\' src=\'images/emoji/blush.png\' align=\'absmiddle\' /></div>', result
+
+    it 'works with spacing after :)', ->
+      el = "<div>:) </div>"
+      result = emojify.run(el)
+      assert.equal '<div><img title=\'&58;blush&58;\' class=\'emoji\' src=\'images/emoji/blush.png\' align=\'absmiddle\' /> </div>', result
+
+    it 'works with spacing before and after :)', ->
+      el = "<div> :) </div>"
+      result = emojify.run(el)
+      assert.equal '<div> <img title=\'&58;blush&58;\' class=\'emoji\' src=\'images/emoji/blush.png\' align=\'absmiddle\' /> </div>', result
+
+  describe 'with multiple emoji beside each other', ->
+    it 'works with multiple :emoji: style', ->
+      el = "<div>:railway_car::railway_car:</div>"
+      result = emojify.run(el)
+      assert.equal '<div><img title=\'&58;railway_car&58;\' class=\'emoji\' src=\'images/emoji/railway_car.png\' align=\'absmiddle\' /><img title=\'&58;railway_car&58;\' class=\'emoji\' src=\'images/emoji/railway_car.png\' align=\'absmiddle\' /></div>', result
+
+    it 'works with multiple :) style', ->
+      el = "<div>:):P</div>"
+      result = emojify.run(el)
+      assert.equal '<div><img title=\'&58;blush&58;\' class=\'emoji\' src=\'images/emoji/blush.png\' align=\'absmiddle\' /><img title=\'&58;stuck_out_tongue_winking_eye&58;\' class=\'emoji\' src=\'images/emoji/stuck_out_tongue_winking_eye.png\' align=\'absmiddle\' /></div>', result
+
+
+  describe 'isolated cases', ->
+    it ':neckbeard:', ->
+      el = "<div>:neckbeard:</div>"
+      result = emojify.run(el)
+      assert.equal '<div><img title=\'&58;neckbeard&58;\' class=\'emoji\' src=\'images/emoji/neckbeard.png\' align=\'absmiddle\' /></div>', result
diff --git a/tests/tests.coffee b/tests/tests.coffee
new file mode 100644
index 0000000..04b4e19
--- /dev/null
+++ b/tests/tests.coffee
@@ -0,0 +1,2 @@
+require('./dom_test.coffee')
+require('./string_test.coffee')
