mirror of
https://github.com/twitter/twemoji.git
synced 2025-01-29 21:32:42 +00:00
Merge pull request #73 from twitter/derekmooney-gh-pages
Derekmooney gh pages
This commit is contained in:
commit
398533a464
14
README.md
14
README.md
@ -148,6 +148,7 @@ Here the list of properties accepted by the optional object that could be passed
|
||||
```js
|
||||
{
|
||||
callback: Function, // default the common replacer
|
||||
attributes: Function, // default returns {}
|
||||
base: string, // default MaxCDN
|
||||
ext: string, // default ".png"
|
||||
className: string, // default "emoji"
|
||||
@ -173,6 +174,19 @@ function imageSourceGenerator(icon, options) {
|
||||
}
|
||||
```
|
||||
|
||||
##### attributes
|
||||
The function to invoke in order to generate additional, custom attributes for the image tag.
|
||||
|
||||
By default it is a function like the following one:
|
||||
```js
|
||||
function attributesCallback(icon, variant) {
|
||||
return {
|
||||
title: 'Emoji: ' + icon + variant
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Event handlers cannot be specified via this method, and twemoji-provided attributes (src, alt, className, draggable) cannot be re-defined.
|
||||
|
||||
##### base
|
||||
The default url is the same as `twemoji.base`, so if you modify the former, it will reflect as default for all parsed strings or nodes.
|
||||
|
96
test.js
96
test.js
@ -306,6 +306,102 @@ wru.test([{
|
||||
div.getElementsByTagName('img')[0].className === className
|
||||
);
|
||||
}
|
||||
},{
|
||||
name: 'string parsing + attributes callback',
|
||||
test: function () {
|
||||
wru.assert(
|
||||
'custom attributes are inserted',
|
||||
'I <img class="emoji" draggable="false" alt="\u2764" src="' + base + '36x36/2764.png" title="Emoji: \u2764" data-test="We all <3 emoji"> emoji!' ===
|
||||
twemoji.parse(
|
||||
'I \u2764 emoji!',
|
||||
{
|
||||
attributes: function(icon) {
|
||||
return {
|
||||
title: 'Emoji: ' + icon,
|
||||
'data-test': 'We all <3 emoji'
|
||||
};
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
},{
|
||||
name: 'string parsing + attributes callback content properly encoded',
|
||||
test: function () {
|
||||
wru.assert(
|
||||
'custom attributes are inserted',
|
||||
'I <img class="emoji" draggable="false" alt="\u2764" src="' + base + '36x36/2764.png" title="&amp;lt;script&amp;gt;alert("yo")&amp;lt;/script&amp;gt;"> emoji!' ===
|
||||
twemoji.parse(
|
||||
'I \u2764 emoji!',
|
||||
{
|
||||
attributes: function(icon) {
|
||||
return {
|
||||
title: '&lt;script&gt;alert("yo")&lt;/script&gt;'
|
||||
};
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
},{
|
||||
name: 'string parsing + attributes callback "on" attributes are omitted',
|
||||
test: function () {
|
||||
wru.assert(
|
||||
'custom attributes are inserted',
|
||||
'I <img class="emoji" draggable="false" alt="❤" src="' + base + '36x36/2764.png" title="test"> emoji!' ===
|
||||
twemoji.parse(
|
||||
'I \u2764 emoji!',
|
||||
{
|
||||
attributes: function(icon) {
|
||||
return {
|
||||
title: 'test',
|
||||
onsomething: 'whoops!',
|
||||
onclick: 'nope',
|
||||
onmousedown: 'nada'
|
||||
};
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
},{
|
||||
name: 'DOM parsing + attributes callback',
|
||||
test: function () {
|
||||
var img,
|
||||
// without variant
|
||||
div = document.createElement('div');
|
||||
div.appendChild(document.createTextNode('I \u2764 emoji!'));
|
||||
twemoji.parse(
|
||||
div, {
|
||||
attributes: function(icon) {
|
||||
return {
|
||||
title: 'Emoji: ' + icon,
|
||||
'data-test': 'We all <3 emoji',
|
||||
onclick: 'nope',
|
||||
onmousedown: 'nada'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
wru.assert('default parsing works creating 3 nodes', div.childNodes.length === 3);
|
||||
wru.assert('first child is the expected one', div.removeChild(div.firstChild).nodeValue === 'I ');
|
||||
img = div.removeChild(div.firstChild);
|
||||
wru.assert('second child is the image', img.nodeName === 'IMG');
|
||||
wru.assert('img attributes are OK',
|
||||
img.className === 'emoji' &&
|
||||
img.getAttribute('draggable') === 'false' &&
|
||||
img.src === base + '36x36/2764.png' &&
|
||||
img.alt === '\u2764' &&
|
||||
img.onerror === twemoji.onerror &&
|
||||
img.getAttribute('title') === 'Emoji: \u2764' &&
|
||||
img.getAttribute('data-test') === 'We all <3 emoji'
|
||||
);
|
||||
wru.assert('img on attributes are omitted',
|
||||
img.onclick === null &&
|
||||
img.onmousedown === null
|
||||
);
|
||||
}
|
||||
},{
|
||||
name: 'folder option',
|
||||
test: function () {
|
||||
|
@ -500,10 +500,22 @@ function createTwemoji(re) {
|
||||
test: test
|
||||
},
|
||||
|
||||
// used to escape HTML special chars in attributes
|
||||
escaper = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
"'": ''',
|
||||
'"': '"'
|
||||
},
|
||||
|
||||
// RegExp based on emoji's official Unicode standards
|
||||
// http://www.unicode.org/Public/UNIDATA/EmojiSources.txt
|
||||
re = /twemoji/,
|
||||
|
||||
// used to find HTML special chars in attributes
|
||||
rescaper = /[&<>'"]/g,
|
||||
|
||||
// nodes with type 1 which should **not** be parsed
|
||||
shouldntBeParsed = /IFRAME|NOFRAMES|NOSCRIPT|SCRIPT|SELECT|STYLE|TEXTAREA/,
|
||||
|
||||
@ -527,6 +539,15 @@ function createTwemoji(re) {
|
||||
return document.createTextNode(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to escape html attribute text
|
||||
* @param string text use in HTML attribute
|
||||
* @return string text encoded to use in HTML attribute
|
||||
*/
|
||||
function escapeHTML(s) {
|
||||
return s.replace(rescaper, replacer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default callback used to generate emoji src
|
||||
* based on Twitter CDN
|
||||
@ -604,6 +625,8 @@ function createTwemoji(re) {
|
||||
var
|
||||
allText = grabAllTextNodes(node, []),
|
||||
length = allText.length,
|
||||
attrib,
|
||||
attrname,
|
||||
modified,
|
||||
fragment,
|
||||
subnode,
|
||||
@ -642,8 +665,19 @@ function createTwemoji(re) {
|
||||
if (src) {
|
||||
img = new Image();
|
||||
img.onerror = twemoji.onerror;
|
||||
img.className = options.className;
|
||||
img.setAttribute('draggable', 'false');
|
||||
attrib = options.attributes(icon, variant);
|
||||
for (attrname in attrib) {
|
||||
if (
|
||||
attrib.hasOwnProperty(attrname) &&
|
||||
// don't allow any handlers to be set + don't allow overrides
|
||||
attrname.indexOf('on') !== 0 &&
|
||||
!img.hasAttribute(attrname)
|
||||
) {
|
||||
img.setAttribute(attrname, attrib[attrname]);
|
||||
}
|
||||
}
|
||||
img.className = options.className;
|
||||
img.alt = alt;
|
||||
img.src = src;
|
||||
modified = true;
|
||||
@ -684,7 +718,11 @@ function createTwemoji(re) {
|
||||
*/
|
||||
function parseString(str, options) {
|
||||
return replace(str, function (match, icon, variant) {
|
||||
var src;
|
||||
var
|
||||
ret = match,
|
||||
attrib,
|
||||
attrname,
|
||||
src;
|
||||
// verify the variant is not the FE0E one
|
||||
// this variant means "emoji as text" and should not
|
||||
// require any action/replacement
|
||||
@ -698,25 +736,53 @@ function createTwemoji(re) {
|
||||
if (src) {
|
||||
// recycle the match string replacing the emoji
|
||||
// with its image counter part
|
||||
match = '<img '.concat(
|
||||
ret = '<img '.concat(
|
||||
'class="', options.className, '" ',
|
||||
'draggable="false" ',
|
||||
// needs to preserve user original intent
|
||||
// when variants should be copied and pasted too
|
||||
'alt="',
|
||||
match,
|
||||
'" ',
|
||||
'src="',
|
||||
src,
|
||||
'"',
|
||||
'>'
|
||||
' src="',
|
||||
src,
|
||||
'"'
|
||||
);
|
||||
attrib = options.attributes(icon, variant);
|
||||
for (attrname in attrib) {
|
||||
if (
|
||||
attrib.hasOwnProperty(attrname) &&
|
||||
// don't allow any handlers to be set + don't allow overrides
|
||||
attrname.indexOf('on') !== 0 &&
|
||||
ret.indexOf(' ' + attrname + '=') === -1
|
||||
) {
|
||||
ret = ret.concat(' ', attrname, '="', escapeHTML(attrib[attrname]), '"');
|
||||
}
|
||||
}
|
||||
ret = ret.concat('>');
|
||||
}
|
||||
}
|
||||
return match;
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to actually replace HTML special chars
|
||||
* @param string HTML special char
|
||||
* @return string encoded HTML special char
|
||||
*/
|
||||
function replacer(m) {
|
||||
return escaper[m];
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options.attribute callback
|
||||
* @return null
|
||||
*/
|
||||
function returnNull() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a generic value, creates its squared counterpart if it's a number.
|
||||
* As example, number 36 will return '36x36'.
|
||||
@ -757,11 +823,12 @@ function createTwemoji(re) {
|
||||
// if first argument is string, inject html <img> tags
|
||||
// otherwise use the DOM tree and parse text nodes only
|
||||
return (typeof what === 'string' ? parseString : parseNode)(what, {
|
||||
callback: how.callback || defaultImageSrcGenerator,
|
||||
base: typeof how.base === 'string' ? how.base : twemoji.base,
|
||||
ext: how.ext || twemoji.ext,
|
||||
size: how.folder || toSizeSquaredAsset(how.size || twemoji.size),
|
||||
className:how.className || twemoji.className
|
||||
callback: how.callback || defaultImageSrcGenerator,
|
||||
attributes: typeof how.attributes === 'function' ? how.attributes : returnNull,
|
||||
base: typeof how.base === 'string' ? how.base : twemoji.base,
|
||||
ext: how.ext || twemoji.ext,
|
||||
size: how.folder || toSizeSquaredAsset(how.size || twemoji.size),
|
||||
className: how.className || twemoji.className
|
||||
});
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
93
twemoji.js
93
twemoji.js
File diff suppressed because one or more lines are too long
2
twemoji.min.js
vendored
2
twemoji.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user