mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-22 08:27:31 +09:00
273 lines
8.4 KiB
JavaScript
273 lines
8.4 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
this.EXPORTED_SYMBOLS = ['GlodaContent', 'whittlerRegistry',
|
|
'mimeMsgToContentAndMeta', 'mimeMsgToContentSnippetAndMeta'];
|
|
|
|
var Cc = Components.classes;
|
|
var Ci = Components.interfaces;
|
|
var Cr = Components.results;
|
|
var Cu = Components.utils;
|
|
|
|
Cu.import("resource:///modules/gloda/log4moz.js");
|
|
|
|
var LOG = Log4Moz.repository.getLogger("gloda.connotent");
|
|
|
|
|
|
|
|
/**
|
|
* Given a MimeMsg and the corresponding folder, return the GlodaContent object.
|
|
*
|
|
* @param aMimeMsg: the MimeMessage instance
|
|
* @param folder: the nsIMsgDBFolder
|
|
* @return an array containing the GlodaContent instance, and the meta dictionary
|
|
* that the Gloda content providers may have filled with useful data.
|
|
*/
|
|
|
|
function mimeMsgToContentAndMeta(aMimeMsg, folder) {
|
|
let content = new GlodaContent();
|
|
let meta = {subject: aMimeMsg.get("subject")};
|
|
let bodyLines = aMimeMsg.coerceBodyToPlaintext(folder).split(/\r?\n/);
|
|
|
|
for (let whittler of whittlerRegistry.getWhittlers())
|
|
whittler.contentWhittle(meta, bodyLines, content);
|
|
|
|
return [content, meta];
|
|
}
|
|
|
|
|
|
/**
|
|
* Given a MimeMsg, return the whittled content string, suitable for summarizing
|
|
* a message.
|
|
*
|
|
* @param aMimeMsg: the MimeMessage instance
|
|
* @param folder: the nsIMsgDBFolder
|
|
* @param length: optional number of characters to trim the whittled content.
|
|
* If the actual length of the message is greater than |length|, then the return
|
|
* value is the first (length-1) characters with an ellipsis appended.
|
|
* @return an array containing the text of the snippet, and the meta dictionary
|
|
* that the Gloda content providers may have filled with useful data.
|
|
*/
|
|
|
|
function mimeMsgToContentSnippetAndMeta(aMimeMsg, folder, length) {
|
|
let [content, meta] = mimeMsgToContentAndMeta(aMimeMsg, folder);
|
|
|
|
let text = content.getContentSnippet(length + 1);
|
|
if (length && text.length > length)
|
|
text = text.substring(0, length-1) + "\u2026"; // ellipsis
|
|
|
|
return [text, meta];
|
|
}
|
|
|
|
|
|
/**
|
|
* A registry of gloda providers that have contentWhittle() functions.
|
|
* used by mimeMsgToContentSnippet, but populated by the Gloda object as it's
|
|
* processing providers.
|
|
*/
|
|
function WhittlerRegistry() {
|
|
this._whittlers = [];
|
|
}
|
|
|
|
WhittlerRegistry.prototype = {
|
|
/**
|
|
* Add a provider as a content whittler.
|
|
*/
|
|
registerWhittler: function whittler_registry_registerWhittler(provider) {
|
|
this._whittlers.push(provider);
|
|
},
|
|
/**
|
|
* get the list of content whittlers, sorted from the most specific to
|
|
* the most generic
|
|
*/
|
|
getWhittlers: function whittler_registry_getWhittlers() {
|
|
// Use the concat() trick to avoid mutating the internal object and
|
|
// leaking an internal representation.
|
|
return this._whittlers.concat().reverse();
|
|
}
|
|
}
|
|
|
|
this.whittlerRegistry = new WhittlerRegistry();
|
|
|
|
function GlodaContent() {
|
|
this._contentPriority = null;
|
|
this._producing = false;
|
|
this._hunks = [];
|
|
}
|
|
|
|
GlodaContent.prototype = {
|
|
kPriorityBase: 0,
|
|
kPriorityPerfect: 100,
|
|
|
|
kHunkMeta: 1,
|
|
kHunkQuoted: 2,
|
|
kHunkContent: 3,
|
|
|
|
_resetContent: function gloda_content__resetContent() {
|
|
this._keysAndValues = [];
|
|
this._keysAndDeltaValues = [];
|
|
this._hunks = [];
|
|
this._curHunk = null;
|
|
},
|
|
|
|
/* ===== Consumer API ===== */
|
|
hasContent: function gloda_content_hasContent() {
|
|
return (this._contentPriority != null);
|
|
},
|
|
|
|
/**
|
|
* Return content suitable for snippet display. This means that no quoting
|
|
* or meta-data should be returned.
|
|
*
|
|
* @param aMaxLength The maximum snippet length desired.
|
|
*/
|
|
getContentSnippet: function gloda_content_getContentSnippet(aMaxLength) {
|
|
let content = this.getContentString();
|
|
if (aMaxLength)
|
|
content = content.substring(0, aMaxLength);
|
|
return content;
|
|
},
|
|
|
|
getContentString: function gloda_content_getContent(aIndexingPurposes) {
|
|
let data = "";
|
|
for (let hunk of this._hunks) {
|
|
if (hunk.hunkType == this.kHunkContent) {
|
|
if (data)
|
|
data += "\n" + hunk.data;
|
|
else
|
|
data = hunk.data;
|
|
}
|
|
}
|
|
|
|
if (aIndexingPurposes) {
|
|
// append the values for indexing. we assume the keywords are cruft.
|
|
// this may be crazy, but things that aren't a science aren't an exact
|
|
// science.
|
|
for (let kv of this._keysAndValues) {
|
|
data += "\n" + kv[1];
|
|
}
|
|
for (let kon of this._keysAndValues) {
|
|
data += "\n" + kon[1] + "\n" + kon[2];
|
|
}
|
|
}
|
|
|
|
return data;
|
|
},
|
|
|
|
/* ===== Producer API ===== */
|
|
/**
|
|
* Called by a producer with the priority they believe their interpretation
|
|
* of the content comes in at.
|
|
*
|
|
* @returns true if we believe the producer's interpretation will be
|
|
* interesting and they should go ahead and generate events. We return
|
|
* false if we don't think they are interesting, in which case they should
|
|
* probably not issue calls to us, although we don't care. (We will
|
|
* ignore their calls if we return false, this allows the simplification
|
|
* of code that needs to run anyways.)
|
|
*/
|
|
volunteerContent: function gloda_content_volunteerContent(aPriority) {
|
|
if (this._contentPriority === null || this._contentPriority < aPriority) {
|
|
this._contentPriority = aPriority;
|
|
this._resetContent();
|
|
this._producing = true;
|
|
return true;
|
|
}
|
|
this._producing = false;
|
|
return false;
|
|
},
|
|
|
|
keyValue: function gloda_content_keyValue(aKey, aValue) {
|
|
if (!this._producing)
|
|
return;
|
|
|
|
this._keysAndValues.push([aKey, aValue]);
|
|
},
|
|
keyValueDelta: function gloda_content_keyValueDelta (aKey, aOldValue,
|
|
aNewValue) {
|
|
if (!this._producing)
|
|
return;
|
|
|
|
this._keysAndDeltaValues.push([aKey, aOldValue, aNewValue]);
|
|
},
|
|
|
|
/**
|
|
* Meta lines are lines that have to do with the content but are not the
|
|
* content and can generally be related to an attribute that has been derived
|
|
* and stored on the item.
|
|
* For example, a bugzilla bug may note that an attachment was created; this
|
|
* is not content and wouldn't be desired in a snippet, but is still
|
|
* potentially interesting meta-data.
|
|
*
|
|
* @param aLineOrLines The line or list of lines that are meta-data.
|
|
* @param aAttr The attribute this meta-data is associated with.
|
|
* @param aIndex If the attribute is non-singular, indicate the specific
|
|
* index of the item in the attribute's bound list that the meta-data
|
|
* is associated with.
|
|
*/
|
|
meta: function gloda_content_meta(aLineOrLines, aAttr, aIndex) {
|
|
if (!this._producing)
|
|
return;
|
|
|
|
let data;
|
|
if (typeof(aLineOrLines) == "string")
|
|
data = aLineOrLines;
|
|
else
|
|
data = aLineOrLines.join("\n");
|
|
|
|
this._curHunk = {hunkType: this.kHunkMeta, attr: aAttr, index: aIndex,
|
|
data: data};
|
|
this._hunks.push(this._curHunk);
|
|
},
|
|
/**
|
|
* Quoted lines reference previous messages or what not.
|
|
*
|
|
* @param aLineOrLiens The line or list of lines that are quoted.
|
|
* @param aDepth The depth of the quoting.
|
|
* @param aOrigin The item that originated the original content, if known.
|
|
* For example, perhaps a GlodaMessage?
|
|
* @param aTarget A reference to the location in the original content, if
|
|
* known. For example, the index of a line in a message or something?
|
|
*/
|
|
quoted: function gloda_content_quoted(aLineOrLines, aDepth, aOrigin,
|
|
aTarget) {
|
|
if (!this._producing)
|
|
return;
|
|
|
|
let data;
|
|
if (typeof(aLineOrLines) == "string")
|
|
data = aLineOrLines;
|
|
else
|
|
data = aLineOrLines.join("\n");
|
|
|
|
if (!this._curHunk ||
|
|
this._curHunk.hunkType != this.kHunkQuoted ||
|
|
this._curHunk.depth != aDepth ||
|
|
this._curHunk.origin != aOrigin || this._curHunk.target != aTarget) {
|
|
this._curHunk = {hunkType: this.kHunkQuoted, data: data,
|
|
depth: aDepth, origin: aOrigin, target: aTarget};
|
|
this._hunks.push(this._curHunk);
|
|
}
|
|
else
|
|
this._curHunk.data += "\n" + data;
|
|
},
|
|
|
|
content: function gloda_content_content(aLineOrLines) {
|
|
if (!this._producing)
|
|
return;
|
|
|
|
let data;
|
|
if (typeof(aLineOrLines) == "string")
|
|
data = aLineOrLines;
|
|
else
|
|
data = aLineOrLines.join("\n");
|
|
|
|
if (!this._curHunk || this._curHunk.hunkType != this.kHunkContent) {
|
|
this._curHunk = {hunkType: this.kHunkContent, data: data};
|
|
this._hunks.push(this._curHunk);
|
|
}
|
|
else
|
|
this._curHunk.data += "\n" + data;
|
|
},
|
|
};
|