By Ming (Amos) Zhang
Has no other scripts.
// Gmail Filter Assistant
// Copyright (c) 2007, Ming Zhang (kfkming@gmail.com)
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
// GUID: {bffc3178-2849-4727-a62c-866b8ec0523d}
// --------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// To uninstall, go to Tools/Manage User Scripts,
// select "Gmail Filter Assistant", and click Uninstall.
//
// --------------------------------------------------------------------
// ==UserScript==
// @name Gmail Filter Assistant
// @description Help to manage the Gmail filters more efficiently
// @source http://userscripts.org/scripts/show/7997
// @identifier http://userscripts.org/scripts/source/7997.user.js
// @author Ming (Amos) Zhang
// @date 2007-03-17
// @namespace http://www.amoszhang.com/GmailFilterAssistant
// @include https://mail.google.com/*
// @include http://mail.google.com/*
// ==/UserScript==
//
window.addEventListener('load', function() {
autoupdate();
// autoupdate function credit to Bjorn Rosell http://userscripts.org/scripts/show/7715
function autoupdate() {
debug("[autoupdate]: Entering...");
GM_addStyle(FILTERASST_STYLES);
// only check for updates one time a day
var d = new Date();
if (GM_getValue('_gfa_lastcheck') == d.getDate()) {
debug('[autoupdate]: Only check update once per day. Leaving...');
return;
}
// check for update
GM_xmlhttpRequest({
method:"GET",
headers:{ 'Accept': 'text/plain'},
url: SCRIPT_SHOW_URL,
onload:function(result) {
var data = result.responseText ;
var ver = parseFloat(getDataItem(data, 'Gmail Filter Assistant v', ';')); // get version number
debug('The latest version=['+ver+'], and current version=['+Version+']');
if ( ver > Version ) { // current version is an old one
floatingMsg=getWord('newversion')+'<a href="'+SCRIPT_SHOW_URL+'" target="_blank">'+getWord('here')+'</a>'
}
// getting new version info successfully, set the lastcheck flag to prevent more than one checking every day.
var d = new Date();
GM_setValue('_gfa_lastcheck', d.getDate());
}
});
debug('[autoupdate]: Leaving...');
}
}, true);
const Version = 0.19; // the ';' at the end is necessary
const SCRIPT_SHOW_URL = 'http://userscripts.org/scripts/show/7997';
const NUM_FILTER_ITEMS = 14;
const DEBUG = false;
const FILTERASST_STYLES =
"table._gfa_ {"+
" width: 100%;"+
" border-width: 3;"+
" border-style: solid;" +
" border-color:#FAD163;" +
" background: #FFF7D7;"+
" cellspacing: 5px;"+
" cellpadding: 5px;"+
"}"+
"td._gfa_label {" +
" font-weight: bold;" +
" text-align: right;" +
" vertical-align: middle;" +
" padding: 5px;" +
" white-space: nowrap;"+
" font-family: arial,sans-serif;" +
"}" +
"span._gfa_navLink {" +
" background: #FAD163;"+
" border-color: #FAD163;"+
" border-width: 3;"+
" border-style: solid;" +
" font-weight: bold;" +
" text-align: middle;" +
" vertical-align: bottom;" +
" padding-top: 2px;" +
" padding-left: 4px;" +
" padding-bottom: 2px;" +
" padding-right: 4px;" +
" font-family: arial,sans-serif;" +
" white-space: nowrap;"+
" cursor: pointer;"+
" float: right;"+
" -moz-border-radius-topleft: 4px;"+
" -moz-border-radius-topright: 4px;"+
"}" +
"input._gfa_inputbox {" +
" font-weight: normal;" +
" text-align: left;" +
" vertical-align: middle;" +
" width : 90%;"+
" font-family: arial,sans-serif;" +
" font-size: 90%;" +
"}" +
"span.hovereffect:hover{background-color:#d0dbeb;}";
const RSC_LANG={
'en' :{
title: "Filter Assistant",
loading:"Loading...",
running:'Running...',
createfilter:"Create Filter",
newversion:"New version of Gmail Filter Assistant available, install it ",
here:"here",
from: "From:",
to: "To:",
subject:"Subject:",
hasWord:"Hasword:",
doesntHave:"Doesn't have:",
hasAttachment:"Has attachment:",
skipInbox:"Skip the inbox(Archive it)",
markasread:"Mark it as read",
starIt:"Star it",
applyLabel:"Apply the label",
forward:"Forward it to",
deleteIt:"Delete it",
applytoexistingconvs:"Apply the filter to existing conversations",
moreactions:"More actions...",
newlabel:"New label...",
backup:"Backup filter&label",
restore:"Restore filter&label",
consolidate:"Consolidate filters",
hidenavtag:'Hide the navigation tag',
popuplabel:'Your filter & label data',
close:'Close',
cancel:'Cancel',
readingfilterlabeldataerror:"Reading filter and label data error, please try again later",
popuptitlebackup:'Javascript doesn\'t have the capability to write disk file, please copy/paste the content in the textbox to a .txt file, and keep this file in a safe place.',
popuptitlerestore:'Please copy/paste Filter & Label data from where you saved them to the text box below and click "Close"',
popuprestorewarning:'WARNING!!\n\Restoring filter data will overwrite existing filters and may take several seconds to several minutes, depending on your connection speed. \n\nAre you sure you want to continue?',
inputlabeltitle:'Please input the label name (maximal length 100):',
failtoretrieveresult:'Fail to retrieve the result, the action may not be completed',
emptycriteriawarning:'Warning: "From", "To", "Subject", "Hasword" and "Doesn\'t have" are all empty, are you sure you want to proceed?',
selectatleastoneaction:'Please select at least one action',
selectalabelorcreateone:'Please select a label or create a new one',
itemintrashwarning:'Warning: Items in the Trash are not displayed in label views or the starred view.\nDo you still wish to create the filter?',
foundduplicatefilterwarning:'Found a filter having the same criteria, overwrite it?',
showdetails:'show details',
hidedetails:'hide details',
},
'zh-CN' :{
title: "过滤器助手",
loading:"载入中...",
running:"正在运行...",
createfilter:"创建过滤器",
newversion:"发现新版本的 Gmail Filter Assistant, 安装请点",
here:"这里",
from: "来自:",
to: "发往:",
subject:"标题:",
hasWord:"含有:",
doesntHave:"没有:",
hasAttachment:"带附件:",
skipInbox:"跳过收件箱(直接存档)",
markasread:"标记为已读",
starIt:"标星",
applyLabel:"标记为",
forward:"转发至",
deleteIt:"删除",
applytoexistingconvs:"将本过滤器作用于已有对话",
moreactions:"其他操作...",
newlabel:"新建标签...",
backup:"备份过滤器及标签",
restore:"恢复过滤器及标签",
consolidate:"整合过滤器",
hidenavtag:'隐藏导航标签',
popuplabel:'您的过滤器及标签数据',
close:'关闭',
cancel:'取消',
readingfilterlabeldataerror:"读取过滤器及标签数据过程中发生错误,请迟些时候再试",
popuptitlebackup:"Javascript 无法读写磁盘文件,请将文本框内的数据拷贝/粘贴至一个文本文件并妥善保存",
popuptitlerestore:"请将保存的过滤器及标签数据拷贝/粘贴至文本框然后点击“关闭”",
popuprestorewarning:"警告!恢复操作将覆盖已有的过滤器及标签数据!而且根据您的网络速度,整个过程可能持续几秒钟至几分钟不等。\n\n确定要进行恢复操作么?",
inputlabeltitle:"请输入标签名(最长100字符):",
failtoretrieveresult:"读取结果失败,此项操作可能未顺利完成",
emptycriteriawarning:"请填写搜索条件",
selectatleastoneaction:"请选择至少一项操作",
selectalabelorcreateone:"请选择一个标签或创建一个新标签",
itemintrashwarning:"警告:标签视图或已加注星标的邮件视图中不显示已删除邮件中的内容。\n 是否仍要创建过滤器?",
foundduplicatefilterwarning:"发现一个已有过滤器含有相同的搜索条件,是否覆盖?",
showdetails:'显示详细信息',
hidedetails:'隐藏详细信息',
},
'de' :{
title: "Filter Assistent",
loading:"Lade...",
running:'Läuft...',
createfilter:"Filter erstellen",
newversion:"Eine neue Version von Gmail Filter Assistant ist verfügbar, installieren Sie es von",
here:"hier",
from: "Von:",
to: "An:", subject:"Betreff:",
hasWord:"Mit diesen Worten:",
doesntHave:"Ohne:",
hasAttachment:"Mit Anhang",
skipInbox:"Posteingang überspringen (Archivieren)",
markasread:"Markieren",
starIt:"Markierung hinzufügen", applyLabel:"Label anwenden",
forward:"Weiterleiten an",
deleteIt:"Löschen",
applytoexistingconvs:"Filter auf existierende Konversationen anwenden",
moreactions:"Weitere Aktionen...",
newlabel:"Neues Label...",
backup:"Filter&Label sichern",
restore:"Filter&Label wiederherstellen",
consolidate:"Filter konsolidieren",
hidenavtag:'Navigationslink verstecken',
popuplabel:'Ihre Filter & Label Daten',
close:'Schließen',
cancel:'Abbrechen',
readingfilterlabeldataerror:"Fehler beim lesen der Filter und Label Daten. Bitte später noch einmal versuchen",
popuptitlebackup:'Javascript kann nicht auf die Festplatte schreiben. Bitte den Inhalt der Textbox in eine Text-Datei sichern.',
popuptitlerestore:'Bitte kopieren (kopieren/einfügen) Sie die Filter & Label Daten aus Ihrer Sicherung in die Textbox unten und klicken Sie "Schlieéen"',
popuprestorewarning:'WARNUNG!! Wiederherstellen der Filterdaten wird existierende Filter überschreiben und kann (abhängig von Iherer Verbindungsgeschwindigkeit) mehrere Minuten dauern. Sind Sie sicher, dass sie fortfahren wollen?',
inputlabeltitle:'Bitte den neuen Label-Namen eingeben (maximal 100 Zeichen):',
failtoretrieveresult:'Fehler das Ergebnis zu empfangen. Die aktion konnte evtl. nicht durchgeführt werden',
emptycriteriawarning:'Warnung: "Von", "An", "Betreff", "Mit diesen Worten" und "Ohne" sind alle leer. Sind Sie sicher, dass sie weitermachen wollen?',
selectatleastoneaction:'Bitte wählen Sie mindestens eine Aktion',
selectalabelorcreateone:'Bitte wählen Sie ein Label oder erstellen Sie ein neues',
itemintrashwarning:'Warnung: Dinge im Papierkorb, oder markierte Nachrichten werden nicht in der Markiert oder Papierporb-Anschicht angezeigt.\nWollen Sie den Filter immer noch erstellen?',
foundduplicatefilterwarning:'Ein Filter mit gleichen Kriterien wurde gefunden. Soll er überschrieben werden?',
},
};
//=========================
// Definition of variables
//=========================
var baseURI=document.baseURI;
var labelData, filterData;
var isDataReady=false;
var currentLabel="";
var currentFilterData=null;
var at, ik;
var rawFilterLabelData='';
var lang='en'; // default language: English
var baseUrl='';
var messageIndex=-1; // the index of the message on which GFA is called, only one GFA instance is allowed at any time
var floatingMsg='';
//=======================
// Definition of objects
//=======================
function ParamParsingData() {
this.currentPosition = 0;
}
function Filter() {
this.id=null;
this.name='';
this.from = '';
this.to = '';
this.subject = '';
this.hasWord = '';
this.doesntHave = '';
this.hasAttachment = false;
this.skipInbox = false;
this.markAsRead = false;
this.starIt = false;
this.applyLabel = false;
this.labelToApply = '';
this.forward = false;
this.forwardTo = '';
this.deleteIt = false;
this.applyOnExistingConvs = false;
}
var tmpdiv = newNode('div');
function myUnescape(s) {
s=s.replace(/\\u/g, '%u');
tmpdiv.innerHTML = unescape(s);
return tmpdiv.innerHTML;
}
function getWord(s) {
if (lang && RSC_LANG[lang] && RSC_LANG[lang][s])
s= RSC_LANG[lang][s];
else {
debug('[getWord]: retrieve resource fail: RSC_LANG['+lang+']['+s+'] return null');
s= RSC_LANG['en'][s];
lang='en';
}
return myUnescape(s);
}
init();
function init() {
// get base URL
baseUrl=baseURI.match(/http[s]{0,1}:\/\/mail.google.com\/a\/[^\.].*\.[a-z]{2,}\//); // google App for Your Domain mailbox
if (!baseUrl) //not found
baseUrl=baseURI.match(/http[s]{0,1}:\/\/mail.google.com\/mail\//); // gmail
if (!baseUrl) {
debug("[autoupdate]: Not a gmail page.");
return; // not a proper page
}
at = getDataItem(document.cookie,'GMAIL_AT=', ';');
ik = GM_getValue('_gfa_'+baseUrl+'ik', null);
var new_ik = getDataItem(baseURI, 'ik=', '&');
if (new_ik != null) {
ik = new_ik;
GM_setValue('_gfa_'+baseUrl+'ik', ik);
debug('ik = '+ik);
}
window.setInterval(function() {
drawFilterAsstBtn();
}, 1000);
}
function drawFilterAsstBtn() {
var els = getElementsByTag("SPAN");
if (!els || els.length <= 0) return;
//GM_log(getWord('showdetails'));
var found = false;
for (var key in els) {
if (els[key] && els[key].innerHTML && (els[key].innerHTML==getWord('showdetails') || els[key].innerHTML==getWord('hidedetails'))) {
var node = els[key].parentNode.parentNode.parentNode.lastChild;
var id = node.firstChild.firstChild.getAttribute("id");
if (getNode('_gfa_navlink_'+id)) continue;
node = node.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0];
var separator=newNode('TD');
separator.className='t9K9Me';
separator.innerHTML='<DIV class="d3MNGb s77Qlb"/>';
node.parentNode.insertBefore(separator, node.nextSibling.nextSibling);
var link=newNode('TD');
link.className="t9K9Me";
link.innerHTML= '<DIV class="HHDPQd"><TABLE><TBODY><TR><TD><DIV class="SvrlRe" id="_gfa_navlink_'+id+'">'+getWord('title')+'</DIV></TD></TR></TBODY></TABLE></DIV>';
link.style.cursor="pointer";
node.parentNode.insertBefore(link, separator.nextSibling);
debug('insert GFA link');
link = getNode('_gfa_navlink_'+id);
link.addEventListener('mousedown', handleFilterAsstBtnClick, false);
debug('insert separator');
}
}
els = getElementsByTag("a");
if (els && els.length > 0)
for (var key in els) {
if (els[key] && els[key].getAttribute){
var url = els[key].getAttribute("href");
if (url)
{
var idx = -1;
if ((idx = url.indexOf("hl="))!=-1)
{
url = url.substr(idx+3);
if ((idx = url.indexOf("\&"))!=-1)
{
lang = url.substring(0, idx);
lang = lang.replace("_", "-");
break;
}
}
}
}
}
}
function handleFilterAsstBtnClick(e) {
e.stopPropagation();
debug('[handleFilterAsstBtnClick]: Entering...');
var old_msg_idx=messageIndex;
var s=this.id;
debug("this.id="+this.id);
if (!s || !s.match(/^_gfa_navlink_[a-zA-Z0-9]+$/)) return;
messageIndex= s.substr(13);
if (old_msg_idx!=messageIndex) {
var node=getNode('_gfa_ws');
if (node) node.parentNode.removeChild(node);
}
asynGetLabelFilterData(drawFilterAsstWorkspace, (getNode('_gfa_ws_div')==null));
debug('[handleFilterAsstBtnClick]: Leaving...');
}
function asynGetLabelFilterData(callbackfunc, argument)
{
if (typeof callbackfunc!='function') {
debug('[asynGetLabelFilterData]: Invalid callback function. Leaving...');
return;
}
if (isDataReady) {
callbackfunc(argument);
debug('[asynGetLabelFilterData]: Data is ready, no need to read again. Leaving...');
return;
}
debug('[asynGetLabelFilterData]: Entering...');
var navfilter=null;
if (messageIndex!=-1) navfilter=getNode('_gfa_navlink_'+messageIndex);
if (navfilter) navfilter.innerHTML='<b>'+getWord('loading')+'</b>';
var filter_ref_url = baseUrl + '?ui=2&view=pu&usus=1&rt=j&ik=' + ik;
GM_xmlhttpRequest({
method:'GET',
url: filter_ref_url,
onload: function(responseDetails) {
if (navfilter) navfilter.innerHTML=getWord('title');
var data = responseDetails.responseText;
var status = responseDetails.status;
rawFilterLabelData=data;
if (status!=200) return; // not OK
debug('[asynGetLabelFilterData]: Reading label data....');
labelData = getReturnValue(data, '["ub",[');
if (labelData) {
labelData.sort();
debug('[asynGetLabelFilterData]: labelData='+labelData.toString());
var i = 0;
while (i<labelData.length)
{
if (labelData[i][0][0]=='^'){
labelData.splice(i,1);
}
else
i++;
}
}
debug('[asynGetLabelFilterData]: Reading filter data....');
filterData = getReturnValue(data, '["fi",');
if (filterData) debug('[asynGetLabelFilterData]: filterData='+filterData.toString());
isDataReady=true;
debug('[asynGetLabelFilterData]: All data ready');
callbackfunc(argument);
if (currentFilterData!=null) {
// recover the status before refreshing
if (currentLabel!="") {
currentFilterData.labelToApply=currentLabel;
var node = getNode('chkBoxApplyLabel');
if (!node) node.setAttribute('checked', '1');
}
restoreUserChanges(currentFilterData);
currentFilterData=null;
}
debug('[asynGetLabelFilterData]: Leaving...');
},
onerror: function(responseDetails){
if (navfilter) navfilter.innerHTML=getWord('title');
debug('[asynGetLabelFilterData]: Leaving...');
}
});
}
//=================================
// Draw Filter Assistant Workspace
//=================================
function createTitlebox() {
var titlebox=getNode('_gfa_ws'); // the whole workspace
var msg=getNode(messageIndex);
// create the titlebox if it's not there.
if (!titlebox) {
titlebox=newNode('tr');
titlebox.id='_gfa_ws';
titlebox.setAttribute('idx',messageIndex);
msg.parentNode.parentNode.parentNode.parentNode.appendChild(titlebox);
}
return titlebox;
}
function drawFilterAsstWorkspace(show_boxes) {
debug('[drawFilterAsstWorkspace]: Entering... show_boxes = '+show_boxes);
var node=getNode('_gfa_ws_div');
if (node) // the workspace is there, hide it.
node.parentNode.removeChild(node);
debug('[drawFilterAsstWorkspace]: floatingMsg = '+floatingMsg);
var titlebox=createTitlebox();
titlebox.innerHTML=
'<td colspan="3" style="width: 100%;"><table cellspacing="5px" cellpadding="5px" style="width: 100%;"><tbody>'
+ (floatingMsg!=''?
('<tr id="_gfa_FloatingMsg">'
+ '<td class="cbln"><div class="mb">'
+ '<div align=center style="padding-top:5px;padding-bottom:5px;"><span style="background: #FAD163;-moz-border-radius:15px;padding: 3px 10px 3px 10px;"><b>'+myUnescape(floatingMsg)+'</b></span></div></div>'
+ '<td class="cbrn">'
+ '</tr>'):'')
+'</tbody></table></td>';
if (floatingMsg!='') {
window.setTimeout(function(){
var node=getNode('_gfa_FloatingMsg');
if (node) node.parentNode.removeChild(node);
}, 5000); // delete the msg
}
floatingMsg='';
if (!show_boxes) return;
node=newNode('tr');
titlebox.firstChild.firstChild.firstChild.appendChild(node);
node.innerHTML= '<div class="ArwC7c" style="font-weight: bold;" id="_gfa_ws_div"/>' ;
var div=getNode('_gfa_ws_div');
if (!div) return;
div.addEventListener('mousedown', function(e){e.stopPropagation();}, false);
var LabelsCombBox = newNode('select');
LabelsCombBox.id = "cbBoxLabels";
var i;
var opList;
if (labelData)
for (i=0; i<labelData.length;i++ ) {
opList = newNode('option');
opList.innerHTML = labelData[i][0];
opList.addEventListener('click', checkApplyTheLabel, false);
LabelsCombBox.appendChild (opList);
}
opList = newNode('option');
opList.id = "opNewLabel" // new label option
opList.innerHTML = getWord('newlabel');
opList.addEventListener ('click', addLabel, false);
if (labelData.length==1)
LabelsCombBox.value=opList.value;
else
LabelsCombBox.value=currentLabel;
LabelsCombBox.appendChild(opList);
var subject='', sender='', recver='';
//
// !!! FIXME !!!
//
// The way to get subject, sender and receiver is very unstable. if multi-emails exist
// in the conversation, the receiver may not be captured.
var node = getNode(messageIndex);
var email, item;
try {
var node = node.parentNode.parentNode.parentNode.firstChild.firstChild.firstChild.firstChild.firstChild.firstChild;
if (node.childNodes[2]) {
sender = node.childNodes[2].getAttribute('email');
if (!sender) sender = '';
}
if (node.childNodes[4]) {
// Search all the nodes and try to find "email" attribute.
node=node.childNodes[4];
for (i=0;i<node.childNodes.length;i++) {
item = node.childNodes[i];
email = '';
if (item.getAttribute) {
email = item.getAttribute('email');
if (!email && item.firstChild && item.firstChild.getAttribute)
email = item.firstChild.getAttribute('email');
if (email.length>0) recver = (recver=='' ? email : recver + ' OR ' + email);
}
}
}
}catch(e) {
GM_log('Error occurred: Getting sender/receiver fail. \n\nDetailed error msg:\n\n['+e.name+']:'+e.message);
}
// ======================= add "Characteristic" part =============================
div.innerHTML=
'<table class="_gfa_">'
+ '<tbody>'
+ '<tr>'
+ '<td class="_gfa_label">'+getWord('from')
+ '<td width=40%><input type="text" class="_gfa_inputbox" id="txtBoxFrom" value="'+sender+'">'
+ '<td class="_gfa_label" width=10%>'
+ '<td class="_gfa_label">'+getWord('hasWord')
+ '<td width=40%><input class="_gfa_inputbox" type="text" id="txtBoxHasWord">'
+ '</tr>'
+ '<tr>'
+ '<td class="_gfa_label">'+getWord('to')
+ '<td width=40%><input class="_gfa_inputbox" type="text" id="txtBoxTo" value="'+recver+'">'
+ '<td class="_gfa_label" width=10%>'
+ '<td class="_gfa_label">'+getWord('doesntHave')
+ '<td width=40%><input class="_gfa_inputbox" type="text" id="txtBoxDoesntHave">'
+ '</tr>'
+ '<tr>'
+ '<td class="_gfa_label">'+getWord('subject')
+ '<td width=40%><input class="_gfa_inputbox" type="text" id="txtBoxSubject" value="'+subject+'">'
+ '<td class="_gfa_label" width=10%>'
+ '<td>'
+ '<td class="_gfa_label" style="text-align:left" width=40%><input type="checkbox" id="chkBoxHasAttachment">'+getWord('hasAttachment')
+ '</tr>'
+ '</tbody>'
+ '</table>'
+ '<table class="_gfa_" style="border-top:0px;">'
+ '<tbody>'
+ '<tr>'
+ '<td width=30%>'
+ '<td class="_gfa_label" style="text-align:left" width=40%><input type="checkbox" id="chkBoxSkipInbox">'+getWord('skipInbox')
+ '<td class="_gfa_label" width=30% colspan=2>'
+ ' <select id="selFilterMoreOptions">'
+ ' <option style="color: #808080;" >'+getWord('moreactions')+'</option>'
+ ' <option style="color: #808080;" >--------------------</option>'
+ ' <option id="opBackupFilters" >'+getWord('backup')+'</option>'
+ ' <option id="opRestoreFilters" >'+getWord('restore')+'</option>'
+ ' <option id="opConsolidateFilters" style="color: #808080;" >'+getWord('consolidate')+'</option>'
+ ' </select>'
+ '</tr>'
+ '<tr>'
+ '<td width=30%>'
+ '<td class="_gfa_label" style="text-align:left" width=40%><input type="checkbox" id="chkBoxMarkAsRead">'+getWord('markasread')
+ '<td width=30%>'
+ '</tr>'
+ '<tr>'
+ '<td width=30%>'
+ '<td class="_gfa_label" style="text-align:left" width=40%><input type="checkbox" id="chkBoxStarIt">'+getWord('starIt')
+ '<td width=30%>'
+ '</tr>'
+ '<tr>'
+ '<td width=30%>'
+ '<td class="_gfa_label" style="text-align:left" width=40%><input type="checkbox" id="chkBoxApplyLabel">'+getWord('applyLabel')
+ '<td width=30%>'
+ '</tr>'
+ '<tr>'
+ '<td width=30%>'
+ '<td class="_gfa_label" style="text-align:left" width=40%><input type="checkbox" id="chkBoxForwardTo">'+getWord('forward')+'<input type="text" id="txtBoxForwardTo">'
+ '<td width=30%>'
+ '</tr>'
+ '<tr>'
+ '<td width=30%>'
+ '<td class="_gfa_label" style="text-align:left" width=40%><input type="checkbox" id="chkBoxDeleteIt">'+getWord('deleteIt')
+ '<td width=30%>'
+ '</tr>'
+ '<tr>'
+ '<td width=30%>'
+ '<td class="_gfa_label" style="text-align:left" width=40%><input type="checkbox" id="chkBoxApplyToExistingConvs">'+getWord('applytoexistingconvs')
+ '<td width=15% style="padding: 5px;"><span id="_gfa_navlink_lblCancel'+messageIndex+'" class="_gfa_navLink" style="-moz-border-radius:8px;"><b>'+getWord('cancel')+'</b></span>'
+ '<td width=15% style="padding: 5px;"><span id="lblCreateFilter" class="_gfa_navLink" style="-moz-border-radius:8px;"><b>'+getWord('createfilter')+'</b></span>'
+ '</tr>'
+ '</tbody>'
+ '</table>'
+'</td>';
getNode('opBackupFilters').addEventListener('click', handleBackupFilters, false);
getNode('opRestoreFilters').addEventListener('click', handleRestoreFilters, false);
getNode('opConsolidateFilters').addEventListener('click', handleConsolidateFilters, false);
node = getNode('lblCreateFilter');
node.addEventListener('click', handleCreateFilter, false);
node = getNode('_gfa_navlink_lblCancel'+messageIndex);
node.addEventListener('click', handleFilterAsstBtnClick, false);
node = getNode('chkBoxApplyLabel');
node.parentNode.appendChild(LabelsCombBox);
debug('[drawFilterAsstWorkspace]: Leaving...');
}
function checkApplyTheLabel() {
try{getNode('chkBoxApplyLabel').checked=true;}catch(e){};
}
//=======================
// Draw the popup window
//=======================
function drawPopup(title, callback) {
if (callback==null || typeof callback!='function') return;
var popup = newNode('div');
popup.id='_gfa_popup';
popup.setAttribute("style", "z-index:1102;position:fixed;overflow:auto;width:470px;left:20px;top:20px;background-color:#FFF7D7;padding:15px;border:2px solid #FAD163");
document.body.appendChild(popup);
if (typeof title!='string' || title==null) title='';
popup.innerHTML=
''+title+'<br><br>'
+ '<b>'+getWord('popuplabel')+':<b><br>'
+ '<textarea id="txtBoxFilters" style="width:465px; height:250px;">'
+ '</textarea><br><br>'
+ '<button id="btnClosePopup">'+getWord('close')+'</button>';
getNode('btnClosePopup').addEventListener('click', callback, false);
}
//===========================
// Close popup event handler
//===========================
function handleClosePopup() {
var node=getNode('_gfa_popup');
if (node) node.parentNode.removeChild(node);
}
//=============================
// Backup filter event handler
//=============================
function handleBackupFilters() {
if (rawFilterLabelData=='') {
alert(getWord('readingfilterlabeldataerror'));
return;
}
drawPopup(getWord('popuptitlebackup'), handleClosePopup);
var node=getNode('txtBoxFilters');
var s='';
var header = '["fi",';
s=s+header+getRawData(rawFilterLabelData, header)+']';
s+='\n\n';
header = '["ub",[';
s=s+header+getRawData(rawFilterLabelData, header)+']';
node.innerHTML=s;
}
//=============================
// Restore filter event handler
//=============================
function handleRestoreFilters() {
drawPopup(getWord('popuptitlerestore'), RestoreFilters);
}
function RestoreFilters() {
var f, l; //filter data, label data
var node=getNode('txtBoxFilters');
var s='';
if (node) s = node.value;
handleClosePopup();
if (s=='') return;
f=getReturnValue(s, '["fi",');
l=getReturnValue(s, '["ub",[');
var node=getNode('_gfa_navlink_'+messageIndex);
if (node) node.innerHTML=' <b>'+getWord('running')+'</b>';
if (f) {
if (!confirm(getWord('popuprestorewarning'))) {
if (node) node.innerHTML=getWord('title');
return;
}
var nHasInvalidData=0;
var nEdit=0, nAdd=0;
for (var i=0;i<f.length;i++) {
// check data validity
if (f[i].length<3 || f[i][2].length!=NUM_FILTER_ITEMS ||
!isBoolValue(f[i][2][5]) || // Has attachment
!isBoolValue(f[i][2][6]) || // Skip Inbox
!isBoolValue(f[i][2][7]) || // Mark as read
!isBoolValue(f[i][2][8]) || // Star It
!isBoolValue(f[i][2][9]) || // Apply label
!isBoolValue(f[i][2][11]) || // Forward it
!isBoolValue(f[i][2][13]) ) // Delete it
continue;
else
// Further check if label/email are available if label/forward actions are taken.
if ((f[i][2][9]=='true' && f[i][2][10]=='') || // try to apply a label, but given an empty string as label name
(f[i][2][11]=='true' && f[i][2][12].match(/^.+@[^\.].*\.[a-z]{2,}$/)==null) ) //try to forward the email but given an invalid email address
continue;
// OK, now we think the data is valid, try to restore the filter data
// check if the filter is already in the system
var fd = new Filter();
fd.id = '';
fd.name = '';
fd.from = f[i][2][0];
fd.to = f[i][2][1];
fd.subject = f[i][2][2];
fd.hasWord = f[i][2][3];
fd.doesntHave = f[i][2][4];
fd.hasAttachment = f[i][2][5];
fd.skipInbox = f[i][2][6];
fd.markAsRead = f[i][3][7];
fd.starIt = f[i][2][8];
fd.applyLabel = f[i][2][9];
fd.labelToApply = f[i][2][10];
fd.forward = f[i][2][11];
fd.forwardTo = f[i][2][12];
fd.deleteIt = f[i][2][13];
var k=hasFilter(fd); // returns a filter id if found.
if (k>=0) {
// found a filter having the same criteria
// check if actions are same, too
var same=true;
for (var j=6;j<NUM_FILTER_ITEMS;j++)
if (same)
same = same && (f[i][2][j]!=filterData[k][2][j]);
else
break;
// the two filters are the same, skip it
if (same) continue;
// The new filter has different actions, overwrite the old one.
fd.id = filterData[k][0]; // store the ID
fd.name = filterData[k][1]; // store the name
}
addFilter(fd, i==(f.length-1));
}
}
if (node) node.innerHTML=getWord('title');
}
function isBoolValue(v) {
return (v=='true' || v=='false' || v=='');
}
//==================================
// Consolidate filter event handler
//==================================
function handleConsolidateFilters() {
// in progress
}
//==================
// Add Label
//==================
function addLabel() {
debug('[addLabel]: Entering...');
currentFilterData = getUserChanges();
currentLabel=prompt(getWord('inputlabeltitle'),'');
if (!currentLabel || currentLabel.length<=0) {
debug('[addLabel]: Empty label. Leaving...');
return;
}
else if (currentLabel.length>100)
currentLabel=currentLabel.substr(0,100);
debug("currentLabel="+currentLabel);
var add_lbl_url=baseUrl + '?ui=2&view=up&rt=h';
var postData='ik='+ik+'&act=cc_'+encodeURIComponent(currentLabel)+'&at='+at;
GM_xmlhttpRequest({
method:'POST',
url: add_lbl_url,
headers:{
'Referer': baseURI,
'Content-type' : 'application/x-www-form-urlencoded'
},
data : postData,
onload: function(responseDetails) {
debug(responseDetails.responseText);
var ret=getReturnValue( responseDetails.responseText, 'D([\"a\",');
if (ret!=null) debug('< D(["a" > returns : '+ret.toString()+" ret.length = "+ret.length);
if(ret && ret[1]) {
floatingMsg = ret[1];
currentFilterData.applyLabel=true;
}
else
floatingMsg = getWord('failtoretrieveresult');
isDataReady=false;
asynGetLabelFilterData(drawFilterAsstWorkspace, true);
debug('[addLabel]: Leaving...');
},
onerror: function(responseDetails){
GM_log("ERROR: "+responseDetailsText);
debug('[addLabel]: Leaving...');
}
});
}
//==========================================
// Save user change to the workspace
//==========================================
function getUserChanges() {
f=new Filter();
f.id = '';
f.name = '';
f.from = getNode('txtBoxFrom').value;
f.to=getNode('txtBoxTo').value;
f.subject=getNode('txtBoxSubject').value;
f.hasWord=getNode('txtBoxHasWord').value;
f.doesntHave=getNode ('txtBoxDoesntHave').value;
f.hasAttachment=getNode('chkBoxHasAttachment').checked;
f.skipInbox=getNode('chkBoxSkipInbox').checked;
f.markAsRead = getNode('chkBoxMarkAsRead').checked;
f.starIt=getNode('chkBoxStarIt').checked;
f.applyLabel=getNode('chkBoxApplyLabel').checked;
f.labelToApply=getNode('cbBoxLabels').value;
f.forward=getNode('chkBoxForwardTo').checked;
f.forwardTo=getNode('txtBoxForwardTo').value;
f.deleteIt=getNode('chkBoxDeleteIt').checked;
f.applyOnExistingConvs=getNode('chkBoxApplyToExistingConvs').checked;
return f;
}
//==========================================
// Restore the user change to the workspace
//==========================================
function restoreUserChanges(f) {
try {
getNode('txtBoxFrom').value=f.from;
getNode('txtBoxTo').value= f.to;
getNode('txtBoxSubject').value=f.subject;
getNode('txtBoxHasWord').value=f.hasWord;
getNode('txtBoxDoesntHave').value=f.doesntHave;
getNode('chkBoxHasAttachment').checked=f.hasAttachment;
getNode('chkBoxSkipInbox').checked=f.skipInbox;
getNode('chkBoxMarkAsRead').checked=f.markAsRead;
getNode('chkBoxStarIt').checked=f.starIt;
getNode('chkBoxApplyLabel').checked= f.applyLabel;
getNode('cbBoxLabels').value=f.labelToApply;
getNode('chkBoxForwardTo').checked=f.forward;
getNode('txtBoxForwardTo').value=f.forwardTo;
getNode('chkBoxDeleteIt').checked= f.deleteIt;
getNode('chkBoxApplyToExistingConvs').checked=f.applyOnExistingConvs;
}catch(e) {
GM_log('Error occured: Setting current filter data failed. \n\nDetailed error msg:\n\n['+e.name+']:'+e.message);
}
}
function hasFilter(fd) {
if (filterData!=null) {
//check existing filters to see if the rule has already been there
var len=filterData.length;
for (var i=0;i<len;i++) {
if (fd.from !=filterData[i][2][0] ||
fd.to !=filterData[i][2][1] ||
fd.subject !=filterData[i][2][2] ||
fd.hasWord !=filterData[i][2][3] ||
fd.doesntHave !=filterData[i][2][4] ||
fd.hasAttachment.toString()!=filterData[i][2][5]) continue;
return i;
}
}
return -1;
}
//=================================
// Create new filter event handler
//=================================
function handleCreateFilter() {
debug('[handleCreateFilter]: Entering...');
/*
filterData Format:
+---0 ID
1 Name
2 Data
|
+---0 From
1 To
2 Subject
3 Has word
4 Doesn't have
5 Has attachment (T/F)
6 Skip Inbox (T/F)
7 Mark as read (T/F)
8 Star it (T/F)
9 Apply label (T/F)
10 Label to apply (the label name)
11 Forward it (T/F)
12 Forward to (email)
13 Delete it (T/F)
*/
var fd;
try {
fd=getUserChanges();
}catch(e) {
GM_log('Error occured: Getting necessary values failed. \n\nDetailed error msg:\n\n['+e.name+']:'+e.message);
return;
}
if (fd.from == "" && fd.to == "" && fd.subject == "" && fd.hasWord == "" && fd.doesntHave == "") {
alert(getWord('emptycriteriawarning'));
debug('[handleCreateFilter]: Creating filter canceled. Leaving...');
return;
}
if (!( fd.skipInbox||fd.markAsRead || fd.starIt||fd.applyLabel||fd.forward||fd.deleteIt)) {
// no action
alert(getWord('selectatleastoneaction'));
debug('[handleCreateFilter]: No action selected. Leaving...');
return;
}
if (fd.applyLabel && (fd.labelToApply=="" || fd.labelToApply==getWord('newlabel'))) {
alert(getWord('selectalabelorcreateone'));
debug('[handleCreateFilter]: No label selected. Leaving...');
return;
}
if (fd.deleteIt) {
if (fd.starIt || fd.applyLabel) {
if (!confirm(getWord('itemintrashwarning'))) {
debug('[handleCreateFilter]: Creating filter canceled. Leaving...');
return;
}
}
}
var i=hasFilter(fd);
if (i>=0) {
// found a filter having the same criteria
if (!confirm(getWord('foundduplicatefilterwarning'))) {
debug('[handleCreateFilter]: Overwriting filter canceled. Leaving...');
return;
}
fd.id = filterData[i][0]; // store the ID
fd.name = filterData[i][1]; // store the name
// TODO(kfk):
// 1. Combine the two filters with same criteria (edit/overwrite);
// 2. group/consolidate filters that target to the same label.
}
addFilter(fd, true);
}
//==================
// Add Filter
//==================
function addFilter(fd, refresh) {
// Generate the URL
var add_flt_url = baseUrl + '?ui=2'
+ '&view=up'
+ '&rt=h'
+ '&ik=' + ik
+ '&at='+ at
+ (fd.id ? '&act=rf&ofid=' + fd.id : '&act=cf') // action = rf - edit filter, cf - create filter
+ '&zx=' + UniqueURL();
var postData = 'search=cf'
+ '&cf1_from=' + encodeURIComponent( fd.from)
+ '&cf1_to=' + encodeURIComponent(fd.to)
+ '&cf1_subj=' + encodeURIComponent(fd.subject)
+ '&cf1_has=' + encodeURIComponent( fd.hasWord)
+ '&cf1_hasnot=' + encodeURIComponent(fd.doesntHave)
+ '&cf1_attach=' + encodeURIComponent(fd.hasAttachment)
+ '&cf2_ar=' + encodeURIComponent( fd.skipInbox)
+ '&cf2_st=' + encodeURIComponent(fd.starIt)
+ '&cf2_mr=' + encodeURIComponent(fd.markAsRead)
+ '&cf2_cat=' + encodeURIComponent(fd.applyLabel)
+ '&cf2_sel=' + encodeURIComponent(fd.labelToApply )
+ '&cf2_emc=' + encodeURIComponent(fd.forward)
+ '&cf2_email=' + encodeURIComponent(fd.forwardTo)
+ '&cf2_tr=' + encodeURIComponent(fd.deleteIt)
+ '&irf=' + encodeURIComponent( fd.applyOnExistingConvs); //whether to apply the filter to all existing mails
var node=getNode('lblCreateFilter');
if (refresh) {
node.innerHTML = '<b>'+getWord('running')+'</b>';
node.removeEventListener('click', handleCreateFilter, false);
}
GM_xmlhttpRequest({
method:'POST',
url: add_flt_url,
headers:{
'Referer': baseURI,
'Content-type' : 'application/x-www-form-urlencoded'
},
data : postData,
onload: function(responseDetails) {
debug( responseDetails.responseText);
if (refresh) {
var ret=getReturnValue(responseDetails.responseText, 'D([\"a\",');
if (ret!=null) {
debug('< D(["a" > returns : '+ret.toString());
}
if(ret && ret[1])
floatingMsg = ret[1];
else
floatingMsg = getWord('failtoretrieveresult');
isDataReady=false;
asynGetLabelFilterData(drawFilterAsstWorkspace, false);
node.innerHTML = '<b>'+getWord('createfilter')+'</b>';
}
debug('[addFilter]: Leaving...');
},
onerror: function(responseDetails){
GM_log("ERROR: \nstatus="+responseDetails.status+"\nHeaders="+responseDetails.responseHeaders+"\nFeed data="+ responseDetails.responseText);
debug('[addFilter]: Leaving...');
}
});
}
//======================================
// Get data item value given the header
//======================================
function getReturnValue(data, header) {
//debug('[getReturnValue]: Entering...');
data = data.replace(/[\r\n\t\v\f\b\0]/g,""); // remove some escaped characters which doesn't affect the result
data = data.replace(/\\\\/g, "\\"); // replace \\ with \
// header is the data header like < D(["ar" >
var param;
var ret=null;
param=new ParamParsingData();
param.currentPosition=0;
var s=getRawData(data, header);
debug('[getReturnValue]: getRawData() returns <'+s+'>');
if (s!='')
ret = parseData(s, 0, s.length-1, param); // parse the block data, data is the data string,
else
ret = null;
//debug('[getReturnValue]: Leaving...');
return ret;
}
function getRawData(data, header) {
// header is the data header like < ["ar",[ >
var sp = data.indexOf(header), ep;
if (sp!=-1) {
// the labels found
sp = sp + header.length; // the starting point of this data block
var n = 1, i = sp;
var len = data.length;
while (i<=len && n!=0) {
if (data[i]=='[') n++;
if (data[i]==']') n--;
i++;
}
// Something is wrong, '[' and ']' doesn't match.
if (n!=0) return null;
ep = i-1; // the ending point of this data block
return data.substring(sp, ep);
}
return '';
}
//=================================
// Parse raw data read from google
//=================================
function parseData(data, sp, ep, param) {
var i = sp;
var quoted=false;
var s="";
var arr = new Array();
while (i<=ep) {
switch (data[i]) {
case '[': // found an inner block
if (!quoted) {
arr.push(parseData(data, i+1, ep, param));
i = param.currentPosition;
}
else
s=s+data[i];
break;
case '"': // find an element
if (!quoted) {
// left quote in a pair is found
quoted=true;
s="";
}
else {
// right quote in a pair is found
arr.push(myUnescape(s)); // push the current element into array
s="";
quoted = false;
}
break;
case '\\': // take care of the escaped character
switch (data[i+1]) {
// check the next character
case '"': // \"
case '\'':
case '\<':
case '\>':
s=s+data[i+1];
i++;
break;
default: // error, I don't do anything here, assuming Google doesn't make mistake in the data :)
s=s+data[i];
break;
}
break;
case ',': // delimiter
if (!quoted) {
// not quoted
if (data[i-1]!='"' && data[i-1]!=']') arr.push(s);
s = ""; // clear the current element
} else
s = s + ',';
break;
case ']': // finish processing current array
if (!quoted) {
if (s!="") arr.push(myUnescape(s));
s = "";
param.currentPosition = i;
return arr; // I assume the brackets are well paired
}
else
s = s + ']';
break;
default:
s = s + data[i];
}
param.currentPosition = ++i;
}
return arr;
}
var charset='abcdefghijklmnopqrstuvwxyz0123456789';
//====================
// Make a unique URL
//====================
function UniqueURL() {
var s='';
for (i=0;i<12;i++) {
s=s+charset[Math.floor(Math.random()*36)];
}
return s;
}
//====================
// show debug message
//====================
function debug(msg) {
if (DEBUG) GM_log(msg);
}
//============================
// trim space around a string
//============================
function trim(s) {
var i, len=s.length, sp=0, ep=len-1;
for (i=0;i<len;i++)
if (s[i]==' ') sp++;
else break;
for (i=len-1;i>=sp;i--)
if (s[i]==' ') ep--;
else break;
return s.subString(sp, ep+1);
}
// Following function credited to RoBorg, http://javascript.geniusbug.com/
function clone(obj) {
if(typeof(obj) != 'object') return obj;
if(obj == null) return obj;
var newobj = new Object();
for(var i in obj) newobj[i] = clone(obj[i]);
return newobj;
}
// Following functions credited to Mihai @ http://persistent.info/
function newNode(type) {
return unsafeWindow.document.createElement(type);
}
function getNode(id) {
return unsafeWindow.document.getElementById(id);
}
function getElementsByTag(tag) {
return unsafeWindow.document.getElementsByTagName(tag);
}
function getDataItem(data, header, endchar) {
var re = new RegExp(header + "([^"+endchar+"]+)");
var value = re.exec(data);
return (value != null) ? myUnescape(value[1]) : null;
}