Replace pagedown by markdown-it
This commit is contained in:
parent
c246e80b1d
commit
026773194a
18 changed files with 936 additions and 1763 deletions
18
Gemfile
18
Gemfile
|
|
@ -82,13 +82,17 @@ gem 'entypo-rails', '2.2.2'
|
||||||
|
|
||||||
# JavaScript
|
# JavaScript
|
||||||
|
|
||||||
gem 'backbone-on-rails', '1.1.2'
|
gem 'backbone-on-rails', '1.1.2'
|
||||||
gem 'handlebars_assets', '0.18.0'
|
gem 'handlebars_assets', '0.18.0'
|
||||||
gem 'jquery-rails', '3.1.2'
|
gem 'jquery-rails', '3.1.2'
|
||||||
gem 'rails-assets-jquery', '1.11.1' # Should be kept in sync with jquery-rails
|
gem 'rails-assets-jquery', '1.11.1' # Should be kept in sync with jquery-rails
|
||||||
gem 'js_image_paths', '0.0.1'
|
gem 'js_image_paths', '0.0.1'
|
||||||
gem 'js-routes', '0.9.9'
|
gem 'js-routes', '0.9.9'
|
||||||
gem 'rails-assets-punycode', '1.3.2'
|
gem 'rails-assets-punycode', '1.3.2'
|
||||||
|
gem 'rails-assets-markdown-it', '3.0.1'
|
||||||
|
gem 'rails-assets-markdown-it-hashtag', '0.2.1'
|
||||||
|
gem 'rails-assets-markdown-it-diaspora-mention', '0.1.0'
|
||||||
|
gem 'rails-assets-markdown-it--markdown-it-for-inline', '0.1.0'
|
||||||
|
|
||||||
# jQuery plugins
|
# jQuery plugins
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -438,6 +438,10 @@ GEM
|
||||||
rails-assets-jquery (>= 1.6)
|
rails-assets-jquery (>= 1.6)
|
||||||
rails-assets-jquery.slimscroll (1.3.3)
|
rails-assets-jquery.slimscroll (1.3.3)
|
||||||
rails-assets-jquery (>= 1.7)
|
rails-assets-jquery (>= 1.7)
|
||||||
|
rails-assets-markdown-it--markdown-it-for-inline (0.1.0)
|
||||||
|
rails-assets-markdown-it (3.0.1)
|
||||||
|
rails-assets-markdown-it-diaspora-mention (0.1.0)
|
||||||
|
rails-assets-markdown-it-hashtag (0.2.1)
|
||||||
rails-assets-perfect-scrollbar (0.5.7)
|
rails-assets-perfect-scrollbar (0.5.7)
|
||||||
rails-assets-jquery (>= 1.10)
|
rails-assets-jquery (>= 1.10)
|
||||||
rails-assets-punycode (1.3.2)
|
rails-assets-punycode (1.3.2)
|
||||||
|
|
@ -681,6 +685,10 @@ DEPENDENCIES
|
||||||
rails-assets-jquery-idletimer (= 1.0.1)
|
rails-assets-jquery-idletimer (= 1.0.1)
|
||||||
rails-assets-jquery-placeholder (= 2.0.8)
|
rails-assets-jquery-placeholder (= 2.0.8)
|
||||||
rails-assets-jquery-textchange (= 0.2.3)
|
rails-assets-jquery-textchange (= 0.2.3)
|
||||||
|
rails-assets-markdown-it (= 3.0.1)
|
||||||
|
rails-assets-markdown-it--markdown-it-for-inline (= 0.1.0)
|
||||||
|
rails-assets-markdown-it-diaspora-mention (= 0.1.0)
|
||||||
|
rails-assets-markdown-it-hashtag (= 0.2.1)
|
||||||
rails-assets-perfect-scrollbar (= 0.5.7)
|
rails-assets-perfect-scrollbar (= 0.5.7)
|
||||||
rails-assets-punycode (= 1.3.2)
|
rails-assets-punycode (= 1.3.2)
|
||||||
rails-i18n (= 4.0.3)
|
rails-i18n (= 4.0.3)
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ Handlebars.registerHelper('fmtTags', function(tags) {
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('fmtText', function(text) {
|
Handlebars.registerHelper('fmtText', function(text) {
|
||||||
return new Handlebars.SafeString(app.helpers.textFormatter(text, null));
|
return new Handlebars.SafeString(app.helpers.textFormatter(text));
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('isCurrentPage', function(path_helper, id, options){
|
Handlebars.registerHelper('isCurrentPage', function(path_helper, id, options){
|
||||||
|
|
|
||||||
|
|
@ -1,146 +1,76 @@
|
||||||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
|
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
|
||||||
|
|
||||||
// cache url regex globally, for direct acces when testing
|
|
||||||
$(function() {
|
|
||||||
Diaspora.url_regex = /(^|\s)\b((?:(?:https?|ftp):(?:\/{1,3})|www\.)(?:[^"<>\)\s]|\(([^\s()<>]+|(\([^\s()<>]+\)))\))+)(?=\s|$)/gi;
|
|
||||||
});
|
|
||||||
|
|
||||||
(function(){
|
(function(){
|
||||||
//make it so I take text and mentions rather than the modelapp.helpers.textFormatter(
|
app.helpers.textFormatter = function(text, mentions) {
|
||||||
var textFormatter = function textFormatter(text, model) {
|
mentions = mentions ? mentions : [];
|
||||||
var mentions = model ? model.get("mentioned_people") : [];
|
|
||||||
|
|
||||||
return textFormatter.mentionify(
|
var punycodeURL = function(url){
|
||||||
textFormatter.hashtagify(
|
try {
|
||||||
textFormatter.markdownify(text)
|
while(url.indexOf("%") !== -1 && url != decodeURI(url)) url = decodeURI(url);
|
||||||
), mentions
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
textFormatter.markdownify = function markdownify(text){
|
|
||||||
var converter = Markdown.getSanitizingConverter();
|
|
||||||
|
|
||||||
// punycode non-ascii chars in urls
|
|
||||||
converter.hooks.chain("preConversion", function(text) {
|
|
||||||
|
|
||||||
// add < > around plain urls, effectively making them "autolinks"
|
|
||||||
text = text.replace(Diaspora.url_regex, function() {
|
|
||||||
var url = arguments[2];
|
|
||||||
if( url.match(/^[^\w]/) ) return url; // evil witchcraft, noop
|
|
||||||
return arguments[1]+"<"+url+">";
|
|
||||||
});
|
|
||||||
|
|
||||||
// process links
|
|
||||||
// regex copied from: https://code.google.com/p/pagedown/source/browse/Markdown.Converter.js#1198 (and slightly expanded)
|
|
||||||
var linkRegex = /(\[.*\]:\s)?(<|\()((?:(https?|ftp):\/\/[^\/'">\s]|www)[^'">\s]+?)([>\)]{1,2})/gi;
|
|
||||||
text = text.replace(linkRegex, function() {
|
|
||||||
var unicodeUrl = arguments[3];
|
|
||||||
var urlSuffix = arguments[5];
|
|
||||||
|
|
||||||
unicodeUrl = ( unicodeUrl.match(/^www/) ) ? ('http://' + unicodeUrl) : unicodeUrl;
|
|
||||||
|
|
||||||
// handle parentheses, especially in case the link ends with ')'
|
|
||||||
if( urlSuffix.indexOf(')') != -1 && urlSuffix.indexOf('>') != -1 ) {
|
|
||||||
unicodeUrl += ')';
|
|
||||||
urlSuffix = '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// url*DE*code as much as possible
|
|
||||||
try {
|
|
||||||
while( unicodeUrl.indexOf("%") !== -1 && unicodeUrl != decodeURI(unicodeUrl) ) {
|
|
||||||
unicodeUrl = decodeURI(unicodeUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e){}
|
|
||||||
|
|
||||||
// markdown doesn't like '(' or ')' anywhere, except where it wants
|
|
||||||
var workingUrl = unicodeUrl.replace(/\(/, "%28").replace(/\)/, "%29");
|
|
||||||
|
|
||||||
var addr = parse_url(unicodeUrl);
|
|
||||||
if( !addr.host ) addr.host = ""; // must not be 'undefined'
|
|
||||||
|
|
||||||
var asciiUrl = // rebuild the url
|
|
||||||
(!addr.scheme ? '' : addr.scheme +
|
|
||||||
( (addr.scheme.toLowerCase()=="mailto") ? ':' : '://')) +
|
|
||||||
(!addr.user ? '' : addr.user +
|
|
||||||
(!addr.pass ? '' : ':'+addr.pass) + '@') +
|
|
||||||
punycode.toASCII(addr.host) +
|
|
||||||
(!addr.port ? '' : ':' + addr.port) +
|
|
||||||
(!addr.path ? '' : encodeURI(addr.path) ) +
|
|
||||||
(!addr.query ? '' : '?' + encodeURI(addr.query) ) +
|
|
||||||
(!addr.fragment ? '' : '#' + encodeURI(addr.fragment) );
|
|
||||||
if( !arguments[1] || arguments[1] == "") { // inline link
|
|
||||||
if(arguments[2] == "<") return "["+workingUrl+"]("+asciiUrl+")"; // without link text
|
|
||||||
else return arguments[2]+asciiUrl+urlSuffix; // with link text
|
|
||||||
} else { // reference style link
|
|
||||||
return arguments[1]+asciiUrl;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return text;
|
|
||||||
});
|
|
||||||
|
|
||||||
// make nice little utf-8 symbols
|
|
||||||
converter.hooks.chain("preConversion", function(text) {
|
|
||||||
var input_strings = [
|
|
||||||
"<->", "->", "<-",
|
|
||||||
"(c)", "(r)", "(tm)",
|
|
||||||
"<3"
|
|
||||||
];
|
|
||||||
var output_symbols = [
|
|
||||||
"↔", "→", "←",
|
|
||||||
"©", "®", "™",
|
|
||||||
"♥"
|
|
||||||
];
|
|
||||||
// quote function from: http://stackoverflow.com/a/494122
|
|
||||||
var quote = function(str) {
|
|
||||||
return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
|
|
||||||
};
|
|
||||||
|
|
||||||
_.each(input_strings, function(str, idx) {
|
|
||||||
var r = new RegExp(quote(str), "gi");
|
|
||||||
text = text.replace(r, output_symbols[idx]);
|
|
||||||
});
|
|
||||||
return text;
|
|
||||||
});
|
|
||||||
|
|
||||||
converter.hooks.chain("postConversion", function (text) {
|
|
||||||
return text.replace(/(\"(?:(?:http|https):\/\/)?[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(?:\/\S*)?\")(\>)/g, '$1 target="_blank">')
|
|
||||||
});
|
|
||||||
|
|
||||||
return converter.makeHtml(text)
|
|
||||||
};
|
|
||||||
|
|
||||||
textFormatter.hashtagify = function hashtagify(text){
|
|
||||||
var utf8WordCharcters =/(<a[^>]*>.*?<\/a>)|(\s|^|>)#([\u0080-\uFFFF|\w|-]+|<3)/g;
|
|
||||||
|
|
||||||
return text.replace(utf8WordCharcters, function(result, linkTag, preceeder, tagText) {
|
|
||||||
if(linkTag)
|
|
||||||
return linkTag;
|
|
||||||
else
|
|
||||||
return preceeder + "<a href='/tags/" + tagText.toLowerCase() +
|
|
||||||
"' class='tag'>#" + tagText + "</a>";
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
textFormatter.mentionify = function mentionify(text, mentions) {
|
|
||||||
var mentionRegex = /@\{([^;]+); ([^\}]+)\}/g
|
|
||||||
return text.replace(mentionRegex, function(mentionText, fullName, diasporaId) {
|
|
||||||
var person = _.find(mentions, function(person){
|
|
||||||
return (diasporaId == person.diaspora_id || person.handle) //jquery.mentionsInput gives us person.handle
|
|
||||||
})
|
|
||||||
if(person) {
|
|
||||||
var url = person.url || "/people/" + person.guid //jquery.mentionsInput gives us person.url
|
|
||||||
, personText = "<a href='" + url + "' class='mention'>" + fullName + "</a>"
|
|
||||||
} else {
|
|
||||||
personText = fullName;
|
|
||||||
}
|
}
|
||||||
|
catch(e){}
|
||||||
|
|
||||||
return personText
|
var addr = parse_url(url);
|
||||||
})
|
if( !addr.host ) addr.host = ""; // must not be 'undefined'
|
||||||
}
|
|
||||||
|
|
||||||
app.helpers.textFormatter = textFormatter;
|
url = // rebuild the url
|
||||||
|
(!addr.scheme ? '' : addr.scheme +
|
||||||
|
( (addr.scheme.toLowerCase()=="mailto") ? ':' : '://')) +
|
||||||
|
(!addr.user ? '' : addr.user +
|
||||||
|
(!addr.pass ? '' : ':'+addr.pass) + '@') +
|
||||||
|
punycode.toASCII(addr.host) +
|
||||||
|
(!addr.port ? '' : ':' + addr.port) +
|
||||||
|
(!addr.path ? '' : encodeURI(addr.path) ) +
|
||||||
|
(!addr.query ? '' : '?' + encodeURI(addr.query) ) +
|
||||||
|
(!addr.fragment ? '' : '#' + encodeURI(addr.fragment) );
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
var md = window.markdownit({
|
||||||
|
breaks: true,
|
||||||
|
html: false,
|
||||||
|
linkify: true,
|
||||||
|
typographer: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var inlinePlugin = window.markdownitForInline;
|
||||||
|
md.use(inlinePlugin, 'utf8_symbols', 'text', function (tokens, idx) {
|
||||||
|
tokens[idx].content = tokens[idx].content.replace(/<->/g, "↔")
|
||||||
|
.replace(/<-/g, "←")
|
||||||
|
.replace(/->/g, "→")
|
||||||
|
.replace(/<3/g, "♥");
|
||||||
|
});
|
||||||
|
|
||||||
|
md.use(inlinePlugin, 'link_new_window_and_punycode', 'link_open', function (tokens, idx) {
|
||||||
|
tokens[idx].href = punycodeURL(tokens[idx].href);
|
||||||
|
tokens[idx].target = "_blank";
|
||||||
|
});
|
||||||
|
|
||||||
|
md.use(inlinePlugin, 'image_punycode', 'image', function (tokens, idx) {
|
||||||
|
tokens[idx].src = punycodeURL(tokens[idx].src);
|
||||||
|
});
|
||||||
|
|
||||||
|
var hashtagPlugin = window.markdownitHashtag;
|
||||||
|
md.use(hashtagPlugin, {
|
||||||
|
// compare tag_text_regexp in app/models/acts_as_taggable_on-tag.rb
|
||||||
|
hashtagRegExp: '[' + PosixBracketExpressions.alnum + '_\\-]+|<3',
|
||||||
|
// compare tag_strings in lib/diaspora/taggabe.rb
|
||||||
|
preceding: '^|\\s'
|
||||||
|
});
|
||||||
|
|
||||||
|
var mentionPlugin = window.markdownitDiasporaMention;
|
||||||
|
md.use(mentionPlugin, mentions);
|
||||||
|
|
||||||
|
// TODO this is a temporary fix
|
||||||
|
// remove it as soon as markdown-it fixes its autolinking feature
|
||||||
|
var linkifyPlugin = window.markdownitDiasporaLinkify;
|
||||||
|
md.use(linkifyPlugin);
|
||||||
|
|
||||||
|
// Bootstrap table markup
|
||||||
|
md.renderer.rules.table_open = function () { return '<table class="table table-striped">\n'; };
|
||||||
|
|
||||||
|
return md.render(text);
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
// @license-end
|
// @license-end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ app.pages.SinglePostViewer = app.views.Base.extend({
|
||||||
postRenderTemplate : function() {
|
postRenderTemplate : function() {
|
||||||
if(this.model.get("title")){
|
if(this.model.get("title")){
|
||||||
// formats title to html...
|
// formats title to html...
|
||||||
var html_title = app.helpers.textFormatter(this.model.get("title"), this.model);
|
var html_title = app.helpers.textFormatter(this.model.get("title"), this.model.get("mentioned_people"));
|
||||||
//... and converts html to plain text
|
//... and converts html to plain text
|
||||||
document.title = $('<div>').html(html_title).text();
|
document.title = $('<div>').html(html_title).text();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ app.views.Comment = app.views.Content.extend({
|
||||||
presenter : function() {
|
presenter : function() {
|
||||||
return _.extend(this.defaultPresenter(), {
|
return _.extend(this.defaultPresenter(), {
|
||||||
canRemove: this.canRemove(),
|
canRemove: this.canRemove(),
|
||||||
text : app.helpers.textFormatter(this.model.get("text"), this.model)
|
text : app.helpers.textFormatter(this.model.get("text"))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ app.views.Content = app.views.Base.extend({
|
||||||
|
|
||||||
presenter : function(){
|
presenter : function(){
|
||||||
return _.extend(this.defaultPresenter(), {
|
return _.extend(this.defaultPresenter(), {
|
||||||
text : app.helpers.textFormatter(this.model.get("text"), this.model),
|
text : app.helpers.textFormatter(this.model.get("text"), this.model.get("mentioned_people")),
|
||||||
largePhoto : this.largePhoto(),
|
largePhoto : this.largePhoto(),
|
||||||
smallPhotos : this.smallPhotos(),
|
smallPhotos : this.smallPhotos(),
|
||||||
location: this.location()
|
location: this.location()
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ app.views.Post = app.views.Base.extend({
|
||||||
return _.extend(this.defaultPresenter(), {
|
return _.extend(this.defaultPresenter(), {
|
||||||
authorIsCurrentUser : app.currentUser.isAuthorOf(this.model),
|
authorIsCurrentUser : app.currentUser.isAuthorOf(this.model),
|
||||||
showPost : this.showPost(),
|
showPost : this.showPost(),
|
||||||
text : app.helpers.textFormatter(this.model.get("text"), this.model)
|
text : app.helpers.textFormatter(this.model.get("text"), this.model.get("mentioned_people"))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ app.views.SinglePostContent = app.views.Base.extend({
|
||||||
return _.extend(this.defaultPresenter(), {
|
return _.extend(this.defaultPresenter(), {
|
||||||
authorIsCurrentUser :app.currentUser.isAuthorOf(this.model),
|
authorIsCurrentUser :app.currentUser.isAuthorOf(this.model),
|
||||||
showPost : this.showPost(),
|
showPost : this.showPost(),
|
||||||
text : app.helpers.textFormatter(this.model.get("text"), this.model)
|
text : app.helpers.textFormatter(this.model.get("text"), this.model.get("mentioned_people"))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,12 @@
|
||||||
//= require keycodes
|
//= require keycodes
|
||||||
//= require fileuploader-custom
|
//= require fileuploader-custom
|
||||||
//= require handlebars.runtime
|
//= require handlebars.runtime
|
||||||
//= require markdown
|
//= require posix-bracket-expressions
|
||||||
|
//= require markdown-it
|
||||||
|
//= require markdown-it-hashtag
|
||||||
|
//= require markdown-it-diaspora-linkify
|
||||||
|
//= require markdown-it-diaspora-mention
|
||||||
|
//= require markdown-it-for-inline
|
||||||
//= require punycode
|
//= require punycode
|
||||||
//= require parse_url
|
//= require parse_url
|
||||||
//= require clear-form
|
//= require clear-form
|
||||||
|
|
|
||||||
|
|
@ -1102,8 +1102,8 @@ en:
|
||||||
discard_post: "Discard post"
|
discard_post: "Discard post"
|
||||||
new_user_prefill:
|
new_user_prefill:
|
||||||
newhere: "NewHere"
|
newhere: "NewHere"
|
||||||
hello: "Hey everyone, I'm #%{new_user_tag}. "
|
hello: "Hey everyone, I’m #%{new_user_tag}. "
|
||||||
i_like: "I'm interested in %{tags}. "
|
i_like: "I’m interested in %{tags}. "
|
||||||
invited_by: "Thanks for the invite, "
|
invited_by: "Thanks for the invite, "
|
||||||
poll:
|
poll:
|
||||||
remove_poll_answer: "Remove option"
|
remove_poll_answer: "Remove option"
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ Feature: new user registration
|
||||||
And I confirm the alert
|
And I confirm the alert
|
||||||
Then I should be on the stream page
|
Then I should be on the stream page
|
||||||
When I submit the publisher
|
When I submit the publisher
|
||||||
Then "Hey everyone, I'm #NewHere." should be post 1
|
Then "Hey everyone, I’m #NewHere." should be post 1
|
||||||
|
|
||||||
Scenario: new user with some tags posts first status message
|
Scenario: new user with some tags posts first status message
|
||||||
When I fill in the following:
|
When I fill in the following:
|
||||||
|
|
@ -54,7 +54,7 @@ Feature: new user registration
|
||||||
And I follow "awesome_button"
|
And I follow "awesome_button"
|
||||||
Then I should be on the stream page
|
Then I should be on the stream page
|
||||||
When I submit the publisher
|
When I submit the publisher
|
||||||
Then "Hey everyone, I'm #NewHere. I'm interested in #rockstar." should be post 1
|
Then "Hey everyone, I’m #NewHere. I’m interested in #rockstar." should be post 1
|
||||||
|
|
||||||
Scenario: closing a popover clears getting started
|
Scenario: closing a popover clears getting started
|
||||||
When I follow "awesome_button"
|
When I follow "awesome_button"
|
||||||
|
|
|
||||||
180
lib/assets/javascripts/markdown-it-diaspora-linkify.js
Normal file
180
lib/assets/javascripts/markdown-it-diaspora-linkify.js
Normal file
File diff suppressed because one or more lines are too long
571
lib/assets/javascripts/posix-bracket-expressions.js
Normal file
571
lib/assets/javascripts/posix-bracket-expressions.js
Normal file
|
|
@ -0,0 +1,571 @@
|
||||||
|
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
|
||||||
|
|
||||||
|
var PosixBracketExpressions = {
|
||||||
|
alnum : '\\u0030-\\u0039'
|
||||||
|
+ '\\u0041-\\u005a'
|
||||||
|
+ '\\u0061-\\u007a'
|
||||||
|
+ '\\u00aa'
|
||||||
|
+ '\\u00b5'
|
||||||
|
+ '\\u00ba'
|
||||||
|
+ '\\u00c0-\\u00d6'
|
||||||
|
+ '\\u00d8-\\u00f6'
|
||||||
|
+ '\\u00f8-\\u02c1'
|
||||||
|
+ '\\u02c6-\\u02d1'
|
||||||
|
+ '\\u02e0-\\u02e4'
|
||||||
|
+ '\\u02ec'
|
||||||
|
+ '\\u02ee'
|
||||||
|
+ '\\u0345'
|
||||||
|
+ '\\u0370-\\u0374'
|
||||||
|
+ '\\u0376-\\u0377'
|
||||||
|
+ '\\u037a-\\u037d'
|
||||||
|
+ '\\u0386'
|
||||||
|
+ '\\u0388-\\u038a'
|
||||||
|
+ '\\u038c'
|
||||||
|
+ '\\u038e-\\u03a1'
|
||||||
|
+ '\\u03a3-\\u03f5'
|
||||||
|
+ '\\u03f7-\\u0481'
|
||||||
|
+ '\\u048a-\\u0527'
|
||||||
|
+ '\\u0531-\\u0556'
|
||||||
|
+ '\\u0559'
|
||||||
|
+ '\\u0561-\\u0587'
|
||||||
|
+ '\\u05b0-\\u05bd'
|
||||||
|
+ '\\u05bf'
|
||||||
|
+ '\\u05c1-\\u05c2'
|
||||||
|
+ '\\u05c4-\\u05c5'
|
||||||
|
+ '\\u05c7'
|
||||||
|
+ '\\u05d0-\\u05ea'
|
||||||
|
+ '\\u05f0-\\u05f2'
|
||||||
|
+ '\\u0610-\\u061a'
|
||||||
|
+ '\\u0620-\\u0657'
|
||||||
|
+ '\\u0659-\\u0669'
|
||||||
|
+ '\\u066e-\\u06d3'
|
||||||
|
+ '\\u06d5-\\u06dc'
|
||||||
|
+ '\\u06e1-\\u06e8'
|
||||||
|
+ '\\u06ed-\\u06fc'
|
||||||
|
+ '\\u06ff'
|
||||||
|
+ '\\u0710-\\u073f'
|
||||||
|
+ '\\u074d-\\u07b1'
|
||||||
|
+ '\\u07c0-\\u07ea'
|
||||||
|
+ '\\u07f4-\\u07f5'
|
||||||
|
+ '\\u07fa'
|
||||||
|
+ '\\u0800-\\u0817'
|
||||||
|
+ '\\u081a-\\u082c'
|
||||||
|
+ '\\u0840-\\u0858'
|
||||||
|
+ '\\u08a0'
|
||||||
|
+ '\\u08a2-\\u08ac'
|
||||||
|
+ '\\u08e4-\\u08e9'
|
||||||
|
+ '\\u08f0-\\u08fe'
|
||||||
|
+ '\\u0900-\\u093b'
|
||||||
|
+ '\\u093d-\\u094c'
|
||||||
|
+ '\\u094e-\\u0950'
|
||||||
|
+ '\\u0955-\\u0963'
|
||||||
|
+ '\\u0966-\\u096f'
|
||||||
|
+ '\\u0971-\\u0977'
|
||||||
|
+ '\\u0979-\\u097f'
|
||||||
|
+ '\\u0981-\\u0983'
|
||||||
|
+ '\\u0985-\\u098c'
|
||||||
|
+ '\\u098f-\\u0990'
|
||||||
|
+ '\\u0993-\\u09a8'
|
||||||
|
+ '\\u09aa-\\u09b0'
|
||||||
|
+ '\\u09b2'
|
||||||
|
+ '\\u09b6-\\u09b9'
|
||||||
|
+ '\\u09bd-\\u09c4'
|
||||||
|
+ '\\u09c7-\\u09c8'
|
||||||
|
+ '\\u09cb-\\u09cc'
|
||||||
|
+ '\\u09ce'
|
||||||
|
+ '\\u09d7'
|
||||||
|
+ '\\u09dc-\\u09dd'
|
||||||
|
+ '\\u09df-\\u09e3'
|
||||||
|
+ '\\u09e6-\\u09f1'
|
||||||
|
+ '\\u0a01-\\u0a03'
|
||||||
|
+ '\\u0a05-\\u0a0a'
|
||||||
|
+ '\\u0a0f-\\u0a10'
|
||||||
|
+ '\\u0a13-\\u0a28'
|
||||||
|
+ '\\u0a2a-\\u0a30'
|
||||||
|
+ '\\u0a32-\\u0a33'
|
||||||
|
+ '\\u0a35-\\u0a36'
|
||||||
|
+ '\\u0a38-\\u0a39'
|
||||||
|
+ '\\u0a3e-\\u0a42'
|
||||||
|
+ '\\u0a47-\\u0a48'
|
||||||
|
+ '\\u0a4b-\\u0a4c'
|
||||||
|
+ '\\u0a51'
|
||||||
|
+ '\\u0a59-\\u0a5c'
|
||||||
|
+ '\\u0a5e'
|
||||||
|
+ '\\u0a66-\\u0a75'
|
||||||
|
+ '\\u0a81-\\u0a83'
|
||||||
|
+ '\\u0a85-\\u0a8d'
|
||||||
|
+ '\\u0a8f-\\u0a91'
|
||||||
|
+ '\\u0a93-\\u0aa8'
|
||||||
|
+ '\\u0aaa-\\u0ab0'
|
||||||
|
+ '\\u0ab2-\\u0ab3'
|
||||||
|
+ '\\u0ab5-\\u0ab9'
|
||||||
|
+ '\\u0abd-\\u0ac5'
|
||||||
|
+ '\\u0ac7-\\u0ac9'
|
||||||
|
+ '\\u0acb-\\u0acc'
|
||||||
|
+ '\\u0ad0'
|
||||||
|
+ '\\u0ae0-\\u0ae3'
|
||||||
|
+ '\\u0ae6-\\u0aef'
|
||||||
|
+ '\\u0b01-\\u0b03'
|
||||||
|
+ '\\u0b05-\\u0b0c'
|
||||||
|
+ '\\u0b0f-\\u0b10'
|
||||||
|
+ '\\u0b13-\\u0b28'
|
||||||
|
+ '\\u0b2a-\\u0b30'
|
||||||
|
+ '\\u0b32-\\u0b33'
|
||||||
|
+ '\\u0b35-\\u0b39'
|
||||||
|
+ '\\u0b3d-\\u0b44'
|
||||||
|
+ '\\u0b47-\\u0b48'
|
||||||
|
+ '\\u0b4b-\\u0b4c'
|
||||||
|
+ '\\u0b56-\\u0b57'
|
||||||
|
+ '\\u0b5c-\\u0b5d'
|
||||||
|
+ '\\u0b5f-\\u0b63'
|
||||||
|
+ '\\u0b66-\\u0b6f'
|
||||||
|
+ '\\u0b71'
|
||||||
|
+ '\\u0b82-\\u0b83'
|
||||||
|
+ '\\u0b85-\\u0b8a'
|
||||||
|
+ '\\u0b8e-\\u0b90'
|
||||||
|
+ '\\u0b92-\\u0b95'
|
||||||
|
+ '\\u0b99-\\u0b9a'
|
||||||
|
+ '\\u0b9c'
|
||||||
|
+ '\\u0b9e-\\u0b9f'
|
||||||
|
+ '\\u0ba3-\\u0ba4'
|
||||||
|
+ '\\u0ba8-\\u0baa'
|
||||||
|
+ '\\u0bae-\\u0bb9'
|
||||||
|
+ '\\u0bbe-\\u0bc2'
|
||||||
|
+ '\\u0bc6-\\u0bc8'
|
||||||
|
+ '\\u0bca-\\u0bcc'
|
||||||
|
+ '\\u0bd0'
|
||||||
|
+ '\\u0bd7'
|
||||||
|
+ '\\u0be6-\\u0bef'
|
||||||
|
+ '\\u0c01-\\u0c03'
|
||||||
|
+ '\\u0c05-\\u0c0c'
|
||||||
|
+ '\\u0c0e-\\u0c10'
|
||||||
|
+ '\\u0c12-\\u0c28'
|
||||||
|
+ '\\u0c2a-\\u0c33'
|
||||||
|
+ '\\u0c35-\\u0c39'
|
||||||
|
+ '\\u0c3d-\\u0c44'
|
||||||
|
+ '\\u0c46-\\u0c48'
|
||||||
|
+ '\\u0c4a-\\u0c4c'
|
||||||
|
+ '\\u0c55-\\u0c56'
|
||||||
|
+ '\\u0c58-\\u0c59'
|
||||||
|
+ '\\u0c60-\\u0c63'
|
||||||
|
+ '\\u0c66-\\u0c6f'
|
||||||
|
+ '\\u0c82-\\u0c83'
|
||||||
|
+ '\\u0c85-\\u0c8c'
|
||||||
|
+ '\\u0c8e-\\u0c90'
|
||||||
|
+ '\\u0c92-\\u0ca8'
|
||||||
|
+ '\\u0caa-\\u0cb3'
|
||||||
|
+ '\\u0cb5-\\u0cb9'
|
||||||
|
+ '\\u0cbd-\\u0cc4'
|
||||||
|
+ '\\u0cc6-\\u0cc8'
|
||||||
|
+ '\\u0cca-\\u0ccc'
|
||||||
|
+ '\\u0cd5-\\u0cd6'
|
||||||
|
+ '\\u0cde'
|
||||||
|
+ '\\u0ce0-\\u0ce3'
|
||||||
|
+ '\\u0ce6-\\u0cef'
|
||||||
|
+ '\\u0cf1-\\u0cf2'
|
||||||
|
+ '\\u0d02-\\u0d03'
|
||||||
|
+ '\\u0d05-\\u0d0c'
|
||||||
|
+ '\\u0d0e-\\u0d10'
|
||||||
|
+ '\\u0d12-\\u0d3a'
|
||||||
|
+ '\\u0d3d-\\u0d44'
|
||||||
|
+ '\\u0d46-\\u0d48'
|
||||||
|
+ '\\u0d4a-\\u0d4c'
|
||||||
|
+ '\\u0d4e'
|
||||||
|
+ '\\u0d57'
|
||||||
|
+ '\\u0d60-\\u0d63'
|
||||||
|
+ '\\u0d66-\\u0d6f'
|
||||||
|
+ '\\u0d7a-\\u0d7f'
|
||||||
|
+ '\\u0d82-\\u0d83'
|
||||||
|
+ '\\u0d85-\\u0d96'
|
||||||
|
+ '\\u0d9a-\\u0db1'
|
||||||
|
+ '\\u0db3-\\u0dbb'
|
||||||
|
+ '\\u0dbd'
|
||||||
|
+ '\\u0dc0-\\u0dc6'
|
||||||
|
+ '\\u0dcf-\\u0dd4'
|
||||||
|
+ '\\u0dd6'
|
||||||
|
+ '\\u0dd8-\\u0ddf'
|
||||||
|
+ '\\u0df2-\\u0df3'
|
||||||
|
+ '\\u0e01-\\u0e3a'
|
||||||
|
+ '\\u0e40-\\u0e46'
|
||||||
|
+ '\\u0e4d'
|
||||||
|
+ '\\u0e50-\\u0e59'
|
||||||
|
+ '\\u0e81-\\u0e82'
|
||||||
|
+ '\\u0e84'
|
||||||
|
+ '\\u0e87-\\u0e88'
|
||||||
|
+ '\\u0e8a'
|
||||||
|
+ '\\u0e8d'
|
||||||
|
+ '\\u0e94-\\u0e97'
|
||||||
|
+ '\\u0e99-\\u0e9f'
|
||||||
|
+ '\\u0ea1-\\u0ea3'
|
||||||
|
+ '\\u0ea5'
|
||||||
|
+ '\\u0ea7'
|
||||||
|
+ '\\u0eaa-\\u0eab'
|
||||||
|
+ '\\u0ead-\\u0eb9'
|
||||||
|
+ '\\u0ebb-\\u0ebd'
|
||||||
|
+ '\\u0ec0-\\u0ec4'
|
||||||
|
+ '\\u0ec6'
|
||||||
|
+ '\\u0ecd'
|
||||||
|
+ '\\u0ed0-\\u0ed9'
|
||||||
|
+ '\\u0edc-\\u0edf'
|
||||||
|
+ '\\u0f00'
|
||||||
|
+ '\\u0f20-\\u0f29'
|
||||||
|
+ '\\u0f40-\\u0f47'
|
||||||
|
+ '\\u0f49-\\u0f6c'
|
||||||
|
+ '\\u0f71-\\u0f81'
|
||||||
|
+ '\\u0f88-\\u0f97'
|
||||||
|
+ '\\u0f99-\\u0fbc'
|
||||||
|
+ '\\u1000-\\u1036'
|
||||||
|
+ '\\u1038'
|
||||||
|
+ '\\u103b-\\u1049'
|
||||||
|
+ '\\u1050-\\u1062'
|
||||||
|
+ '\\u1065-\\u1068'
|
||||||
|
+ '\\u106e-\\u1086'
|
||||||
|
+ '\\u108e'
|
||||||
|
+ '\\u1090-\\u1099'
|
||||||
|
+ '\\u109c-\\u109d'
|
||||||
|
+ '\\u10a0-\\u10c5'
|
||||||
|
+ '\\u10c7'
|
||||||
|
+ '\\u10cd'
|
||||||
|
+ '\\u10d0-\\u10fa'
|
||||||
|
+ '\\u10fc-\\u1248'
|
||||||
|
+ '\\u124a-\\u124d'
|
||||||
|
+ '\\u1250-\\u1256'
|
||||||
|
+ '\\u1258'
|
||||||
|
+ '\\u125a-\\u125d'
|
||||||
|
+ '\\u1260-\\u1288'
|
||||||
|
+ '\\u128a-\\u128d'
|
||||||
|
+ '\\u1290-\\u12b0'
|
||||||
|
+ '\\u12b2-\\u12b5'
|
||||||
|
+ '\\u12b8-\\u12be'
|
||||||
|
+ '\\u12c0'
|
||||||
|
+ '\\u12c2-\\u12c5'
|
||||||
|
+ '\\u12c8-\\u12d6'
|
||||||
|
+ '\\u12d8-\\u1310'
|
||||||
|
+ '\\u1312-\\u1315'
|
||||||
|
+ '\\u1318-\\u135a'
|
||||||
|
+ '\\u135f'
|
||||||
|
+ '\\u1380-\\u138f'
|
||||||
|
+ '\\u13a0-\\u13f4'
|
||||||
|
+ '\\u1401-\\u166c'
|
||||||
|
+ '\\u166f-\\u167f'
|
||||||
|
+ '\\u1681-\\u169a'
|
||||||
|
+ '\\u16a0-\\u16ea'
|
||||||
|
+ '\\u16ee-\\u16f0'
|
||||||
|
+ '\\u1700-\\u170c'
|
||||||
|
+ '\\u170e-\\u1713'
|
||||||
|
+ '\\u1720-\\u1733'
|
||||||
|
+ '\\u1740-\\u1753'
|
||||||
|
+ '\\u1760-\\u176c'
|
||||||
|
+ '\\u176e-\\u1770'
|
||||||
|
+ '\\u1772-\\u1773'
|
||||||
|
+ '\\u1780-\\u17b3'
|
||||||
|
+ '\\u17b6-\\u17c8'
|
||||||
|
+ '\\u17d7'
|
||||||
|
+ '\\u17dc'
|
||||||
|
+ '\\u17e0-\\u17e9'
|
||||||
|
+ '\\u1810-\\u1819'
|
||||||
|
+ '\\u1820-\\u1877'
|
||||||
|
+ '\\u1880-\\u18aa'
|
||||||
|
+ '\\u18b0-\\u18f5'
|
||||||
|
+ '\\u1900-\\u191c'
|
||||||
|
+ '\\u1920-\\u192b'
|
||||||
|
+ '\\u1930-\\u1938'
|
||||||
|
+ '\\u1946-\\u196d'
|
||||||
|
+ '\\u1970-\\u1974'
|
||||||
|
+ '\\u1980-\\u19ab'
|
||||||
|
+ '\\u19b0-\\u19c9'
|
||||||
|
+ '\\u19d0-\\u19d9'
|
||||||
|
+ '\\u1a00-\\u1a1b'
|
||||||
|
+ '\\u1a20-\\u1a5e'
|
||||||
|
+ '\\u1a61-\\u1a74'
|
||||||
|
+ '\\u1a80-\\u1a89'
|
||||||
|
+ '\\u1a90-\\u1a99'
|
||||||
|
+ '\\u1aa7'
|
||||||
|
+ '\\u1b00-\\u1b33'
|
||||||
|
+ '\\u1b35-\\u1b43'
|
||||||
|
+ '\\u1b45-\\u1b4b'
|
||||||
|
+ '\\u1b50-\\u1b59'
|
||||||
|
+ '\\u1b80-\\u1ba9'
|
||||||
|
+ '\\u1bac-\\u1be5'
|
||||||
|
+ '\\u1be7-\\u1bf1'
|
||||||
|
+ '\\u1c00-\\u1c35'
|
||||||
|
+ '\\u1c40-\\u1c49'
|
||||||
|
+ '\\u1c4d-\\u1c7d'
|
||||||
|
+ '\\u1ce9-\\u1cec'
|
||||||
|
+ '\\u1cee-\\u1cf3'
|
||||||
|
+ '\\u1cf5-\\u1cf6'
|
||||||
|
+ '\\u1d00-\\u1dbf'
|
||||||
|
+ '\\u1e00-\\u1f15'
|
||||||
|
+ '\\u1f18-\\u1f1d'
|
||||||
|
+ '\\u1f20-\\u1f45'
|
||||||
|
+ '\\u1f48-\\u1f4d'
|
||||||
|
+ '\\u1f50-\\u1f57'
|
||||||
|
+ '\\u1f59'
|
||||||
|
+ '\\u1f5b'
|
||||||
|
+ '\\u1f5d'
|
||||||
|
+ '\\u1f5f-\\u1f7d'
|
||||||
|
+ '\\u1f80-\\u1fb4'
|
||||||
|
+ '\\u1fb6-\\u1fbc'
|
||||||
|
+ '\\u1fbe'
|
||||||
|
+ '\\u1fc2-\\u1fc4'
|
||||||
|
+ '\\u1fc6-\\u1fcc'
|
||||||
|
+ '\\u1fd0-\\u1fd3'
|
||||||
|
+ '\\u1fd6-\\u1fdb'
|
||||||
|
+ '\\u1fe0-\\u1fec'
|
||||||
|
+ '\\u1ff2-\\u1ff4'
|
||||||
|
+ '\\u1ff6-\\u1ffc'
|
||||||
|
+ '\\u2071'
|
||||||
|
+ '\\u207f'
|
||||||
|
+ '\\u2090-\\u209c'
|
||||||
|
+ '\\u2102'
|
||||||
|
+ '\\u2107'
|
||||||
|
+ '\\u210a-\\u2113'
|
||||||
|
+ '\\u2115'
|
||||||
|
+ '\\u2119-\\u211d'
|
||||||
|
+ '\\u2124'
|
||||||
|
+ '\\u2126'
|
||||||
|
+ '\\u2128'
|
||||||
|
+ '\\u212a-\\u212d'
|
||||||
|
+ '\\u212f-\\u2139'
|
||||||
|
+ '\\u213c-\\u213f'
|
||||||
|
+ '\\u2145-\\u2149'
|
||||||
|
+ '\\u214e'
|
||||||
|
+ '\\u2160-\\u2188'
|
||||||
|
+ '\\u24b6-\\u24e9'
|
||||||
|
+ '\\u2c00-\\u2c2e'
|
||||||
|
+ '\\u2c30-\\u2c5e'
|
||||||
|
+ '\\u2c60-\\u2ce4'
|
||||||
|
+ '\\u2ceb-\\u2cee'
|
||||||
|
+ '\\u2cf2-\\u2cf3'
|
||||||
|
+ '\\u2d00-\\u2d25'
|
||||||
|
+ '\\u2d27'
|
||||||
|
+ '\\u2d2d'
|
||||||
|
+ '\\u2d30-\\u2d67'
|
||||||
|
+ '\\u2d6f'
|
||||||
|
+ '\\u2d80-\\u2d96'
|
||||||
|
+ '\\u2da0-\\u2da6'
|
||||||
|
+ '\\u2da8-\\u2dae'
|
||||||
|
+ '\\u2db0-\\u2db6'
|
||||||
|
+ '\\u2db8-\\u2dbe'
|
||||||
|
+ '\\u2dc0-\\u2dc6'
|
||||||
|
+ '\\u2dc8-\\u2dce'
|
||||||
|
+ '\\u2dd0-\\u2dd6'
|
||||||
|
+ '\\u2dd8-\\u2dde'
|
||||||
|
+ '\\u2de0-\\u2dff'
|
||||||
|
+ '\\u2e2f'
|
||||||
|
+ '\\u3005-\\u3007'
|
||||||
|
+ '\\u3021-\\u3029'
|
||||||
|
+ '\\u3031-\\u3035'
|
||||||
|
+ '\\u3038-\\u303c'
|
||||||
|
+ '\\u3041-\\u3096'
|
||||||
|
+ '\\u309d-\\u309f'
|
||||||
|
+ '\\u30a1-\\u30fa'
|
||||||
|
+ '\\u30fc-\\u30ff'
|
||||||
|
+ '\\u3105-\\u312d'
|
||||||
|
+ '\\u3131-\\u318e'
|
||||||
|
+ '\\u31a0-\\u31ba'
|
||||||
|
+ '\\u31f0-\\u31ff'
|
||||||
|
+ '\\u3400-\\u4db5'
|
||||||
|
+ '\\u4e00-\\u9fcc'
|
||||||
|
+ '\\ua000-\\ua48c'
|
||||||
|
+ '\\ua4d0-\\ua4fd'
|
||||||
|
+ '\\ua500-\\ua60c'
|
||||||
|
+ '\\ua610-\\ua62b'
|
||||||
|
+ '\\ua640-\\ua66e'
|
||||||
|
+ '\\ua674-\\ua67b'
|
||||||
|
+ '\\ua67f-\\ua697'
|
||||||
|
+ '\\ua69f-\\ua6ef'
|
||||||
|
+ '\\ua717-\\ua71f'
|
||||||
|
+ '\\ua722-\\ua788'
|
||||||
|
+ '\\ua78b-\\ua78e'
|
||||||
|
+ '\\ua790-\\ua793'
|
||||||
|
+ '\\ua7a0-\\ua7aa'
|
||||||
|
+ '\\ua7f8-\\ua801'
|
||||||
|
+ '\\ua803-\\ua805'
|
||||||
|
+ '\\ua807-\\ua80a'
|
||||||
|
+ '\\ua80c-\\ua827'
|
||||||
|
+ '\\ua840-\\ua873'
|
||||||
|
+ '\\ua880-\\ua8c3'
|
||||||
|
+ '\\ua8d0-\\ua8d9'
|
||||||
|
+ '\\ua8f2-\\ua8f7'
|
||||||
|
+ '\\ua8fb'
|
||||||
|
+ '\\ua900-\\ua92a'
|
||||||
|
+ '\\ua930-\\ua952'
|
||||||
|
+ '\\ua960-\\ua97c'
|
||||||
|
+ '\\ua980-\\ua9b2'
|
||||||
|
+ '\\ua9b4-\\ua9bf'
|
||||||
|
+ '\\ua9cf-\\ua9d9'
|
||||||
|
+ '\\uaa00-\\uaa36'
|
||||||
|
+ '\\uaa40-\\uaa4d'
|
||||||
|
+ '\\uaa50-\\uaa59'
|
||||||
|
+ '\\uaa60-\\uaa76'
|
||||||
|
+ '\\uaa7a'
|
||||||
|
+ '\\uaa80-\\uaabe'
|
||||||
|
+ '\\uaac0'
|
||||||
|
+ '\\uaac2'
|
||||||
|
+ '\\uaadb-\\uaadd'
|
||||||
|
+ '\\uaae0-\\uaaef'
|
||||||
|
+ '\\uaaf2-\\uaaf5'
|
||||||
|
+ '\\uab01-\\uab06'
|
||||||
|
+ '\\uab09-\\uab0e'
|
||||||
|
+ '\\uab11-\\uab16'
|
||||||
|
+ '\\uab20-\\uab26'
|
||||||
|
+ '\\uab28-\\uab2e'
|
||||||
|
+ '\\uabc0-\\uabea'
|
||||||
|
+ '\\uabf0-\\uabf9'
|
||||||
|
+ '\\uac00-\\ud7a3'
|
||||||
|
+ '\\ud7b0-\\ud7c6'
|
||||||
|
+ '\\ud7cb-\\ud7fb'
|
||||||
|
+ '\\uf900-\\ufa6d'
|
||||||
|
+ '\\ufa70-\\ufad9'
|
||||||
|
+ '\\ufb00-\\ufb06'
|
||||||
|
+ '\\ufb13-\\ufb17'
|
||||||
|
+ '\\ufb1d-\\ufb28'
|
||||||
|
+ '\\ufb2a-\\ufb36'
|
||||||
|
+ '\\ufb38-\\ufb3c'
|
||||||
|
+ '\\ufb3e'
|
||||||
|
+ '\\ufb40-\\ufb41'
|
||||||
|
+ '\\ufb43-\\ufb44'
|
||||||
|
+ '\\ufb46-\\ufbb1'
|
||||||
|
+ '\\ufbd3-\\ufd3d'
|
||||||
|
+ '\\ufd50-\\ufd8f'
|
||||||
|
+ '\\ufd92-\\ufdc7'
|
||||||
|
+ '\\ufdf0-\\ufdfb'
|
||||||
|
+ '\\ufe70-\\ufe74'
|
||||||
|
+ '\\ufe76-\\ufefc'
|
||||||
|
+ '\\uff10-\\uff19'
|
||||||
|
+ '\\uff21-\\uff3a'
|
||||||
|
+ '\\uff41-\\uff5a'
|
||||||
|
+ '\\uff66-\\uffbe'
|
||||||
|
+ '\\uffc2-\\uffc7'
|
||||||
|
+ '\\uffca-\\uffcf'
|
||||||
|
+ '\\uffd2-\\uffd7'
|
||||||
|
+ '\\uffda-\\uffdc'
|
||||||
|
+ '\\u10000-\\u1000b'
|
||||||
|
+ '\\u1000d-\\u10026'
|
||||||
|
+ '\\u10028-\\u1003a'
|
||||||
|
+ '\\u1003c-\\u1003d'
|
||||||
|
+ '\\u1003f-\\u1004d'
|
||||||
|
+ '\\u10050-\\u1005d'
|
||||||
|
+ '\\u10080-\\u100fa'
|
||||||
|
+ '\\u10140-\\u10174'
|
||||||
|
+ '\\u10280-\\u1029c'
|
||||||
|
+ '\\u102a0-\\u102d0'
|
||||||
|
+ '\\u10300-\\u1031e'
|
||||||
|
+ '\\u10330-\\u1034a'
|
||||||
|
+ '\\u10380-\\u1039d'
|
||||||
|
+ '\\u103a0-\\u103c3'
|
||||||
|
+ '\\u103c8-\\u103cf'
|
||||||
|
+ '\\u103d1-\\u103d5'
|
||||||
|
+ '\\u10400-\\u1049d'
|
||||||
|
+ '\\u104a0-\\u104a9'
|
||||||
|
+ '\\u10800-\\u10805'
|
||||||
|
+ '\\u10808'
|
||||||
|
+ '\\u1080a-\\u10835'
|
||||||
|
+ '\\u10837-\\u10838'
|
||||||
|
+ '\\u1083c'
|
||||||
|
+ '\\u1083f-\\u10855'
|
||||||
|
+ '\\u10900-\\u10915'
|
||||||
|
+ '\\u10920-\\u10939'
|
||||||
|
+ '\\u10980-\\u109b7'
|
||||||
|
+ '\\u109be-\\u109bf'
|
||||||
|
+ '\\u10a00-\\u10a03'
|
||||||
|
+ '\\u10a05-\\u10a06'
|
||||||
|
+ '\\u10a0c-\\u10a13'
|
||||||
|
+ '\\u10a15-\\u10a17'
|
||||||
|
+ '\\u10a19-\\u10a33'
|
||||||
|
+ '\\u10a60-\\u10a7c'
|
||||||
|
+ '\\u10b00-\\u10b35'
|
||||||
|
+ '\\u10b40-\\u10b55'
|
||||||
|
+ '\\u10b60-\\u10b72'
|
||||||
|
+ '\\u10c00-\\u10c48'
|
||||||
|
+ '\\u11000-\\u11045'
|
||||||
|
+ '\\u11066-\\u1106f'
|
||||||
|
+ '\\u11082-\\u110b8'
|
||||||
|
+ '\\u110d0-\\u110e8'
|
||||||
|
+ '\\u110f0-\\u110f9'
|
||||||
|
+ '\\u11100-\\u11132'
|
||||||
|
+ '\\u11136-\\u1113f'
|
||||||
|
+ '\\u11180-\\u111bf'
|
||||||
|
+ '\\u111c1-\\u111c4'
|
||||||
|
+ '\\u111d0-\\u111d9'
|
||||||
|
+ '\\u11680-\\u116b5'
|
||||||
|
+ '\\u116c0-\\u116c9'
|
||||||
|
+ '\\u12000-\\u1236e'
|
||||||
|
+ '\\u12400-\\u12462'
|
||||||
|
+ '\\u13000-\\u1342e'
|
||||||
|
+ '\\u16800-\\u16a38'
|
||||||
|
+ '\\u16f00-\\u16f44'
|
||||||
|
+ '\\u16f50-\\u16f7e'
|
||||||
|
+ '\\u16f93-\\u16f9f'
|
||||||
|
+ '\\u1b000-\\u1b001'
|
||||||
|
+ '\\u1d400-\\u1d454'
|
||||||
|
+ '\\u1d456-\\u1d49c'
|
||||||
|
+ '\\u1d49e-\\u1d49f'
|
||||||
|
+ '\\u1d4a2'
|
||||||
|
+ '\\u1d4a5-\\u1d4a6'
|
||||||
|
+ '\\u1d4a9-\\u1d4ac'
|
||||||
|
+ '\\u1d4ae-\\u1d4b9'
|
||||||
|
+ '\\u1d4bb'
|
||||||
|
+ '\\u1d4bd-\\u1d4c3'
|
||||||
|
+ '\\u1d4c5-\\u1d505'
|
||||||
|
+ '\\u1d507-\\u1d50a'
|
||||||
|
+ '\\u1d50d-\\u1d514'
|
||||||
|
+ '\\u1d516-\\u1d51c'
|
||||||
|
+ '\\u1d51e-\\u1d539'
|
||||||
|
+ '\\u1d53b-\\u1d53e'
|
||||||
|
+ '\\u1d540-\\u1d544'
|
||||||
|
+ '\\u1d546'
|
||||||
|
+ '\\u1d54a-\\u1d550'
|
||||||
|
+ '\\u1d552-\\u1d6a5'
|
||||||
|
+ '\\u1d6a8-\\u1d6c0'
|
||||||
|
+ '\\u1d6c2-\\u1d6da'
|
||||||
|
+ '\\u1d6dc-\\u1d6fa'
|
||||||
|
+ '\\u1d6fc-\\u1d714'
|
||||||
|
+ '\\u1d716-\\u1d734'
|
||||||
|
+ '\\u1d736-\\u1d74e'
|
||||||
|
+ '\\u1d750-\\u1d76e'
|
||||||
|
+ '\\u1d770-\\u1d788'
|
||||||
|
+ '\\u1d78a-\\u1d7a8'
|
||||||
|
+ '\\u1d7aa-\\u1d7c2'
|
||||||
|
+ '\\u1d7c4-\\u1d7cb'
|
||||||
|
+ '\\u1d7ce-\\u1d7ff'
|
||||||
|
+ '\\u1ee00-\\u1ee03'
|
||||||
|
+ '\\u1ee05-\\u1ee1f'
|
||||||
|
+ '\\u1ee21-\\u1ee22'
|
||||||
|
+ '\\u1ee24'
|
||||||
|
+ '\\u1ee27'
|
||||||
|
+ '\\u1ee29-\\u1ee32'
|
||||||
|
+ '\\u1ee34-\\u1ee37'
|
||||||
|
+ '\\u1ee39'
|
||||||
|
+ '\\u1ee3b'
|
||||||
|
+ '\\u1ee42'
|
||||||
|
+ '\\u1ee47'
|
||||||
|
+ '\\u1ee49'
|
||||||
|
+ '\\u1ee4b'
|
||||||
|
+ '\\u1ee4d-\\u1ee4f'
|
||||||
|
+ '\\u1ee51-\\u1ee52'
|
||||||
|
+ '\\u1ee54'
|
||||||
|
+ '\\u1ee57'
|
||||||
|
+ '\\u1ee59'
|
||||||
|
+ '\\u1ee5b'
|
||||||
|
+ '\\u1ee5d'
|
||||||
|
+ '\\u1ee5f'
|
||||||
|
+ '\\u1ee61-\\u1ee62'
|
||||||
|
+ '\\u1ee64'
|
||||||
|
+ '\\u1ee67-\\u1ee6a'
|
||||||
|
+ '\\u1ee6c-\\u1ee72'
|
||||||
|
+ '\\u1ee74-\\u1ee77'
|
||||||
|
+ '\\u1ee79-\\u1ee7c'
|
||||||
|
+ '\\u1ee7e'
|
||||||
|
+ '\\u1ee80-\\u1ee89'
|
||||||
|
+ '\\u1ee8b-\\u1ee9b'
|
||||||
|
+ '\\u1eea1-\\u1eea3'
|
||||||
|
+ '\\u1eea5-\\u1eea9'
|
||||||
|
+ '\\u1eeab-\\u1eebb'
|
||||||
|
+ '\\u20000-\\u2a6d6'
|
||||||
|
+ '\\u2a700-\\u2b734'
|
||||||
|
+ '\\u2b740-\\u2b81d'
|
||||||
|
+ '\\u2f800-\\u2fa1d'
|
||||||
|
};
|
||||||
|
// @license-end
|
||||||
|
|
@ -5,26 +5,80 @@ describe("app.helpers.textFormatter", function(){
|
||||||
this.formatter = app.helpers.textFormatter;
|
this.formatter = app.helpers.textFormatter;
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("main", function(){
|
// Some basic specs. For more detailed specs see
|
||||||
it("calls mentionify, hashtagify, and markdownify", function(){
|
// https://github.com/svbergerem/markdown-it-hashtag/tree/master/test
|
||||||
spyOn(app.helpers.textFormatter, "mentionify")
|
context("hashtags", function() {
|
||||||
spyOn(app.helpers.textFormatter, "hashtagify")
|
beforeEach(function() {
|
||||||
spyOn(app.helpers.textFormatter, "markdownify")
|
this.tags = [
|
||||||
|
"tag",
|
||||||
|
"diaspora",
|
||||||
|
"PARTIES",
|
||||||
|
"<3"
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
app.helpers.textFormatter(this.statusMessage.get("text"), this.statusMessage)
|
it("renders tags as links", function() {
|
||||||
expect(app.helpers.textFormatter.mentionify).toHaveBeenCalled()
|
var formattedText = this.formatter('#'+this.tags.join(" #"));
|
||||||
expect(app.helpers.textFormatter.hashtagify).toHaveBeenCalled()
|
_.each(this.tags, function(tag) {
|
||||||
expect(app.helpers.textFormatter.markdownify).toHaveBeenCalled()
|
var link ='<a class="tag" href="/tags/'+tag.toLowerCase()+'">#'+tag+'</a>';
|
||||||
|
expect(formattedText).toContain(link);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Some basic specs. For more detailed specs see
|
||||||
|
// https://github.com/diaspora/markdown-it-diaspora-mention/tree/master/test
|
||||||
|
context("mentions", function() {
|
||||||
|
beforeEach(function(){
|
||||||
|
this.alice = factory.author({
|
||||||
|
name : "Alice Smith",
|
||||||
|
diaspora_id : "alice@example.com",
|
||||||
|
id : "555"
|
||||||
|
})
|
||||||
|
|
||||||
|
this.bob = factory.author({
|
||||||
|
name : "Bob Grimm",
|
||||||
|
diaspora_id : "bob@example.com",
|
||||||
|
id : "666"
|
||||||
|
})
|
||||||
|
|
||||||
|
this.statusMessage.set({text: "hey there @{Alice Smith; alice@example.com} and @{Bob Grimm; bob@example.com}"})
|
||||||
|
this.statusMessage.set({mentioned_people : [this.alice, this.bob]})
|
||||||
})
|
})
|
||||||
|
|
||||||
// A couple of complex (intergration) test cases here would be rad.
|
it("matches mentions", function(){
|
||||||
})
|
var formattedText = this.formatter(this.statusMessage.get("text"), this.statusMessage.get("mentioned_people"))
|
||||||
|
var wrapper = $("<div>").html(formattedText);
|
||||||
|
|
||||||
describe(".markdownify", function(){
|
_.each([this.alice, this.bob], function(person) {
|
||||||
// NOTE: for some strange reason, links separated by just a whitespace character
|
expect(wrapper.find("a[href='/people/" + person.guid + "']").text()).toContain(person.name)
|
||||||
// will not be autolinked; thus we join our URLS here with (" and ").
|
})
|
||||||
// This test will fail if our join is just (" ") -- an edge case that should be addressed.
|
});
|
||||||
|
|
||||||
|
it("returns mentions for on posts that haven't been saved yet (framer posts)", function(){
|
||||||
|
var freshBob = factory.author({
|
||||||
|
name : "Bob Grimm",
|
||||||
|
handle : "bob@example.com",
|
||||||
|
url : 'googlebot.com',
|
||||||
|
id : "666"
|
||||||
|
})
|
||||||
|
|
||||||
|
this.statusMessage.set({'mentioned_people' : [freshBob] })
|
||||||
|
|
||||||
|
var formattedText = this.formatter(this.statusMessage.get("text"), this.statusMessage.get("mentioned_people"))
|
||||||
|
var wrapper = $("<div>").html(formattedText);
|
||||||
|
expect(wrapper.find("a[href='googlebot.com']").text()).toContain(freshBob.name)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the name of the mention if the mention does not exist in the array', function(){
|
||||||
|
var text = "hey there @{Chris Smith; chris@example.com}"
|
||||||
|
var formattedText = this.formatter(text, [])
|
||||||
|
expect(formattedText.match(/\<a/)).toBeNull();
|
||||||
|
expect(formattedText).toContain('Chris Smith');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context("markdown", function(){
|
||||||
it("autolinks", function(){
|
it("autolinks", function(){
|
||||||
var links = [
|
var links = [
|
||||||
"http://google.com",
|
"http://google.com",
|
||||||
|
|
@ -37,11 +91,7 @@ describe("app.helpers.textFormatter", function(){
|
||||||
"www.google.com"
|
"www.google.com"
|
||||||
];
|
];
|
||||||
|
|
||||||
// The join that would make this particular test fail:
|
var formattedText = this.formatter(links.join(" "));
|
||||||
//
|
|
||||||
// var formattedText = this.formatter.markdownify(links.join(" "))
|
|
||||||
|
|
||||||
var formattedText = this.formatter.markdownify(links.join(" and "));
|
|
||||||
var wrapper = $("<div>").html(formattedText);
|
var wrapper = $("<div>").html(formattedText);
|
||||||
|
|
||||||
_.each(links, function(link) {
|
_.each(links, function(link) {
|
||||||
|
|
@ -67,13 +117,13 @@ describe("app.helpers.textFormatter", function(){
|
||||||
|
|
||||||
it("correctly converts the input strings to their corresponding output symbol", function() {
|
it("correctly converts the input strings to their corresponding output symbol", function() {
|
||||||
_.each(this.input_strings, function(str, idx) {
|
_.each(this.input_strings, function(str, idx) {
|
||||||
var text = this.formatter.markdownify(str);
|
var text = this.formatter(str);
|
||||||
expect(text).toContain(this.output_symbols[idx]);
|
expect(text).toContain(this.output_symbols[idx]);
|
||||||
}, this);
|
}, this);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("converts all symbols at once", function() {
|
it("converts all symbols at once", function() {
|
||||||
var text = this.formatter.markdownify(this.input_strings.join(" "));
|
var text = this.formatter(this.input_strings.join(" "));
|
||||||
_.each(this.output_symbols, function(sym) {
|
_.each(this.output_symbols, function(sym) {
|
||||||
expect(text).toContain(sym);
|
expect(text).toContain(sym);
|
||||||
});
|
});
|
||||||
|
|
@ -97,21 +147,21 @@ describe("app.helpers.textFormatter", function(){
|
||||||
"http://xn--4gbrim.xn----ymcbaaajlc6dj7bxne2c.xn--wgbh1c/",
|
"http://xn--4gbrim.xn----ymcbaaajlc6dj7bxne2c.xn--wgbh1c/",
|
||||||
"http:///scholar.google.com/citations?view_op=top_venues",
|
"http:///scholar.google.com/citations?view_op=top_venues",
|
||||||
"http://lyricstranslate.com/en/someone-you-%E0%B4%A8%E0%B4%BF%E0%B4%A8%E0%B5%8D%E0%B4%A8%E0%B5%86-%E0%B4%AA%E0%B5%8B%E0%B4%B2%E0%B5%8A%E0%B4%B0%E0%B4%BE%E0%B4%B3%E0%B5%8D%E2%80%8D.html",
|
"http://lyricstranslate.com/en/someone-you-%E0%B4%A8%E0%B4%BF%E0%B4%A8%E0%B5%8D%E0%B4%A8%E0%B5%86-%E0%B4%AA%E0%B5%8B%E0%B4%B2%E0%B5%8A%E0%B4%B0%E0%B4%BE%E0%B4%B3%E0%B5%8D%E2%80%8D.html",
|
||||||
"http://de.wikipedia.org/wiki/Liste_der_Abk%C3%BCrzungen_%28Netzjargon%29",
|
"http://de.wikipedia.org/wiki/Liste_der_Abk%C3%BCrzungen_(Netzjargon)",
|
||||||
"http://wiki.com/?query=Kr%E4fte",
|
"http://wiki.com/?query=Kr%E4fte",
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
it("correctly encodes to punycode", function() {
|
it("correctly encodes to punycode", function() {
|
||||||
_.each(this.evilUrls, function(url, num) {
|
_.each(this.evilUrls, function(url, num) {
|
||||||
var text = this.formatter.markdownify( "<" + url + ">" );
|
var text = this.formatter( "<" + url + ">" );
|
||||||
expect(text).toContain(this.asciiUrls[num]);
|
expect(text).toContain(this.asciiUrls[num]);
|
||||||
}, this);
|
}, this);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't break link texts", function() {
|
it("doesn't break link texts", function() {
|
||||||
var linkText = "check out this awesome link!";
|
var linkText = "check out this awesome link!";
|
||||||
var text = this.formatter.markdownify( "["+linkText+"]("+this.evilUrls[0]+")" );
|
var text = this.formatter( "["+linkText+"]("+this.evilUrls[0]+")" );
|
||||||
|
|
||||||
expect(text).toContain(this.asciiUrls[0]);
|
expect(text).toContain(this.asciiUrls[0]);
|
||||||
expect(text).toContain(linkText);
|
expect(text).toContain(linkText);
|
||||||
|
|
@ -119,30 +169,29 @@ describe("app.helpers.textFormatter", function(){
|
||||||
|
|
||||||
it("doesn't break reference style links", function() {
|
it("doesn't break reference style links", function() {
|
||||||
var postContent = "blabla blab [my special link][1] bla blabla\n\n[1]: "+this.evilUrls[0]+" and an optional title)";
|
var postContent = "blabla blab [my special link][1] bla blabla\n\n[1]: "+this.evilUrls[0]+" and an optional title)";
|
||||||
var text = this.formatter.markdownify(postContent);
|
var text = this.formatter(postContent);
|
||||||
|
|
||||||
expect(text).not.toContain(this.evilUrls[0]);
|
expect(text).not.toContain('"'+this.evilUrls[0]+'"');
|
||||||
expect(text).toContain(this.asciiUrls[0]);
|
expect(text).toContain(this.asciiUrls[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can be used as img src", function() {
|
it("can be used as img src", function() {
|
||||||
var postContent = "";
|
var postContent = "";
|
||||||
var niceImg = 'src="'+ this.asciiUrls[1] +'"'; // the "" are from src=""
|
var niceImg = 'src="'+ this.asciiUrls[1] +'"'; // the "" are from src=""
|
||||||
var text = this.formatter.markdownify(postContent);
|
var text = this.formatter(postContent);
|
||||||
|
|
||||||
expect(text).toContain(niceImg);
|
expect(text).toContain(niceImg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't break linked images", function() {
|
it("doesn't break linked images", function() {
|
||||||
var postContent = "I am linking an image here []("+this.evilUrls[3]+")";
|
var postContent = "I am linking an image here []("+this.evilUrls[3]+")";
|
||||||
var text = this.formatter.markdownify(postContent);
|
var text = this.formatter(postContent);
|
||||||
var linked_image = 'src="'+this.asciiUrls[1]+'"';
|
var linked_image = 'src="'+this.asciiUrls[1]+'"';
|
||||||
var image_link = 'href="'+this.asciiUrls[3]+'"';
|
var image_link = 'href="'+this.asciiUrls[3]+'"';
|
||||||
|
|
||||||
expect(text).toContain(linked_image);
|
expect(text).toContain(linked_image);
|
||||||
expect(text).toContain(image_link);
|
expect(text).toContain(image_link);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context("misc breakage and/or other issues with weird urls", function(){
|
context("misc breakage and/or other issues with weird urls", function(){
|
||||||
|
|
@ -151,10 +200,10 @@ describe("app.helpers.textFormatter", function(){
|
||||||
var text_part = 'Revert "rails admin is conflicting with client side validations: see https://github.com/sferik/rails_admin/issues/985"';
|
var text_part = 'Revert "rails admin is conflicting with client side validations: see https://github.com/sferik/rails_admin/issues/985"';
|
||||||
var link_part = 'https://github.com/diaspora/diaspora/commit/61f40fc6bfe6bb859c995023b5a17d22c9b5e6e5';
|
var link_part = 'https://github.com/diaspora/diaspora/commit/61f40fc6bfe6bb859c995023b5a17d22c9b5e6e5';
|
||||||
var content = '['+text_part+']('+link_part+')';
|
var content = '['+text_part+']('+link_part+')';
|
||||||
var parsed = this.formatter.markdownify(content);
|
var parsed = this.formatter(content);
|
||||||
|
|
||||||
var link = 'href="' + link_part + '"';
|
var link = 'href="' + link_part + '"';
|
||||||
var text = '>'+ text_part +'<';
|
var text = '>Revert “rails admin is conflicting with client side validations: see https://github.com/sferik/rails_admin/issues/985”<';
|
||||||
|
|
||||||
expect(parsed).toContain(link);
|
expect(parsed).toContain(link);
|
||||||
expect(parsed).toContain(text);
|
expect(parsed).toContain(text);
|
||||||
|
|
@ -167,149 +216,16 @@ describe("app.helpers.textFormatter", function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't get double-encoded", function(){
|
it("doesn't get double-encoded", function(){
|
||||||
var parsed = this.formatter.markdownify(this.input);
|
var parsed = this.formatter(this.input);
|
||||||
expect(parsed).toContain(this.correctHref);
|
expect(parsed).toContain(this.correctHref);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("gets correctly decoded, even when multiply encoded", function() {
|
it("gets correctly decoded, even when multiply encoded", function() {
|
||||||
var uglyUrl = encodeURI(encodeURI(encodeURI(this.input)));
|
var uglyUrl = encodeURI(encodeURI(encodeURI(this.input)));
|
||||||
var parsed = this.formatter.markdownify(uglyUrl);
|
var parsed = this.formatter(uglyUrl);
|
||||||
expect(parsed).toContain(this.correctHref);
|
expect(parsed).toContain(this.correctHref);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("tests a bunch of benchmark urls", function(){
|
|
||||||
var self = this;
|
|
||||||
$.ajax({
|
|
||||||
async: false,
|
|
||||||
cache: false,
|
|
||||||
url: '/spec/fixtures/good_urls.txt',
|
|
||||||
success: function(data) { self.url_list = data.split("\n"); }
|
|
||||||
});
|
|
||||||
|
|
||||||
_.each(this.url_list, function(url) {
|
|
||||||
// 'comments'
|
|
||||||
if( url.match(/^#/) ) return;
|
|
||||||
|
|
||||||
// regex.test is stupid, use match and boolean-ify it
|
|
||||||
var result = !!url.match(Diaspora.url_regex);
|
|
||||||
expect(result).toBeTruthy();
|
|
||||||
if( !result && console && console.log ) {
|
|
||||||
console.log(url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: try to match the 'bad_urls.txt' and have as few matches as possible
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
describe(".hashtagify", function(){
|
|
||||||
context("changes hashtags to links", function(){
|
|
||||||
it("creates links to hashtags", function(){
|
|
||||||
var formattedText = this.formatter.hashtagify("I love #parties and #rockstars and #unicorns")
|
|
||||||
var wrapper = $("<div>").html(formattedText);
|
|
||||||
|
|
||||||
_.each(["parties", "rockstars", "unicorns"], function(tagName){
|
|
||||||
expect(wrapper.find("a[href='/tags/" + tagName + "']").text()).toContain(tagName)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("requires hashtags to be preceeded with a space", function(){
|
|
||||||
var formattedText = this.formatter.hashtagify("I love the#parties")
|
|
||||||
expect(formattedText).not.toContain('/tags/parties')
|
|
||||||
})
|
|
||||||
|
|
||||||
// NOTE THIS DIVERGES FROM GRUBER'S ORIGINAL DIALECT OF MARKDOWN.
|
|
||||||
// We had to edit Markdown.Converter.js line 747
|
|
||||||
//
|
|
||||||
// text = text.replace(/^(\#{1,6})[ \t]+(.+?)[ \t]*\#*\n+/gm,
|
|
||||||
// [ \t]* changed to [ \t]+
|
|
||||||
//
|
|
||||||
it("doesn't create a header tag if the first word is a hashtag", function(){
|
|
||||||
var formattedText = this.formatter.hashtagify("#parties, I love")
|
|
||||||
var wrapper = $("<div>").html(formattedText);
|
|
||||||
|
|
||||||
expect(wrapper.find("h1").length).toBe(0)
|
|
||||||
expect(wrapper.find("a[href='/tags/parties']").text()).toContain("#parties")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("and the resultant link has the tags name downcased", function(){
|
|
||||||
var formattedText = this.formatter.hashtagify("#PARTIES, I love")
|
|
||||||
|
|
||||||
expect(formattedText).toContain("/tags/parties")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("doesn't create tag if the text is a link", function(){
|
|
||||||
var tags = ['diaspora', 'twitter', 'hrabrahabr'];
|
|
||||||
|
|
||||||
var text = $('<a/>', { href: 'http://me.co' }).html('#me')[0].outerHTML;
|
|
||||||
_.each(tags, function(tagName){
|
|
||||||
text += ' #'+tagName+',';
|
|
||||||
});
|
|
||||||
text += 'I love';
|
|
||||||
|
|
||||||
var formattedText = this.formatter.hashtagify(text);
|
|
||||||
var wrapper = $('<div>').html(formattedText);
|
|
||||||
|
|
||||||
expect(wrapper.find("a[href='http://me.co']").text()).toContain('#me');
|
|
||||||
_.each(tags, function(tagName){
|
|
||||||
expect(wrapper.find("a[href='/tags/"+tagName+"']").text()).toContain('#'+tagName);
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(".mentionify", function(){
|
|
||||||
context("changes mention markup to links", function(){
|
|
||||||
beforeEach(function(){
|
|
||||||
this.alice = factory.author({
|
|
||||||
name : "Alice Smith",
|
|
||||||
diaspora_id : "alice@example.com",
|
|
||||||
id : "555"
|
|
||||||
})
|
|
||||||
|
|
||||||
this.bob = factory.author({
|
|
||||||
name : "Bob Grimm",
|
|
||||||
diaspora_id : "bob@example.com",
|
|
||||||
id : "666"
|
|
||||||
})
|
|
||||||
|
|
||||||
this.statusMessage.set({text: "hey there @{Alice Smith; alice@example.com} and @{Bob Grimm; bob@example.com}"})
|
|
||||||
this.statusMessage.set({mentioned_people : [this.alice, this.bob]})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("matches mentions", function(){
|
|
||||||
var formattedText = this.formatter.mentionify(this.statusMessage.get("text"), this.statusMessage.get("mentioned_people"))
|
|
||||||
var wrapper = $("<div>").html(formattedText);
|
|
||||||
|
|
||||||
_.each([this.alice, this.bob], function(person) {
|
|
||||||
expect(wrapper.find("a[href='/people/" + person.guid + "']").text()).toContain(person.name)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns mentions for on posts that haven't been saved yet (framer posts)", function(){
|
|
||||||
var freshBob = factory.author({
|
|
||||||
name : "Bob Grimm",
|
|
||||||
handle : "bob@example.com",
|
|
||||||
url : 'googlebot.com',
|
|
||||||
id : "666"
|
|
||||||
})
|
|
||||||
|
|
||||||
this.statusMessage.set({'mentioned_people' : [freshBob] })
|
|
||||||
|
|
||||||
var formattedText = this.formatter.mentionify(this.statusMessage.get("text"), this.statusMessage.get("mentioned_people"))
|
|
||||||
var wrapper = $("<div>").html(formattedText);
|
|
||||||
expect(wrapper.find("a[href='googlebot.com']").text()).toContain(freshBob.name)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns the name of the mention if the mention does not exist in the array', function(){
|
|
||||||
var text = "hey there @{Chris Smith; chris@example.com}"
|
|
||||||
var formattedText = this.formatter.mentionify(text, [])
|
|
||||||
expect(formattedText.match(/\<a/)).toBeNull();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
1
vendor/assets/javascripts/markdown.js
vendored
1
vendor/assets/javascripts/markdown.js
vendored
|
|
@ -1 +0,0 @@
|
||||||
//= require_tree ./markdown
|
|
||||||
1332
vendor/assets/javascripts/markdown/Markdown.Converter.js
vendored
1332
vendor/assets/javascripts/markdown/Markdown.Converter.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,108 +0,0 @@
|
||||||
(function () {
|
|
||||||
var output, Converter;
|
|
||||||
if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module
|
|
||||||
output = exports;
|
|
||||||
Converter = require("./Markdown.Converter").Converter;
|
|
||||||
} else {
|
|
||||||
output = window.Markdown;
|
|
||||||
Converter = output.Converter;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.getSanitizingConverter = function () {
|
|
||||||
var converter = new Converter();
|
|
||||||
converter.hooks.chain("postConversion", sanitizeHtml);
|
|
||||||
converter.hooks.chain("postConversion", balanceTags);
|
|
||||||
return converter;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitizeHtml(html) {
|
|
||||||
return html.replace(/<[^>]*>?/gi, sanitizeTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// (tags that can be opened/closed) | (tags that stand alone)
|
|
||||||
var basic_tag_whitelist = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol|p|pre|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i;
|
|
||||||
// <a href="url..." optional title>|</a>
|
|
||||||
var a_white = /^(<a\shref="((https?|ftp):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+"(\stitle="[^"<>]+")?\s?>|<\/a>)$/i;
|
|
||||||
|
|
||||||
// <img src="url..." optional width optional height optional alt optional title
|
|
||||||
var img_white = /^(<img\ssrc="(https?:\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+"(\swidth="\d{1,3}")?(\sheight="\d{1,3}")?(\salt="[^"<>]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i;
|
|
||||||
|
|
||||||
function sanitizeTag(tag) {
|
|
||||||
if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white))
|
|
||||||
return tag;
|
|
||||||
else
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// attempt to balance HTML tags in the html string
|
|
||||||
/// by removing any unmatched opening or closing tags
|
|
||||||
/// IMPORTANT: we *assume* HTML has *already* been
|
|
||||||
/// sanitized and is safe/sane before balancing!
|
|
||||||
///
|
|
||||||
/// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593
|
|
||||||
/// </summary>
|
|
||||||
function balanceTags(html) {
|
|
||||||
|
|
||||||
if (html == "")
|
|
||||||
return "";
|
|
||||||
|
|
||||||
var re = /<\/?\w+[^>]*(\s|$|>)/g;
|
|
||||||
// convert everything to lower case; this makes
|
|
||||||
// our case insensitive comparisons easier
|
|
||||||
var tags = html.toLowerCase().match(re);
|
|
||||||
|
|
||||||
// no HTML tags present? nothing to do; exit now
|
|
||||||
var tagcount = (tags || []).length;
|
|
||||||
if (tagcount == 0)
|
|
||||||
return html;
|
|
||||||
|
|
||||||
var tagname, tag;
|
|
||||||
var ignoredtags = "<p><img><br><li><hr>";
|
|
||||||
var match;
|
|
||||||
var tagpaired = [];
|
|
||||||
var tagremove = [];
|
|
||||||
var needsRemoval = false;
|
|
||||||
|
|
||||||
// loop through matched tags in forward order
|
|
||||||
for (var ctag = 0; ctag < tagcount; ctag++) {
|
|
||||||
tagname = tags[ctag].replace(/<\/?(\w+).*/, "$1");
|
|
||||||
// skip any already paired tags
|
|
||||||
// and skip tags in our ignore list; assume they're self-closed
|
|
||||||
if (tagpaired[ctag] || ignoredtags.search("<" + tagname + ">") > -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
tag = tags[ctag];
|
|
||||||
match = -1;
|
|
||||||
|
|
||||||
if (!/^<\//.test(tag)) {
|
|
||||||
// this is an opening tag
|
|
||||||
// search forwards (next tags), look for closing tags
|
|
||||||
for (var ntag = ctag + 1; ntag < tagcount; ntag++) {
|
|
||||||
if (!tagpaired[ntag] && tags[ntag] == "</" + tagname + ">") {
|
|
||||||
match = ntag;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match == -1)
|
|
||||||
needsRemoval = tagremove[ctag] = true; // mark for removal
|
|
||||||
else
|
|
||||||
tagpaired[match] = true; // mark paired
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!needsRemoval)
|
|
||||||
return html;
|
|
||||||
|
|
||||||
// delete all orphaned tags from the string
|
|
||||||
|
|
||||||
var ctag = 0;
|
|
||||||
html = html.replace(re, function (match) {
|
|
||||||
var res = tagremove[ctag] ? "" : match;
|
|
||||||
ctag++;
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
Loading…
Reference in a new issue