//declare namespace
this.Lazarus = this.Lazarus || {}
Lazarus.STATE_UNINITALIZED = 0;
Lazarus.STATE_DISABLED = 1;
Lazarus.STATE_PASSWORD_REQUIRED = 2;
Lazarus.STATE_ENABLED = 3;
Lazarus.STATE_DISABLED_FOR_DOMAIN = 4;
Lazarus.STATE_PRIVATE_BROWSING = 5;
Lazarus.STATE_GENERATING_KEYS = 6;
Lazarus.LOGIN_HOSTNAME = 'chrome://lazarus';
Lazarus.LOGIN_REALM = 'Private Key Password';
Lazarus.LOGIN_USERNAME = 'lazarus-private-key';
Lazarus.IFRAME_NAME = '<content-editable-iframes>';
Lazarus.MIN_TEXT_NEEDED_TO_SHOW_NOTIFICATION = 1024; //characters
//flag to indicate if this browser is ready yet.
Lazarus.initalized = false;
//timers
Lazarus.cleanupSavedFormsTimer = 0;
Lazarus.autoSaveFormTimer = 0;
//pointer to last autosave form.
Lazarus.currAutoSaveForm = null;
//pointer to the last editor (txetbox or iframe) that a user put input into.
Lazarus.currentEditor = null;
//flag to say if context menu is currently being shown
Lazarus.isContextMenuShowing = false;
/* known input types
case "text":
case "textarea":
case "file":
case "radio":
case "checkbox":
case "select":
case "password":
case "hidden":
case "submit":
case "reset":
case "button":
case "image":
*/
//array of editor infos that the user has typed into this session
Lazarus.editorInfos = [];
/**
* window has loaded
*/
Lazarus.init = function(){
Lazarus.Global = Components.utils.import("resource://lazarus/global.js").Global;
Lazarus.Crypto = Components.utils.import("resource://lazarus/crypto.js").Crypto;
Lazarus.initalized = true;
Lazarus.initDevEnviroment();
//set loading icon
Lazarus.refreshIcon();
Lazarus.repositionNotification();
//Update UI elements
Lazarus.$("lazarus-statusbaricon-tooltip-title").setAttribute("value", Lazarus.getString("lazarus.statusbarpanel.image.tooltip", Lazarus.getVersionStr() +" ["+ Lazarus.build +"]"));
Lazarus.refreshMenuIcons();
Lazarus.Pref.addObserver('extensions.lazarus.showContextMenuIcons', Lazarus.refreshMenuIcons);
if (Lazarus.getPref('extensions.lazarus.showDonateNotification')){
Lazarus.checkForDonateThanks();
}
//check crypto component (breaks a lot with various Linux builds)
if (Lazarus.checkCrypto()){
//open the database
if (Lazarus.initDB()){
if (Lazarus.initEncryptionKeys()){
if (Lazarus.loadPublicKey()){
Lazarus.enable();
}
Lazarus.refreshIcon();
Lazarus.saveAutoSaveText();
//remove expired forms
Lazarus.cleanupSavedForms();
//hmmm, interesting. Cleaning the database makes little difference to the database size (33MB -> 32MB),
// unless a lot of deletions have been made
//
if (Lazarus.getPref('extensions.lazarus.cleanDatabaseAtStartup')){
Lazarus.cleanDB();
}
}
//else we are generating new encryption keys, do nothing
}
else {
//errors *should* have been thrown in the error console.
//inform the user something went wrong :(
Lazarus.error("Failed to load database");
Lazarus.disable();
}
}
else {
Lazarus.error("Failed to load crypto component");
Lazarus.disable();
}
Lazarus.refreshIcon();
}
/**
* turn off the donate notifications if this user has donated.
*/
Lazarus.checkForDonateThanks = function(){
var onload = function(uri){
if (uri && uri.spec && uri.spec.indexOf('//lazarus.interclue.com/donate-thanks.html') > -1){
Lazarus.setPref('extensions.lazarus.showDonateNotification', false);
Lazarus.Event.remove("location-change", onload);
}
}
Lazarus.Event.add("location-change", onload);
}
/**
* check to make sure the crypto component has loaded correctly
*/
Lazarus.checkCrypto = function(){
try {
var crypto = Components.classes["@labs.mozilla.com/Weave/Crypto;1"].createInstance(Components.interfaces.IWeaveCrypto);
return true;
}
catch(e){
return false;
}
}
/**
* reposition the notification bar so that it's always the last element
*/
Lazarus.repositionNotification = function(){
//#124: Zotero compatiilty problem
var notif = document.getElementById('lazarus-notification');
notif.parentNode.appendChild(notif);
}
/**
* enables Lazarus for this browser
*/
Lazarus.enable = function(){
Lazarus.saveAutoSavedForms();
//add preference observers
Lazarus.Pref.addObserver("extensions.lazarus.expireSavedForms", Lazarus.startCleanupTimer);
Lazarus.Pref.addObserver("extensions.lazarus.expireSavedFormsInterval", Lazarus.startCleanupTimer);
Lazarus.Pref.addObserver("extensions.lazarus.showInStatusbar", Lazarus.refreshIcon);
//we need to capture any onsubmit event from forms within a webpage
gBrowser.addEventListener("submit", Lazarus.onFormSubmit, false);
gBrowser.addEventListener("submit", Lazarus.saveLastSubmittedForm, false);
gBrowser.addEventListener("DOMContentLoaded", Lazarus.autofillEvent, true);
gBrowser.addEventListener("DOMContentLoaded", Lazarus.initRecoverForm, true);
gBrowser.addEventListener("reset", Lazarus.onFormReset, false);
gBrowser.addEventListener("change", Lazarus.onFormChange, false);
//we also need to save forms if people are typing into them.
gBrowser.addEventListener("keyup", Lazarus.onKeyUp, false);
//kjd: removing lazarus icon for now
//gBrowser.addEventListener("keydown", Lazarus.onKeyDown, false);
//clear the saved forms if user wants to when "clear private data" is hit.
Lazarus.$("Tools:Sanitize").addEventListener("command", Lazarus.fireClearPrivateDataIfNoPrompt, false);
//add handlers to the context menu
Lazarus.$("contentAreaContextMenu").addEventListener("popupshowing", Lazarus.onContextMenuShowing, false);
Lazarus.$("contentAreaContextMenu").addEventListener("popuphidden", Lazarus.onContextMenuHide, false);
//need events when a user changes the current document
Lazarus.Event.add("location-change", Lazarus.onLocationChange);
Lazarus.Event.add("extension-uninstall", Lazarus.onUninstall);
Lazarus.Event.add("extension-uninstall-request", Lazarus.onUninstallRequest);
Lazarus.Event.add("application-startup", Lazarus.onStartUp);
Lazarus.Event.add("application-startup", Lazarus.removeOldForms);
Lazarus.Event.add("application-shutdown", Lazarus.fireClearPrivateDataOnShutdown);
Lazarus.Event.add("application-shutdown", Lazarus.onShutdown);
Lazarus.Event.add("clear-private-data", Lazarus.onClearPrivateData);
Lazarus.startCleanupTimer();
//show the statusbar image (if user wants it)
Lazarus.refreshIcon();
}
Lazarus.onKeyDown = function(evt){
var ele = evt.target;
//ignore if we have already attached an onblur handler to this element
if (!ele.lazarusIconAdded){
switch(Lazarus.getElementType(ele)){
case "text":
case "password":
case "textarea":
//are we saving this element?
var form = Lazarus.findFormFromElement(ele);
var editor = Lazarus.findEditorFromElement(ele);
if ((form && Lazarus.shouldSaveForm(form)) || (editor && Lazarus.shouldSaveEditorInfo(form))){
//highlight element
Lazarus.addBackgroundIcon(ele);
}
else {
//ignore furthur keypresses on this element?
}
default:
//ignore this element
}
}
}
Lazarus.addBackgroundIcon = function(ele){
if (!ele.lazarusIconAdded){
ele.lazarusIconAdded = true;
var doc = ele.ownerDocument;
if (!doc.lazarusIcon){
var div = doc.createElement('div');
div.style.width = "11px";
div.style.height = "14px";
div.style.position = "absolute";
div.style.background = "url("+ Lazarus.icon +") no-repeat center center";
div.title = Lazarus.getString('Lazarus.savingText');
doc.body.appendChild(div);
doc.lazarusIcon = div;
}
//now position the image over the end of the textbox
var rect = ele.getBoundingClientRect();
doc.lazarusIcon.style.top = rect.top +"px";
doc.lazarusIcon.style.left = (rect.right - parseInt(doc.lazarusIcon.style.width)) +"px";
doc.lazarusIcon.style.display = "block";
var onBlur = function(){
//hide the icon
doc.lazarusIcon.style.display = "none";
ele.lazarusIconAdded = false;
}
//when adding the icon, we should hide it on blur
ele.addEventListener("blur", onBlur, false);
}
}
/**
* restart the browser
*/
Lazarus.restart = function(){
var nsIAppStartup = Components.interfaces.nsIAppStartup;
Components.classes["@mozilla.org/toolkit/app-startup;1"].getService(nsIAppStartup).quit(nsIAppStartup.eRestart | nsIAppStartup.eAttemptQuit);
}
/**
* turns Lazarus off for this browser
*/
Lazarus.disable = function(){
//add preference observers
Lazarus.Pref.addObserver("extensions.lazarus.expireSavedForms", Lazarus.startCleanupTimer);
Lazarus.Pref.addObserver("extensions.lazarus.expireSavedFormsInterval", Lazarus.startCleanupTimer);
Lazarus.Pref.addObserver("extensions.lazarus.showInStatusbar", Lazarus.refreshIcon);
//we need to capture any onsubmit event from forms within a webpage
gBrowser.removeEventListener("submit", Lazarus.onFormSubmit, false);
gBrowser.removeEventListener("submit", Lazarus.saveLastSubmittedForm, false);
gBrowser.removeEventListener("DOMContentLoaded", Lazarus.autofillEvent, true);
gBrowser.removeEventListener("DOMContentLoaded", Lazarus.initRecoverForm, true);
gBrowser.removeEventListener("reset", Lazarus.onFormReset, false);
gBrowser.removeEventListener("change", Lazarus.onFormChange, false);
//we also need to save forms if people are typing into them.
gBrowser.removeEventListener("keyup", Lazarus.onKeyUp, false);
//need events when a user changes the current document
Lazarus.Event.remove("location-change", Lazarus.onLocationChange);
//clear the saved forms if user wants to when "clear private data" is hit.
Lazarus.$("Tools:Sanitize").removeEventListener("command", Lazarus.fireClearPrivateDataIfNoPrompt, false);
Lazarus.stopCleanupTimer();
//and close the database
Lazarus.db.close();
//update the statusbar image
Lazarus.refreshIcon();
}
/**
* return TRUE if we can encrypt a string (ie the Crypto component is working, and the public key exists)
*/
Lazarus.canEncrypt = function(){
return Lazarus.Crypto.publicKey ? true : false;
}
Lazarus.canDecrypt = function(){
return Lazarus.Crypto.privateKey ? true : false;
}
/**
* initalizes the lazarus recover-form page
*/
Lazarus.initRecoverForm = function(evt){
var doc = evt.originalTarget;
if (Lazarus.isDocRecoveryForm(doc)){
doc.title = Lazarus.getString("recoverform.title");
//Lazarus.$('heading', doc).innerHTML = Lazarus.getString("recoverform.title");
Lazarus.$('description', doc).innerHTML = Lazarus.getString("recoverform.description");
Lazarus.$('notes', doc).innerHTML= Lazarus.getString("recoverform.notes");
Lazarus.$('form-url-label', doc).innerHTML= Lazarus.getString("recoverform.form.url");
Lazarus.$('form-action-label', doc).innerHTML= Lazarus.getString("recoverform.form.action");
Lazarus.$('notes', doc).innerHTML= Lazarus.getString("recoverform.notes");
var m = doc.URL.match(/[\?&]id=(\d+)/)
var id = m ? parseInt(m[1]) : -1;
if (id > -1){
var row = Lazarus.db.getRow("SELECT * FROM forms WHERE id = ?1", id);
if (row){
if (Lazarus.canDecrypt()){
var formInfo = Lazarus.JSON.decode(Lazarus.decrypt(row["forminfo"]));
Lazarus.$('form-url', doc).innerHTML = formInfo.origURL ? Lazarus.generateLinkFromURL(formInfo.origURL) : '';
Lazarus.$('form-action', doc).innerHTML = formInfo.action ? Lazarus.generateLinkFromURL(formInfo.action) : '';
var form = Lazarus.buildForm(formInfo, doc);
form.setAttribute("lazarus-form-id", row["formid"]);
Lazarus.$('form-box', doc).appendChild(form);
Lazarus.restoreForm(form, formInfo);
}
else {
//need to explain to user why we cannot fill in the form
Lazarus.showNotificationBox("password-required");
}
}
else {
Lazarus.$('form-box', doc).innerHTML = Lazarus.getString("error.form.not.found");
Lazarus.$('form-box', doc).className = "warning";
}
}
else {
Lazarus.$('form-box', doc).innerHTML = Lazarus.getString("error.form.not.found");
Lazarus.$('form-box', doc).className = "warning";
}
}
}
/**
* return TRUE if the given document is the lazarus Recover Form page.
*/
Lazarus.isDocRecoveryForm = function(doc){
return (doc && doc.URL && doc.URL.indexOf("chrome://lazarus/content/recover-form.html") == 0);
}
/**
* generate an HTML link given a url.
* if url is too long, then truncate url to maxChars
*/
Lazarus.generateLinkFromURL = function(url, maxChars){
maxChars = maxChars || 50;
if (/^(file|http|https):/.test(url)){
var text = url;
if (text.length > maxChars){
text = text.substring(0, maxChars -3) +"...";
}
return '<a href="'+ Lazarus.htmlEncode(url) +'" title="'+ Lazarus.htmlEncode(url) +'">'+ Lazarus.htmlEncode(text) +'</a>';
}
else {
return Lazarus.htmlEncode(url);
}
}
/**
* encode a string for display in an html page
*/
Lazarus.htmlEncode = function(str){
str = str.replace(/&/g, "&");
str = str.replace(/</g, "<");
str = str.replace(/>/g, ">");
str = str.replace(/"/g, """);
return str;
}
/**
* builds an HTML form from a formInfo object
*/
Lazarus.buildForm = function(formInfo, doc){
var form = doc.createElement("form");
form.setAttribute("method", formInfo.method || "get");
form.setAttribute("enctype", formInfo.enctype || "");
//support for AJAX textareas
form.isTextarea = formInfo.isTextarea;
for(var name in formInfo.fields){
for (var i=0; i<formInfo.fields[name].length; i++){
var fieldInfo = formInfo.fields[name][i];
var ele = null;
var eleLabel = '';
switch(fieldInfo.type){
case "radio":
case "checkbox":
if (formInfo.version && formInfo.version >= 1 && fieldInfo.value && typeof fieldInfo.value.valueAttr != "undefined"){
ele = doc.createElement("input");
ele.setAttribute("type", fieldInfo.type);
ele.setAttribute("value", fieldInfo.value.valueAttr);
eleLabel = name +"["+ fieldInfo.value.valueAttr +"]";
}
break;
case "password":
case "hidden":
case "file":
case "text":
ele = doc.createElement("input");
ele.setAttribute("type", fieldInfo.type);
break;
case "textarea":
ele = doc.createElement("textarea");
break;
case "select":
if (formInfo.version && formInfo.version >= 1 && Lazarus.isArray(fieldInfo.value) && fieldInfo.value.length){
ele = doc.createElement("select");
for (var i=0; i<fieldInfo.value.length; i++){
var opt = doc.createElement("option");
opt.setAttribute("value", fieldInfo.value[i]);
opt.appendChild(doc.createTextNode(fieldInfo.value[i]));
ele.appendChild(opt);
}
if (fieldInfo.value.length > 1){
ele.setAttribute("size", fieldInfo.value.length);
ele.setAttribute("multiple", "true");
}
}
break;
//no saved
case "submit":
case "reset":
case "button":
case "image":
break;
case "iframe":
//ignore iframes for now?
break;
default:
Lazarus.error("Unknown element type ["+ fieldInfo.type +"]");
}
if (ele){
if (fieldInfo.type == "hidden"){
form.appendChild(ele);
}
else {
var box = doc.createElement("div");
box.setAttribute("class", "form-field-box");
var label = doc.createElement("label");
var text = doc.createTextNode(eleLabel || name);
label.appendChild(text);
box.appendChild(label);
ele.setAttribute("name", fieldInfo.name);
ele.setAttribute("class", "form-field "+ fieldInfo.type);
box.appendChild(ele);
form.appendChild(box);
}
}
}
}
return form;
}
/**
* save the last submitted form id for use in auto restore template
*/
Lazarus.saveLastSubmittedForm = function(evt){
var form = Lazarus.findFormFromElement(evt.target);
if (form){
Lazarus.lastSubmittedFormId = Lazarus.getFormId(form);
}
}
/**
* call autofill if this is a valid html document.
*/
Lazarus.autofillEvent = function(evt){
if (evt.originalTarget instanceof HTMLDocument){
Lazarus.autofillDoc(evt.originalTarget);
}
}
/**
* convert a string of HTML into human readable text
*/
Lazarus.htmlToText = function(html){
//replace headings (</h1>) and paragraph ends with 2 line breaks
var text = html.replace(/\s*<\/((h\d)|p)\s*>\s*/ig," \n\n");
//replace divs blocks with single line breaks
text = text.replace(/\s*<(\/div)\b[^>]*>\s*/ig,"\n");
//replace list items with line breaks and dots
text = text.replace(/\s*<(li)\b[^>]*>\s*/ig,"\n * ");
//replace line breaks
text = text.replace(/<(br)\b[^>]*>/ig,"\n");
//strip all other tags
text = text.replace(/<(\/|\w)[^>]*>/g,' ');
//convert html spaces into normal spaces
text = text.replace(/ /g, ' ');
//compress whitespace
text = text.replace(/[ \t\f\v]+/g, ' ');
//never have more than 2 line breaks in a row.
text = text.replace(/\n\s*?\n(\s*?\n)*/g, "\n\n");
//and finally trim.
text = text.replace(/^\s+/, '').replace(/\s+$/, '');
return text;
}
/**
* return TRUE if forms on the given document should be saved
*/
Lazarus.isValidDoc = function(doc){
return (doc && doc.URL && /^(file|http|https):/.test(doc.URL));
}
/**
* autofills forms found in the given document with there respective templates if they exist
*/
Lazarus.autofillDoc = function(doc){
if (Lazarus.isValidDoc(doc) && doc.forms && doc.forms.length){
var rsAutoFillTemplates = Lazarus.db.rs("SELECT id, formid FROM forms WHERE autofill = 1 AND savetype = "+ Lazarus.FORM_TYPE_TEMPLATE +" ORDER BY created DESC");
if (rsAutoFillTemplates.length > 0){
var templates = {};
//convert the template id's to a hash table
//TODO: there should only ever be one autofill template for a given form
//raise a warning if this is not the case
for (var i=0; i<rsAutoFillTemplates.length; i++){
templates[rsAutoFillTemplates[i]["formid"]] = rsAutoFillTemplates[i]["id"];
}
for (var i=0; i<doc.forms.length; i++){
var form = doc.forms[i];
var formId = Lazarus.getFormId(form);
//only autofill if the form is empty, and the form hasn't just been submitted.
if (formId != Lazarus.lastSubmittedFormId && templates[formId] && Lazarus.isFormEmpty(form)){
//if the user has a password and is not yet logged in, then we cannot autofill the form
if (Lazarus.canDecrypt()){
var encryptedFormInfo = Lazarus.db.getStr("SELECT forminfo FROM forms WHERE id = ?1", templates[formId]);
var formInfo = Lazarus.JSON.decode(Lazarus.decrypt(encryptedFormInfo));
Lazarus.restoreForm(form, formInfo);
}
else {
//need to explain to user why we cannot autofill the template.
Lazarus.showNotificationBox("password-required-autofill");
break;
}
}
}
}
}
//if this is the currently visible tab, then clear the lastSubmittedFormId
if (content.document && doc === content.document){
Lazarus.lastSubmittedFormId = null;
}
}
/**
* return TRUE if the given form contains no user entered text.
*/
Lazarus.isFormEmpty = function(form){
for (var i=0; i<form.elements.length; i++){
var ele = form.elements[i];
switch (Lazarus.getElementType(ele)){
case "text":
case "textarea":
case "file":
case "password":
var text = Lazarus.getElementValue(ele);
if (Lazarus.trim(text)){
return false;
}
//ignore other input types
default:
}
}
return true;
}
/**
* initalise any developer specific functionality
*/
Lazarus.initDevEnviroment = function(){
if (Lazarus.getExtPref("openErrorConsoleAtStartup", false)){
//open the javascript console
toJavaScriptConsole();
}
if (Lazarus.getExtPref("debugMode") >= 4){
Lazarus.$('lazarus-statusbar-menuitem-test').hidden = false;
}
}
/**
* handle text within the address bar changing
*/
Lazarus.onLocationChange = function(evt){
//only close the notification if current page was not the result of submitting a form.
if (!Lazarus.lastSubmittedFormId){
Lazarus.closeNotificationBox(true);
}
Lazarus.refreshIcon();
}
/**
* fire the "clear private data" event
*/
Lazarus.fireClearPrivateDataIfNoPrompt = function(){
if (!Lazarus.getPref("privacy.sanitize.promptOnSanitize", true)){
Lazarus.Event.fire("clear-private-data");
}
}
/**
* return the current state of lazarus
*/
Lazarus.getState = function(){
if (!Lazarus.initalized){
return Lazarus.STATE_UNINITALIZED;
}
else if (Lazarus.Crypto.generatingKeys || Lazarus.cleaningDatabase){
return Lazarus.STATE_GENERATING_KEYS;
}
else if (!Lazarus.canEncrypt()){
return Lazarus.STATE_DISABLED;
}
else if (Lazarus.isDisabledByPrivateBrowsing()){
return Lazarus.STATE_PRIVATE_BROWSING;
}
else if (Lazarus.isPageDisabled()){
return Lazarus.STATE_DISABLED_FOR_DOMAIN;
}
else if (!Lazarus.canDecrypt()){
return Lazarus.STATE_PASSWORD_REQUIRED;
}
//all good?
else {
return Lazarus.STATE_ENABLED;
}
}
/**
* fix for multiline xul:description elements
*/
Lazarus.setDescriptionText = function(ele, text){
for (var i=ele.childNodes.length-1; i>=0; i--){
ele.removeChild(ele.childNodes[i]);
}
ele.appendChild(ele.ownerDocument.createTextNode(text));
}
/**
* updates the statusbar icon
*/
Lazarus.refreshIcon = function(){
Lazarus.$("lazarus-statusbarpanel").hidden = !Lazarus.getExtPref("showInStatusbar");
var iconURL = "";
var tooltipId = "";
switch (Lazarus.getState()){
case Lazarus.STATE_ENABLED:
iconURL = "chrome://lazarus/skin/lazarus.png";
tooltipId = "lazarus-statusbaricon-tooltip-enabled"
break;
case Lazarus.STATE_GENERATING_KEYS:
iconURL = 'chrome://lazarus/skin/lazarus-loading.gif';
tooltipId = 'lazarus-statusbaricon-tooltip-generatingkeys';
break;
case Lazarus.STATE_PASSWORD_REQUIRED:
iconURL = "chrome://lazarus/skin/lazarus-login.png";
tooltipId = "lazarus-statusbaricon-tooltip-passwordrequired";
break;
case Lazarus.STATE_DISABLED_FOR_DOMAIN:
iconURL = "chrome://lazarus/skin/lazarus-disabled.png";
tooltipId = "lazarus-statusbaricon-tooltip-disabledfordomain";
break;
case Lazarus.STATE_PRIVATE_BROWSING:
iconURL = "chrome://lazarus/skin/lazarus-disabled.png";
tooltipId = "lazarus-statusbaricon-tooltip-private-browsing";
break;
case Lazarus.STATE_UNINITALIZED:
case Lazarus.STATE_DISABLED:
default:
iconURL = "chrome://lazarus/skin/lazarus-disabled.png";
tooltipId = "lazarus-statusbaricon-tooltip-disabled";
}
//this appears to screw over firefox, missing images and such, if called during startup?
//NOTE: firfox must be completely closed for this effect, using "restart" will NOT recreate it.
//Lazarus.setDescriptionText(Lazarus.$("lazarus-statusbaricon-tooltip-description"), tooltip);
Lazarus.$("lazarus-statusbarpanel-image").setAttribute("src", iconURL);
Lazarus.$("lazarus-statusbarpanel-image").setAttribute("tooltip", tooltipId);
}
/**
* displays the lazarus welcome message
*/
Lazarus.showWelcome = function(){
//dont show the welcome message immediately, need to wait a sec so the
//browser is open, and we can center the dialog relative to it.
//~ setTimeout(function(){
//~ Lazarus.openOptionsDialog("welcome-pane");
//~ }, 100);
//hmmm, session recovery is happening after this event,
//which is replacing our tab with the recovered sessions tabs
setTimeout(function(){
Lazarus.openLazarusWebsite("oninstall.html?ver="+ Lazarus.getVersionStr());
}, 3000);
}
/**
* opens the lazarus onupdate page for this version of lazarus
*/
Lazarus.showUpdatePage = function(){
//hmmm, session recovery is happening after this event,
//which is replacing our tab with the recovered sessions tabs
setTimeout(function(){
Lazarus.openLazarusWebsite("onupdate.html?ver="+ Lazarus.getVersionStr());
}, 3000);
}
/**
* handle when the first browser window is opened.
*/
Lazarus.onStartUp = function(){
}
/**
* onUninstall
*/
Lazarus.onUninstall = function(ext){
if (ext.id == Lazarus.guid){
//var msg = '';
if (Lazarus.getExtPref("uninstall.removeSavedForms", false)){
//remove database
//unable to disconnect the database, so the best we can do is empty it.
Lazarus.emptyDB();
}
if (Lazarus.getExtPref("uninstall.removeUserSettings", false)){
//cleanup prefs
Lazarus.killPref("extensions.lazarus");
Lazarus.Pref.savePrefFile();
}
}
}
/**
*
*/
Lazarus.onUninstallRequest = function(ext){
if (ext.id == Lazarus.guid){
//ask the user if we should remove their preferences/saved forms
//we need this dialog to appear from the addons dialog (if it exists)
//~ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
//~ var win = wm.getMostRecentWindow("Extension:Manager");
//~ //if theres no extension manager window (eg all-in-one-sidebar)
//~ //use this window to open the dialog
//~ if (!win){
//~ win = window;
//~ }
//disabling dialog, uninstall process is stuffed up
//win.openDialog("chrome://lazarus/content/uninstall.xul", "LazarusUninstallOptions", "chrome,dialog,modal,resizable=yes,titlebar=yes");
}
}
/**
* return TRUE if the given editor still exists in the browser
*/
Lazarus.editorExists = function(info){
try {
//KLUDGE:
//if a page is refreshed then the page remains, even if you then navigate away from it.
//it appears to still exist, even though the user cannot see it.
if (Lazarus.isIframe(info.editor) && (!info.editor.contentWindow || !info.editor.contentWindow.document)){
return false;
}
else {
return (info.editor.ownerDocument.defaultView && (info.url == info.editor.ownerDocument.defaultView.top.location.href));
}
}
catch(e){
return false;
}
}
Lazarus.saveEditorInfo = function(info, saveType){
if (Lazarus.shouldSaveEditorInfo(info)){
var textHash = Lazarus.md5(info.text);
if (saveType == Lazarus.FORM_TYPE_AUTOSAVE){
var record = Lazarus.db.getRow("SELECT id, text_hash FROM textdata WHERE domain_hash = ?1 AND savetype = ?2 LIMIT 1", info.domainHash, Lazarus.FORM_TYPE_AUTOSAVE);
//if form hasn't changed
if (record && record.text_hash == textHash){
//do nothing...
}
//if it has changed, or doesn't exist yet, save the changes
else {
Lazarus.debug("Saving textdata - autosave", info);
if (record){
Lazarus.db.exe("DELETE FROM textdata WHERE id = ?1", record.id);
//and get rid of any fulltext index as well
Lazarus.db.exe("DELETE FROM textdata_fulltext WHERE docid = ?1", record.id);
}
var encText = Lazarus.encrypt(info.text);
var encSummary = Lazarus.encrypt(info.summary);
var id = Lazarus.db.insert("INSERT INTO textdata (text_encrypted, summary_encrypted, created, domain_hash, url_encrypted, text_hash, text_length, savetype) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
encText, encSummary, info.created, info.domainHash, info.urlEncrypted, textHash, info.text.length, Lazarus.FORM_TYPE_AUTOSAVE);
//and update the full text index,
if (!Lazarus.getPref('extensions.lazarus.disableSearch')){
//we're going to add domain info into this as well
var hashedText = Lazarus.hashText(info.text);
hashedText += ' '+ Lazarus.hashText(info.domain.replace(/\./g, ' '));
Lazarus.db.exe("INSERT INTO textdata_fulltext (docid, hashed_text) VALUES (?1, ?2)", id, hashedText);
}
}
}
else {
//if the info object already exists, with exactly the same text, then just update the timestamp
var id = Lazarus.db.getInt("SELECT id FROM textdata WHERE domain_hash = ?1 AND text_hash = ?2 LIMIT 1", info.domainHash, textHash);
if (id){
Lazarus.debug("Updating textdata - perm", info);
Lazarus.db.exe("UPDATE textdata SET created = ?1, savetype = ?2 WHERE id = ?3", info.created, Lazarus.FORM_TYPE_NORMAL, id);
}
//otherwise, insert a new info object
else {
Lazarus.debug("Saving textdata - perm", info);
var encText = Lazarus.encrypt(info.text);
var encSummary = Lazarus.encrypt(info.summary);
var id = Lazarus.db.insert("INSERT INTO textdata (text_encrypted, summary_encrypted, created, domain_hash, url_encrypted, text_hash, text_length, savetype) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
encText, encSummary, info.created, info.domainHash, info.urlEncrypted, textHash, info.text.length, Lazarus.FORM_TYPE_NORMAL);
if (!Lazarus.getPref('extensions.lazarus.disableSearch')){
var hashedText = Lazarus.hashText(info.text);
hashedText += ' '+ Lazarus.hashText(info.domain.replace(/\./g, ' '));
Lazarus.db.exe("INSERT INTO textdata_fulltext (docid, hashed_text) VALUES (?1, ?2)", id, hashedText);
}
}
}
}
}
/**
* genereate a random seed for use in the hashing function
*/
Lazarus.generateRandomHashSeed = function(){
var rnd = Math.random().toString() +':'+ Lazarus.timestamp(true).toString();
return Lazarus.FNV1a(rnd);
}
/**
* hashes individual words in a bunch of text
*/
Lazarus.hashText = function(text){
//we'll need to grab the seed from the database.
var seed = Lazarus.db.getStr("SELECT value FROM settings WHERE name = 'hash-seed'");
if (!seed){
seed = Lazarus.generateRandomHashSeed();
Lazarus.db.exe("INSERT INTO settings (name, value) VALUES ('hash-seed', ?1)", seed);
//and delete the full text index, because none of the others will work any more
Lazarus.db.exe("DELETE FROM textdata_fulltext");
}
//var p = new Profiler("hashedText: "+ text.length);
var map = {};
//p.mark("setup");
//remove all non-text characters (strip HTML?)
text = Lazarus.trim(text.toLowerCase().replace(/[^\s\w\-_]+/g, ' '));
//p.mark("remove non-text: "+ text.length);
var words = text.split(/\s+/g);
//p.mark("split into words : "+ words.length);
//we should also remove useless words (like "the", "and", "as" etc...)
var len = words.length;
var hashedWords = [];
for (var i=0; i<len; i++){
var word = words[i];
if (!map[word]){
map[word] = Lazarus.FNV1a(word, seed);
}
hashedWords.push(map[word]);
}
//p.mark("hash words");
//hashed text comes out at 70,000 characters for a 56,000 character start.
//acceptable.
var hashedText = hashedWords.join(" ");
//debug(p.stop("join text: "+ hashedText.length));
return hashedText;
}
/**
* hash text within a MATCH query whilst keeping SQLite MATCH keywords/characters
*/
Lazarus.hashQuery = function(query){
//phrase searches ('"broccoli cheese"')
//Excluding terms ('onions -celery')
//OR queries ('onions OR cheese')
//Prefix search ('ch*') cannot be used due to hashing of the text strings.
return query.replace(/\w+/g, function(m){
if (m.toLowerCase() == "or"){
return "OR";
}
else {
return Lazarus.hashText(m);
}
});
}
Lazarus.saveAutoSaveText = function(){
Lazarus.db.exe("UPDATE textdata SET savetype = ?1 WHERE savetype = ?2", Lazarus.FORM_TYPE_NORMAL, Lazarus.FORM_TYPE_AUTOSAVE);
}
Lazarus.autoSaveEditors = function(){
//NOTE: traversing backwards through the list so we can remove items without it affecting
//the rest of the list
var removed = [];
for (var i=Lazarus.editorInfos.length-1; i>=0; i--){
var info = Lazarus.editorInfos[i];
//the page may have been submitted, or closed without submitting
//if the editor is suddenly empty, we should assume it has been submitted (possibly via AJAX)
if (!Lazarus.editorExists(info) || Lazarus.getEditorInfo(info.editor).isEmpty){
removed.push(info);
Lazarus.editorInfos.splice(i, 1);
}
}
//and save any that have been removed
for (var i=0; i<removed.length; i++){
Lazarus.saveEditorInfo(removed[i]);
}
//we also want to update the current textbox (if any)
//all others should be saved as "temporary saves"
for (var i=0; i<Lazarus.editorInfos.length; i++){
if (Lazarus.editorInfos[i].editor === Lazarus.currentEditor){
Lazarus.saveEditorInfo(Lazarus.editorInfos[i], Lazarus.FORM_TYPE_AUTOSAVE);
break;
}
}
if (Lazarus.editorInfos.length == 0){
Lazarus.stopEditorAutoSaveTimer();
}
}
/**
* return the previous editosInfo
*/
Lazarus.getPreviousEditorInfo = function(editor){
for (var i=0; i<Lazarus.editorInfos.length; i++){
if (Lazarus.editorInfos[i].editor === editor){
return Lazarus.editorInfos[i];
}
}
return null;
}
/**
* remove an editor info object from our list
*/
Lazarus.removeEditorInfo = function(info){
for (var i=0; i<Lazarus.editorInfos.length; i++){
if (Lazarus.editorInfos[i].editor === info.editor){
Lazarus.editorInfos.splice(i, 1);
return true;
}
}
return false;
}
/**
* update editor info
*/
Lazarus.updateEditorInfo = function(info){
for (var i=0; i<Lazarus.editorInfos.length; i++){
if (Lazarus.editorInfos[i].editor === info.editor){
Lazarus.editorInfos[i] = info;
return true;
}
}
return false;
}
/**
* save forms if people are typing into them
*/
Lazarus.onKeyUp = function(evt){
//dont save form for every keypress (CPU hog)
//we'll restart a timer whenever a key is pressed,
//and save when the timer fires.
var form = Lazarus.findFormFromElement(evt.target);
if (Lazarus.isPageDisabled()){
return;
}
//if the event happened within a form, set a timer to auto save the form
if (form && form.ownerDocument instanceof HTMLDocument){
Lazarus.restartAutoSaveTimer(form);
}
//we'll do the same for textarea's and contentEditable iframes
var editor = Lazarus.findEditorFromElement(evt.target);
if (editor){
Lazarus.currentEditor = editor;
var info = Lazarus.getEditorInfo(editor);
var prevInfo = Lazarus.getPreviousEditorInfo(editor);
if (prevInfo){
//if the textbox is suddenly empty, the form may have been submitted or reset (or all contents removed (ctrl+a + del))
if (info.isEmpty){
Lazarus.saveEditorInfo(prevInfo);
//then remove the editorInfo from the list
Lazarus.removeEditorInfo(prevInfo);
}
//otherwise update the previous editorInfo, and wait for the timer to save it.
else {
Lazarus.updateEditorInfo(info);
}
}
//this is the first time the user has typed into this textbox
//save it, if it's got some text in it
else if (!info.isEmpty){
Lazarus.editorInfos.push(info);
Lazarus.restartEditorAutoSaveTimer();
}
else {
//editor is empty, dont keep it
}
Lazarus.restartEditorAutoSaveTimer();
}
}
/**
* return an editor info object
*/
Lazarus.getEditorInfo = function(editor){
var info = {}
info.editor = editor;
info.text = Lazarus.extractText(editor);
info.isEmpty = Lazarus.isEditorEmpty(editor);
info.summary = Lazarus.generateSummary(info.text);
info.created = Lazarus.timestamp();
info.domain = Lazarus.getDomainFromElement(editor);
info.domainHash = Lazarus.md5(info.domain);
info.basedomain = Lazarus.getBaseDomain(info.domain);
info.url = editor.ownerDocument.defaultView.top.location.href;
info.urlEncrypted = Lazarus.encrypt(info.url);
return info;
}
/**
* return TRUE if the editor doesn't contain any text
*/
Lazarus.isEditorEmpty = function(editor){
var text = Lazarus.extractText(editor);
var type = editor.nodeName.toLowerCase();
if (type == "textarea"){
return text.match(/^\s*$/) ? true : false;
}
else if (type == "iframe"){
return text.replace(/(<(\/|\w)[^>]*>)|( )/g, '').match(/^\s*$/) ? true : false;
}
else {
throw Error("Unknown editor type: "+ type);
}
}
/**
* return the text/html from an editable iframe or textarea
*/
Lazarus.extractText = function(ele){
var text = '';
if (Lazarus.isTextarea(ele)){
text = (typeof ele.value == "string") ? ele.value : '';
}
else if (Lazarus.isIframe(ele)){
text = (typeof ele.contentWindow.document.body.innerHTML) ? ele.contentWindow.document.body.innerHTML : '';
}
return Lazarus.trim(text);
}
/**
* generate a safe summary for display within a XUL:menuitem or XUL:tooltip
*/
Lazarus.generateSummary = function(text){
//strip html
text = Lazarus.htmlToText(text);
return (text.length > 255) ? (text.substr(0, 252) +"...") : text;
}
Lazarus.restartEditorAutoSaveTimer = function(){
Lazarus.stopEditorAutoSaveTimer();
Lazarus.editorAutoSaveFormTimer = setInterval(Lazarus.autoSaveEditors, Lazarus.getExtPref("autoSaveInterval", 2000));
}
Lazarus.stopEditorAutoSaveTimer = function(){
if (Lazarus.editorAutoSaveFormTimer){
clearInterval(Lazarus.editorAutoSaveFormTimer);
}
}
/**
* start the autosave timer
*/
Lazarus.restartAutoSaveTimer = function(form){
Lazarus.stopAutoSaveTimer();
//we'll save the form here, so we can retrieve it when the timer fires
Lazarus.currAutoSaveForm = form;
Lazarus.autoSaveFormTimer = setTimeout(Lazarus.autoSaveForm, Lazarus.getExtPref("autoSaveInterval", 2000));
}
/**
* stops the autosave timer.
*/
Lazarus.stopAutoSaveTimer = function(){
if (Lazarus.autoSaveFormTimer){
clearTimeout(Lazarus.autoSaveFormTimer);
}
}
/**
* run cleanup for this window
*/
Lazarus.cleanup = function(){
gBrowser.removeEventListener("keyup", Lazarus.onKeyUp, false);
gBrowser.removeEventListener("submit", Lazarus.onFormSubmit, false);
gBrowser.removeEventListener("reset", Lazarus.onFormReset, false);
gBrowser.removeEventListener("change", Lazarus.onFormChange, false);
Lazarus.$("Tools:Sanitize").removeEventListener("command", Lazarus.fireCLearPrivateDataIfNoDialog, false);
Lazarus.stopCleanupTimer();
}
/**
* handle last browser window shutting down
*/
Lazarus.onShutdown = function(){
}
/**
*
*/
Lazarus.onContextMenuHide = function(){
Lazarus.isContextMenuShowing = false;
}
/**
* show or hide the menu item depending on what item caused the context menu to appear.
*/
Lazarus.onContextMenuShowing = function(evt){
//we need to set a flag to prevent new autosaves whilst the context menu is shown
Lazarus.isContextMenuShowing = true;
//this event is fired whenever a submenu is shown, as well as when the main context menu it shown
//bugfix: only re-calculate the popup menu when it's first opened.
//trying to alter the initial menu when a submenu is opened can cause the browser to hang.
//hmmm this is causing a noticable hang when the form contains a lot (30k) of info
//as a workaround, we will not calculate any submenu until the submenu is opened.
var evtTargetId = evt.target.id;
//quick check
if (evtTargetId != "contentAreaContextMenu" && evtTargetId != "lazarus-restoreform-submenu-menupopup" && evtTargetId != "lazarus-restoretext-submenu-menupopup"){
return;
}
//did the user click on a form?
var form = Lazarus.findFormFromElement(gContextMenu.target);
var editor = Lazarus.findEditorFromElement(gContextMenu.target);
if (evtTargetId == "contentAreaContextMenu"){
//assume we should not show any menuitems
var showMainMenu = false;
var showSubMenu = false;
var showLogin = false;
var showSaveForm = false;
var showPageDisabled = false;
var showPrivateBrowsing = false;
var showRestoreText = false;
var showRestoreTextDisabled = false;
if (!form && !editor){
//dont show anything
}
else if (Lazarus.isDisabledByPrivateBrowsing()){
showPrivateBrowsing = true;
}
else if (Lazarus.isPageDisabled(form.ownerDocument.URL)){
showPageDisabled = true;
}
else if (!Lazarus.canDecrypt()){
showLogin = true;
}
else {
if (form){
showSaveForm = true;
var savedForms = Lazarus.getFormInfo(form, "id, formid, created, savetype, forminfohash, formtext, formname");
//just show the one menu item, but dont show it if the form is identical to this one.
if (savedForms.length == 1){
showMainMenu = true;
var info = Lazarus.formInfo(form);
var infoHash = Lazarus.generateHash(info.fields);
if (infoHash == savedForms[0]["forminfohash"]){
Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('src', "chrome://lazarus/skin/lazarus-disable.png");
Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('disabled', "true");
Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('tooltiptext', Lazarus.getString("form.is.equal"));
}
else {
var savedForm = savedForms[0];
Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('src', "chrome://lazarus/skin/lazarus.png");
Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('lazarus-forms-id', savedForm["id"]);
Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('tooltiptext', Lazarus.generateSavedFormTooltip(savedForm));
Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('disabled', "");
}
}
//show submenu
else if (savedForms.length > 1){
showSubMenu = true;
}
}
if (editor){
showRestoreTextDisabled = true;
//do we have any text saved for this domain/basedomain?
var domain = Lazarus.getDomainFromElement(editor);
if (domain){
var domainHash = Lazarus.md5(domain);
if (Lazarus.db.getInt("SELECT count(id) FROM textdata WHERE domain_hash = ?1 LIMIT 1", domainHash)){
showRestoreText = true;
showRestoreTextDisabled = false;
}
}
}
}
//only show our menu item if over a form.
Lazarus.$('lazarus-restoretextdisabled-contextmenuitem').hidden = !showRestoreTextDisabled;
Lazarus.$('lazarus-restoretext-submenu').hidden = !showRestoreText;
Lazarus.$('lazarus-restoreform-contextmenuitem').hidden = !showMainMenu;
Lazarus.$('lazarus-restoreform-submenu').hidden = !showSubMenu;
Lazarus.$('lazarus-enterpassword-contextmenuitem').hidden = !showLogin;
Lazarus.$('lazarus-domaindisabled-contextmenuitem').hidden = !showPageDisabled;
Lazarus.$('lazarus-privatebrowsing-contextmenuitem').hidden = !showPrivateBrowsing;
Lazarus.$('lazarus-saveform-contextmenuitem').hidden = !(showSaveForm && Lazarus.getExtPref("includeExperimental", false));
}
else if (evtTargetId == "lazarus-restoreform-submenu-menupopup" && form){
Lazarus.buildSubMenu(form);
}
else if (evtTargetId == "lazarus-restoretext-submenu-menupopup" && editor){
Lazarus.buildRestoreTextSubMenu(editor);
}
}
Lazarus.getDomainFromElement = function(ele){
try {
return ele.ownerDocument.defaultView.top.location.host;
}
catch(e){
return null;
}
}
/**
* builds the list of items that can be restored for this editor element
*/
Lazarus.buildRestoreTextSubMenu = function(editor){
//
var menu = Lazarus.$("lazarus-restoretext-submenu-menupopup");
//remove all the current submenu items
while(menu.lastChild){
menu.removeChild(menu.lastChild);
}
var domainHash = Lazarus.md5(Lazarus.getDomainFromElement(editor));
//and build the new ones
var items = Lazarus.db.rs("SELECT id, text_hash, summary_encrypted, created FROM textdata WHERE domain_hash = ?1 ORDER BY created DESC LIMIT ?2", domainHash, Lazarus.getPref('extensions.lazarus.maxTextItemsInSubmenu', 20));
var text = Lazarus.extractText(editor);
var textHash = Lazarus.md5(text);
for (var i=0; i<items.length; i++){
var item = items[i];
var menuitem = document.createElement("menuitem");
if (Lazarus.getExtPref("showFormTextInSubMenu") && Lazarus.canDecrypt()){
var summary = Lazarus.decrypt(item["summary_encrypted"]);
menuitem.setAttribute("label", summary);
menuitem.setAttribute("tooltiptext", summary);
}
else {
var time = Lazarus.timestamp();
menuitem.setAttribute("label", Lazarus.getTimeString(time - item["created"]));
//show the tooltip if possible
if (Lazarus.canDecrypt()){
menuitem.setAttribute("tooltiptext", Lazarus.decrypt(item["summary_encrypted"]));
}
}
menuitem.setAttribute("lazarus-restoretext-id", item["id"]);
menuitem.setAttribute("oncommand", "Lazarus.onRestoreTextMenuItem(this)");
//disable the menu item if the form is identical
if (item["text_hash"] == textHash){
menuitem.setAttribute('tooltiptext', Lazarus.getString("text.is.equal"));
menuitem.setAttribute('disabled', "true");
}
menu.appendChild(menuitem);
}
}
/**
*
*/
Lazarus.getFormRestoredNotificationText = function(charsRestored, msgId){
//we're trying out a bunch of text at the moment
var numChars = Lazarus.formatNumber(charsRestored);
var numForms = Lazarus.formatNumber(Lazarus.db.getInt("SELECT COUNT(*) FROM forms"));
var numText = Lazarus.formatNumber(Lazarus.db.getInt("SELECT COUNT(*) FROM textdata"));
var msgs = [
'Lazarus has just saved you from having to retype '+ numChars +' characters. If you feel this has helped you, then please consider donating to this project so we can make Lazarus even better.',
'Lazarus restored '+ numChars +' characters.\nDatabase contains '+ numForms +' Forms and '+ numText +' Text-blocks.',
'Lazarus restored '+ numChars +' characters.\nDatabase contains '+ numForms +' Saved Forms and '+ numText +' Textareas.',
'Lazarus resurrected '+ numChars +' characters.\nLazarus is securely storing '+ numForms +' Forms and '+ numText +' Text-blocks'
];
return msgs[msgId];
}
/**
*
*/
Lazarus.onRestoreTextMenuItem = function(menuitem){
if (!Lazarus.canDecrypt()){
Lazarus.debug("Unable to restore form, password required");
Lazarus.showNotificationBox("password-required");
return;
}
var editor = Lazarus.findEditorFromElement(gContextMenu.target);
if (editor){
var id = parseInt(menuitem.getAttribute("lazarus-restoretext-id"));
Lazarus.debug("Attempting to restore text: "+ id);
var enc_text = Lazarus.db.getStr("SELECT text_encrypted FROM textdata WHERE id = ?1", id);
if (enc_text){
var text = Lazarus.decrypt(enc_text);
if (text){
Lazarus.setElementValue(editor, text);
Lazarus.incPref("extensions.lazarus.restoreFormCount");
if ((text.length > Lazarus.MIN_TEXT_NEEDED_TO_SHOW_NOTIFICATION) && Lazarus.getPref("extensions.lazarus.showDonateNotification") && Lazarus.getPref("extensions.lazarus.restoreFormCount", 0) > 3){
var msgId = Math.floor(Math.random() * 4);
Lazarus.showNotificationBox("form-restored", Lazarus.getFormRestoredNotificationText(text.length, msgId), "msgid-"+ msgId);
}
}
else {
alert(Lazarus.getString("error.form.db.corrupt"));
}
}
else {
Lazarus.error("Unable to find textdata: "+ id);
alert(Lazarus.getString("error.form.not.found"));
}
}
else {
//should never get here
alert(Lazarus.getString("error.form.object.not.found"));
}
}
/**
* generates an md5 hash of a javascript object
*/
Lazarus.generateHash = function(obj){
return Lazarus.md5(Lazarus.JSON.encode(obj));
}
/**
* return the current unix timestamp
*/
Lazarus.timestamp = function(asFloat){
var s = new Date().getTime() / 1000;
return asFloat ? s : Math.floor(s);
}
/**
* return text usable by a menuitem (XUL Label)
*/
Lazarus.getMenuItemText = function(text){
//strip any html found in the text
text = Lazarus.trim(Lazarus.cleanText(text));
//labels only handle a single line of text
text = text.split(/\n/, 2)[0];
//and we dont want it to be too long
if (text.length > 48){
text = text.substr(0, 48) +"...";
}
return Lazarus.trim(text);
}
/**
* ask the user for their Lazarus password
*/
Lazarus.showEnterPasswordDialog = function(){
var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
//keep asking until they hit cancel
while(true){
var password = {value: ""};
var check = {value: false};
//if a user has a master password set, then allow the lazarus password to be saved in the SSD
var checkText = Lazarus.isMasterPasswordSet() ? Lazarus.getString('password.dialog.checkbox.label') : null;
//
if (prompts.promptPassword(null, Lazarus.getString("password.dialog.title"), Lazarus.getString("password.dialog.label"), password, checkText, check)){
if (Lazarus.loadPrivateKey(password.value)){
if (check.value){
Lazarus.savePassword(password.value);
}
return true;
}
}
else {
break;
}
}
return false;
}
Lazarus.savePassword = function(password){
//remove the existing password first.
Lazarus.removePassword();
var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1", Components.interfaces.nsILoginInfo, "init");
var loginInfo = new nsLoginInfo(Lazarus.LOGIN_HOSTNAME, null, Lazarus.LOGIN_REALM, Lazarus.LOGIN_USERNAME, password, '', '');
var nsLoginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
nsLoginManager.addLogin(loginInfo);
}
Lazarus.removePassword = function(){
// Get Login Manager
var nsLoginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
// Find users for this extension
var logins = nsLoginManager.findLogins({}, Lazarus.LOGIN_HOSTNAME, null, Lazarus.LOGIN_REALM);
for (var i = 0; i < logins.length; i++) {
if (logins[i].username == Lazarus.LOGIN_USERNAME){
nsLoginManager.removeLogin(logins[i]);
}
}
}
/**
* load the password from the loginManager
*/
Lazarus.loadPassword = function(){
if (Lazarus.isMasterPasswordSet() && !Lazarus.isMasterPasswordRequired()){
// Get Login Manager
var nsLoginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
// Find users for the given parameters
var logins = nsLoginManager.findLogins({}, Lazarus.LOGIN_HOSTNAME, null, Lazarus.LOGIN_REALM);
// Find user from returned array of nsILoginInfo objects
for (var i = 0; i < logins.length; i++) {
if (logins[i].username == Lazarus.LOGIN_USERNAME){
return logins[i].password;
}
}
}
//we default to an empty password
return '';
}
Lazarus.logout = function(){
Lazarus.unloadPrivateKey();
Lazarus.refreshIcon();
}
/**
* generate a label for saved form menuitem
*/
Lazarus.generateSavedFormLabel = function(savedForm, time){
var label = '';
//template should ALWAYS use their template name as a label
if (savedForm["savetype"] == Lazarus.FORM_TYPE_TEMPLATE){
label = savedForm["formname"];
}
//use summary
else if (Lazarus.getExtPref("showFormTextInSubMenu") && Lazarus.canDecrypt()){
label = Lazarus.getMenuItemText(Lazarus.decrypt(savedForm["formtext"])) || ("["+ Lazarus.getString("untitled") +"]");
}
//use timestamps if we are unable to decrypt the forms
else {
time = time || Lazarus.timestamp();
label = Lazarus.getTimeString(time - savedForm["created"]);
}
//mark autosaved forms so users can easily tell the difference.
switch(savedForm["savetype"]){
case Lazarus.FORM_TYPE_AUTOSAVE:
case Lazarus.FORM_TYPE_STALE_AUTOSAVE:
label += " "+ Lazarus.getString("menuitem.label.append.autosave");
break;
case Lazarus.FORM_TYPE_TEMPLATE:
label += " "+ Lazarus.getString("menuitem.label.append.template");
break;
default:
label += " "+ Lazarus.getString("menuitem.label.append.normal");
break;
}
return label;
}
/**
* generate a tooltip for saved form menuitem
*/
Lazarus.generateSavedFormTooltip = function(savedForm, /**/time){
time = time || Lazarus.timestamp();
var tooltip = '';
//show time sting as tooltip
if (Lazarus.getExtPref("showFormTextInSubMenu")){
tooltip = Lazarus.getTimeString(time - savedForm["created"]);
}
//show date and time of save
else {
var date = new Date(savedForm["created"] * 1000);
tooltip = Lazarus.formatDate(date);
}
//mark autosaved forms so users can easily tell the difference.
switch(savedForm["savetype"]){
case Lazarus.FORM_TYPE_AUTOSAVE:
case Lazarus.FORM_TYPE_STALE_AUTOSAVE:
tooltip += " "+ Lazarus.getString("menuitem.tooltip.append.autosave");
break;
case Lazarus.FORM_TYPE_TEMPLATE:
tooltip += " "+ Lazarus.getString("menuitem.tooltip.append.template");
break;
default:
tooltip += " "+ Lazarus.getString("menuitem.tooltip.append.normal");
break;
//do nothing
}
return tooltip;
}
/**
* builds the list of available restore point into the submenu
*/
Lazarus.buildSubMenu = function(form){
var menu = Lazarus.$("lazarus-restoreform-submenu-menupopup");
//remove all the current submenu items
for (var i=menu.childNodes.length-1; i>=0; i--){
var node = menu.childNodes[i];
if (node.getAttribute("lazarus-dynamic-submenu")){
menu.removeChild(node);
}
}
var time = Lazarus.timestamp();
var separator = Lazarus.$("lazarus-submenu-separator");
//now build the new ones
var lastSaveType = -1;
var savedForms = Lazarus.getFormInfo(form, "id, formid, created, savetype, forminfohash, formtext, formname, forminfo");
var info = Lazarus.formInfo(form);
var infoHash = Lazarus.generateHash(info.fields);
for (var i=0; i<savedForms.length; i++){
var savedForm = savedForms[i];
var menuitem = document.createElement("menuitem");
//generate text for the label
//menuitems can only handle a single line of text, and we don't want it to be too long
var label = Lazarus.generateSavedFormLabel(savedForm, time);
var tooltip = Lazarus.generateSavedFormTooltip(savedForm, time);
menuitem.setAttribute("label", label);
//menuitem.setAttribute('tooltiptext', tooltip);
menuitem.setAttribute('oncommand', "Lazarus.onRestoreFormMenuItem(this)");
menuitem.setAttribute('lazarus-forms-id', savedForm["id"]);
menuitem.setAttribute('lazarus-dynamic-submenu', "true");
//disable the menu item if the form is identical
if (savedForm["forminfohash"] == infoHash){
menuitem.setAttribute('tooltiptext', Lazarus.getString("form.is.equal"));
menuitem.setAttribute('disabled', "true");
}
else {
menuitem.setAttribute('tooltiptext', tooltip);
menuitem.setAttribute('disabled', "");
}
//add separators between templates, normal saves, and autosaves
if (lastSaveType != -1 && lastSaveType !== savedForm["savetype"] && savedForm["savetype"] != Lazarus.FORM_TYPE_STALE_AUTOSAVE){
var newSeparator = document.createElement("menuseparator");
newSeparator.setAttribute('lazarus-dynamic-submenu', "true");
menu.insertBefore(newSeparator, separator);
}
lastSaveType = savedForm["savetype"];
menu.insertBefore(menuitem, separator);
}
}
/**
* return the period (in seconds) that forms should be saved for
*/
Lazarus.getExpiryTime = function(){
if (Lazarus.getExtPref("expireSavedForms")){
return (Lazarus.getExtPref("expireSavedFormsInterval") * Lazarus.getExtPref("expireSavedFormsUnit") * 60);
}
else {
return 0;
}
}
/**
* retrieves a list of details about a form from the database.
*/
Lazarus.getFormInfo = function(form, fields){
var formId = Lazarus.getFormId(form);
var expires = Lazarus.getExpiryTime();
var cutoffTime = (expires > 0) ? (Lazarus.timestamp() - expires) : 0;
fields = fields || "*";
//we need to get ALL the templates for this form first
var forms = [];
forms = forms.concat(Lazarus.db.rs("SELECT "+ fields +" FROM forms WHERE formid = ?1 AND created >= ?2 AND savetype = "+ Lazarus.FORM_TYPE_TEMPLATE +" ORDER BY savetype DESC, created DESC", formId, cutoffTime));
//and then the autosaves
var maxAutoSaves = Lazarus.getExtPref("maxAutosavesPerForm", 3);
forms = forms.concat(Lazarus.db.rs("SELECT "+ fields +" FROM forms WHERE formid = ?1 AND created >= ?2 AND savetype IN ("+ Lazarus.FORM_TYPE_AUTOSAVE +","+ Lazarus.FORM_TYPE_STALE_AUTOSAVE +") ORDER BY created DESC LIMIT ?3", formId, cutoffTime, maxAutoSaves));
//and then add normal saved forms
var maxForms = Lazarus.getExtPref("maxSavesPerForm", 10);
forms = forms.concat(Lazarus.db.rs("SELECT "+ fields +" FROM forms WHERE formid = ?1 AND created >= ?2 AND savetype = "+ Lazarus.FORM_TYPE_NORMAL +" ORDER BY created DESC LIMIT ?3", formId, cutoffTime, maxForms));
return forms;
}
/**
* return a fieldInfo object filled with information about the given field
*/
Lazarus.fieldInfo = function(ele){
var getPassword = Lazarus.getExtPref("savePasswordFields");
var getHidden = Lazarus.getExtPref("saveHiddenFields");
var info = {};
info.name = ele.getAttribute("name");
info.type = Lazarus.getElementType(ele);
info.value = Lazarus.getElementValue(ele);
if (info.type == "password" && !getPassword){
info.value = null;
}
if (info.type == "hidden" && !getHidden){
info.value = null;
}
switch (info.type){
case "text":
case "textarea":
case "file":
case "password":
case "iframe":
if (info.value && Lazarus.trim(info.value)){
info.text = Lazarus.trim(info.value);
}
break;
default:
}
return info;
}
/**
* clean HTML tags and entities from an html string
*/
Lazarus.cleanText = function(text){
return text.replace(/<[^>]*>/g, ' ').replace(/&\w+;?/g, ' ').replace(/ +/g, ' ');
}
/**
* return a formInfo object filled with details about a form
*/
Lazarus.formInfo = function(form){
var info = {};
info.version = Lazarus.FORM_INFO_VERSION;
info.formid = Lazarus.getFormId(form);
info.action = Lazarus.getUrlPage(form.action || form.ownerDocument.URL);
info.origURL = form.ownerDocument.URL;
info.domain = Lazarus.getDomainFromElement(form);
info.method = (form.method && form.method.toLowerCase()) == "post" ? "post" : "get";
info.enctype = form.enctype ? form.enctype.toLowerCase() : '';
info.fields = {};
info.formtext = [];
info.textLen = 0;
for (var i=0; i<form.elements.length; i++){
var ele = form.elements[i];
var name = ele.getAttribute("name");
if (name){
switch (Lazarus.getElementType(ele)){
case "text":
case "textarea":
case "file":
case "radio":
case "checkbox":
case "select":
case "password":
case "hidden":
var fieldInfo = Lazarus.fieldInfo(ele);
info.fields[name] = info.fields[name] || [];
info.fields[name].push(fieldInfo);
if (fieldInfo.text){
info.formtext.push(fieldInfo.text);
info.textLen += fieldInfo.text.length;
}
break;
default:
//ignore all other types
}
}
//support for ajax textareas
else if (form.isTextarea && Lazarus.getElementType(ele) == "textarea"){
var fieldInfo = Lazarus.fieldInfo(ele);
info.isTextarea = true;
info.fields["textarea"] = [fieldInfo];
if (fieldInfo.text){
info.formtext.push(fieldInfo.text);
info.textLen += fieldInfo.text.length;
}
}
//support for ajax textareas
else if (form.isIframe && Lazarus.getElementType(ele) == "iframe"){
var fieldInfo = Lazarus.fieldInfo(ele);
info.isIframe = true;
info.fields["iframe"] = [fieldInfo];
if (fieldInfo.text){
info.formtext.push(fieldInfo.text);
info.textLen += fieldInfo.text.length;
}
}
}
//we need to add any WYSIWYG iframes into the form as well
var iframes = Lazarus.getEditableIframes(form);
if (iframes.length > 0){
info.fields[Lazarus.IFRAME_NAME] = [];
info.isIframe = true;
for (var i=0; i<iframes.length; i++){
var name = Lazarus.IFRAME_NAME;
var fieldInfo = Lazarus.fieldInfo(iframes[i]);
info.fields[name] = info.fields[name] || [];
info.fields[name].push(fieldInfo);
if (fieldInfo.text){
info.formtext.push(fieldInfo.text);
info.textLen += fieldInfo.text.length;
}
}
}
info.formtext = Lazarus.trim(info.formtext.join("\n\n"));
return info;
}
/**
* return an array of editable iframes found within the given node
*/
Lazarus.getEditableIframes = function(ele){
var editableIframes = [];
if (ele && ele.getElementsByTagName){
var iframes = ele.getElementsByTagName('iframe');
for (var i=0; i<iframes.length; i++){
if (Lazarus.isEditableDoc(iframes[i].contentWindow.document)){
editableIframes.push(iframes[i]);
}
//better get iframes within the iframes as well
if (iframes[i].contentWindow.document.body){
editableIframes = editableIframes.concat(Lazarus.getEditableIframes(iframes[i].contentWindow.document.body));
}
}
}
return editableIframes;
}
/**
* return TRUE if the given document is in edit mode
*/
Lazarus.isEditableDoc = function(doc){
return (doc && (doc.designMode == "on" || (doc.body && doc.body.contentEditable === "true")));
}
/**
* return the base domain (wikipedia.org) given a full domain (en.wikipedia.org)
*/
Lazarus.getBaseDomain = function(domain){
//if domain is an ip address return that
var regexIp = /\d+\.\d+\.\d+\.\d+$/
var m = domain.match(regexIp);
if (m){return m[0]}
//known top level domains (TLDs) including country specific ones
//http://en.wikipedia.org/wiki/Country_code_top-level_domain
//all domains *should* either end in a 2 letter tld (google.co.nz, google.com.au) or none (google.com) for american sites
//that should be preceeded by the generic tld
//and then the basedomain
var regex = /[^\.]+\.[^\.]+(\.\w{2})?$/;
var m = domain.match(regex);
//if we cant figure it out then return the whole domain
return m ? m[0] : domain;
}
/**
* restores a form to the given state
*/
Lazarus.restoreForm = function(form, formInfo){
//attempt to restore the saved fields for this form
var restorePassword = Lazarus.getExtPref("savePasswordFields");
var restoreHidden = Lazarus.getExtPref("saveHiddenFields");
//try and recover as many fields as possible for this form.
var iRestored = 0;
var iNamedElements = 0;
var formFields = {};
for (var i=0; i<form.elements.length; i++){
var ele = form.elements[i];
var name = ele.getAttribute("name");
var eleType = Lazarus.getElementType(ele);
var eleValue = Lazarus.getElementValue(ele);
if (name){
switch (eleType){
case "text":
case "textarea":
case "file":
case "radio":
case "checkbox":
case "select":
case "password":
case "hidden":
//try to restore this element
iNamedElements++;
//do we have an match for this element in our saved form
if (formInfo.fields[name]){
for (var j=0; j<formInfo.fields[name].length; j++){
var info = formInfo.fields[name][j];
if (!info.restored && info.type == eleType){
switch (eleType){
case "text":
case "textarea":
case "file":
case "select":
Lazarus.setElementValue(ele, info.value);
iRestored++;
break;
case "radio":
case "checkbox":
if (info.value && (eleValue.valueAttr == info.value.valueAttr)){
Lazarus.setElementValue(ele, info.value.checked);
iRestored++;
}
break;
case "password":
if (restorePassword){
Lazarus.setElementValue(ele, info.value);
}
iRestored++;
break;
case "hidden":
if (restoreHidden){
Lazarus.setElementValue(ele, info.value);
}
iRestored++;
break;
default:
//no need to restore
iRestored++;
}
}
}
}
default:
//ignore other types of elements
}
}
else if (form.isTextarea && Lazarus.isTextarea(ele)){
var info = formInfo.fields["textarea"][0];
Lazarus.setElementValue(ele, info.value);
iRestored++;
iNamedElements++;
}
else if (form.isIframe && ele.tagName == "iframe"){
var info = formInfo.fields["iframe"][0];
Lazarus.setElementValue(ele, info.value);
iRestored++;
iNamedElements++;
}
}
//try and restore any WYSIWYG editors as well
var iframes = Lazarus.getEditableIframes(form);
if (iframes && formInfo.fields[Lazarus.IFRAME_NAME]){
for (var i=0; i<iframes.length; i++){
var info = formInfo.fields[Lazarus.IFRAME_NAME][i];
if (info){
Lazarus.setElementValue(iframes[i], info.value);
}
}
}
//check for errors
if (iRestored == 0){
alert(Lazarus.getString("error.restore.none"));
return false;
}
//tell them if we couldn't restore some fields
else if (iRestored < iNamedElements){
//only show the partial restore message if the form is not an old style form
if (formInfo.version && formInfo.version >= 1){
alert(Lazarus.getString("error.restore.partial"));
}
return (iRestored / iNamedElements);
}
else {
return true;
}
}
/**
* handle the onselectmenuitem event
*/
Lazarus.onRestoreFormMenuItem = function(menuitem){
if (!Lazarus.canDecrypt()){
Lazarus.debug("Unable to restore form, password required");
Lazarus.showNotificationBox("password-required");
return;
}
var id = parseInt(menuitem.getAttribute("lazarus-forms-id"));
Lazarus.debug("Attempting to restore form "+ id);
var form = Lazarus.findFormFromElement(gContextMenu.target, "form");
if (form){
var row = Lazarus.db.getRow("SELECT * FROM forms WHERE id = ?1", id);
if (row){
var formInfo;
try {
formInfo = Lazarus.JSON.decode(Lazarus.decrypt(row["forminfo"]));
}
catch(e){
Lazarus.error(e);
}
if (formInfo){
//if we fully restore a form, then show the "donate" popup
if (Lazarus.restoreForm(form, formInfo) === true){
Lazarus.incPref("extensions.lazarus.restoreFormCount");
if ((formInfo.textLen > Lazarus.MIN_TEXT_NEEDED_TO_SHOW_NOTIFICATION) && Lazarus.getPref("extensions.lazarus.showDonateNotification") && Lazarus.getPref("extensions.lazarus.restoreFormCount", 0) > 3){
var msgId = Math.floor(Math.random() * 4);
Lazarus.showNotificationBox("form-restored", Lazarus.getFormRestoredNotificationText(formInfo.textLen, msgId), "msgid-"+ msgId);
}
}
}
else {
alert(Lazarus.getString("error.form.db.corrupt"));
}
}
else {
alert(Lazarus.getString("error.form.not.found"));
}
}
else {
alert(Lazarus.getString("error.form.object.not.found"));
}
}
/**
* return TRUE if form is a form we should save
*/
Lazarus.shouldSaveForm = function(form){
//only save forms on file/http/https sites
var doc = form.ownerDocument;
if (doc && doc instanceof HTMLDocument && doc.URL && /^(file|http|https):/.test(doc.URL) && !Lazarus.isPageDisabled(doc.URL) && !Lazarus.isDisabledByPrivateBrowsing()){
return (Lazarus.isSearchForm(form)) ? Lazarus.getPref("extensions.lazarus.saveSearchForms") : true;
}
else {
return false;
}
}
/**
* return TRUE if we should be saving this info
*/
Lazarus.shouldSaveEditorInfo = function(info){
if (Lazarus.isPageDisabled(info.url)){
return false;
}
else if (Lazarus.isDisabledByPrivateBrowsing()){
return false;
}
else {
return true;
}
}
Lazarus.disabledDomains = null;
Lazarus.isPageDisabled = function(url){
if (!url && content.document && content.document.URL){
url = content.document.URL;
}
if (url){
var domainId = Lazarus.urlToDomainId(url);
if (domainId){
var domains = Lazarus.getDisabledDomains();
return (typeof domains[domainId] !== "undefined") ? domains[domainId] : false;
}
}
return false;
}
/**
* return TRUE if lazarus can save forms from this site
* return FALSE for non-valid sites (eg about:config, chrome://... etc..)
*/
Lazarus.isValidSite = function(url){
//default to the current documents url
if (!url && content.document && content.document.URL){
url = content.document.URL;
}
return (url && Lazarus.urlToDomainId(url)) ? true : false;
}
Lazarus.getDisabledDomains = function(){
var domainlist = Lazarus.getPref('extensions.lazarus.domainBlacklist', '');
if (!Lazarus.disabledDomains || Lazarus.disabledDomains['__origList__'] != domainlist){
//rebuild the list
Lazarus.disabledDomains = {
'__origList__': domainlist
}
var domains = domainlist.split(/\s*,\s*/g);
for (var i=0; i<domains.length; i++){
Lazarus.disabledDomains[domains[i]] = true;
}
}
return Lazarus.disabledDomains;
}
Lazarus.saveDisabledDomains = function(domainsTable){
//convert the table back into an array and save it.
var domains = [];
for(var domain in domainsTable){
if (domainsTable[domain] && /^\w+:/.test(domain)){
domains.push(domain);
}
}
return Lazarus.setPref('extensions.lazarus.domainBlacklist', domains.join(','));
}
Lazarus.enableForCurrentDomain = function(){
//fetch the current domain, and disable Lazarus
if (content.document && content.document.URL){
var domainId = Lazarus.urlToDomainId(content.document.URL);
//and add to disabled domains list.
var domains = Lazarus.getDisabledDomains();
if (!domains[domainId]){
domains[domainId] = true;
Lazarus.saveDisabledDomains(domains);
Lazarus.debug('Domain disabled ['+ domainId +']');
}
else {
//should already be disabled!
Lazarus.warning('Domain is already disabled ['+ domainId +']');
}
//and refresh the statusbar icon
Lazarus.refreshIcon();
}
else {
alert(Lazarus.getString("error.no.document"));
}
}
/**
* enables or disabled on the current website
*/
Lazarus.toggleCurrentDomain = function(enable){
//fetch the current domain, and disable Lazarus
if (content.document && content.document.URL){
var domains = Lazarus.getDisabledDomains();
var domainId = Lazarus.urlToDomainId(content.document.URL);
if (domainId){
//add/remove from disabled domains list
domains[domainId] = enable ? false : true;
Lazarus.saveDisabledDomains(domains);
Lazarus.debug('Domain ['+ domainId +'] enabled = '+ enable);
//and refresh the statusbar icon
Lazarus.refreshIcon();
}
else {
Lazarus.error("Failed to extract domainId from URL ["+ content.document.URL +"]");
}
}
else {
alert(Lazarus.getString("error.no.document"));
}
}
Lazarus.onStatusbarMenuShowing = function(popupmenu){
var validSite = Lazarus.isValidSite();
var siteDisabled = Lazarus.isPageDisabled();
var isPrivate = Lazarus.isDisabledByPrivateBrowsing();
Lazarus.$('lazarus-statusbar-menuitem-toggledomain-separator').hidden = !(validSite && !isPrivate);
Lazarus.$('lazarus-statusbar-menuitem-enablefordomain').hidden = !(validSite && siteDisabled && !isPrivate);
Lazarus.$('lazarus-statusbar-menuitem-disablefordomain').hidden = !(validSite && !siteDisabled && !isPrivate);
Lazarus.$('lazarus-statusbar-menuitem-logout').hidden = !(Lazarus.Crypto.isPasswordEntered);
}
/**
* return a website identifier
* http://google.com is different from https://google.com
*/
Lazarus.urlToDomainId = function(url){
var uri = Lazarus.urlToURI(url);
return (uri && uri.scheme && uri.host && (/^(http|https|file)$/.test(uri.scheme))) ? (uri.scheme +":"+ ((uri.port > -1 && uri.port != 80) ? uri.port : '') +'//'+ Lazarus.getBaseDomain(uri.host) +'/') : '';
}
/**
* return TRUE if form appears to be a search form
*/
Lazarus.isSearchForm = function(form){
//search forms are forms that have exactly one textbox, and may contain a number of other non-text fields
var MAX_TEXT_FIELDS = 1;
var MAX_NON_TEXT_FIELDS = 5;
var iTextFields = 0;
var iNonTextFields = 0;
for (var i=0; i<form.elements.length; i++){
var ele = form.elements[i];
switch(Lazarus.getElementType(ele)){
case "text":
iTextFields++;
break;
case "radio":
case "checkbox":
case "select":
iNonTextFields++;
break;
case "file":
case "password":
case "textarea":
return false;
case "hidden":
case "submit":
case "reset":
case "button":
case "image":
default:
//ignore all other types
}
}
//NOTE: we MUST have a single text field
return (iTextFields == MAX_TEXT_FIELDS && iNonTextFields <= MAX_NON_TEXT_FIELDS);
}
/**
* check a submit event, and saves form details if the event is from a valid form
*/
Lazarus.onFormSubmit = function(evt){
var form = Lazarus.findFormFromElement(evt.target);
//is the form on an html document?
if (form && form.ownerDocument instanceof HTMLDocument){
Lazarus.stopAutoSaveTimer();
Lazarus.saveForm(form, Lazarus.FORM_TYPE_NORMAL);
}
}
/**
* return "post" of "get" depending on the given forms method
*/
Lazarus.getFormMethod = function(form){
var method = form.getAttribute("method");
return (method && method.toLowerCase() == "post") ? "post" : "get";
}
/**
* autosave a form if we change anything about it.
*/
Lazarus.onFormChange = function(evt){
var form = Lazarus.findFormFromElement(evt.target);
if (form && form.ownerDocument instanceof HTMLDocument){
//I think we should chuck a timer in here, because we might end up saving a lot.
Lazarus.restartAutoSaveTimer(form);
}
}
/**
* check a reset event, and saves form details if the event is from a valid form
*/
Lazarus.onFormReset = function(evt){
var form = Lazarus.findFormFromElement(evt.target);
//is the form on an html document?
if (form && form.ownerDocument instanceof HTMLDocument){
Lazarus.stopAutoSaveTimer();
Lazarus.saveForm(form, Lazarus.FORM_TYPE_AUTOSAVE);
}
}
/**
* create a searchable index of hashed words
*/
Lazarus.indexFormText = function(id, text, url){
//and update the full text index,
if (!Lazarus.getPref('extensions.lazarus.disableSearch')){
//we're going to add domain info into this as well so a user can search for domain fragments too
var hashedText = Lazarus.hashText(text);
//extract the domain from the url
var domain = Lazarus.urlToURI(url).host;
hashedText += ' '+ Lazarus.hashText(domain.replace(/\./g, ' '));
Lazarus.db.exe("DELETE FROM forms_fulltext WHERE docid = ?1", id);
Lazarus.db.exe("INSERT INTO forms_fulltext (docid, hashed_text) VALUES (?1, ?2)", id, hashedText);
}
}
/**
* save all the named elements in a form to the permanent store
*/
Lazarus.saveForm = function(form, formType, templateName, autofill){
autofill = autofill ? 1 : 0;
if (Lazarus.shouldSaveForm(form)){
templateName = templateName || '';
var info = Lazarus.formInfo(form);
//dont save empty forms?
//No. what about forms with lots of radio buttons (eg multichoice surveys)
var infoJSON = Lazarus.JSON.encode(info)
var encryptedInfo = Lazarus.encrypt(infoJSON);
var encryptedText = Lazarus.encrypt(Lazarus.getMenuItemText(info.formtext));
var encryptedFormURL = Lazarus.encrypt(Lazarus.getFormURL(form));
var infoHash = Lazarus.generateHash(info.fields);
//each form type needs to be saved differently
switch(formType){
case Lazarus.FORM_TYPE_AUTOSAVE:
case Lazarus.FORM_TYPE_STALE_AUTOSAVE:
var savedForm = Lazarus.db.getRow("SELECT id, savetype FROM forms WHERE formid = ?1 AND forminfohash = ?2 AND savetype IN ("+ Lazarus.FORM_TYPE_AUTOSAVE +","+ Lazarus.FORM_TYPE_STALE_AUTOSAVE +") LIMIT 1", info.formid, infoHash);
//add or update the current autosave
if (!savedForm){
var lastAutoSaveId = Lazarus.db.getInt("SELECT id FROM forms WHERE formid = ?1 AND savetype = "+ Lazarus.FORM_TYPE_AUTOSAVE +" ORDER BY created DESC LIMIT 1", info.formid);
var lastAutoSave = (lastAutoSaveId && Lazarus.lastAutoSaveForm && Lazarus.lastAutoSaveForm["formid"] == info.formid) ? Lazarus.lastAutoSaveForm : null;
var newId = 0;
if (lastAutoSave && Lazarus.shouldCreateNewAutosave(info, lastAutoSave)){
//if the saved form is smaller than the last saved form by a substantial amount,
//create a new restore point.
Lazarus.debug("Creating additional autosave point "+ info.formid);
Lazarus.db.exe("INSERT INTO forms (formid, created, forminfo, formtext, forminfohash, formurl, text_length, savetype) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, "+ Lazarus.FORM_TYPE_AUTOSAVE +")", info.formid, Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, encryptedFormURL, info.textLen);
newId = Lazarus.db.getInt("SELECT MAX(id) FROM forms");
}
else if (lastAutoSave){
Lazarus.debug("Updating autosave form "+ info.formid);
Lazarus.db.exe("UPDATE forms SET created = ?1, forminfo = ?2, formtext = ?3, forminfohash = ?4, formurl = ?5, text_length = ?6 \
WHERE id = ?7", Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, encryptedFormURL, info.textLen, lastAutoSaveId);
newId = lastAutoSaveId;
}
else {
Lazarus.debug("Creating new autosave form "+ info.formid);
Lazarus.db.exe("INSERT INTO forms (formid, created, forminfo, formtext, forminfohash, formurl, text_length, savetype) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, "+ Lazarus.FORM_TYPE_AUTOSAVE +")", info.formid, Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, encryptedFormURL, info.textLen);
newId = Lazarus.db.getInt("SELECT MAX(id) FROM forms");
}
//update the full text search as well
Lazarus.indexFormText(newId, info.formtext, info.origURL);
//and save this forminfo as the last autosave point
Lazarus.lastAutoSaveForm = info;
}
else {
Lazarus.debug("No need to autosave form, form already exists: "+ info.formid);
}
break;
case Lazarus.FORM_TYPE_NORMAL:
//delete all autosaves
Lazarus.removeForms(Lazarus.db.getColumn("SELECT id FROM forms WHERE formid = ?1 AND savetype IN ("+ Lazarus.FORM_TYPE_AUTOSAVE +","+ Lazarus.FORM_TYPE_STALE_AUTOSAVE +")", info.formid));
//if this form already exists, delete it
Lazarus.removeForms(Lazarus.db.getColumn("SELECT id FROM forms WHERE formid = ?1 AND forminfohash = ?2 AND savetype = "+ Lazarus.FORM_TYPE_NORMAL, info.formid, infoHash));
//and save this form
Lazarus.db.exe("INSERT INTO forms (formid, created, forminfo, formtext, forminfohash, formname, formurl, text_length, savetype, autofill) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, 0)",
info.formid, Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, templateName, encryptedFormURL, info.textLen, formType);
Lazarus.debug("form saved ["+ info.formid +"]");
//update the full text search as well
var newId = Lazarus.db.getInt("SELECT MAX(id) FROM forms");
Lazarus.indexFormText(newId, info.formtext, info.origURL);
break;
case Lazarus.FORM_TYPE_TEMPLATE:
//if another template with the same name exists, delete it
Lazarus.removeForms(Lazarus.db.getColumn("SELECT id FROM forms WHERE formname = ?1 AND savetype = "+ Lazarus.FORM_TYPE_TEMPLATE, templateName));
//likewise we are not allowed to have more then one autofill for the same form.
if (autofill){
Lazarus.db.exe("UPDATE forms SET autofill = 0 WHERE formid = ?1", info.formid);
}
//and save this form
Lazarus.db.exe("INSERT INTO forms (formid, created, forminfo, formtext, forminfohash, formname, formurl, text_length, savetype, autofill) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
info.formid, Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, templateName, encryptedFormURL, info.textLen, formType, autofill);
var newId = Lazarus.db.getInt("SELECT MAX(id) FROM forms");
Lazarus.indexFormText(newId, info.formtext, info.origURL);
Lazarus.debug("template saved ["+ info.formid +"]");
break;
default:
Lazarus.error(Error("Unknown form type ["+ row["formtype"] +"]"));
}
Lazarus.startCleanupTimer();
}
else {
Lazarus.debug("Form not saved: invalid form");
}
}
/**
* remove forms from the database
*/
Lazarus.removeForms = function(ids){
Lazarus.debug("removing forms", ids);
if (!ids || (Lazarus.isArray(ids) && ids.length === 0)){
return;
}
if (!Lazarus.isArray(ids)){
ids = [ids];
}
//make sure the ids are safe.
for(var i=0; i<ids.length; i++){
ids[i] = parseInt(ids[i]);
}
Lazarus.db.exe("DELETE FROM forms WHERE id IN ("+ ids.join(",") +")");
Lazarus.db.exe("DELETE FROM forms_fulltext WHERE docid IN ("+ ids.join(",") +")");
Lazarus.debug(ids.length +" forms removed");
}
/**
* return the URL of the page this form is currently on, but leave any query info
*/
Lazarus.getFormURL = function(form){
//we'll strip any anchor tags from the URL
return form.ownerDocument.URL.replace(/#.*/, '');
}
/**
* return an integer representing how different one form is from another
* 0 being identical
* 100 being 100 characters/values are different
*/
Lazarus.shouldCreateNewAutosave = function(newForm, oldForm){
//number of characters to ignore when calculating if the form has changed substantially
var TEXT_DIFFERENCE = 32; //
if (oldForm.formtext.length > (TEXT_DIFFERENCE * 2)){
var str = oldForm.formtext.substr(0, oldForm.formtext.length - TEXT_DIFFERENCE);
//if new form is a continuation of old form (ie the first (X - TEXT_DIFFERENCE) characters are still the same),
//then overwrite current autosave,
//otherwise create a new autosave.
return (newForm.formtext.indexOf(str) == -1);
}
//form contains little text, overwrite the current autosave
else {
return false;
}
}
/**
* automatically save the form the user is working on.
*/
Lazarus.autoSaveForm = function(){
//does the form still exist?
if (Lazarus.currAutoSaveForm && !Lazarus.isContextMenuShowing){
Lazarus.saveForm(Lazarus.currAutoSaveForm, Lazarus.FORM_TYPE_AUTOSAVE);
}
}
/**
* start the cleanup timer
*/
Lazarus.startCleanupTimer = function(){
Lazarus.stopCleanupTimer();
if (Lazarus.getExpiryTime()){
Lazarus.cleanupSavedFormsTimer = setInterval(Lazarus.cleanupSavedForms, 1000 * 60);
}
}
/**
* stop the cleanup timer
*/
Lazarus.stopCleanupTimer = function(){
if (Lazarus.cleanupSavedFormsTimer){
clearInterval(Lazarus.cleanupSavedFormsTimer);
}
}
/**
* remove old forms from the database
*/
Lazarus.cleanupSavedForms = function(){
//dont cleanup any forms if a user might be trying to retore them
if (Lazarus.isContextMenuShowing){return}
var expires = Lazarus.getExpiryTime();
if (expires > 0){
//add a couple of minutes to the expiry time, so we don't accidentally
//remove a form whilst we are current restoring it.
var cutoffTime = Lazarus.timestamp() - (expires + 120);
var ids = Lazarus.db.getColumn("SELECT id FROM forms WHERE created < ?1 AND savetype != "+ Lazarus.FORM_TYPE_TEMPLATE, cutoffTime);
Lazarus.removeForms(ids);
var ids = Lazarus.db.getColumn("SELECT id FROM textdata WHERE created < ?1", cutoffTime);
if (ids.length > 0){
Lazarus.db.exe("DELETE FROM textdata WHERE id IN ("+ ids.join(",") +")");
Lazarus.db.exe("DELETE FROM textdata_fulltext WHERE docid IN ("+ ids.join(",") +")");
}
}
else {
Lazarus.stopCleanupTimer();
}
}
/**
* return an identifier for a url
*/
Lazarus.getUrlPage = function(url){
var uri = Lazarus.urlToURI(url);
if (uri){
return uri.scheme +"://"+ uri.hostPort + uri.path.replace(/[\?#].*$/, '');
}
//for testing on file systems
else if ((Lazarus.getExtPref("debugMode") >= 3) && form.action.match(/^file:\/\//i)){
return "file://just/testing";
}
else {
return null;
}
}
/**
* return the user editable fields within a form.
*/
Lazarus.getEditableFields = function(form){
var fields = [];
var saveHidden = Lazarus.getExtPref("saveHiddenFields");
var savePassword = Lazarus.getExtPref("savePasswordFields");
for (var i=0; i<form.elements.length; i++){
var ele = form.elements[i];
//ignore fields with no name (they dont get submitted to the server)
if (ele.getAttribute("name")){
switch(Lazarus.getElementType(ele)){
case "text":
case "textarea":
case "file":
case "radio":
case "checkbox":
case "select":
fields.push(ele);
break;
case "password":
if (savePassword){
fields.push(ele);
}
break;
case "hidden":
if (saveHidden){
fields.push(ele);
}
break;
//ignore buttons
case "submit":
case "reset":
case "button":
case "image":
//and unknown elements
default:
//unknown element type
break;
}
}
}
return fields;
}
/**
* return TRUE if element is a textarea
*/
Lazarus.isTextarea = function(ele){
return (ele && ele.nodeName && ele.nodeName.toLowerCase() == "textarea");
}
/**
* return TRUE if element is an iframe
*/
Lazarus.isIframe = function(ele){
return (ele && ele.nodeName && ele.nodeName.toLowerCase() == "iframe");
}
/**
* generate a formid for this form
*/
Lazarus.getFormId = function(form){
//if this form is from the Lazarus Form Recovery page, then use the id specified in the form
if (Lazarus.isDocRecoveryForm(form.ownerDocument)){
return form.getAttribute("lazarus-form-id");
}
//forms with no "action" attribute default to sending data to the current page
var action = form.action || form.ownerDocument.URL;
var uri = Lazarus.urlToURI(action);
var formId = (uri && uri.host) ? Lazarus.getBaseDomain(uri.host) : '';
//debugging (on local file system)
if (!formId && (Lazarus.getExtPref("debugMode") >= 3) && action.match(/^file:\/\//i)){
formId = "file://just/testing";
}
//and point to the same place
if (formId){
//support for ajax textareas
if (form.isTextarea){
formId += "<textarea>";
}
//and iframes not contained within forms
else if (form.isIframe){
formId += "<iframe>";
}
//#7: Not saving trac "comment" forms
//sometimes forms will add additional hidden fields
//if the form has a name, formId , or class we'll add that as well.
//no not class. If a form has an error the classname might be changed to highlight
//the error to the user
//we will generate an formId from the editable fields name property
//only fields with a name property are submitted.
else if (form.name || form.id){
formId += form.name ? ("@"+ form.name) : "";
//KLUDGE: gmails email form changes the id of the form every time you start a new email
formId += form.id && (form.ownerDocument.domain.indexOf("google") == -1) ? ("#"+ form.id) : "";
}
//fall back to using an identifier generated form the elements within the page
else {
var names = [];
for (var i=0; i<form.elements.length; i++){
var ele = form.elements[i];
var name = ele.getAttribute("name");
if (name){
switch(Lazarus.getElementType(ele)){
case "text":
case "textarea":
case "file":
case "radio":
case "checkbox":
case "select":
names.push(name.toLowerCase());
}
}
}
//sort and remove duplicate names
names.sort();
names = Lazarus.arrayUnique(names);
formId += "["+ names.join(",") +"]";
}
//#48: including the domain within the formId constitutes a privacy risk.
formId = Lazarus.md5(formId);
return formId;
}
else {
Lazarus.warning("Lazarus: Unable to convert url to uri ["+ action +"]");
return '';
}
}
/**
* return a new array containing members of the given array with no duplicates
*/
Lazarus.arrayUnique = function(arr){
var newArr = [];
for (var i=0; i<arr.length; i++){
if (!Lazarus.inArray(arr[i], newArr)){
newArr.push(arr[i]);
}
}
return newArr;
}
/**
* return a user friendly string stating the elapsed time in seconds, minutes, hours or days
*/
Lazarus.getTimeString = function(sec){
if (sec < 1){sec = 1}
var units = {
"day" : 60 * 60 * 24,
"hour" : 60 * 60,
"minute": 60,
"second": 1
}
for(var unit in units){
if (sec >= units[unit]){
var numUnits = Math.floor(sec / units[unit]);
if (numUnits == 1){
return Lazarus.getString("elapsed."+ unit);
}
else {
return Lazarus.getString("elapsed."+ unit +"s", numUnits);
}
}
}
//should never get here
return Lazarus.getString("elapsed.second");
}
/**
* returns an elements value
*/
Lazarus.getElementValue = function(ele){
switch(Lazarus.getElementType(ele)){
//text fields
case "text":
case "password":
case "textarea":
case "file":
case "hidden":
return ele.value;
case "radio":
case "checkbox":
return {
"valueAttr": ele.value,
"checked": ele.checked
}
//buttons
case "submit":
case "reset":
case "button":
case "image":
return ele.value;
case "select":
//select boxes have the option to allow multiple selections
var selected = [];
if (ele.options){
for (var i=0; i<ele.options.length; i++){
if (ele.options[i].selected){
selected.push(ele.options[i].value);
}
}
}
return selected;
case "iframe":
var doc = ele.contentWindow.document;
return (doc && doc.body && doc.body.innerHTML) ? doc.body.innerHTML : '';
default:
//unknown element type
return null;
}
}
/**
* returns an elements value
*/
Lazarus.setElementValue = function(ele, value){
switch(Lazarus.getElementType(ele)){
//text fields
case "text":
case "password":
case "textarea":
case "file":
case "hidden":
ele.value = value;
break;
case "radio":
case "checkbox":
ele.checked = value;
break;
//buttons
case "submit":
case "reset":
case "button":
case "image":
ele.value = value;
break;
case "select":
//select boxes have the option to allow multiple selections
var selected = [];
if (ele.options){
//bugfix: RT: 101284
//inArray is taking too long for large (10,000+) select boxes, so we'll convert the values array into
//a hash table instead
//hmmm, the problem is not actually in the inArray function
//but rather the line
//ele.options[i].selected = table[ele.options[i].value] ? true : false;
// it appears that a lot goes on behind the scenes when a selectbox option
//is set, even if the value doesn't change.
var table = Lazarus.arrayToHashTable(value);
for (var i=0; i<ele.options.length; i++){
var selectOption = table[ele.options[i].value] ? true : false;
if (ele.options[i].selected != selectOption){
ele.options[i].selected = selectOption;
}
}
}
break;
case "iframe":
var doc = ele.contentWindow.document;
if (doc && doc.body){
doc.body.innerHTML = value;
}
break;
default:
//unknown element type
break;
}
}
/**
* return a string explaining the type of a form element
* differentiates between different input types.
*/
Lazarus.getElementType = function(ele){
if (ele.nodeName.toLowerCase() == "input"){
return ele.type.toLowerCase();
}
else {
return ele.nodeName.toLowerCase();
}
}
/**
* return a fake form object
*/
Lazarus.createFakeForm = function(ele, type){
return {
ownerDocument: ele.ownerDocument,
elements: [ele],
action: Lazarus.urlToDomainId(ele.ownerDocument.URL) || '',
isTextarea: (type == "textarea"),
isIframe: (type == "iframe"),
isFakeForm: true
};
}
Lazarus.getParentIframe = function(ele){
var win = ele.ownerDocument.defaultView.frameElement;
var win = doc && doc.defaultView;
return win.frameElement;
}
/**
* return the first content editable iframe or textarea from a given element
*/
Lazarus.findEditorFromElement = function(ele){
while(ele){
if (Lazarus.isTextarea(ele)){
return ele;
}
else if (Lazarus.isEditableDoc(ele.ownerDocument) && ele.ownerDocument.defaultView.frameElement){
return ele.ownerDocument.defaultView.frameElement;
}
else {
ele = ele.parentNode;
}
}
}
/**
* finds a parent form from this element
*/
Lazarus.findFormFromElement = function(ele){
var iframe = false;
while(ele){
if (ele.nodeName && ele.nodeName.toLowerCase() == "form"){
return ele;
}
else if (ele.form && ele.form.nodeName.toLowerCase() == "form"){
return ele.form;
}
//add support for AJAX textareas that are not contained within a form.
else if (Lazarus.isTextarea(ele)){
//we're going to build a fake form here, that contains the , so the rest of the code can handle
return Lazarus.createFakeForm(ele, "textarea");
}
else if (Lazarus.isEditableDoc(ele.ownerDocument)){
iframe = ele.ownerDocument.defaultView.frameElement;
//move to parent iframe
//Hmmm. Problem exists when the editable iframe is contained within another iframe (eg FCKEditor)
//we'll need to iterate up through all the frames. But we also need to check if this frame contains the form element
ele = iframe;
}
else if (ele.parentNode && ele.parentNode.tagName){
ele = ele.parentNode;
}
//an iframe has been detected, but no form was found in this iframe
//so move up to the containing frame (if any)
else if (iframe){
ele = ele.ownerDocument.defaultView.frameElement;
}
else {
return null;
}
}
if (iframe){
//we have an editable iframe that is NOT contained within a form!
//probably some type of AJAX form
//we'll need to build a fake form and the iframe in there
return Lazarus.createFakeForm(iframe, "iframe");
}
return null;
}
/**
* finds the first node of type nodeName from ele up the dom tree
*/
Lazarus.findParent = function(ele, nodeName){
nodeName = nodeName.toLowerCase();
while(ele){
if (ele.nodeName && ele.nodeName.toLowerCase() == nodeName){
return ele;
}
else {
ele = ele.parentNode;
}
}
return null;
}
/**
*
*/
Lazarus.onStatusbarImageClick = function(evt){
var state = Lazarus.getState()
if (state == Lazarus.STATE_DISABLED){
evt.preventDefault();
Lazarus.openGenerateKeysDialog();
if (Lazarus.reloadKeys()){
Lazarus.enable();
}
return false;
}
//only on left click
if (evt.which == 1){
switch (state){
case Lazarus.STATE_ENABLED:
Lazarus.openStatusbarMenu(evt);
return;
case Lazarus.STATE_PASSWORD_REQUIRED:
Lazarus.showEnterPasswordDialog();
return
case Lazarus.STATE_PRIVATE_BROWSING:
alert(Lazarus.getString('private.browsing.mode'));
return;
case Lazarus.STATE_GENERATING_KEYS:
return;
case Lazarus.STATE_DISABLED_FOR_DOMAIN:
alert(Lazarus.getString('status.disabledfordomain'))
return;
case Lazarus.STATE_UNINITALIZED:
default:
Lazarus.error("Not initalized");
return
}
}
}
/**
* show the statusbar menu
*/
Lazarus.openStatusbarMenu = function(evt){
Lazarus.$('lazarus-statusbar-menupopup').openPopup(Lazarus.$('lazarus-statusbarpanel'), 'before_end', 0, 0, true);
}
/**
* open the options dialog
*/
Lazarus.openOptionsDialog = function(optionsPane){
optionsPane = optionsPane || null;
var features = "chrome,titlebar,toolbar,centerscreen,modal";
features += Lazarus.getPref("browser.preferences.animateFadeIn", false) ? "" : ",resizable";
window.open("chrome://lazarus/content/options.xul", "LazarusOptions", features, optionsPane);
}
/**
* open the about dialog
*/
Lazarus.openAboutDialog = function(){
window.open("chrome://lazarus/content/about.xul", "LazarusAbout", "chrome,titlebar,toolbar,centerscreen,modal,resizable");
}
/**
* open the about dialog
*/
Lazarus.openLazarusWebsite = function(page){
page = page || '';
Lazarus.openURL(Lazarus.website + page, true, true);
}
/**
* open a URL in the current browser
*/
Lazarus.openURL = function(url, newTab, selectTab){
var browser = document.getElementById('content');
if (newTab){
var tab = browser.addTab(url);
if (tab && selectTab){
browser.selectedTab = tab;
}
}
else {
content.location = url;
}
}
/**
* returns a localized string
*/
Lazarus.getString = function(strId){
try {
var strbundle = Lazarus.$("lazarus-strings");
if (arguments.length == 1){
return strbundle.getString(strId);
}
else {
var replacements = [];
//NOTE: first argument is the strId
for (var i=1; i<arguments.length; i++){
replacements.push(arguments[i])
}
return strbundle.getFormattedString(strId, replacements);
}
}
catch(e){
Lazarus.error(e, "failed to getString ["+ strId +"]");
return '';
}
}
Lazarus.checkDatabase = function(db){
//build the database tables
try {
//forms
db.exe('CREATE TABLE IF NOT EXISTS forms (\
id INTEGER PRIMARY KEY, formid TEXT, \
created INT, \
formname TEXT, \
forminfo TEXT, \
formtext TEXT, \
savetype INT, \
forminfohash TEXT, \
formurl TEXT, \
text_length INT, \
autofill INT);');
db.exe('CREATE INDEX IF NOT EXISTS index_forms_created ON forms (created DESC)');
db.exe('CREATE INDEX IF NOT EXISTS index_forms_formid ON forms (formid ASC)');
//settings
db.exe('CREATE TABLE IF NOT EXISTS settings (name TEXT PRIMARY KEY, value TEXT)');
//texdata
db.exe('CREATE TABLE IF NOT EXISTS textdata (\
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \
text_encrypted TEXT, \
text_hash TEXT, \
text_length INT, \
summary_encrypted TEXT, \
created INTEGER DEFAULT \'0\' NOT NULL, \
domain_hash TEXT, \
url_encrypted TEXT, \
savetype INTEGER \
)');
Lazarus.debug("DB: Creating indexes");
db.exe('CREATE INDEX IF NOT EXISTS index_textdata_domain ON textdata (domain_hash ASC)');
db.exe('CREATE INDEX IF NOT EXISTS index_textdata_created ON textdata (created DESC)');
Lazarus.debug("DB: Creating Full Text indexes");
//textdata full text index
if (!db.tableExists('textdata_fulltext')){
db.exe('CREATE VIRTUAL TABLE textdata_fulltext USING fts3(hashed_text)');
}
//forms: full text index
if (!db.tableExists('forms_fulltext')){
db.exe('CREATE VIRTUAL TABLE forms_fulltext USING fts3(hashed_text)');
}
Lazarus.debug("DB: Testing read/write");
//test read and write
var now = (new Date()).getTime();
db.exe("DELETE FROM settings WHERE name = 'last-accessed'");
db.exe("INSERT INTO settings (name, value) VALUES ('last-accessed', ?1)", now);
var lastAccessed = db.getInt("SELECT value FROM settings WHERE name = 'last-accessed'");
var success = (lastAccessed === now);
Lazarus.debug("DB: read/write success? "+ success);
return success;
}
catch(e){
Lazarus.error(e);
return false;
}
}
/**
* initalizes the database
*/
Lazarus.initDB = function(){
//create / connect to the database
//we might have already connected in an update script.
if (!Lazarus.Global.db){
if (Lazarus.getPref("extensions.lazarus.deleteDatabase", false)){
Lazarus.killPref("extensions.lazarus.deleteDatabase", false);
Lazarus.killDB();
}
if (Lazarus.getPref("extensions.lazarus.restoreDatabase", false)){
Lazarus.killPref("extensions.lazarus.restoreDatabase");
if (Lazarus.file.exists("%profile%/lazarus-backup.sqlite")){
Lazarus.file.move("%profile%/lazarus-backup.sqlite", "%profile%/lazarus.sqlite", true);
}
else {
Lazarus.error("Unable to restore database: backup not found");
}
}
//if a backup exists, and the original database is missing, then try and use the backup database
if (Lazarus.file.exists("%profile%/lazarus-backup.sqlite") && !Lazarus.file.exists("%profile%/lazarus.sqlite")){
Lazarus.file.move("%profile%/lazarus-backup.sqlite", "%profile%/lazarus.sqlite");
}
else if (Lazarus.getPref("extensions.lazarus.restoreFromBackup", false) && Lazarus.file.exists("%profile%/lazarus-backup.sqlite")){
Lazarus.file.move("%profile%/lazarus-backup.sqlite", "%profile%/lazarus.sqlite");
}
var db = new Lazarus.SQLite("%profile%/lazarus.sqlite", false);
if (!Lazarus.checkDatabase(db)){
//save the corrupted database file
Lazarus.warning("Database is corrupted, saving corrupt database");
var d = Lazarus.formatDate(new Date(), true);
Lazarus.file.kill("%profile%/lazarus-"+ d +"-corrupted.sqlite");
Lazarus.file.move("%profile%/lazarus.sqlite", "%profile%/lazarus-"+ d +"-corrupted.sqlite");
db = null;
}
//use the backup database if it exists.
if (!db && Lazarus.file.exists("%profile%/lazarus-backup.sqlite")){
Lazarus.warning("Restoring previous backup database");
Lazarus.file.move("%profile%/lazarus-backup.sqlite", "%profile%/lazarus.sqlite");
db = new Lazarus.SQLite("%profile%/lazarus.sqlite", false);
if (!Lazarus.checkDatabase(db)){
//delete the broken backup file
Lazarus.file.kill("%profile%/lazarus.sqlite");
db = null;
}
}
if (!db){
//delete any existing database, and build a new one from scratch
Lazarus.file.kill("%profile%/lazarus.sqlite");
db = new Lazarus.SQLite("%profile%/lazarus.sqlite", false);
if (!Lazarus.checkDatabase(db)){
//oh, so screwed
Lazarus.error("Unable to create the Lazarus database");
db = null;
}
}
if (db){
//ok we should now have a valid and checked database
//make a backup of it.
//remove the old backup (if it exists)
if (Lazarus.getPref("extensions.lazarus.backupDatabase")){
Lazarus.debug("Backing up database");
Lazarus.file.copy("%profile%/lazarus.sqlite", "%profile%/lazarus-backup.sqlite", true);
}
}
Lazarus.Global.db = db;
}
Lazarus.db = Lazarus.Global.db;
return Lazarus.db ? true : false;
}
Lazarus.cleanDB = function(callback){
//takes ages to clean the database
Lazarus.debug("Cleaning database");
Lazarus.cleaningDatabase = true;
Lazarus.refreshIcon();
Lazarus.backgroundTask(function(){
Lazarus.db.exe("VACUUM");
}, function(){
Lazarus.cleaningDatabase = false;
Lazarus.refreshIcon();
if (callback){
callback();
}
});
}
/**
* convert autosaved forms to semi-permanent save points
*/
Lazarus.saveAutoSavedForms = function(){
//convert autosaved forms to semi-permanent points
var rs = Lazarus.db.rs("SELECT id, formid FROM forms WHERE savetype = "+ Lazarus.FORM_TYPE_AUTOSAVE +" ORDER BY formid, created DESC");
var lastFormId = '';
var lastFormCnt = 0;
var MAX_AUTOSAVES_TO_KEEP = 2;
for (var i=0; i<rs.length; i++){
var savedForm = rs[i];
if (lastFormId != savedForm["formid"]){
lastFormId = savedForm["formid"];
lastFormCnt = 1;
}
else {
lastFormCnt++;
}
if (lastFormCnt <= MAX_AUTOSAVES_TO_KEEP){
Lazarus.db.exe('UPDATE forms SET savetype = '+ Lazarus.FORM_TYPE_STALE_AUTOSAVE +' WHERE id = ?1', savedForm["id"]);
}
else {
//excess saved form, remove it
Lazarus.removeForms(savedForm["id"]);
}
}
}
/**
* removes old/excess forms from the database
*/
Lazarus.removeOldForms = function(){
var rs = Lazarus.db.rs("SELECT count(*) as cnt, formid FROM forms WHERE savetype = "+ Lazarus.FORM_TYPE_NORMAL +" GROUP BY formid");
var maxForms = Lazarus.getExtPref("maxSavesPerForm", 10);
for (var i=0; i<rs.length; i++){
var save = rs[i];
if (save["cnt"] > maxForms){
Lazarus.debug("Removing excess forms "+ save["formid"]);
var ids = Lazarus.db.getColumn("SELECT id FROM forms WHERE formid = ?1 AND savetype = "+ Lazarus.FORM_TYPE_NORMAL +" ORDER BY created DESC LIMIT -1 OFFSET ?2", save["formid"], maxForms);
if (ids.length){
//and remove
Lazarus.debug("cleaning up forms "+ ids.join(","));
Lazarus.removeForms(ids);
}
}
}
}
/**
* empties all the tables in the database
* except those specified.
*/
Lazarus.emptyDB = function(ignoreFormTypes){
//always remove text data
Lazarus.db.exe('DELETE FROM textdata');
Lazarus.db.exe('DELETE FROM textdata_fulltext');
if (typeof ignoreFormTypes == "undefined"){
Lazarus.db.exe('DELETE FROM forms');
Lazarus.db.exe('DELETE FROM forms_fulltext');
}
else {
var ids = Lazarus.db.getColumn('SELECT id FROM forms WHERE savetype NOT IN ('+ ignoreFormTypes +')');
Lazarus.removeForms(ids);
}
}
/**
* completely remove the database
*/
Lazarus.killDB = function(){
Lazarus.file.kill("%profile%/lazarus.sqlite");
Lazarus.file.kill("%profile%/lazarus-backup.sqlite");
}
/**
* run any update functions
*/
Lazarus.runUpdates = function(prevVersion){
for(var key in Lazarus.updates){
if (Lazarus.versionCompare(key, prevVersion) == 1){
Lazarus.debug("running update "+ key);
try {
Lazarus.updates[key](prevVersion);
}catch(e){
Lazarus.error(e);
}
}
}
}
/**
* fire a "clear-private-data" event if the user has asked to clear private data on shutdown with no prompt
*/
Lazarus.fireClearPrivateDataOnShutdown = function(){
if (Lazarus.getPref("privacy.sanitize.sanitizeOnShutdown", false) && !Lazarus.getPref("privacy.sanitize.promptOnSanitize", true)){
Lazarus.Event.fire("clear-private-data");
}
}
/**
* clear private data if all the preferences say so
*/
Lazarus.onClearPrivateData = function(action){
if (Lazarus.getExtPref("privacy.item.saved.forms")){
Lazarus.emptyDB(Lazarus.FORM_TYPE_TEMPLATE);
Lazarus.debug("ClearPrivateData: removing all forms");
}
}
/**
* return TRUE if lazarus requires a password before data can be decrypted
*/
Lazarus.isPasswordSet = function(){
//we test this by attempting to decrypt the private key with a blank password
var encb64Key = Lazarus.db.getStr("SELECT value FROM settings WHERE name = 'private-key'");
//we need to unencrypt the private key
return Lazarus.Crypto.AESDecrypt(encb64Key, "") ? false : true;
}
/*
* encrypts a string
*/
Lazarus.encrypt = function(str){
return Lazarus.Crypto.encrypt(str);
}
/*
* encrypts a string
*/
Lazarus.decrypt = function(str){
return Lazarus.Crypto.decrypt(str);
}
/*
* encrypts a string
*/
Lazarus.encrypt2 = function(str){
//hmmm interesting, the encrypted string is not always the same.
//a certain amount of decryption information must be kept within the encrypted string
//so we cannot compare encrypted string against each other to test if they are the same
//all encrypted string must be DECRYPTED before comparison.
//Unable to decrypt extended characters (eg \u8888)!
//IMPORTANT: extended characters are not correctly encoded/decoded in the bsao function used by the encryptor, so (as of 2008-07-24)
//we'll be encoding all strings before encrypting them, and then decoding afterwards.
//by adding a 4 character header ("uri:") we can tell which type of string we have (encodeURIComponent).
Lazarus.decoderRing = Lazarus.decoderRing || Components.classes["@mozilla.org/security/sdr;1"].getService(Components.interfaces.nsISecretDecoderRing);
var encStr = encodeURIComponent(str);
return "uri:"+ Lazarus.decoderRing.encryptString(encStr);
}
/**
* decrypt a string
*/
Lazarus.decrypt2 = function(str){
//IMPORTANT: extended characters (eg \u8888) are not correctly encoded/decoded, so (as of 2008-07-24)
//we'll be encoding all strings before encrypting them, and then decoding afterwards.
//by inspecting the 4 character header we can tell which type of string we have.
Lazarus.decoderRing = Lazarus.decoderRing || Components.classes["@mozilla.org/security/sdr;1"].getService(Components.interfaces.nsISecretDecoderRing);
if (str.indexOf("uri:") == 0){
str = str.substr(4);
var strEnc = Lazarus.decoderRing.decryptString(str);
return decodeURIComponent(strEnc);
}
else {
return Lazarus.decoderRing.decryptString(str);
}
}
/**
* calulate md5 hash of a string
* ref: http://developer.mozilla.org/en/docs/nsICryptoHash#Computing_the_Hash_of_a_String
*/
Lazarus.md5 = function(str){
var nsICryptoHash = Components.classes['@mozilla.org/security/hash;1'].createInstance(Components.interfaces.nsICryptoHash);
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
// result is an out parameter,
// result.value will contain the array length
var result = {};
// data is an array of bytes
var data = converter.convertToByteArray(str, result);
nsICryptoHash.init(Components.interfaces.nsICryptoHash.MD5);
nsICryptoHash.update(data, data.length);
var hash = nsICryptoHash.finish(false);
// Unpack the binary data bin2hex style
var ascii = [];
var len = hash.length;
for (var i = 0; i < len; ++i) {
var c = hash.charCodeAt(i);
var ones = c % 16;
var tens = c >> 4;
ascii.push(String.fromCharCode(tens + (tens > 9 ? 87 : 48)) + String.fromCharCode(ones + (ones > 9 ? 87 : 48)));
}
return ascii.join('');
}
/**
* return TRUE if master password is set
*/
Lazarus.isMasterPasswordSet = function(){
var secmodDB = Components.classes["@mozilla.org/security/pkcs11moduledb;1"].getService(Components.interfaces.nsIPKCS11ModuleDB);
var slot = secmodDB.findSlotByName("");
return (slot && (slot.status == Components.interfaces.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN || slot.status == Components.interfaces.nsIPKCS11Slot.SLOT_LOGGED_IN));
}
/**
* return TRUE if a user has set the firefox master password and has not yet logged in.
*/
Lazarus.isMasterPasswordRequired = function(){
var secmodDB = Components.classes["@mozilla.org/security/pkcs11moduledb;1"].getService(Components.interfaces.nsIPKCS11ModuleDB);
var slot = secmodDB.findSlotByName("");
return (slot && slot.status == Components.interfaces.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN);
}
/**
*
*/
Lazarus.enterMasterPassword = function(){
//encrypting a string should open the enter master password dialog
try {
var decoderRing = Components.classes["@mozilla.org/security/sdr;1"].getService(Components.interfaces.nsISecretDecoderRing);
decoderRing.encryptString("dummy");
return true;
}
catch(e){
return false;
}
}
Lazarus.openGenerateKeysDialog = function(){
window.open("chrome://lazarus/content/generate-keys.xul", "LazarusGenerateKeys", "chrome,dialog,modal,resizable,centerscreen");
}
/**
* dictionary of notification to display in the popup.
*/
Lazarus.notifications = {};
Lazarus.notifications["password-required"] = {
"buttons" : [
{
"label-string": "notification.password.required.button1.label",
"accessKey-string": "notification.password.required.button1.accesskey",
"callback": function(notif, label){
Lazarus.showEnterPasswordDialog();
Lazarus.refreshIcon();
}
}
],
"text-string" : "notification.password.required.text",
"iconUrl" : "chrome://lazarus/skin/lazarus-error.png"
}
Lazarus.notifications["password-required-autofill"] = {
"buttons" : [
{
"label-string": "notification.password.required.autofill.button1.label",
"accessKey-string": "notification.password.required.autofill.button1.accesskey",
"callback": function(notif, label){
Lazarus.showEnterPasswordDialog();
Lazarus.refreshIcon();
Lazarus.autofillDoc(content.document);
}
}
],
"text-string" : "notification.password.required.autofill.text",
"iconUrl" : "chrome://lazarus/skin/lazarus-error.png"
}
Lazarus.notifications["form-restored"] = {
"buttons" : [
{
"label-string": "notification.form.restored.button1.label",
"accessKey-string": "notification.form.restored.button1.accesskey",
"callback": function(notif, label){
var data = Lazarus.$('lazarus-notification').getAttribute('lazarus-data');
var page = 'donate.html';
page += (data) ? ('?'+ data) : '';
Lazarus.openLazarusWebsite(page);
}
}
],
"text-string" : "notification.form.restored.text",
"iconUrl" : "chrome://lazarus/skin/lazarus.png"
}
/**
* displays the lazarus notification box
*/
Lazarus.showNotificationBox = function(id, text, data){
var notif = Lazarus.notifications[id];
if (Lazarus.$('lazarus-notification').currentNotification && Lazarus.$('lazarus-notification').currentNotificationId == id){
//do nothing, notification box is already shown
}
else if (notif){
//translate text, and buttons
if (text){
notif["text"] = text;
}
else if (!notif["text"] && notif["text-string"]){
notif["text"] = Lazarus.getString(notif["text-string"]);
}
for (var i=0; i<notif.buttons.length; i++){
var button = notif.buttons[i];
if (!button["label"] && button["label-string"]){
button["label"] = Lazarus.getString(button["label-string"]);
}
if (!button["accessKey"] && button["accessKey-string"]){
button["accessKey"] = Lazarus.getString(button["accessKey-string"]);
}
}
var notificationBox = Lazarus.$('lazarus-notification');
notificationBox.setAttribute('lazarus-data', data || '');
notificationBox.currentNotificationId = id;
notificationBox.appendNotification(notif["text"], id, notif["iconUrl"], notificationBox.PRIORITY_INFO_LOW, notif["buttons"]);
}
else {
Lazarus.error(Error("Lazarus: invalid notification id ["+ id +"]"));
}
}
/**
* closes the notification box
*/
Lazarus.closeNotificationBox = function(immediate){
Lazarus.$('lazarus-notification').removeAllNotifications(immediate);
}
/**
* returns a list of the templates found in the database
*/
Lazarus.getTemplateNames = function(){
return Lazarus.db.getColumn("SELECT formname FROM forms WHERE savetype = "+ Lazarus.FORM_TYPE_TEMPLATE +" ORDER BY formname");
}
/**
* handle user clicking on the "Save form as template..." menuitem
*/
Lazarus.onSaveFormMenuItem = function(){
//find the current form
var form = Lazarus.findFormFromElement(gContextMenu.target, "form");
//and show the save as template dialog
if (form){
var info = Lazarus.formInfo(form, Lazarus.FORM_TYPE_NORMAL);
var args = {};
args.templateName = '';
//add the names of the current templates within the database
args.templateNames = Lazarus.getTemplateNames();
//suggest using the default name for this form
args.defaultName = Lazarus.getMenuItemText(info.formtext) || ("["+ Lazarus.getString("untitled") +"]");
//WYSIWYG and TEXTAREAs cannot be autofilled
args.isTextarea = form.isTextarea;
args.isIframe = form.isIframe;
//IMPORTANT: must use open dialog here otherwise the arguments are not passed.
window.openDialog("chrome://lazarus/content/template-save.xul", "LazarusTemplateSave", "chrome,modal,resizable", args);
if (args.templateName){
Lazarus.saveForm(form, Lazarus.FORM_TYPE_TEMPLATE, args.templateName, args.autofill);
}
}
//or throw an error
else {
alert(Lazarus.getString("error.form.object.not.found"));
Lazarus.error(Error("Unable to find form in the document"));
}
}
/**
* compare two version numbers (eg 2.0.0.1beta3, 2.0.0rc1)
* return 1 if version1 > version2
* return 0 if version1 == version2
* return -1 if version1 < version2
*/
Lazarus.versionCompare = function(version1, version2){
function getValueOfVersionSegment(seg){
if (typeof seg === "undefined"){
return 0;
}
else if (/^\d+$/.test(seg)){
return parseInt(seg);
}
else {
switch(seg){
case "rc": return -1;
case "beta": return -2;
case "alpha": return -3;
case "dev": return -4;
default:
throw Error("Lazarus.versionCompare: Unknown version fragment ["+ seg +"]");
}
}
}
//verify version strings
var regexVerify = /^[\d\.(dev|alpha|beta|rc)]+$/i;
if (!regexVerify.test(version1)){
Lazarus.error("Lazarus.versionCompare: Invalid version string ["+ version1 +"]");
version1 = "0";
}
else if (!regexVerify.test(version2)){
Lazarus.error("Lazarus.versionCompare: Invalid version string ["+ version2 +"]");
version2 = "0";
}
//split each version into sections
var ver1 = version1.toLowerCase().replace(/(\w)(\d)/g, "$1.$2").replace(/(\d)(\w)/g, "$1.$2").split(/[\.\b]/g);
var ver2 = version2.toLowerCase().replace(/(\w)(\d)/g, "$1.$2").replace(/(\d)(\w)/g, "$1.$2").split(/[\.\b]/g);
//compare each section until a non match occurs
var maxLen = Math.max(ver1.length, ver2.length);
for (var i=0; i<maxLen; i++){
//convert the version segment into a numeric value
var seg1 = (typeof ver1[i] === "undefined") ? 0 : getValueOfVersionSegment(ver1[i]);
var seg2 = (typeof ver2[i] === "undefined") ? 0 : getValueOfVersionSegment(ver2[i]);
if (seg1 > seg2){
return 1;
}
else if (seg1 < seg2){
return -1;
}
}
//all parts are equal
return 0;
}
/**
* return the path to this extension install directory
* gives correct path even if extension is not installed in a users profile directory.
*/
Lazarus.getExtensionDir = function(extId){
// the extension's id from install.rdf
extId = extId || Lazarus.guid;
var em = Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager);
return em.getInstallLocation(extId).getItemFile(extId, "install.rdf").parent.path;
}
/**
* check the website to see if there is a new update available
*/
Lazarus.checkForUpdates = function(){
//never check more than once a day
if (Lazarus.getExtPref("lastUpdateCheck") + (24 * 60 * 60) < Lazarus.timestamp()){
Lazarus.setExtPref("lastUpdateCheck", Lazarus.timestamp());
// create browser
var browser = document.createElement("browser");
browser.collapsed = true;
//browser.style.height = "0px";
document.documentElement.appendChild(browser);
// set restrictions as needed
browser.webNavigation.allowAuth = false;
browser.webNavigation.allowImages = true;
browser.webNavigation.allowJavascript = true; // so the google analytic code works
browser.webNavigation.allowMetaRedirects = false;
browser.webNavigation.allowPlugins = false;
browser.webNavigation.allowSubframes = false;
// listen for load
browser.addEventListener("DOMContentReady", function(evt){
// the document of the HTML in the DOM
var doc = evt.originalTarget;
//check URL (could be about:blank)
if (doc && doc.URL != "about:blank"){
var ele = doc.getElementById('latest-stable');
if (ele && ele.innerHTML){
Lazarus.setExtPref("lastestStableVersion", ele.innerHTML);
}
var ele = doc.getElementById('latest-beta');
if (ele && ele.innerHTML){
Lazarus.setExtPref("lastestBetaVersion", ele.innerHTML);
}
// remove browser element when done
//mouse pointer has loading symbol unless we do this?
browser.contentDocument.location = "about:blank";
browser.parentNode.removeChild(browser);
Lazarus.updateVersionIcons();
}
}, true);
// load update check
browser.contentDocument.location.href = "http://lazarus.interclue.com/version-check.php";
//check again tomorrow
setTimeout(Lazarus.checkForUpdates, 24 * 60 * 60 * 1000);
}
else {
//didn't check, so check again in 10 minutes
setTimeout(Lazarus.checkForUpdates, 10 * 60 * 1000);
}
}
/**
* displays the "new beta" or "new stable" icons is a newer version is available.
*/
Lazarus.updateVersionIcons = function(){
var stable = Lazarus.getExtPref("lastestStableVersion", "");
var beta = Lazarus.getExtPref("lastestBetaVersion", "");
var thisVersion = Lazarus.getVersionStr();
if (beta && Lazarus.getExtPref("checkForUpdatesBeta") && Lazarus.versionCompare(thisVersion, beta) == -1){
//show the new beta icon
Lazarus.$("lazarus-update-beta-statusbar-image").hidden = false;
}
else if (stable && Lazarus.getExtPref("checkForUpdates") && Lazarus.versionCompare(thisVersion, stable) == -1){
//show the new stable version icon
Lazarus.$("lazarus-update-stable-statusbar-image").hidden = false;
}
}
/**
* attempts to download the latest versin of Lazarus
*/
Lazarus.getUpdate = function(version){
var urls = {
"stable": "http://lazarus.interclue.com/downloads/lazarus.xpi",
"beta" : "http://lazarus.interclue.com/downloads/lazarus-beta.xpi"
}
var url = urls[version] ? urls[version] : urls["stable"];
Lazarus.getBrowser().content.location = url;
}
/**
* return TRUE if the user is in private Browsing mode (Fx 3.1+)
*/
Lazarus.isPrivateBrowsing = function(){
try {
return Components.classes["@mozilla.org/privatebrowsing;1"] && Components.classes["@mozilla.org/privatebrowsing;1"].getService(Components.interfaces.nsIPrivateBrowsingService).privateBrowsingEnabled;
}
catch(e){
return false;
}
}
/**
* return TRUE if Lazarus is currently disabled because the user is in private browsing mode
*/
Lazarus.isDisabledByPrivateBrowsing = function(){
return (Lazarus.isPrivateBrowsing() && !Lazarus.getPref("extensions.lazarus.enableInPrivateBrowsingMode"));
}
/**
* return an FNV1a hash of the given string
*/
Lazarus.FNV1a = function(str, seed){
//hash = offset_basis
var hash = seed ? parseInt(seed, 16) : 2166136261;
//only calculate the length once
var len = str.length;
//for each octet_of_data to be hashed
for (var i=0; i<len; i++){
hash ^= str.charCodeAt(i);
//hash = hash * FNV_prime (apparently this bitshifting does the same thing)
hash += ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24));
}
return Math.abs(Number(hash & 0x00000000ffffffff)).toString(16);
}
Lazarus.openTextManager = function(){
//if not logged in close window
if (!Lazarus.canDecrypt()){
//try loggin in immediately
Lazarus.showEnterPasswordDialog();
}
//if they still can't login do nothing
if (Lazarus.canDecrypt()){
window.open("chrome://lazarus/content/text-manager.xul", "", "chrome,centerscreen,resizable");
}
}
/**
*
**/
Lazarus.refreshMenuIcons = function(){
var items = {
'lazarus-enterpassword-contextmenuitem': 'chrome://lazarus/skin/lazarus-login.png',
'lazarus-restoretext-submenu': 'chrome://lazarus/skin/lazarus.png',
'lazarus-restoretextdisabled-contextmenuitem': 'chrome://lazarus/skin/lazarus-disable.png',
'lazarus-restoreform-contextmenuitem': 'chrome://lazarus/skin/lazarus.png',
'lazarus-restoreform-submenu': 'chrome://lazarus/skin/lazarus.png',
'lazarus-submenu-menuitem-donate': 'chrome://lazarus/skin/donate.png',
'lazarus-saveform-contextmenuitem': 'chrome://lazarus/skin/lazarus-save.png',
'lazarus-domaindisabled-contextmenuitem': 'chrome://lazarus/skin/lazarus-disabled.png',
'lazarus-privatebrowsing-contextmenuitem': 'chrome://lazarus/skin/lazarus-disabled.png'
};
var show = Lazarus.getPref('extensions.lazarus.showContextMenuIcons');
for(var id in items){
Lazarus.setMenuIcon(document.getElementById(id), show ? items[id] : null);
}
}
/**
*
*/
Lazarus.setMenuIcon = function(ele, iconURL){
var classname = ele.tagName.toLowerCase() +"-iconic";
if (iconURL){
ele.setAttribute("image", iconURL);
Lazarus.addClass(ele, classname);
}
else {
//remove the icon
ele.setAttribute("image", "");
Lazarus.removeClass(ele, classname);
}
}
/**
* return TRUE if class exists
*/
Lazarus.classExists = function(ele, classname){
var currName = ele.getAttribute('class') || '';
return ((" "+ currName.toLowerCase() +" ").indexOf(" "+ classname.toLowerCase() +" ") > -1);
}
/**
* adds a classname to an element if the name doesn't exist
*/
Lazarus.addClass = function(ele, classname){
if (!Lazarus.classExists(ele, classname)){
ele.setAttribute('class', (ele.getAttribute('class') || '') +" "+ classname);
}
}
/**
* remove a classname from an element
*/
Lazarus.removeClass = function(ele, classname){
if (Lazarus.classExists(ele, classname)){
var newClassname = ele.getAttribute('class').toLowerCase().replace(classname.toLowerCase(), "").replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '');
ele.setAttribute('class', newClassname);
}
}
/**
*
*/
Lazarus.test = function(evt){
Lazarus.cleanDB();
return;
alert(1);
Lazarus.backgroundTask(function(){
var x = 1234
for(var i=0; i<20000000; i++){
x = x/100;
}
//try throwing an error in the middle of the function
x = a.b.c;
return x;
}, function(result){
alert(result);
})
return;
alert("Private = "+ Lazarus.isPrivateBrowsing());
return;
//~ Unit.assert("Lazarus.canEncrypt()", true, "should be able to encrypt");
//~ alert(Unit.getLog());
//~ return;
var len = 10;
var p = new Profiler("decrypt");
var enc = [];
var dec = [];
for (var i=0; i<len; i++){
enc[i] = Lazarus.encrypt("abc"+ Math.random());
}
p.mark("encrypted "+ len +" strings");
for (var i=0; i<len; i++){
dec[i] = Lazarus.decrypt(enc[i]);
}
debug(p.stop("decrypted"));
return;
debug(Lazarus.db.rs("SELECT hashed_text FROM textdata_fulltext LIMIT 1"));
return;
Lazarus.openTextManager();
return;
var str = prompt("Enter search string");
if (str){
debug(str, Lazarus.hashQuery(str));
var rs = Lazarus.db.rs("SELECT id, text_encrypted FROM textdata JOIN textdata_fulltext ON textdata.id = textdata_fulltext.docid WHERE textdata_fulltext.hashed_text MATCH ?1", Lazarus.hashQuery(str));
debug(rs);
var text = '';
for (var i=0; i<rs.length; i++){
text += rs[i].id +":"+ Lazarus.decrypt(rs[i].text_encrypted) +"\n";
}
alert(text ? text : "== no results ==");
}
return;
alert(Lazarus.hash("chongo <Landon Curt Noll> /\\../\\"));
return;
var p = new Profiler('encrypt large file');
var s = Lazarus.file.readBinary("%profile%/test.sqlite");
p.mark("Read file");
var enc = Lazarus.encrypt(s);
p.mark("Encrypt file");
var dec = Lazarus.decrypt(enc);
p.mark("Decrypt file : "+ (dec == s));
//Lazarus.file.readBinary("%profile%/test.sqlite");
alert(p.stop("finished"));
return;
alert(Lazarus.enterMasterPassword());
return;
alert(1);
Lazarus.isMasterPasswordSet();
alert(2);
Lazarus.isMasterPasswordSet();
return;
Lazarus.db.exe("DELETE FROM settings WHERE name = 'public-key' OR name = 'private-key'");
return;
var p = new Profiler("encryption");
var str = "{testing...}";
var str = '';
for (var i=0; i<200000; i++){
str += "A";
}
var enc = Lazarus.Crypto.encrypt(str);
debug(enc);
p.mark("Encrypted string (Hybrid) of length "+ str.length);
var dec = Lazarus.Crypto.decrypt(enc);
p.mark("Decrypted string (Hybrid): "+ ((dec == str) ? "success" : "FAILED!"));
//compare with SSD encryption
var enc = Lazarus.encrypt(str);
p.mark("Encrypted string (SSD) of length "+ str.length);
var dec = Lazarus.decrypt(enc);
alert(p.stop("Decrypted string (SSD): "+ ((dec == str) ? "success" : "FAILED!")));
return;
var keyPair = Lazarus.Crypto.generateRSAKeyPair("{password}");
p.mark("Generate RSA key pair");
var symKey = Lazarus.Crypto.crypto.generateRandomKey();
//attempting to amke a smaller symkey
symKey = btoa(atob(symKey).substr(0, 16));
p.mark("Generate symetric key ["+ atob(symKey).length +" characters]");
var wrapped = Lazarus.Crypto.crypto.wrapSymmetricKey(symKey, keyPair.pubkey);
p.mark("RSA Encrypt symetric key");
var unwrapped = Lazarus.Crypto.crypto.unwrapSymmetricKey(wrapped, keyPair.privkey,
keyPair.password,
keyPair.passphraseSalt,
keyPair.privkeyWrapIV);
p.mark("RSA Decrypt symetric key");
debug(unwrapped, symKey == unwrapped);
alert(p.stop(symKey == unwrapped ? "success" : "failed!"));
return;
var str = '';
for (var i=0; i<200000; i++){
str += "A";
}
str = "a very short string.";
//speed test
var p = new Profiler("Encrypting a str of length :"+ str.length);
var keypair = Lazarus.Crypto.generateKeyPair(512);
p.mark("generate RSA key pair (512 bit)");
debug(Lazarus.Crypto.crypto);
var enc = Lazarus.Crypto.encrypt(str, keypair.public);
p.mark("hybrid encrypt a string");
var dec = Lazarus.Crypto.decrypt(enc, keypair.private);
debug(dec);
p.mark("hybrid decrypt a string"+ ((str == dec) ? "success" : "FAILED!"));
alert(p.stop());
//var enc = Lazarus.Crypto.encrypt("a string \u5555");
//debug(atob(Lazarus.Crypto.crypto.generateRandomIV()));
//debug(atob(Lazarus.Crypto.crypto.generateRandomKey()));
return;
//labs.mozilla.com/Weave/Crypto
//interclue.com/Lazarus/Crypto2
try {
var xx = Components.classes["@labs.mozilla.com/Weave/Crypto;1"].
createInstance(Components.interfaces.IWeaveCrypto);
}
catch(e){
if (confirm("Failed to create Weave:crypto object, weave not installed?\nInstall Weave now?")){
Lazarus.openURL('http://dev.izeal.com/xpi/lazarus/weave-0.2.98.xpi', true, true);
}
}
debug(xx);
return;
var randIV = xx.generateRandomIV();
//debug(randIV);
var randKey = xx.generateRandomKey();
//debug(randKey);
var str = '';
for (var i=0; i<200000; i++){
str += "A";
}
//speed test
var p = new Profiler("Encrypting a str of length :"+ str.length);
var enc = xx.encrypt(str, randKey, randIV);
p.mark("weave (aes-256-cbc): encrypted");
var dec = xx.decrypt(enc, randKey, randIV);
p.mark("weave (aes-256-cbc): decrypted : "+ ((str == dec) ? "success" : "FAILED!"));
var enc = Lazarus.encrypt(str);
p.mark("SecretKeyRing: encrypted");
var dec = Lazarus.decrypt(enc);
p.mark("SecretKeyRing: decrypted : "+ ((str == dec) ? "success" : "FAILED!"));
alert(p.stop());
//alert("Private = "+ Lazarus.isPrivateBrowsing());
return;
//~ var pbs = Components.classes["@mozilla.org/privatebrowsing;1"].getService(Components.interfaces.nsIPrivateBrowsingService);
//~ // are we currently in the Private Browsing mode?
//~ var inPrivateBrowsingMode = pbs.privateBrowsingEnabled;
//~ alert(inPrivateBrowsingMode);
//~ return;
//~ var pbs = Components.classes["@mozilla.org/browser/privatebrowsing;1"]
//~ .getService(Components.interfaces.nsIPrivateBrowsingService);
//~ pbs.privateBrowsing = !pbs.privateBrowsing;
//~ alert(pbs.privateBrowsing);
//~ return;
//http://lazarus.interclue.com/
var url = "http://tinyurl.com/5qhvvw";
var log = [];
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("HEAD", url);
xmlhttp.onreadystatechange = function(){
try {
//debug(xmlhttp.readyState);
//debug(xmlhttp.status);
//debug(xmlhttp.getAllResponseHeaders());
}catch(e){}
if (xmlhttp.readyState == 4){
Lazarus.dump(xmlhttp.channel.originalURI);
Lazarus.dump(xmlhttp.channel.URI);
}
}
xmlhttp.send(null);
return;
//Lazarus.checkForUpdates();
return;
alert(decodeURIComponent(encodeURIComponent("\u2663")))
var str = "\u2663";
alert(str +" ["+ str.length +"]");
var enc = Lazarus.encrypt(str);
alert(enc);
var s = Lazarus.decrypt(enc);
alert(s);
//~ var alertsService = Components.classes["@mozilla.org/alerts-service;1"]
//~ .getService(Components.interfaces.nsIAlertsService);
//~ alertsService.showAlertNotification("chrome://mozapps/skin/downloads/downloadIcon.png",
//~ "Alert title", "Alert text goes here.",
//~ false, "", null);
//~ function setToNormalZ(aWindow) {
//~ aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
//~ var webnav = aWindow.getInterface(Components.interfaces.nsIWebNavigation);
//~ var dsti = webnav.QueryInterface(Components.interfaces.nsIDocShellTreeItem);
//~ var treeowner = dsti.treeOwner;
//~ var ifreq = treeowner.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
//~ var xulwin = ifreq.getInterface(Components.interfaces.nsIXULWindow);
//~ xulwin.zLevel = 9;
//~ }
//~ var win = window.open("chrome://lazarus/content/test.xul", "test", "chrome");
//~ setTimeout(function(){
//~ //
//~ //alert("a");
//~ setToNormalZ(win);
//~ }, 3000);
//~ <?xml-stylesheet href="chrome://global/skin/alerts/alert.css" type="text/css"?>
//~ <window id="alertNotification"
//~ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
//~ windowtype="alert:alert"
//~ xmlns:xhtml2="http://www.w3.org/TR/xhtml2"
//~ xmlns:wairole="http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#"
//~ xhtml2:role="wairole:alert"
//~ align="start"
//~ onload="onAlertLoad()">
//~ if (Lazarus.state == Lazarus.STATE_DISABLED){
//~ Lazarus.enable();
//~ }
//~ else {
//~ Lazarus.disable();
//~ }
//Lazarus.warning("test");
//Lazarus.updates["1.0.1beta4"]();
//~ Lazarus.debug(Lazarus.getBaseDomain('www.127.0.0.1'));
//~ Lazarus.debug(Lazarus.getBaseDomain('domain.tv.com'));
//~ Lazarus.debug(Lazarus.getBaseDomain('domain.com.tv'));
//~ Lazarus.debug(Lazarus.getBaseDomain('domain.tv'));
//~ Lazarus.debug(Lazarus.getBaseDomain('domain.com'));
//~ Lazarus.debug(Lazarus.getBaseDomain('secure.email.website.co.uk'));
//~ Lazarus.debug(Lazarus.getBaseDomain('this.is.a.worst.shortly.subdomain.thisIsMyMainWebsite.com.cl'));
}
Lazarus.icon = "data:image/png;base64,\
iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAALHRFWHRDcmVhdGlvbiBUaW1lAFRo\
dSAxMiBOb3YgMjAwOSAxMzoxOToxNiArMTIwMJCaV2oAAAAHdElNRQfZCwwAFimayURVAAAACXBI\
WXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAASZJREFUeNq1ks1KAmEUhh+/GZkGs6IS\
gzYhhREEwbRw56IbSaIbaNe+K2gvFEWB11Cgm4LUKKGfRRqVpKVTpkJpOpOJow6ufVfn8D7nfC8f\
B4YlR39zesHc8wPrRpNx9xildJrw9hZZyxdWkUggV3XWLhPcn8XZcQoyX2W0/mVdODPSesWBojh5\
D+/yYTp4q+g93wZ//mA6Fc4XFlnaO8L38sS8XuIuEukxslWsSISaE3xLPvzVCv56g9rGJpVcltWW\
fWiDYzHU/Ct4vDy6RwlcX3Fyk4Ji0f4JbWlae1CKpwjsH3MQCrHciSkGMieTNEwTwzSYUhQmJRVv\
xzIG4H9Fo4haHa8QuFwqs8GgPYLc3xQ8iN9bpvM51GqZmUKhvaw5tJPo6g+jvlxgBqGatwAAAABJ\
RU5ErkJggg==";