You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

253 lines
8.9 KiB

rangy.createModule("Phraser", ["WrappedSelection", "WrappedRange"], function(api, module) {
function getParts(val, id) {
var words = [];
var chs = [];
var i = 0;
id = (!id ? val : id);
if (!api.words[id]) {
api.analyse(val, {
wordHandler: function(word) {
i++;
words.push(word);
return word;
},
charHandler: function(ch) {
if (!chs[i]) chs[i] = '';
chs[i] += ch;
return ch;
}
});
api.words[id] = words;
api.chs[id] = chs;
} else {
words = api.words[id];
chs = api.chs[id];
}
return {
words: words,
chs: chs
};
}
function expandIndexesToCh(parentParts, indexes, ch) {
for(var start = indexes.start; start > 0; start--) {
if (parentParts.chs[start])
if (parentParts.chs[start].match(ch)) {
indexes.start = start;
break;
}
}
for(var end = indexes.end; end < parentParts.words.length; end++) {
if (parentParts.chs[end])
if (parentParts.chs[end].match(ch)) {
indexes.end = end - 1;
end = parentParts.words.length;
break;
}
}
return indexes;
}
function getWrappedHtml(html, indexes) {
var i = 0;
return api.analyse(html, {
wordHandler: function(word) {
if (i >= indexes.start && i <= indexes.end) {
word = '<span class="rangyPhrase new" style="border: none;">' + word + '</span>';
}
if (i == indexes.start) {
word = '<span class="rangyPhraseStart new" style="border: none;"/>' + word;
}
//it is possible that word's start and end index are the same'
if (i == indexes.end) {
word = word + '<span class="rangyPhraseEnd new" style="border: none;"/>';
}
i++;
return word;
},
charHandler: function(ch) {
if (i > indexes.start && i <= indexes.end) {
ch = '<span class="rangyPhrase new" style="border: none;">' + ch + '</span>';
}
return ch;
}
});
}
function o(rootNode, doc) {
if (rootNode) {
doc = doc || dom.getDocument(rootNode);
} else {
doc = doc || document;
rootNode = doc.documentElement;
}
return (rootNode = $(rootNode));
}
var dom = api.dom, Phraser;
api = $.extend(api, {
words: {},
chs: {},
tags: {},
analyse: function(phrase, options) {
options = $.extend({
wordHandler: function(word) { return word; },
tagHandler: function(tag) { return tag; },
charHandler: function(ch) { return ch; }
}, options);
Phraser = $.extend(Phraser, options);
return Phraser.parse(phrase);
},
phraseIndexes: function(parentWords, phraseWords, allMatches) {
var phraseLength = phraseWords.length - 1,
phraseConcat = phraseWords.join('|'),
parentConcat = parentWords.join('|'),
boundaries = parentConcat.split(phraseConcat),
indexes = [];
var boundaryLength;
for (var i = 0; i < boundaries.length; i++) {
boundaryLength = boundaries[i].split('|').length - 1;
indexes.push({
start: Math.min(parentWords.length - phraseWords.length, boundaryLength),
end: Math.min(parentWords.length, boundaryLength + phraseLength)
});
i++;
}
if (allMatches)
return indexes;
return indexes[0];
},
setPhraseSelection: function(rootNode, phrase, doc) {
phrase = this.setPhrase(rootNode, phrase, doc);
var range = api.createRange(doc);
if (!phrase.start[0] || !phrase.end[0]) {
return {
selection: $('<span class="rangyPhrase" />'),
start: $('<span class="rangyPhraseStart" />'),
end: $('<span class="rangyPhraseEnd" />'),
phrase: phrase,
indexes: [],
rootNode: rootNode
};
}
range.setStartBefore(phrase.start[0]);
range.setEndAfter(phrase.end[0]);
var sel = api.getSelection(window);
var ranges = [range];
sel.setRanges(ranges);
phrase.range = range;
return phrase;
},
setPhraseBetweenNodes: function(node1, node2, doc) {
var range = api.createRange(doc);
range.setStartBefore(node1[0]);
range.setEndAfter(node2[0]);
var sel = api.getSelection(window);
var ranges = [range];
sel.setRanges(ranges);
},
expandPhrase: function(phrase, ch, rootNode, doc) {
rootNode = o(rootNode, doc);
var parent = rootNode.html();
var parentParts = getParts(parent, rootNode.attr('id'));
var phraseParts = getParts(phrase);
var indexes = this.phraseIndexes(parentParts.words, phraseParts.words);
indexes = expandIndexesToCh(parentParts, indexes, ch);
var newPhrase = '';
if (indexes.start > -1 && indexes.end > -1) {
for(var i = indexes.start; i <= indexes.end + 1; i++) {
if (parentParts.chs[i] && i != indexes.start) newPhrase += parentParts.chs[i];
if (parentParts.words[i] && i != indexes.end + 1) newPhrase += parentParts.words[i];
}
return newPhrase
}
return phrase;
},
setPhrase: function(rootNode, phrase, doc) {
rootNode = o(rootNode, doc);
var parent = rootNode.html();
var parentWords = this.sanitizeToWords(parent);
var phraseWords = this.sanitizeToWords(phrase);
var indexes = this.phraseIndexes(parentWords, phraseWords);
if (indexes.start > -1 && indexes.end > -1) {
rootNode.html(getWrappedHtml(parent, indexes));
}
return {
selection: rootNode.find('.rangyPhrase.new').removeClass('new'),
start: rootNode.find('.rangyPhraseStart.new').removeClass('new'),
end: rootNode.find('.rangyPhraseEnd.new').removeClass('new'),
phrase: phrase,
indexes: indexes,
parentWords: parentWords,
phraseWords: phraseWords,
rootNode: rootNode
};
},
isUnique: function(rootNode, phrase, doc) {
rootNode = o(rootNode, doc);
var parentWords = this.sanitizeToWords(rootNode.html());
var phraseWords = this.sanitizeToWords(phrase);
var indexes = this.phraseIndexes(parentWords, phraseWords, true);
return indexes.length <= 1;
},
hasPhrase: function (parent, phrase) {
parent = this.sanitizeToWords(parent);
phrase = this.sanitizeToWords(phrase);
parent = parent.join('|');
phrase = phrase.join('|');
return (parent.indexOf(phrase) > -1);
},
sanitizeToWords: function (html) {
var sanitized = html.replace(/<(.|\n)*?>/g, ' ');
sanitized = sanitized.replace(/\W/g, ' ');
sanitized = sanitized.split(' ');
var sanitizedFiltered = [], i;
for(i in sanitized) {
if (sanitized[i])
sanitizedFiltered.push(sanitized[i]);
}
return sanitizedFiltered;
},
superSanitize: function(html) {
return this.sanitizeToWords(html).join('');
}
});
});