add itsalltext
authorDon Armstrong <don@donarmstrong.com>
Mon, 9 Jul 2007 02:25:49 +0000 (02:25 +0000)
committerDon Armstrong <don@donarmstrong.com>
Mon, 9 Jul 2007 02:25:49 +0000 (02:25 +0000)
27 files changed:
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome.manifest [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/API.js [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/Color.js [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/about.xul [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/badeditor.js [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/badeditor.xul [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/cacheobj.js [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/icon.png [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/itsalltext.js [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/itsalltext.xul [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/newextension.js [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/newextension.xul [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/preferences.js [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/preferences.xul [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/about.dtd [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/badeditor.dtd [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/badeditor.properties [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/gumdrop.png [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/itsalltext.dtd [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/itsalltext.properties [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/newextension.dtd [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/preferences.dtd [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/preferences.properties [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/readme.xhtml [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/defaults/preferences/itsalltext.js [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/gpl.txt [new file with mode: 0644]
.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/install.rdf [new file with mode: 0644]

diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome.manifest b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome.manifest
new file mode 100644 (file)
index 0000000..6953b9f
--- /dev/null
@@ -0,0 +1,4 @@
+content itsalltext chrome/content/
+locale itsalltext en-US chrome/locale/en-US/
+
+overlay chrome://browser/content/browser.xul chrome://itsalltext/content/itsalltext.xul
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/API.js b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/API.js
new file mode 100644 (file)
index 0000000..2e862cd
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+  This file is used to allow external editors to work inside your chrome XUL.
+
+  ***** How To Use This API *****
+  Add this script line to your .xul file:
+  <script type="application/javascript" src="chrome://itsalltext/content/API.js"/>
+
+  If "It's All Text!" isn't installed in the browser, it will fail safely.
+  It only generates an info message in the error console.
+
+  You then have two choices.  You can call ItsAllTextopenEditor() directly
+  via JavaScript or you can add one or two attributes to a XUL element and
+  it'll automatically be set up right.
+
+  The suggested method is to add the correct attributes to your XUL button 
+  or menuitem and let "It's All Text!" do it for you.
+
+  Attributes:
+    'itsalltext-control' -- This should be set to the id of the textbox
+                            that you want to edit when command is executed
+                            on this XUL element. This is required.
+    'itsalltext-extension' -- This is the file extension.  Include the
+                              leading dot character.  Example: '.css'
+                              It defaults to '.txt' and is optional.
+
+  If you don't want this XUL element to be visible unless "It's All Text!"
+  is installed, then you should set it's CSS style display to 'none'.
+
+  Example using attributes (recommended method):
+      <hbox>
+        <spacer flex="1"/>
+        <button label="It's All Text!"
+                itsalltext-control="code"
+                itsalltext-extension=".css"
+                style="display: none;"
+        />
+      </hbox>
+
+  Example calling openEditor() directly:
+     if(some_condition && ItsAllText) {
+         ItsAllText.openEditor('id-of-textarea', '.extension');
+     }
+
+ */
+
+(function () {
+    /* Load up the main It's All Text! file */
+    var objScriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
+    objScriptLoader.loadSubScript('chrome://itsalltext/content/itsalltext.js');
+
+    var onload = function (event) {
+        /* Start watching the document, but force it. */
+        ItsAllText.monitor.watch(document, true);
+
+        /* Turn on all the hidden CSS */
+        var nodes = [];
+        var nodesIter = document.evaluate("//node()[@itsalltext-control]",
+                                          document, null,
+                                          XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null); 
+
+        var node = nodesIter.iterateNext(); 
+        while (node) {
+            nodes.push(node);
+            node = nodesIter.iterateNext();
+        }
+        var command = function(event) {
+            ItsAllText.openEditor( this.getAttribute("itsalltext-control"),
+                                   this.getAttribute("itsalltext-extension") );
+            return false;
+        };
+        for(i in nodes) {
+            node = nodes[i];
+            node.addEventListener('command', command, true);
+            node.style.display = '-moz-box';
+        }
+
+    };
+    window.addEventListener("load", onload, true);
+})();
+
+/**
+ * This is part of the public XUL API.
+ * Use this to open an editor for a specific textarea or textbox with
+ * the id 'id'.  The file will have the extension 'extension'.  Include 
+ * the leading dot in the extension.
+ * @param {String} id The id of textarea or textbody that should be opened in the editor.
+ * @param {String} extension The extension of the file used as a temporary file. Example: '.css' (optional) 
+ */
+ItsAllText.openEditor = function(id, extension) {
+    var node = document.getElementById(id);
+    /* The only way I can adjust the background of the textbox is
+     * to turn off the -moz-appearance attribute.
+     */
+    node.style.MozAppearance = 'none';
+    var cache_object = node && ItsAllText.getCacheObj(node);
+    if(!cache_object) { return; }
+    cache_object.edit(extension);
+};
+
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/Color.js b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/Color.js
new file mode 100644 (file)
index 0000000..1426f1d
--- /dev/null
@@ -0,0 +1,231 @@
+/**
+ * Author: Lachlan Hunt
+ * Date: 2005-11-24
+ * Version: 1.0-cgh1
+ * Contributor: Christian G. Höltje
+ *
+ * Licence: Public Domain
+ * Attribution is considered ethical, but not required.
+ *
+ * Usage:
+ *   Color(255, 255, 255);
+ *   Color(255, 255, 255, 1.0);
+ *   Color("#FFF");
+ *   Color("#FFFFFF");
+ *   Color("rgb(255, 255, 255)");
+ *   Color("rgba(255, 255, 255, 1.0)");
+ *   Color("white"); - CSS 2.1 Color keywords only
+ */
+var Color = function() {
+
+    // CSS 2.1 Colour Keywords
+       var keyword = {
+        maroon   : "#800000",
+        red      : "#ff0000",
+        orange   : "#ffA500",
+        yellow   : "#ffff00",
+        olive    : "#808000",
+        purple   : "#800080",
+        fuchsia  : "#ff00ff",
+        white    : "#ffffff",
+        lime     : "#00ff00",
+        green    : "#008000",
+        navy     : "#000080",
+        blue     : "#0000ff",
+        aqua     : "#00ffff",
+        teal     : "#008080",
+        black    : "#000000",
+        silver   : "#c0c0c0",
+        gray     : "#808080"
+    };
+
+    // CSS Functional Notations and Hex Patterns
+       var func = {
+        rgb   : /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\);?$/,
+        "rgb%"  : /^rgb\(\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\);?$/,
+        rgba  : /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*((?:\d+(?:\.\d+)?)|(?:\.\d+))\s*\);?$/,
+        "rgba%" : /^rgba\(\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*((?:\d+(?:\.\d+)?)|(?:\.\d+))\s*\);?$/,
+        hex3  : /^#([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f]);?$/,
+        hex6  : /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2});?$/
+    };
+
+    /**
+        * Clamp the value between the low value and the high value
+     * @private
+        */
+       var clamp = function(value, low, high) {
+               if (value < low) {
+                       value = low;
+               }
+               else if (value > high) {
+                       value = high;
+               }
+               return value;
+       };
+
+    /**
+     * @private
+     */
+    var alphaBlend = function(forground, background, alpha) {
+               return Math.round(background * (1.0 - alpha) + forground * (alpha));
+       };
+
+       /*
+        * Return the colour in hexadecimal notation: #RRGGBB. e.g. #FF9933
+        * @param bg - Optional parameter used for calculating the colour if an alpha value less than 1.0 has been specified.
+        *             If not specified, the alpha value will be ignored.
+        */
+    this.hex = function(bg) {
+        var r, g, b;
+               if (bg) {
+                       r = alphaBlend(this.red, bg.red, this.alpha);
+                       g = alphaBlend(this.green, bg.green, this.alpha);
+                       b = alphaBlend(this.blue, bg.blue, this.alpha);
+               } else {
+                       r = this.red;
+                       g = this.green;
+                       b = this.blue;
+               }
+               
+               var strHexR = r.toString(16).toUpperCase();
+               var strHexG = g.toString(16).toUpperCase();
+               var strHexB = b.toString(16).toUpperCase();
+       
+               if (strHexR.length < 2) { strHexR = "0" + strHexR; }
+               if (strHexG.length < 2) { strHexG = "0" + strHexG; }
+               if (strHexB.length < 2) { strHexB = "0" + strHexB; }
+       
+               return "#" + strHexR + strHexG + strHexB;
+       };
+
+    /**
+        * Return the colour in CSS rgb() functional notation, using integers 0-255: rgb(255, 255 255);
+        * @param bg - Optional parameter used for calculating the colour if an alpha value less than 1.0 has been specified.
+        *             If not specified, the alpha value will be ignored.
+        */
+       this.rgb = function(bg) {
+        var r, g, b;
+               if (bg) {
+                       r = alphaBlend(this.red, bg.red, this.alpha);
+                       g = alphaBlend(this.green, bg.green, this.alpha);
+                       b = alphaBlend(this.blue, bg.blue, this.alpha);
+               } else {
+                       r = this.red;
+                       g = this.green;
+                       b = this.blue;
+               }
+       
+               return "rgb(" + r + ", " + g + ", " + b + ")";
+       };
+
+    /**
+        * Return the colour in CSS rgba() functional notation, using integers 0-255 for color components: rgb(255, 255 255, 1.0);
+        * @param bg - Optional parameter used for calculating the colour if an alpha value less than 1.0 has been specified.
+        *             If not specified, and there is an alpha value, black will be used as the background colour.
+        */
+       this.rgba = function() {
+               return "rgba(" + this.red + ", " + this.green + ", " + this.blue + ", " + this.alpha + ")";
+       };
+
+    /**
+     * Returns a Color object with the values inverted. Ignores alpha.
+     */
+    this.invert = function() {
+        return new Color("rgb(" + 
+                         (255 - this.red) + ", " + 
+                         (255 - this.green) + ", " + 
+                         (255 - this.blue) + ")");
+    };
+
+    /**
+        * Blend this colour with the colour specified and return a pallet with all the steps in between.
+        * @param color - The colour to blend with
+        * @param steps - The number of steps to take to reach the color.
+        */
+       this.blend = function(color, steps) {
+        var pallet = [];
+        var r, g, b, i;
+      
+        var step = {
+            red   : (alphaBlend(color.red, this.red, color.alpha) - this.red) / steps,
+            green : (alphaBlend(color.green, this.green, color.alpha) - this.green) / steps,
+            blue  : (alphaBlend(color.blue,  this.blue,  color.alpha) - this.blue) / steps
+        };
+        for (i = 0; i < steps + 1; i++) {
+            r = Math.round(this.red   + (step.red * i));
+            g = Math.round(this.green + (step.green * i));
+            b = Math.round(this.blue  + (step.blue * i));
+            pallet.push(new Color(r, g, b));
+        }
+        return pallet;
+       };
+     
+    /**
+        * Constructor function
+        */
+    this.toString = this.hex;
+    
+    var value;
+    var components, pattern;
+    var key, base, m;
+    var r, g, b, a;
+    if (arguments.length >= 3) {
+        /* r, g, b or r, g, b, a */
+        r = arguments[0];
+        g = arguments[1];
+        b = arguments[2];
+        a = arguments[3];
+      
+        this.red   = (!isNaN(r)) ? clamp(r, 0, 255) : 0;
+        this.green = (!isNaN(g)) ? clamp(g, 0, 255) : 0;
+        this.blue  = (!isNaN(b)) ? clamp(b, 0, 255) : 0;
+        this.alpha = (!isNaN(a)) ? clamp(a, 0.0, 1.0) : 1.0;
+    } else if (arguments.length == 1) {
+        /* CSS Colour keyword or value */
+        value = keyword[arguments[0]] ? keyword[arguments[0]] : arguments[0];
+      
+        for (key in func) {
+            if (func[key].test(value)) {
+                pattern = key;
+            }
+        }
+
+        components = value.match(func[pattern]);
+        base = 10;
+        m = 1; // Multiplier for percentage values
+      
+        switch (pattern) {
+        case "rgb%":
+        case "rgba%":
+            m = 2.55;
+            base = 10;
+            break;
+        case "rgb":
+        case "rgba":
+            base = 10;
+            break;
+        case "hex3":
+            components[1] = components[1] + "" + components[1];
+            components[2] = components[2] + "" + components[2];
+            components[3] = components[3] + "" + components[3];
+            base = 16;
+            break;
+        case "hex6":
+            base = 16;
+            break;
+        default:
+            components = [0, "255", "255", "255", "1.0"];
+        }
+      
+        this.red   = clamp(Math.round(parseInt(components[1],base) * m), 0, 255);
+        this.green = clamp(Math.round(parseInt(components[2],base) * m), 0, 255);
+        this.blue  = clamp(Math.round(parseInt(components[3],base) * m), 0, 255);
+      
+        if (isNaN(components[4])) {
+            this.alpha = 1;
+        } else {
+            this.alpha = clamp(parseFloat("0" + components[4]), 0.0, 1.0);
+        }
+    }
+};
+
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/about.xul b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/about.xul
new file mode 100644 (file)
index 0000000..e1dc5c3
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<!DOCTYPE window SYSTEM "chrome://itsalltext/locale/about.dtd" >
+<window    id="itsalltext_about"
+        title="&title;"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <vbox style="max-width: 40em">
+    <description style="white-space: pre; margin: 1em;">&description;</description>
+    <hbox align="right">
+      <button oncommand="window.close();" tabindex="1">
+        <description>&close;</description>
+      </button>
+    </hbox>
+  </vbox>  
+</window>
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/badeditor.js b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/badeditor.js
new file mode 100644 (file)
index 0000000..f0b8b2d
--- /dev/null
@@ -0,0 +1,40 @@
+/*jslint nomen: true, evil: false, browser: true */
+/**
+ * Pass back the values that that the user selected.
+ */
+function onOK() {
+    window['arguments'][0].out = {
+        do_preferences: true
+    };
+    return true;
+}
+function doOnload() {
+    var locale = document.getElementById("strings");
+    var params = window['arguments'][0];
+    var reason = document.getElementById('reason');
+    var textnode = '**error**';
+    /* Errors are from
+     * http://lxr.mozilla.org/seamonkey/source/xpcom/base/nsError.h#262 */
+    if(params.exception == 'NS_ERROR_FILE_INVALID_PATH' ||
+       params.exception == 'NS_ERROR_FILE_UNRECOGNIZED_PATH' ||
+       params.exception == 'NS_ERROR_FILE_TARGET_DOES_NOT_EXIST' ||
+       params.exception == 'NS_ERROR_FILE_INVALID_PATH' ||
+       params.exception == 'NS_ERROR_FILE_NOT_FOUND' ||
+       params.exception == 'NS_ERROR_FILE_NAME_TOO_LONG' ) {
+        textnode = locale.getFormattedString('bad.noent', [params.path]);
+    } else if(params.exception == 'NS_ERROR_FILE_ACCESS_DENIED' ||
+              params.exception == 'NS_ERROR_FILE_IS_DIRECTORY' ||
+              params.exception == 'NS_ERROR_FILE_IS_LOCKED' ) {
+        textnode = locale.getFormattedString('bad.noexec', []);
+
+    /* At this point, we don't know exactly why it failed...
+     * Try some heuristics. */
+    } else if(!params.path) {
+        textnode = locale.getFormattedString('bad.noset',[]);
+    } else if(params.exists) {
+        textnode = locale.getFormattedString('bad.noexec', []);
+    } else {
+        textnode = locale.getFormattedString('bad.noent', [params.path]);
+    }
+    reason.appendChild(document.createTextNode(textnode));
+}
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/badeditor.xul b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/badeditor.xul
new file mode 100644 (file)
index 0000000..1267ca3
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<!DOCTYPE badeditorwindow SYSTEM "chrome://itsalltext/locale/badeditor.dtd" >
+
+<dialog
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  id="ItsAllTextBadEditor"
+  title="&title;"
+  buttonlabelaccept="&pref.label;"
+  ondialogaccept="return onOK();"
+  onload="doOnload();"
+  >
+
+  <script type="application/x-javascript" src="chrome://itsalltext/content/badeditor.js"/>
+  <stringbundleset id="strbundles">
+    <stringbundle id="strings" src="chrome://itsalltext/locale/badeditor.properties"/>
+  </stringbundleset>
+
+  <dialogheader title="&header;"/>
+  <vbox>
+    <description>&helptext;</description>
+    <spacer flex="1"/>
+    <groupbox flex="1">
+      <caption label="&reason;"/>
+      <description id="reason" style="max-width: 30em;">
+      </description>
+    </groupbox>
+  </vbox>
+    
+</dialog>
+<!-- Local Variables: -->
+<!-- mode: xml -->
+<!-- End: -->
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/cacheobj.js b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/cacheobj.js
new file mode 100644 (file)
index 0000000..1f5a1ce
--- /dev/null
@@ -0,0 +1,589 @@
+/**
+ * A Cache object is used to manage the node and the file behind it.
+ * @constructor
+ * @param {Object} node A DOM Node to watch.
+ */
+function CacheObj(node) {
+    var that = this;
+
+    /* Gumdrop Image URL */
+    that.gumdrop_url    = 'chrome://itsalltext/locale/gumdrop.png';
+    /* Gumdrop Image Width */
+    that.gumdrop_width  = ItsAllText.localeString('gumdrop.width'); 
+    /* Gumdrop Image Height */
+    that.gumdrop_height = ItsAllText.localeString('gumdrop.height');
+
+    that.timestamp = 0;
+    that.size = 0;
+    that.node = node;
+    that.button = null;
+    that.initial_background = '';
+    that._is_watching = false;
+     
+    that.node_id = that.getNodeIdentifier(node);
+    var doc = node.ownerDocument;
+
+    /* This is a unique identifier for use on the web page to prevent the
+     * web page from knowing what it's connected to.
+     * @type String
+     */
+    that.uid = that.hashString([ doc.location.toString(),
+                                 Math.random(),
+                                 that.node_id ].join(':'));
+    // @todo [security] Add a serial to the uid hash.
+
+    node.setAttribute(ItsAllText.MYSTRING+'_UID', that.uid);
+    ItsAllText.tracker[that.uid] = that;
+    
+    /* Figure out where we will store the file.  While the filename can
+     * change, the directory that the file is stored in should not!
+     */
+    var host = window.escape(doc.location.hostname);
+    var hash = that.hashString([ doc.location.protocol,
+                                 doc.location.port,
+                                 doc.location.search,
+                                 doc.location.pathname,
+                                 that.node_id ].join(':'));
+    that.base_filename = [host, hash.slice(0,10)].join('.');
+    /* The current extension.
+     * @type String
+     */
+    that.extension = null;
+
+    /* Stores an nsILocalFile pointing to the current filename.
+     * @type nsILocalFile
+     */
+    that.file = null;
+
+    /* Set the default extension and create the nsIFile object. */
+    var extension = node.getAttribute('itsalltext-extension');
+    if (typeof(extension) != 'string' || !extension.match(/^[.a-z0-9]+$/i)) {
+        extension = ItsAllText.getExtensions()[0];
+    }
+    that.setExtension(extension);
+
+    that.initFromExistingFile();
+
+    /**
+     * A callback for when the textarea/textbox or button has 
+     * the mouse waved over it.
+     * @param {Event} event The event object.
+     */
+    that.mouseover = function(event) {
+        var style = that.button?that.button.style:null;
+        if (style) {
+            style.setProperty('opacity', '0.7', 'important');
+            ItsAllText.refreshTextarea(that.node);
+        }
+    };
+
+    /**
+     * A callback for when the textarea/textbox or button has 
+     * the mouse waved over it and the moved off.
+     * @param {Event} event The event object.
+     */
+    that.mouseout = function(event) {
+        var style = that.button?that.button.style:null;
+        if (style) {
+            style.setProperty('opacity', '0.1', 'important');
+        }
+    };
+}
+
+/**
+ * Set the extension for the file to ext.
+ * @param {String} ext The extension.  Must include the dot.  Example: .txt
+ */
+CacheObj.prototype.setExtension = function(ext) {
+    if (ext == this.extension) {
+        return; /* It's already set.  No problem. */
+    }
+
+    /* Create the nsIFile object */
+    var file = ItsAllText.factoryFile();
+    file.initWithFile(ItsAllText.getEditDir());
+    file.append([this.base_filename,ext].join(''));
+
+    this.extension = ext;
+    this.file = file;
+};
+
+/**
+ * This function looks for an existing file and starts to monitor
+ * if the file exists already.  It also deletes all existing files for
+ * this cache object.
+ */
+CacheObj.prototype.initFromExistingFile = function() {
+    var base = this.base_filename;
+    var fobj = ItsAllText.getEditDir();
+    var entries = fobj.directoryEntries;
+    var ext = null;
+    var tmpfiles = /(\.bak|.tmp|~)$/;
+    var entry;
+    while (entries.hasMoreElements()) {
+        entry = entries.getNext();
+        entry.QueryInterface(Components.interfaces.nsIFile);
+        if (entry.leafName.indexOf(base) === 0) {
+            // startswith
+            if (ext === null && !entry.leafName.match(tmpfiles)) {
+                ext = entry.leafName.slice(base.length);
+            }
+            try{
+                entry.remove(false);
+            } catch(e) {
+                that.debug('unable to remove',entry,'because:',e);
+            }
+        }
+    }
+    if (ext !== null) {
+        this.setExtension(ext);
+        this._is_watching = true;
+    }
+};
+
+/**
+ * Returns a unique identifier for the node, within the document.
+ * @returns {String} the unique identifier.
+ */
+CacheObj.prototype.getNodeIdentifier = function(node) {
+    var id   = node.getAttribute('id');
+    var name, doc, attr, serial;
+    if (!id) {
+        name = node.getAttribute('name');
+        doc = node.ownerDocument.getElementsByTagName('html')[0];
+        attr = ItsAllText.MYSTRING+'_id_serial';
+        
+        /* Get a serial that's unique to this document */
+        serial = doc.getAttribute(attr);
+        if (serial) { serial = parseInt(serial, 10)+1;
+        } else { serial = 1; }
+        id = [ItsAllText.MYSTRING,'generated_id',name,serial].join('_');
+        doc.setAttribute(attr,serial);
+        node.setAttribute('id',id);
+    }
+    return id;
+};
+
+/**
+ * Convert to this object to a useful string.
+ * @returns {String} A string representation of this object.
+ */
+CacheObj.prototype.toString = function() {
+    return [ "CacheObj",
+             " uid=",this.uid,
+             " timestamp=",this.timestamp,
+             " size=",this.size
+    ].join('');
+};
+
+/**
+ * Write out the contents of the node.
+ */
+CacheObj.prototype.write = function() {
+    var foStream = Components.
+        classes["@mozilla.org/network/file-output-stream;1"].
+        createInstance(Components.interfaces.nsIFileOutputStream);
+             
+    /* write, create, truncate */
+    foStream.init(this.file, 0x02 | 0x08 | 0x20, 
+                  parseInt('0600',8), 0); 
+             
+    /* We convert to charset */
+    var conv = Components.
+        classes["@mozilla.org/intl/scriptableunicodeconverter"].
+        createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
+    conv.charset = ItsAllText.getCharset();
+             
+    var text = conv.ConvertFromUnicode(this.node.value);
+    foStream.write(text, text.length);
+    foStream.close();
+             
+    /* Reset Timestamp and filesize, to prevent a spurious refresh */
+    this.timestamp = this.file.lastModifiedTime;
+    this.size      = this.file.fileSize;
+
+    /* Register the file to be deleted on app exit. */
+    Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].
+        getService(Components.interfaces.nsPIExternalAppLauncher).
+        deleteTemporaryFileOnExit(this.file);
+             
+    return this.file.path;
+};
+
+/**
+ * Fetches the computed CSS attribute for a specific node
+ * @param {DOM} node The DOM node to get the information for.
+ * @param {String} attr The CSS-style attribute to fetch (not DOM name).
+ * @returns attribute
+ */
+CacheObj.prototype.getStyle = function(node, attr) {
+    var view  = node ? node.ownerDocument.defaultView : null;
+    var style = view.getComputedStyle(node, '');
+    return  style.getPropertyCSSValue(attr).cssText;
+};
+     
+// @todo [9] IDEA: Pass in the line number to the editor, arbitrary command?
+// @todo [9] IDEA: Allow the user to pick an alternative editor?
+// @todo [9] IDEA: A different editor per extension?
+/**
+ * Edit a textarea as a file.
+ * @param {String} extension The extension of the file to edit.
+ */
+CacheObj.prototype.edit = function(extension) {
+    if (typeof(extension) == 'string') {
+        this.setExtension(extension);
+    }
+    var filename = this.write();
+    this.initial_background = this.node.style.backgroundColor;
+    this.initial_color      = this.node.style.color;
+    var program = null; 
+    var process;
+    var args, result, ec, e, params;
+             
+    try {
+        program = ItsAllText.getEditor();
+        // checks
+        if (program === null)        { throw {name:"Editor is not set."}; }
+        if (!program.exists())       { throw {name:"NS_ERROR_FILE_NOT_FOUND"}; }
+        /* Mac check because of 
+         * https://bugzilla.mozilla.org/show_bug.cgi?id=322865 */
+        if (!(ItsAllText.isDarwin() || program.isExecutable())) { 
+            throw {name:"NS_ERROR_FILE_ACCESS_DENIED"}; }
+
+        // create an nsIProcess
+        process = Components.
+            classes["@mozilla.org/process/util;1"].
+            createInstance(Components.interfaces.nsIProcess);
+        process.init(program);
+             
+        // Run the process.
+        // If first param is true, calling thread will be blocked until
+        // called process terminates.
+        // Second and third params are used to pass command-line arguments
+        // to the process.
+        args = [filename];
+        result = {};
+        ec = process.run(false, args, args.length, result);
+        this._is_watching = true;
+    } catch(e) {        
+        params = {out:null,
+                      exists: program ? program.exists() : false,
+                      path: ItsAllText.preferences.editor,
+                      exception: e.name };
+        window.openDialog('chrome://itsalltext/chrome/badeditor.xul',
+                          null,
+                          "chrome,titlebar,toolbar,centerscreen,modal",
+                          params);
+        if(params.out !== null && params.out.do_preferences) {
+            ItsAllText.openPreferences(true);
+            this.edit(extension);
+        }
+    }
+};
+
+/**
+ * Delete the file from disk.
+ */
+CacheObj.prototype.remove = function() {
+    if(this.file.exists()) {
+        try {
+            this.file.remove();
+        } catch(e) {
+            that.debug('remove(',this.file.path,'): ',e);
+            return false;
+        }
+    }
+    return true;
+};
+
+/**
+ * Read the file from disk.
+ */
+CacheObj.prototype.read = function() {
+    /* read file, reset ts & size */
+    var DEFAULT_REPLACEMENT_CHARACTER = 65533;
+    var buffer = [];
+    var fis, istream, str, e;
+         
+    try {
+        fis = Components.
+            classes["@mozilla.org/network/file-input-stream;1"].
+            createInstance(Components.interfaces.nsIFileInputStream);
+        fis.init(this.file, 0x01, parseInt('00400',8), 0); 
+        // MODE_RDONLY | PERM_IRUSR
+             
+        istream = Components.
+            classes["@mozilla.org/intl/converter-input-stream;1"].
+            createInstance(Components.interfaces.nsIConverterInputStream);
+        istream.init(fis, ItsAllText.getCharset(), 4096, DEFAULT_REPLACEMENT_CHARACTER);
+             
+        str = {};
+        while (istream.readString(4096, str) !== 0) {
+            buffer.push(str.value);
+        }
+        
+        istream.close();
+        fis.close();
+             
+        this.timestamp = this.file.lastModifiedTime;
+        this.size      = this.file.fileSize;
+             
+        return buffer.join('');
+    } catch(e) {
+        return null;
+    }
+};
+
+/**
+ * Has the file object changed?
+ * @returns {boolean} returns true if the file has changed on disk.
+ */
+ CacheObj.prototype.hasChanged = function() {
+     /* Check exists.  Check ts and size. */
+     if(!this._is_watching ||
+        !this.file.exists() ||
+        !this.file.isReadable() ||
+        (this.file.lastModifiedTime == this.timestamp && 
+         this.file.fileSize         == this.size)) {
+         return false;
+     } else {
+         return true;
+     }
+ };
+
+/**
+ * Part of the fading technique.
+ * @param {Object} pallet A Color blend pallet object.
+ * @param {int}    step   Size of a step.
+ * @param {delay}  delay  Delay in microseconds.
+ */
+CacheObj.prototype.fadeStep = function(background_pallet, color_pallet, step, delay) {
+    var that = this;
+    return function() {
+        if (step < background_pallet.length) {
+            that.node.style.backgroundColor = background_pallet[step].hex();
+            that.node.style.color = color_pallet[step].hex();
+            step++;
+            setTimeout(that.fadeStep(background_pallet, color_pallet, step, delay),delay);
+        } else {
+            that.node.style.backgroundColor = that.initial_background;
+            that.node.style.color = that.initial_color;
+        }
+    };
+};
+
+/**
+ * Node fade technique.
+ * @param {int} steps  Number of steps in the transition.
+ * @param {int} delay  How long to wait between delay (microseconds).
+ */
+CacheObj.prototype.fade = function(steps, delay) {
+    var color             = this.getStyle(this.node, 'color');
+    var color_stop        = new ItsAllText.Color(color);
+    var color_start       = new ItsAllText.Color('black');
+    var color_pallet      = color_start.blend(color_stop, steps);
+
+    var background        = this.getStyle(this.node, 'background-color');
+    var background_stop   = new ItsAllText.Color(background);
+    var background_start  = new ItsAllText.Color('yellow');
+    var background_pallet = background_start.blend(background_stop, steps);
+    setTimeout(this.fadeStep(background_pallet, color_pallet, 0, delay), delay);
+};
+
+/**
+ * Update the node from the file.
+ * @returns {boolean} Returns true ifthe file changed.
+ */
+CacheObj.prototype.update = function() {
+    var value;
+    if (this.hasChanged()) {
+        value = this.read();
+        if (value !== null) {
+            this.fade(20, 100);
+            this.node.value = value;
+            return true;
+        }
+    }
+    return false; // If we fall through, we 
+};
+
+/**
+ * Add the gumdrop to a textarea.
+ * @param {Object} cache_object The Cache Object that contains the node.
+ */
+CacheObj.prototype.addGumDrop = function() {
+    var cache_object = this;
+    if (cache_object.button !== null) {
+        cache_object.adjust();
+        return; /*already done*/
+    }
+    if (ItsAllText.getDisableGumdrops()) {
+        return;
+    }
+    ItsAllText.debug('addGumDrop()',cache_object.node_id,cache_object.uid);
+    
+    var node = cache_object.node;
+    var doc = node.ownerDocument;
+    var offsetNode = node;
+    if (!node.parentNode) { return; }
+    
+    var gumdrop = doc.createElementNS(ItsAllText.XHTMLNS, "img");
+    gumdrop.setAttribute('src', this.gumdrop_url);
+    var gid = cache_object.getNodeIdentifier(gumdrop);
+    
+    if (ItsAllText.getDebug()) {
+        gumdrop.setAttribute('title', cache_object.node_id);
+    } else {
+        gumdrop.setAttribute('title', ItsAllText.localeString('program_name'));
+    }
+    cache_object.button = gumdrop; // Store it for easy finding in the future.
+    
+    // Image Attributes
+    gumdrop.style.setProperty('cursor',   'pointer', 'important');
+    gumdrop.style.setProperty('display',  'block', 'important');
+    gumdrop.style.setProperty('position',  'absolute', 'important');
+    gumdrop.style.setProperty('padding',   '0', 'important');
+    gumdrop.style.setProperty('margin',   '0', 'important');
+    gumdrop.style.setProperty('border',    'none', 'important');
+    gumdrop.style.setProperty('zIndex',    '1', 'important'); // we want it just above normal items.
+    
+    gumdrop.style.setProperty('width',  this.gumdrop_width+'px',  'important');
+    gumdrop.style.setProperty('height', this.gumdrop_height+'px', 'important');
+
+    gumdrop.setAttribute(ItsAllText.MYSTRING+'_UID', cache_object.uid);
+
+    var clickfun = function(event) {
+        cache_object.edit();
+        event.stopPropagation();
+        return false;
+    };
+    var contextfun = function(event) {
+        /* This took forever to fix; roughly 80+ man hours were spent
+         * over 5 months trying to make this stupid thing work.
+         * The documentation is completely wrong and useless.
+         *
+         * Excuse me while I scream.
+         *
+         * See Mozilla bugs: 287357, 362403, 279703
+         */
+        var popup = ItsAllText.rebuildMenu(cache_object.uid);
+        document.popupNode = popup;
+        popup.showPopup(popup,
+                        event.screenX, event.screenY,
+                        'context', null, null);
+        event.stopPropagation();
+        return false;
+    };
+    
+    // Click event handler
+    gumdrop.addEventListener("click", clickfun, false);
+    gumdrop.addEventListener("contextmenu", contextfun, false);
+    
+    // Insert it into the document
+    var parent = node.parentNode;
+    var nextSibling = node.nextSibling;
+    
+    if (nextSibling) {
+        parent.insertBefore(gumdrop, nextSibling);
+    } else {
+        parent.appendChild(gumdrop);
+    }
+    
+    // Add mouseovers/outs
+    node.addEventListener("mouseover",    cache_object.mouseover, false);
+    node.addEventListener("mouseout",     cache_object.mouseout, false);
+    gumdrop.addEventListener("mouseover", cache_object.mouseover, false);
+    gumdrop.addEventListener("mouseout",  cache_object.mouseout, false);
+    
+    cache_object.mouseout(null);
+    cache_object.adjust();
+};
+
+/**
+ * Updates the position of the gumdrop, incase the textarea shifts around.
+ */
+CacheObj.prototype.adjust = function() {
+    var gumdrop  = this.button;
+    var el       = this.node;
+    var doc      = el.ownerDocument;
+
+    if (ItsAllText.getDisableGumdrops()) {
+        if(gumdrop && gumdrop.style.display != 'none') {
+            gumdrop.style.setProperty('display', 'none', 'important');
+        }
+        return;
+    }
+
+    var style    = gumdrop.style;
+    if (!gumdrop || !el) { return; }
+    var display  = '';
+    var cstyle = doc.defaultView.getComputedStyle(el, '');
+    if (cstyle.display == 'none' ||
+        cstyle.visibility == 'hidden' ||
+        el.getAttribute('readonly') ||
+        el.getAttribute('disabled')
+        ) {
+        display = 'none';
+    }
+    if (style.display != display) {
+        style.setProperty('display', display, 'important');
+    }
+
+    /* Reposition the gumdrops incase the dom changed. */
+    var left = Math.max(1, el.offsetWidth-this.gumdrop_width);
+    var top  = el.offsetHeight;
+    var coord;
+    if (el.offsetParent === gumdrop.offsetParent) {
+        left += el.offsetLeft;
+        top  += el.offsetTop;
+    } else {
+        coord = ItsAllText.getContainingBlockOffset(el, gumdrop.offsetParent);
+        left += coord[0];
+        top  += coord[1];
+    }
+    if(left && top) {
+        left = [left,'px'].join('');
+        top  = [top,'px'].join('');
+        if(style.left != left) { style.setProperty('left', left, 'important');}
+        if(style.top != top)   { style.setProperty('top',  top, 'important');}
+    }
+};
+
+/**
+ * Creates a mostly unique hash of a string
+ * Most of this code is from:
+ *    http://developer.mozilla.org/en/docs/nsICryptoHash
+ * @param {String} some_string The string to hash.
+ * @returns {String} a hashed string.
+ */
+CacheObj.prototype.hashString = function(some_string) {
+    var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
+    converter.charset = "UTF-8";
+    
+    /* result is the result of the hashing.  It's not yet a string,
+     * that'll be in retval.
+     * result.value will contain the array length
+     */
+    var result = {};
+    
+    /* data is an array of bytes */
+    var data = converter.convertToByteArray(some_string, result);
+    var ch   = Components.classes["@mozilla.org/security/hash;1"].createInstance(Components.interfaces.nsICryptoHash);
+    
+    ch.init(ch.MD5);
+    ch.update(data, data.length);
+    var hash = ch.finish(true);
+    
+    // return the two-digit hexadecimal code for a byte
+    var toHexString = function(charCode) {
+        return ("0" + charCode.toString(36)).slice(-2);
+    };
+    
+    // convert the binary hash data to a hex string.
+    var retval = [];
+    for(i in hash) {
+        retval[i] = toHexString(hash.charCodeAt(i));
+    }
+    
+    return(retval.join(""));
+};
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/icon.png b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/icon.png
new file mode 100644 (file)
index 0000000..ceff24a
Binary files /dev/null and b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/icon.png differ
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/itsalltext.js b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/itsalltext.js
new file mode 100644 (file)
index 0000000..e0de08e
--- /dev/null
@@ -0,0 +1,838 @@
+/*
+ *  It's All Text - Easy external editing of web forms.
+ *  Copyright 2006 Christian Höltje
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// @todo [9] IDEA: dropdown list for charsets (utf-8, western-iso, default)?
+// @todo [3] Have a menu/context menu item for turning on monitoring/watch.
+// @todo [9] Menu item to pick the file to load into a textarea.
+// @todo [9] Hot-keys for editing or opening the context menu.
+
+var ItsAllText = function() {
+    /**
+     * This data is all private, which prevents security problems and it
+     * prevents clutter and collection.
+     * @type Object
+     */
+    var that = this;
+
+    /**
+     * Used for tracking all the all the textareas that we are watching.
+     * @type Hash
+     */
+    that.tracker = {};
+
+    /**
+     * Keeps track of all the refreshes we are running.
+     * @type Array
+     */
+    var cron = [null]; // Eat the 0th position
+
+    /**
+     * A constant, a string used for things like the preferences.
+     * @type String
+     */
+    that.MYSTRING = 'itsalltext';
+
+    /**
+     * A constant, the version number.  Set by the Makefile.
+     * @type String
+     */
+    that.VERSION = '0.7.3';
+
+    /**
+     * A constant, the url to the readme.
+     * @type String
+     */
+    that.README = 'chrome://itsalltext/locale/readme.xhtml';
+
+    /* The XHTML Namespace */
+    that.XHTMLNS = "http://www.w3.org/1999/xhtml";
+
+    /* The XUL Namespace */
+    that.XULNS   = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+
+    var string_bundle = Components.classes["@mozilla.org/intl/stringbundle;1"].
+        getService(Components.interfaces.nsIStringBundleService);
+    /**
+     * A localization bundle.  Use it like so:
+     * ItsAllText.locale.getStringFromName('blah');
+     */
+    that.locale = string_bundle.createBundle("chrome://itsalltext/locale/itsalltext.properties");
+    /**
+     * Formats a locale string, replacing $N with the arguments in arr.
+     * @param {String} name Locale property name
+     * @param {Array} arr Array of strings to replace in the string.
+     * @returns String
+     */
+    that.localeFormat = function(name, arr) {
+        return this.locale.formatStringFromName(name, arr, arr.length);
+    };
+    /**
+     * Returns the locale string matching name.
+     * @param {String} name Locale property name
+     * @returns String
+     */
+    that.localeString = function(name) {
+        return this.locale.GetStringFromName(name);
+    };
+
+    /**
+     * Create an error message from given arguments.
+     * @param {Object} message One or more objects to be made into strings...
+     */
+    that.logString = function() {
+        var args = Array.prototype.slice.apply(arguments,[0]);
+        for (var i=0; i<args.length; i++) {
+            try {
+                args[i] = args[i].toString();
+            } catch(e) {
+                Components.utils.reportError(e);
+                args[i] = 'toStringFailed';
+            }
+        }
+        args.unshift(that.MYSTRING+':');
+        return args.join(' ');
+    };
+
+    /**
+     * This is a handy debug message.  I'll remove it or disable it when
+     * I release this.
+     * @param {Object} message One or more objects can be passed in to display.
+     */
+    that.log = function() {
+        var message = that.logString.apply(that, arguments);
+        var consoleService, e;
+        try {
+            // idiom: Convert arguments to an array for easy handling.
+            consoleService = Components.
+                classes["@mozilla.org/consoleservice;1"].
+                getService(Components.interfaces.nsIConsoleService);
+            consoleService.logStringMessage(message);
+        } catch(e) {
+            Components.utils.reportError(message);
+        }
+    };
+
+    /**
+     * Uses log iff debugging is turned on.  Used for messages that need to
+     * globally logged (firebug only logs locally).
+     * @param {Object} message One or more objects can be passed in to display.
+     */
+    that.debuglog = function() {
+        if (that.preferences.debug) {
+            that.log.apply(that,arguments);
+        }
+    };
+
+    /**
+     * Displays debug information, if debugging is turned on.
+     * Requires Firebug.
+     * @param {Object} message One or more objects can be passed in to display.
+     */
+    that.debug = function() {
+        if (that.preferences.debug) {
+            try { Firebug.Console.logFormatted(arguments); } 
+            catch(e) {
+                that.log.apply(that,arguments);
+            }
+        }
+    };
+
+    /**
+     * A factory method to make an nsILocalFile object.
+     * @param {String} path A path to initialize the object with (optional).
+     * @returns {nsILocalFile}
+     */
+    that.factoryFile = function(path) {
+        var file = Components.
+            classes["@mozilla.org/file/local;1"].
+            createInstance(Components.interfaces.nsILocalFile);
+        if (typeof(path) == 'string' && path !== '') {
+            file.initWithPath(path);
+        }
+        return file;
+    };
+
+    /**
+     * Returns the directory where we put files to edit.
+     * @returns nsILocalFile The location where we should write editable files.
+     */
+    that.getEditDir = function() {
+        /* Where is the directory that we use. */
+        var fobj = Components.classes["@mozilla.org/file/directory_service;1"].
+            getService(Components.interfaces.nsIProperties).
+            get("ProfD", Components.interfaces.nsIFile);
+        fobj.append(that.MYSTRING);
+        if (!fobj.exists()) {
+            fobj.create(Components.interfaces.nsIFile.DIRECTORY_TYPE,
+                        parseInt('0700',8));
+        }
+        if (!fobj.isDirectory()) {
+            that.error(that.localeFormat('problem_making_directory', [fobj.path]));
+        }
+        return fobj;
+    };
+
+    /**
+     * Cleans out the edit directory, deleting all old files.
+     */
+    that.cleanEditDir = function(force) {
+        force = (force && typeof(force) != 'undefined');
+        var last_week = Date.now() - (1000*60*60*24*7);
+        var fobj = that.getEditDir();
+        var entries = fobj.directoryEntries;
+        var entry;
+        while (entries.hasMoreElements()) {
+            entry = entries.getNext();
+            entry.QueryInterface(Components.interfaces.nsIFile);
+            if(force || !entry.exists() || entry.lastModifiedTime < last_week){
+                try{
+                    entry.remove(false);
+                } catch(e) {
+                    that.debug('unable to remove',entry,'because:',e);
+                }
+            }
+        }
+    };
+
+    /* Clean the edit directory whenever we create a new window. */
+    that.cleanEditDir();
+
+    /* Load the various bits needed to make this work. */
+    var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
+    loader.loadSubScript('chrome://itsalltext/content/Color.js', that);
+    loader.loadSubScript('chrome://itsalltext/content/cacheobj.js', that);
+
+    /**
+     * Dictionary for storing the preferences in.
+     * @type Hash
+     */
+    that.preferences = {
+        debug: true,
+
+        /**
+         * Fetches the current value of the preference.
+         * @private
+         * @param {String} aData The name of the pref to fetch.
+         * @returns {Object} The value of the preference.
+         */
+        _get: function(aData) {
+            var po = that.preference_observer;
+            return po._branch['get'+(po.types[aData])+'Pref'](aData);
+        },
+
+        /**
+         * Sets the current preference.
+         * @param {String} aData The name of the pref to change.
+         * @param {Object} value The value to set.
+         */
+        _set: function(aData, value) {
+            var po = that.preference_observer;
+            return po._branch['set'+(po.types[aData])+'Pref'](aData, value);
+        }
+    };
+
+    /**
+     * A Preference Observer.
+     */
+    that.preference_observer = {
+        /**
+         * Dictionary of types (well, really the method needed to get/set the
+         * type.
+         * @type Hash
+         */
+        types: {
+            charset:            'Char',
+            editor:             'Char',
+            refresh:            'Int',
+            debug:              'Bool',
+            disable_gumdrops:   'Bool',
+            extensions:         'Char'
+        },
+
+        /**
+         * Register the observer.
+         */
+        register: function() {
+            var prefService = Components.
+                classes["@mozilla.org/preferences-service;1"].
+                getService(Components.interfaces.nsIPrefService);
+            this._branch = prefService.getBranch("extensions."+that.MYSTRING+".");
+            this._branch.QueryInterface(Components.interfaces.nsIPrefBranch2);
+            this._branch.addObserver("", this, false);
+            /* setup the preferences */
+            for(var type in this.types) {
+                that.preferences[type] = that.preferences._get(type);
+            }
+        },
+
+        /**
+         * Unregister the observer. Not currently used, but may be
+         * useful in the future.
+         */
+        unregister: function() {
+            if (!this._branch) {return;}
+            this._branch.removeObserver("", this);
+        },
+
+        /**
+         * Observation callback.
+         * @param {String} aSubject The nsIPrefBranch we're observing (after appropriate QI)e
+         * @param {String} aData The name of the pref that's been changed (relative to the aSubject).
+         * @param {String} aTopic The string defined by NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
+         */
+        observe: function(aSubject, aTopic, aData) {
+            if (aTopic != "nsPref:changed") {return;}
+            if (that.preferences) {
+                that.preferences[aData] = that.preferences._get(aData);
+                if (aData == 'refresh') {
+                    that.monitor.restart();
+                }
+            }
+        }        
+    };
+
+    /**
+     * A Preference Option: What character set should the file use?
+     * @returns {String} the charset to be used.
+     */
+    that.getCharset = function() {
+        return that.preferences.charset;
+    };
+
+    /**
+     * A Preference Option: How often should we search for new content?
+     * @returns {int} The number of seconds between checking for new content.
+     */
+    that.getRefresh = function() {
+        var refresh = that.preferences.refresh;
+        if (!refresh || refresh < 1) {
+            that.debug('Invalid refresh gotten:',refresh);
+            refresh = 1;
+        }
+        var retval = 1000*refresh;
+        return retval;
+
+    };
+
+    /**
+     * Returns true if the system is running Mac OS X.
+     * @returns {boolean} Is this a Mac OS X system?
+     */
+    that.isDarwin = function() {
+        /* more help:
+         http://developer.mozilla.org/en/docs/Code_snippets:Miscellaneous#Operating_system_detection
+        */
+
+        var is_darwin = that._is_darwin;
+        if (typeof(is_darwin) == 'undefined') {
+            is_darwin = /^Darwin/i.test(Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULRuntime).OS);
+            that._is_darwin = is_darwin;
+        }
+        return is_darwin;
+    };
+
+    /**
+     * A Preference Option: What editor should we use?
+     *
+     * Note: On some platforms, this can return an 
+     * NS_ERROR_FILE_INVALID_PATH exception and possibly others.
+     *
+     * For a complete list of exceptions, see:
+     * http://lxr.mozilla.org/seamonkey/source/xpcom/base/nsError.h#262
+     * @returns {nsILocalFile} A file object of the editor.
+     */
+    that.getEditor = function() {
+        var editor = that.preferences.editor;
+        var retval = null;
+
+        if (editor === '' && that.isDarwin()) {
+            editor = '/usr/bin/open'; 
+            that.preferences._set('editor', editor);
+        }
+
+        if (editor !== '') {
+            retval = that.factoryFile(editor);
+        }
+        return retval;
+    };
+
+    /**
+     * A Preference Option: should we display debugging info?
+     * @returns {bool}
+     */
+    that.getDebug = function() {
+        return that.preferences.debug;
+    };
+
+    /**
+     * A Preference Option: Are the edit gumdrops disabled?
+     * @returns {bool}
+     */
+    that.getDisableGumdrops = function() {
+        return that.preferences.disable_gumdrops;
+    };
+
+    /**
+     * A Preference Option: The list of extensions
+     * @returns Array
+     */
+    that.getExtensions = function() {
+        var string = that.preferences.extensions.replace(/[\n\t ]+/g,'');
+        var extensions = string.split(',');
+        if (extensions.length === 0) {
+            return ['.txt'];
+        } else {
+            return extensions;
+        }
+    };
+    
+    /**
+     * Open the preferences dialog box.
+     * @param{boolean} wait The function won't return until the preference is set.
+     * @private
+     * Borrowed from http://wiki.mozilla.org/XUL:Windows
+     * and utilityOverlay.js's openPreferences()
+     */
+    that.openPreferences = function (wait) {
+        wait = typeof(wait)=='boolean'?wait:false;
+        var paneID = that.MYSTRING + '_preferences';
+        var instantApply = getBoolPref("browser.preferences.instantApply", false) && !wait;
+        var features = "chrome,titlebar,toolbar,centerscreen" + (instantApply ? ",dialog=no" : ",modal");
+
+        var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
+        var win = wm.getMostRecentWindow("Browser:Preferences");
+        var pane;
+        if (win) {
+            win.focus();
+            if (paneID) {
+                pane = win.document.getElementById(paneID);
+                win.document.documentElement.showPane(pane);
+            }
+        } else {
+            openDialog('chrome://itsalltext/chrome/preferences.xul',
+                       "", features, paneID);
+        }
+    };
+
+    /**
+     * A Preference Option: Append an extension
+     * @returns Array
+     */
+    that.appendExtensions = function(ext) {
+        ext = ext.replace(/[\n\t ]+/g,'');
+        var current = that.getExtensions();
+        for(var i=0; i<current.length; i++) {
+            if(ext == current[i]) {
+                return; // Don't add a duplicate.
+            }
+        }
+        
+        var value = that.preferences.extensions;
+        if(value.replace(/[\t\n ]+/g) === '') {
+            value = ext;
+        } else {
+            value = [value,',',ext].join('');
+        }
+        that.preferences._set('extensions', value);
+    };
+
+    // @todo [3] Profiling and optimization.
+    
+    /**
+     * Returns a cache object
+     * Note: These UIDs are only unique for Its All Text.
+     * @param {Object} node A dom object node or ID to one.
+     * @returns {String} the UID or null.
+     */
+    that.getCacheObj = function(node) {
+        var cobj = null;
+        var str = that.MYSTRING+"_UID";
+        if (typeof(node) == 'string') {
+            cobj = that.tracker[node];
+        } else {
+            if (node && node.hasAttribute(str)) {
+                cobj = that.tracker[node.getAttribute(str)];
+            }
+            if (!cobj) {
+                cobj = new ItsAllText.CacheObj(node);
+            }
+        }
+        return cobj;
+    };
+
+    /**
+     * Cleans out all old cache objects.
+     */
+    that.cleanCacheObjs = function() {
+        var count = 0;
+        var cobj, id;
+        for(id in that.tracker) {
+            cobj = that.tracker[id];
+            if (cobj.node.ownerDocument.location === null) {
+                that.debug('cleaning %s', id);
+                delete cobj.node;
+                delete cobj.button;
+                delete that.tracker[id];
+            } else {
+                count += 1;
+            }
+        }
+        that.debuglog('tracker count:', count);
+    };
+
+    /**
+     * Refresh Textarea.
+     * @param {Object} node A specific textarea dom object to update.
+     */
+    that.refreshTextarea = function(node, is_chrome) {
+        var cobj = ItsAllText.getCacheObj(node);
+        if(!cobj) { return; }
+
+        cobj.update();
+        if (!is_chrome) { cobj.addGumDrop(); }
+    };
+
+    // @todo [5] Refresh textarea on editor quit.
+    // @todo [9] IDEA: support for input elements as well?
+
+    /**
+     * Refresh Document.
+     * @param {Object} doc The document to refresh.
+     */
+    that.refreshDocument = function(doc) {
+        if(!doc.location) { return; } // it's being cached, but not shown.
+        var is_chrome = (doc.location.protocol == 'chrome:' &&
+                         doc.location.href != that.README);
+        var nodes = doc.getElementsByTagName('textarea');
+        var i;
+        for(i=0; i < nodes.length; i++) {
+            that.refreshTextarea(nodes[i], is_chrome);
+        }
+        nodes = doc.getElementsByTagName('textbox');
+        for(i=0; i < nodes.length; i++) {
+            that.refreshTextarea(nodes[i], is_chrome);
+        }
+    };
+
+    /**
+     * Returns the offset from the containing block.
+     * @param {Object} node A DOM element.
+     * @param {Object} container If unset, then this will use the offsetParent of node. Pass in null to go all the way to the root.
+     * @return {Array} The X & Y page offsets
+     */
+    that.getContainingBlockOffset = function(node, container) {
+        if(typeof(container) == 'undefined') {
+            container = node.offsetParent;
+        }
+        var pos = [node.offsetLeft, node.offsetTop];
+        var pnode = node.offsetParent;
+        while(pnode && (container === null || pnode != container)) {
+            pos[0] += pnode.offsetLeft || 0;
+            pos[1] += pnode.offsetTop  || 0;
+            pos[0] -= pnode.scrollLeft || 0;
+            pos[1] -= pnode.scrollTop  || 0;
+            pnode = pnode.offsetParent;
+        }
+        return pos;
+    };
+
+
+    /**
+     * This function is called regularly to watch changes to web documents.
+     */
+    that.monitor = {
+        id: null,
+        last_now:0,
+        documents: [],
+        /**
+         * Starts or restarts the document monitor.
+         */
+        restart: function() {
+            var rate = that.getRefresh();
+            var id   = that.monitor.id;
+            if (id) {
+                clearInterval(id);
+            }
+            that.monitor.id = setInterval(that.monitor.watcher, rate);
+        },
+        /**
+         * watches the document 'doc'.
+         * @param {Object} doc The document to watch.
+         */
+        watch: function(doc, force) {
+            var contentType, location, is_html, is_usable, is_my_readme;
+            if (!force) {
+                /* Check that this is a document we want to play with. */
+                contentType = doc.contentType;
+                location = doc.location;
+                is_html = (contentType=='text/html' ||
+                           contentType=='text/xhtml' ||
+                           contentType=='application/xhtml+xml');
+                //var is_xul=(contentType=='application/vnd.mozilla.xul+xml');
+                is_usable = (is_html) && 
+                    location.protocol != 'about:' &&
+                    location.protocol != 'chrome:';
+                is_my_readme = location.href == that.README;
+                if (!(is_usable || is_my_readme)) { 
+                    that.debuglog('watch(): ignoring -- ',
+                                  location, contentType);
+                    return;
+                }
+            }
+
+            that.refreshDocument(doc);
+            that.monitor.documents.push(doc);
+        },
+        /**
+         * Callback to be used by restart()
+         * @private
+         */
+        watcher: function(offset) {
+            var monitor = that.monitor;
+            var rate = that.getRefresh();
+            
+            var now = Date.now();
+            if (now - monitor.last_now < Math.round(rate * 0.9)) {
+                that.debuglog('monitor.watcher(',offset,') -- skipping catchup refresh');
+                return;
+            }
+            monitor.last_now = now;
+
+            /* Walk the documents looking for changes */
+            var documents = monitor.documents;
+            that.debuglog('monitor.watcher(',offset,'): ', documents.length);
+            var i, doc;
+            var did_delete = false;
+            for(i in documents) {
+                doc = documents[i];
+                if (doc.location) {
+                    that.debuglog('refreshing', doc.location);
+                    that.refreshDocument(doc);
+                }
+            }
+        },
+        /**
+         * Stops watching doc.
+         * @param {Object} doc The document to watch.
+         */
+        unwatch: function(doc) {
+            var documents = that.monitor.documents;
+            var i;
+            for(i in documents) {
+                if (documents[i] === doc) {
+                    that.debug('unwatching', doc);
+                    delete documents[i];
+                }
+            }
+            that.cleanCacheObjs();
+            for(i=documents.length - 1; i >= 0; i--) {
+                if(typeof(documents[i]) == 'undefined') {
+                    documents.splice(i,1);
+                }
+            }
+        }
+    };
+
+    /**
+     * Callback whenever the DOM content in a window or tab is loaded.
+     * @param {Object} event An event passed in.
+     */
+    that.onDOMContentLoad = function(event) {
+        if (event.originalTarget.nodeName != "#document") { return; }
+        var doc = event.originalTarget || document;
+        that.monitor.watch(doc);
+        return;
+    };
+
+    /**
+     * Open the editor for a selected node.
+     * @param {Object} node The textarea to get.
+     */
+    that.onEditNode = function(node) {
+        var cobj = that.getCacheObj(node);
+        if(cobj) {
+            cobj.edit();
+        }
+        return;
+    };
+
+    /**
+     * Triggered when the context menu is shown.
+     * @param {Object} event The event passed in by the event handler.
+     */
+    that.onContextMenu = function(event) {
+        var tid, node, tag, is_disabled, cobj, menu;
+        if(event.target) {
+            tid = event.target.id;
+            if (tid == "itsalltext-context-popup" ||
+                tid == "contentAreaContextMenu") {
+                node = document.popupNode;
+                tag = node.nodeName.toLowerCase();
+                is_disabled = (!(tag == 'textarea' || 
+                                     tag == 'textbox') ||
+                                   node.style.display == 'none' ||
+                                   node.getAttribute('readonly') ||
+                                   node.getAttribute('disabled')
+                                   );
+                if (tid == "itsalltext-context-popup") {
+                    cobj = that.getCacheObj(node);
+                    that.rebuildMenu(cobj.uid,
+                                     'itsalltext-context-popup',
+                                     is_disabled);
+                } else {
+                    // tid == "contentAreaContextMenu"
+                    menu = document.getElementById("itsalltext-contextmenu");
+                    menu.setAttribute('hidden', is_disabled);
+                }
+                    
+            }
+        }
+        return true;
+    };
+
+    that.openReadme = function() {
+        browser = getBrowser();
+        browser.selectedTab = browser.addTab(that.README, null);
+    };
+
+    /**
+     * Initialize the module.  Should be called once, when a window is loaded.
+     * @private
+     */
+    var windowload = function(event) {
+        that.debug("startup(): It's All Text! is watching this window...");
+
+        // Start watching the preferences.
+        that.preference_observer.register();
+
+        // Start the monitor
+        that.monitor.restart();
+
+        var appcontent = document.getElementById("appcontent"); // The Browser
+        if (appcontent) {
+            // Normal web-page.
+            appcontent.addEventListener("DOMContentLoaded", that.onDOMContentLoad,
+                                        true);
+        } else {
+            that.onDOMContentLoad(event); 
+        }
+        // Attach the context menu, if we can.
+        var contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
+        if (contentAreaContextMenu) {
+            contentAreaContextMenu.addEventListener("popupshowing",
+                                                    that.onContextMenu, false);
+        }
+    };
+  
+    // Do the startup when things are loaded.
+    window.addEventListener("load", windowload, true);
+    // Do the startup when things are unloaded.
+    window.addEventListener("unload", function(event){that.monitor.unwatch(event.originalTarget||document); that.preference_observer.unregister();}, true);
+
+};
+
+/**
+ * The command that is called when picking a new extension.
+ * @param {Event} event
+ */
+ItsAllText.prototype.menuNewExtEdit = function(event) {
+    var that = this;
+    var uid = this._current_uid;
+    var cobj = that.getCacheObj(uid);
+
+    var params = {out:null};       
+    window.openDialog("chrome://itsalltext/chrome/newextension.xul", "",
+    "chrome, dialog, modal, resizable=yes", params).focus();
+    var ext;
+    if (params.out) {
+        ext = params.out.extension.replace(/[\n\t ]+/g,'');
+        if(params.out.do_save) {
+            that.appendExtensions(ext);
+        }
+        cobj.edit(ext);
+    }
+};
+
+/**
+ * The command that is called when selecting an existing extension.
+ * @param {Event} event
+ */
+ItsAllText.prototype.menuExtEdit = function(event) {
+    var that = this;
+    var uid = that._current_uid;
+    var ext = event.target.getAttribute('label');
+    var cobj = that.getCacheObj(uid);
+    cobj.edit(ext);
+};
+
+/**
+ * Rebuilds the option menu, to reflect the current list of extensions.
+ * @private
+ * @param {String} uid The UID to show in the option menu.
+ */
+ItsAllText.prototype.rebuildMenu = function(uid, menu_id, is_disabled) {
+    menu_id = typeof(menu_id) == 'string'?menu_id:'itsalltext-optionmenu';
+    is_disabled = (typeof(is_disabled) == 'undefined'||!is_disabled)?false:(is_disabled&&true);
+    var i;
+    var that = this;
+    var exts = that.getExtensions();
+    var menu = document.getElementById(menu_id);
+    var items = menu.childNodes;
+    var items_length = items.length - 1; /* We ignore the preferences item */
+    var node;
+    that._current_uid = uid;
+    var magic_stop_node = null;
+    var magic_start = null;
+    var magic_stop = null;
+
+    // Find the beginning and end of the magic replacement parts.
+    for(i=0; i<items_length; i++) {
+        node = items[i];
+        if (node.nodeName.toLowerCase() == 'menuseparator') {
+            if(magic_start === null) {
+                magic_start = i;
+            } else if (magic_stop === null) {
+                magic_stop = i;
+                magic_stop_node = node;
+            }
+        } else if (node.nodeName.toLowerCase() == 'menuitem') {
+            node.setAttribute('disabled', is_disabled?'true':'false');
+        }
+    }
+
+    // Remove old magic bits
+    for(i = magic_stop - 1; i > magic_start; i--) {
+        menu.removeChild(items[i]);
+    }
+   
+    // Insert the new magic bits
+    for(i=0; i<exts.length; i++) {
+        node = document.createElementNS(that.XULNS, 'menuitem');
+        node.setAttribute('label', exts[i]);
+        node.addEventListener('command', function(event){return that.menuExtEdit(event);}, false);
+        node.setAttribute('disabled', is_disabled?'true':'false');
+        menu.insertBefore(node, magic_stop_node);
+
+    }
+    return menu;
+};
+
+ItsAllText = new ItsAllText();
+
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/itsalltext.xul b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/itsalltext.xul
new file mode 100644 (file)
index 0000000..cd8de19
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE overlay SYSTEM "chrome://itsalltext/locale/itsalltext.dtd" >
+
+<overlay id="ItsAllTextOverlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="application/x-javascript" src="chrome://itsalltext/chrome/itsalltext.js" />
+
+  <!-- The merge point is contentAreaContextMenu -->
+  <popup id="contentAreaContextMenu">
+    <menu id="itsalltext-contextmenu" label="&top.label;" accesskey="&top.key;">
+      <menupopup id="itsalltext-context-popup">
+        <menuitem label="&edit.label;" 
+                  oncommand="ItsAllText.onEditNode(document.popupNode)"
+                  accesskey="&edit.key;" />
+        <menuitem label="&newext.label;"
+                  accesskey="&newext.key;"
+                  oncommand="ItsAllText.menuNewExtEdit(event);" />
+        <menuseparator/>
+        <menuitem label=".txt" oncommand="ItsAllText.menuExtEdit(event);"/>
+        <menuseparator/>
+        <menuitem label="&readme.label;"
+                  oncommand="ItsAllText.openReadme();"/>
+        <menuitem label="&pref.label;"
+                  accesskey="&pref.key;"
+                  oncommand="ItsAllText.openPreferences();"/>
+      </menupopup>
+    </menu>
+  </popup>
+
+  <!-- The merge point is main-window -->
+  <window id="main-window">
+    <popupset id="itsalltext-optionmenu-set">
+      <popup id="itsalltext-optionmenu">
+        <menuitem label="&newext.label;"
+                  accesskey="&newext.key;"
+                  oncommand="ItsAllText.menuNewExtEdit(event);" />
+        <menuseparator/>
+        <menuitem label=".txt" oncommand="ItsAllText.menuExtEdit(event);"/>
+        <menuseparator/>
+        <menuitem label="&readme.label;"
+                  oncommand="ItsAllText.openReadme();"/>
+        <menuitem label="&pref.label;"
+                  accesskey="&pref.key;"
+                  oncommand="ItsAllText.openPreferences();"/>
+      </popup>
+    </popupset>
+  </window>
+
+  <!-- The merge point is the Tools menu -->
+  <menupopup id="menu_ToolsPopup">
+    <menu id="menu_itsalltext" class="menuitem-iconic"
+          image="chrome://itsalltext/chrome/icon16.png"
+          label="&top.label;"
+          insertbefore="sanitizeSeparator">
+      <menupopup>
+        <menuitem label="&readme.label;"
+                  oncommand="ItsAllText.openReadme();"/>
+        <menuitem label="&pref.label;"
+                  accesskey="&pref.key;"
+                  oncommand="ItsAllText.openPreferences();"/>
+      </menupopup>
+    </menu>
+  </menupopup>
+
+</overlay>
+<!-- Local Variables: -->
+<!-- mode: xml -->
+<!-- End: -->
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/newextension.js b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/newextension.js
new file mode 100644 (file)
index 0000000..9df2fdf
--- /dev/null
@@ -0,0 +1,10 @@
+/**
+ * Pass back the values that that the user selected.
+ */
+function onOK() {
+    window['arguments'][0].out = {
+        extension: document.getElementById('new_ext').value,
+        do_save: document.getElementById('do_save').checked
+    };
+   return true;
+}
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/newextension.xul b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/newextension.xul
new file mode 100644 (file)
index 0000000..8b1a16a
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<!DOCTYPE newextwindow SYSTEM "chrome://itsalltext/locale/newextension.dtd" >
+<dialog
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  id="ItsAllTextNewExtension"
+  title="&title;"
+  ondialogaccept="return onOK();"
+  persist="screenX screenY width height"
+  windowtype="ItsAllTextWindowType">
+
+  <script type="application/x-javascript" src="chrome://itsalltext/content/newextension.js"/>
+  <grid>
+    <columns>
+      <column/>
+      <column/>
+    </columns>
+    <rows>
+      <row align="center">
+        <label value="&extension.label;"/>
+        <textbox id="new_ext" value=".txt"/>
+      </row>
+      <row align="center">
+        <spacer/>
+        <checkbox id="do_save" label="&save.label;"/>
+      </row>
+    </rows>
+  </grid>
+</dialog>
+<!-- Local Variables: -->
+<!-- mode: xml -->
+<!-- End: -->
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/preferences.js b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/preferences.js
new file mode 100644 (file)
index 0000000..8807595
--- /dev/null
@@ -0,0 +1,81 @@
+// @todo [6] [pref] Better strategy for getting the default editor: EDITOR env variable or view_source.editor.path
+// @todo [8] [pref] Option to make the textarea uneditable when using editor.
+
+/**
+ * Open a filepicker to select the value of the editor.
+ */
+function pref_editor_select() {  
+    var locale = document.getElementById("strings");
+
+    var pref_editor = document.getElementById('pref_editor');
+    var nsIFilePicker = Components.interfaces.nsIFilePicker;
+    var fp = Components.classes["@mozilla.org/filepicker;1"].
+        createInstance(nsIFilePicker);
+    fp.init(window,
+            locale.getString('picker.window.title'),
+            nsIFilePicker.modeOpen);
+    fp.appendFilters(nsIFilePicker.filterApps);
+
+    var initdir = Components.classes["@mozilla.org/file/local;1"].
+        createInstance(Components.interfaces.nsILocalFile);
+    try {
+        initdir.initWithPath(pref_editor.value);
+        initdir = initdir.parent;
+        if (initdir.exists() && initdir.isDirectory()) {
+            fp.displayDirectory = initdir;
+        }
+    } catch(e) {
+        // Ignore error, the pref may not have been set or who knows.
+    }
+  
+    var rv = fp.show();
+    var file;
+    var editor;
+    if (rv == nsIFilePicker.returnOK) {
+        file = fp.file;
+        pref_editor.value = file.path;
+        editor = document.getElementById('editor');
+        editor.style.color = 'inherit';
+        editor.style.backgroundColor = 'inherit';
+    }
+}
+
+function setHelp(text) {
+    var help = document.getElementById('help');
+    while (help.firstChild) {
+        help.removeChild(help.firstChild);
+    }
+    var textnode = document.createTextNode(text);
+    help.appendChild(textnode);
+}
+
+function pref_onload() {
+    var locale = document.getElementById("strings");
+    document.getElementById('browse').focus();
+    var editor;
+    var box;
+    var desc;
+    var textnode;
+    if (window['arguments'] && window['arguments'][0] && window['arguments'][0] == 'badeditor') {
+        editor = document.getElementById('editor');
+        editor.style.color = 'black';
+        editor.style.backgroundColor = '#fb4';
+        box = document.getElementById('help');
+        // Clean it out
+        while (box.firstChild) {
+            box.removeChild(box.firstChild);
+        }
+        desc = document.createElement('description');
+        textnode = document.createTextNode(locale.getFormattedString('problem.editor', [editor.value]));
+        desc.appendChild(textnode);
+        desc.style.maxWidth = '18em';
+        box.appendChild(desc);
+
+        desc = document.createElement('description');
+        textnode = document.createTextNode(locale.getString('mac.hint'));
+        desc.appendChild(textnode);
+        desc.style.maxWidth = '18em';
+        box.appendChild(desc);
+    }
+}
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/preferences.xul b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/content/preferences.xul
new file mode 100644 (file)
index 0000000..47e87be
--- /dev/null
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<!DOCTYPE prefwindow SYSTEM "chrome://itsalltext/locale/preferences.dtd" >
+<prefwindow id="itsalltext-prefs"
+         title="&title;"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+         buttons="accept,cancel"
+         onload="pref_onload();">
+
+<script type="application/x-javascript" src="preferences.js"/>
+
+<stringbundleset id="strbundles">
+<stringbundle id="strings" src="chrome://itsalltext/locale/preferences.properties"/>
+</stringbundleset>
+<prefpane 
+    id="itsalltext-pane"
+    label="&title;"
+    flex="1">
+  <preferences>
+    <preference id="pref_charset"
+                name="extensions.itsalltext.charset" type="string"/>
+    <preference id="pref_editor"
+                name="extensions.itsalltext.editor" type="string"/>
+    <preference id="pref_seconds"
+                name="extensions.itsalltext.refresh" type="int"/>
+    <preference id="pref_extensions"
+                name="extensions.itsalltext.extensions" type="string"/>
+    <preference id="pref_disable_gumdrops"
+                name="extensions.itsalltext.disable_gumdrops" type="bool"/>
+    <preference id="pref_debug"
+                name="extensions.itsalltext.debug" type="bool"/>
+  </preferences>
+
+  <hbox>
+    <grid>
+      <columns>
+        <column/>
+        <column/>
+      </columns>
+      <rows>
+        <row align="center">
+          <label control="editor" value="&editor.label;"/>
+          <vbox>
+            <textbox preference="pref_editor" id="editor" size="20"
+             style="-moz-appearance: none !important; background:
+             inherit;" />
+            <hbox align="right">
+              <spacer/>
+              <button oncommand="pref_editor_select();" label="&picker.label;"
+              id="browse" accesskey="b" tabindex="1"/>
+            </hbox>
+          </vbox>
+        </row>
+        <row align="center">
+          <label control="seconds" value="&seconds.label;"/>
+          <hbox>
+            <textbox preference="pref_seconds" id="seconds" size="2"
+            maxlength="2" tabindex="2"/>
+            <spacer/>
+          </hbox>
+        </row>
+        <row align="center">
+          <label control="charset"
+                 value="&charset.label;"/>
+          <hbox>
+            <textbox preference="pref_charset" id="charset" size="8" tabindex="3"/>
+            <spacer/>
+          </hbox>
+        </row>
+        <row align="center">
+          <label control="extensions"
+                 value="&extensions.label;"/>
+          <textbox preference="pref_extensions" id="extensions" size="30" tabindex="4"/>
+        </row>
+        <row align="center">
+          <label control="disable_gumdrops"
+                 value="&disable_gumdrops.label;"/>
+          <checkbox preference="pref_disable_gumdrops" id="disable_gumdrops" tabindex="5"
+          label="&disable_gumdrops.checkbox;"/>
+        </row>
+        <row align="center">
+          <label control="debug"
+                 value="&debug.label;"/>
+          <checkbox preference="pref_debug" id="debug" tabindex="5"
+          label="&debug.humor;"/>
+        </row>
+      </rows>
+    </grid>
+    <vbox id="help" flex="1">
+    </vbox>
+  </hbox>
+</prefpane>
+</prefwindow>
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/about.dtd b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/about.dtd
new file mode 100644 (file)
index 0000000..8f5bd00
--- /dev/null
@@ -0,0 +1,20 @@
+<!ENTITY title "About It's All Text!">
+<!ENTITY description "It's All Text! - Easy external editing of web forms. 
+
+Copyright (C) 2006-2007 Christian Höltje
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License or
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.">
+<!ENTITY close "Close">
+
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/badeditor.dtd b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/badeditor.dtd
new file mode 100644 (file)
index 0000000..41ccfe0
--- /dev/null
@@ -0,0 +1,10 @@
+<!ENTITY title              "It's All Text! was unable to open your editor">
+<!ENTITY header             "Unable to open your editor">
+<!ENTITY pref.label         "Preferences">
+<!ENTITY helptext           "It's All Text! was unable to run your editor.
+
+You can now either cancel your edit or you can use the preferences to choose a new editor.  Please make sure that you use the full path to the editor and that the editor is marked executable.
+
+Thank you.
+">
+<!ENTITY reason             "Reason:">
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/badeditor.properties b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/badeditor.properties
new file mode 100644 (file)
index 0000000..d0196c4
--- /dev/null
@@ -0,0 +1,4 @@
+bad.noent=The path '%1$S' does not exist.
+bad.noexec=Unable to execute your editor.
+bad.noset=Your editor has not been set.
+
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/gumdrop.png b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/gumdrop.png
new file mode 100644 (file)
index 0000000..de215ff
Binary files /dev/null and b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/gumdrop.png differ
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/itsalltext.dtd b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/itsalltext.dtd
new file mode 100644 (file)
index 0000000..a70ea50
--- /dev/null
@@ -0,0 +1,14 @@
+<!ENTITY top.label     "It's All Text!">
+<!ENTITY top.key       "i">
+
+<!ENTITY edit.label    "Edit with default extension">
+<!ENTITY edit.key      "e">
+
+<!ENTITY newext.label  "Edit with new extension...">
+<!ENTITY newext.key    "n">
+
+<!ENTITY pref.label    "Preferences...">
+<!ENTITY pref.key      "p">
+
+<!ENTITY readme.label  "View the README...">
+
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/itsalltext.properties b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/itsalltext.properties
new file mode 100644 (file)
index 0000000..8eb3e49
--- /dev/null
@@ -0,0 +1,6 @@
+extensions.itsalltext@docwhat.gerf.org.description=Edit text using your favorite editor!
+program_name=It's All Text!
+no_editor_pref=Preferences: Please pick an editor.
+problem_making_directory=I'm having a problem finding or creating the directory: %1$S
+gumdrop.width=28 
+gumdrop.height=14
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/newextension.dtd b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/newextension.dtd
new file mode 100644 (file)
index 0000000..0974d4d
--- /dev/null
@@ -0,0 +1,3 @@
+<!ENTITY title              "It's All Text! New Extension">
+<!ENTITY extension.label    "Extension (with leading dot):">
+<!ENTITY save.label         "Save for future use">
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/preferences.dtd b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/preferences.dtd
new file mode 100644 (file)
index 0000000..af26ad9
--- /dev/null
@@ -0,0 +1,10 @@
+<!ENTITY title                       "It's All Text! Preferences">
+<!ENTITY editor.label                "Editor:">
+<!ENTITY picker.label                "Browse">
+<!ENTITY seconds.label               "Seconds between refreshing:">
+<!ENTITY charset.label               "Character Set (default: UTF-8):">
+<!ENTITY extensions.label            "File Extensions:">
+<!ENTITY debug.label                 "Debugging:">
+<!ENTITY debug.humor                 "Remove all bugs">
+<!ENTITY disable_gumdrops.label      "In-Page Edit Buttons:">
+<!ENTITY disable_gumdrops.checkbox   "Disable">
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/preferences.properties b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/preferences.properties
new file mode 100644 (file)
index 0000000..1d76bc8
--- /dev/null
@@ -0,0 +1,3 @@
+picker.window.title=Choose your editor
+problem.editor=I was unable to run your editor, '%1$S'.  Use the browse button to choose another editor and try again.
+mac.hint=If you use Mac OS X then you probably want to use '/usr/bin/open' -- it will open the file using the default application for that file.
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/readme.xhtml b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/chrome/locale/en-US/readme.xhtml
new file mode 100644 (file)
index 0000000..48b3c9b
--- /dev/null
@@ -0,0 +1,158 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" >
+  <head>
+    <title>Welcome to It's All Text! version 0.7.3</title>
+    <style type="text/css">
+      html { background: #abd; color: #000; padding: 0; margin: 0; }
+      body { background: #fff; color: #000; margin: 1ex auto; padding: 1em; 
+             -moz-border-radius: 1em; width: 30em;
+             font-family: sans-serif;}
+      h1, h2, h3, h4, h5, h6 { background: #ffa; color: #06d; -moz-border-radius: 1ex; padding: 0 0.8em 0 2em; margin-left: -2em;}
+      h1 { padding: 0.5ex 1em 0.5ex 2em; margin: 0 -2em;}
+      h2, h3, h4, h5, h6 { display: block; width: 20em; clear: both; }
+
+      tt, code { font-size: 1.1em; font-weight: bold; color: #048; }
+      dt { font-weight: bold; color: #048; }
+
+      #ver { margin: 0; text-align: right; }
+      #test { width: 50%; height: 10em; background: #cdf; }
+      #nteb { font-size: 0.5em; text-align: right; margin-right: 50%; padding-right: 30px; }
+
+      .iat { font-style: oblique; color: #024; }
+      .warn { background: #fdd; }
+      
+      pre { background: #def; color: black; margin: 1em; padding: 0 1em; }
+
+      #faq dl { margin-left: 1em; }
+      #faq dd { font-size: 0.8em; }
+    </style>
+  </head>
+  <body>
+    <h1>Welcome to <span class="iat">It's All Text!</span></h1>
+    <p id="ver"> version 0.7.3</p>
+    
+    <p class="warn">
+      This software is ALMOST version 1.0! It's inches from being
+      ready to go.  Please <a
+      href="http://docwhat.gerf.org/2007/03/its_all_text_v06">report</a> 
+      any problems you have to help!
+    </p>
+
+    <p>
+      <span class="iat">It's All Text!</span> gives you a simple way to edit textareas, the large text boxes in forms, using your favorite editor.
+    </p>
+
+
+
+    <h3>Quick Start</h3>
+    <p>
+      Upon installation, go to the menu <tt>Tools -&gt; It's All Text!
+      -&gt; Preferences</tt> to set your preferences. Specifically,
+      you'll have to set the editor's full path.
+    </p>
+
+    <p>
+      There are three ways to use <span class="iat">It's All Text!</span>:
+    </p>
+    <ul>
+      <li>Right click on a textarea, select <tt>It's All Text!</tt>.
+      </li>
+      <li>Click on the edit buttons added for your convenience. 
+      </li>
+      <li>Right click on the edit buttons for more options.
+      </li>
+    </ul>
+
+    <p>
+      Here is a test edit box for you to play with&hellip;
+    </p>
+    <form action="" style="margin-left: 2em;">
+      <p style="margin: 0; padding: 0;">
+        <textarea id="test" cols="10" rows="4">Click the edit button and have fun editing!</textarea>
+      </p>
+      <div id="nteb">Note the edit button.&mdash;&gt; </div>
+    </form>
+
+    <div style="clear:both;"/>
+    <p id="signoff"> Ciao! </p>
+
+      
+    <h3>A note to Mac OS X users</h3>
+    
+    <p>
+      Out of the box, <span class="iat">It's All Text!</span> uses the <code>open</code> program. <code>open</code> behaves like double clicking on a file.  It uses the type of the file to choose the correct application to run;  for <tt>.txt</tt> files, that application is the built-in text editor.
+    </p>
+    <p>
+      If this behavior is fine for you, then leave the editor option alone and enjoy!
+    </p>
+    <p>
+      However, if you want to use a different editor or to force the same editor regardless of the file type, then you will need to do something a little more complicated.
+    </p>
+    <p>
+      Firefox <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=307463">cannot run .app applications directly</a>.  To run a program in Mac OS X you need to do one of two things:  If your editor comes with a non-<code>.app</code> version, then use that.  Otherwise you have to write a shell script.
+    </p>
+    <p>
+      Check your editor's documentation; if it comes with a standalone program, usually located in the <code>/usr/bin/</code> directory, then you can enter that into the <span class="iat">It's All Text!</span> preferences and you're done.
+    </p>
+    <p>
+      Otherwise, you need to create a shell script.  Here are the basic steps to create a shell script:
+    </p>
+    <ol>
+      <li> Open your favorite editor. </li>
+      <li> Create a file like the example below. </li>
+      <li> Save it to your home directory: <code>~/iat.sh</code> </li>
+      <li> Open a terminal window. </li>
+      <li> Type this command to make the shell script executable: <code>chmod +x ~/iat.sh</code> </li>
+      <li> In <span class="iat">It's All Text!</span> preferences, use the shell script as your editor. </li>
+    </ol>
+
+    <p>
+      The example shell script.  Replace <code>/path/to/editor.app</code> with the actual path to your .app file.  It'll probably be something like <code>/Applications/MyEditor.app</code>.
+    </p>
+    <pre lang="sh">#!/bin/sh
+# This is an example shell script for It's All Text!
+
+open -a /path/to/editor.app $*</pre>
+
+    <p>
+      Other alternative shell scripts are available at <a href="http://docwhat.gerf.org/2007/03/its_all_text_v06/#comment-2054">here</a>.
+    </p>
+
+
+
+    <h3>FAQ</h3>
+
+    <dl id="faq">
+      <dt>I want to do something more complicated than just running an editor with a file-name.</dt>
+      <dd>
+        <p>In UNIX systems, such as Mac OS X or Linux, you can create a shell script with your commands in it.</p>
+        <p>In windows, you can create a <tt>.cmd</tt> file instead.</p>
+      </dd>
+
+      <dt>I can't find the edit button for (gmail, blogger, etc.)</dt>
+      <dd>
+        <p>Gmail, blogger, and other sites has the option to use "rich text editors".  The editors act similar to a word processor.  Due to the way these work, it isn't possible for <span class="iat">It's All Text!</span> find the <tt>textarea</tt>, it is hidden or, in some cases, absent.
+        </p>
+      <p>
+        Workaround: Turn off the rich text editor, if possible.
+      </p>
+      </dd>
+
+      <dt>I use non-ASCII characters and they turn into blocks or question marks (?).</dt>
+      <dd>
+        <p>
+          The problem is that the encoding <span class="iat">It's All Text!</span> is using and your editor is using don't match.  You can figure out what encoding your editor wants and change the encodings preference in <span class="iat">It's All Text!</span> or you can change the encoding your editor uses.
+        </p>
+        <p>
+          A common problem I get is that someone is using Notepad or WordPad in Windows.  These both do not support sane encodings.  I recommend getting something like <a href="http://notepad-plus.sourceforge.net/">Notepad++</a> for editing in UTF-8 instead.
+        </p>
+      </dd>
+    </dl>
+
+  </body>
+</html>
+
+<!-- LocalWords:  gmail blogger WordPad UTF html abd fff moz ffa tt dt ver cdf
+-->
+<!-- LocalWords:  nteb px iat faq dl cmd
+-->
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/defaults/preferences/itsalltext.js b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/defaults/preferences/itsalltext.js
new file mode 100644 (file)
index 0000000..d7f0bb3
--- /dev/null
@@ -0,0 +1,6 @@
+pref("extensions.itsalltext.charset",  "UTF-8");
+pref("extensions.itsalltext.editor",   "");
+pref("extensions.itsalltext.refresh",  3);
+pref("extensions.itsalltext.debug",  false);
+pref("extensions.itsalltext.disable_gumdrops", false);
+pref("extensions.itsalltext.extensions",  '.txt,.html,.css,.xml,.xsl,.js');
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/gpl.txt b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/gpl.txt
new file mode 100644 (file)
index 0000000..d511905
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/install.rdf b/.mozilla/firefox/default/extensions/itsalltext@docwhat.gerf.org/install.rdf
new file mode 100644 (file)
index 0000000..44aafaa
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:em="http://www.mozilla.org/2004/em-rdf#"
+         xmlns:NC="http://home.netscape.com/NC-rdf#"
+         xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+  <RDF:Description RDF:about="rdf:#$firefox"
+                   em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
+                   em:minVersion="1.5"
+                   em:maxVersion="3.0a6" />
+  <RDF:Description RDF:about="rdf:#$flock"
+                   em:id="{a463f10c-3994-11da-9945-000d60ca027b}"
+                   em:minVersion="0.4"
+                   em:maxVersion="0.8" />
+  <RDF:Description RDF:about="urn:mozilla:install-manifest"
+                   em:id="itsalltext@docwhat.gerf.org"
+                   em:version="0.7.3"
+                   em:type="2"
+                   em:name="It's All Text!"
+                   em:description="Edit text using your favorite editor!"
+                   em:homepageURL="http://addons.mozilla.org/firefox/4125"
+                   em:optionsURL="chrome://itsalltext/content/preferences.xul"
+                   em:iconURL="chrome://itsalltext/content/icon.png"
+                   em:aboutURL="chrome://itsalltext/content/about.xul"
+                   em:creator="Christian Höltje">
+    <em:targetApplication RDF:resource="rdf:#$firefox"/>
+    <em:targetApplication RDF:resource="rdf:#$flock"/>
+  </RDF:Description>
+</RDF:RDF>