/**
 * Copyright 2008, 2009 Lime Labs LLC
 *
 * This file is part of AXIS.
 *
 * AXIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * AXIS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with AXIS.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @fileoverview
 */
function Util()
  {
    /**
     * @constructor
     */
    this.__construct = function()
      {
        AXIS.onDOMReady.subscribe({
          callback: function(){
            var limefooter = document.getElementById('limefooter');
            if (limefooter == null)
              return;
  
            var get_bit_link = limefooter.getElementsByTagName('span')[0];
            if (get_bit_link) {
              get_bit_link = get_bit_link.getElementsByTagName('a')[0];
              AXIS.attachEvent('click', function(e) {
                  if (!e.preventDefault) {
                      e.returnValue = false;
                  }
                  else {
                      e.preventDefault();
                  }
  
                  var bitPath;
                  if (this.getAttribute) {
                      bitPath = this.getAttribute('bitPath') || location.pathname;
                  }
                  else {
                      bitPath = location.pathname;
                  }
  
                AXIS.Util.bit.getBit(bitPath);
              }, get_bit_link);
            }
            
            if (AXIS._siteData.hosts.limebits == 'http://www.limebits.com/')
              return;
            
            var footer_links = limefooter.getElementsByTagName('a');
            for (var i = 0; i < footer_links.length; i++)
              {
                var href = footer_links[i].href;
                if (href.match(/http:\/\/[^\.]*.limebits.com\//))
                  footer_links[i].href = href.replace('limebits.com', AXIS._siteData.hosts.limebits.match(/http:\/\/www\.([^\/]*)\//)[1]);
              }
          }
        });
      };

    this.bit =
      {
        /** 
          * Duplicates a bit: Copies it to the current folder giving it a new name
          * args : @bitPath: Path to the bit
          *       @destFolder : folder where the bit has to be copied
          *       @options : 
          *         - success : success callback
          *         - failure : failure callback
          *         - scope : scope for the callbacks
          * TODO : include scope / failure / success callbacks
          */
        copy: function(bitPath, destFolder, options)
          {
            if(!bitPath || !destFolder) {
                return;
            }
            
            options = options || {};
            
            if(bitPath.charAt(bitPath.length - 1) == '/') {
              bitPath = bitPath.substring(0, bitPath.length - 1);
            }

            AXIS.WebDAV.COPY({
              url: bitPath,
              destination: destFolder,
              tryAlternateNames: true,
              on201: function(r) {
                if(options.success && ((typeof(options.success)).toLowerCase() == "function")) {
                  options.success.call(options.scope, r);
                }
              }
            });
            
          }, 
          
        getBitFromUserDomain: function(bitUrl)
          {
            AXIS.Cookies.create(bitUrl, "copy", { path: '/!lime/root/apps/bar/' });
            location = AXIS._siteData.hosts.limebits + 'apps/copier/#bitUrl=' + encodeURIComponent(bitUrl) +
              '&requestor=' + encodeURIComponent('http://'+location.host+'/');
          },

        getUUID: function(url, callback)
          {
            AXIS.WebDAV.getProperty({
             url: url,
             properties: ['resource-id'],
             asynch: true,
             onSuccess: function(r) {
               var resource_id;
               try {
                 resource_id = r.responseXMLObject().multistatus.response.propstat.prop["resource-id"].href.replace(/urn:uuid:/, '').replace(/-/g, '');
               }
               catch(e) {
                   resource_id = null;
               }

               callback(resource_id);
             }
            });
          },
           

        /* copy bitmarks,

           options.src_uuid: Set to uuid of source bit
           options.url: Set to url of source bit 
           only one of src_uuid & url is required, src_uuid is preferred

           options.destination: Set to url of destination bit
           options.dst_uuid: Set to uuid of destination bit
           only one of src_uuid & url is required, src_uuid is preferred
        */
        copyBitmarks: function(options)
          {
            if (!options.src_uuid) {
                AXIS.Util.bit.getUUID(options.url, function(uuid) {
                  if (!uuid) {
                    return options.callback();
                  }
                  options.src_uuid = uuid;
                  AXIS.Util.bit.copyBitmarks(options);
                });
            }
            else if (!options.dst_uuid) {
                AXIS.Util.bit.getUUID(options.destination, function(uuid) {
                  if (!uuid) {
                    return options.callback();
                  }
                  options.dst_uuid = uuid;
                  AXIS.Util.bit.copyBitmarks(options);
                });
            }
            else {
                options.url = '/bitmarks/' + options.src_uuid;
                options.destination = '/bitmarks/' + options.dst_uuid;
                AXIS.WebDAV.COPY(options);
            }
          },

        getBit: function(bitPath, location_replace)
          {
            if (AXIS._siteData.hosts.limebits != ("http://" + location.host + "/"))
              {
                return AXIS.Util.bit.getBitFromUserDomain(bitPath);
              }

            var bit = AXIS.Util.bit.bitInfo(AXIS.Util.uri.getBitUrl(bitPath));

            var user = AXIS.Cookies.read('user');

            if (user)
              {
                var dst_folder = '/home/' + user + '/bits';
              
                AXIS.WebDAV.COPY({
                  url: bit.tld_root,
                  asynch: true,
                  destination: dst_folder + '/' + bit.name,
                  tryAlternateNames: true,
                  on201: function(r) {
                    if (r.alternateName) {
                      bit.name = r.alternateName;
                    }

                    var dstBit = AXIS.Util.bit.bitInfo(AXIS.Util.uri.getBitUrl(r.destination + bit.tld_path_from_root));
                    AXIS.Cookies.create(dstBit.tld_root, "openConfigure", { path: '/apps/' });
                    var user_bits_url = "/apps/bitfinder/";
                    if (location_replace)
                      top.location.replace(user_bits_url);
                    else
                      top.location = user_bits_url;
                  },
                  on409: function(r) {
                    AXIS.WebDAV.MKCOL({
                      url: dst_folder,
                      asynch: true,
                      on201: function(r) {
                        AXIS.Util.bit.getBit(bitPath, location_replace);
                      },
                      onFailure: function(r) {
                        location.replace(bitPath);
                      }
                    });
                    
                  },
                  onFailure: function(r) {
                    var st = r.getStatus();
                    if(st !== 409 && st !== 412)
                      {
                        location.replace(bitPath);
                      }
                  }
                });
              }
            else
              {
                var rand = "" + Math.random();
                AXIS.Cookies.create(rand + '-url', AXIS.Util.uri.getUserUrl(bitPath), { path: '/apps/copier/' });
                AXIS.Cookies.create(rand + '-confirmed', 'true', { path: '/apps/copier/' });
                AXIS.Login.redirReturnTo = AXIS._siteData.hosts.limebits + 'apps/copier/#token=' + rand;
                AXIS.Login._redirectToAccess("if_new");
              }
          },

        /*
           
         */
        bitInfo: function(bitUrl)
          {
            var bit = {};

            bit.url = bitUrl;

            bit.tld_path = AXIS.Util.uri.getTLDPath(bit.url);
            bit.tld_folder = bit.tld_path.match(/.*\//)[0];
            bit.filename = bit.tld_path.replace(bit.tld_folder, "");

            bit.owner = bit.tld_path.match(/\/home\/([^\/]*)/)[1];

            if (bit.tld_path.match(/\/home\/[^\/]*\/bits\/[^\/]*/))
              {     /* tld path is of the pattern /home/user/bits/bitname/*** */
                bit.tld_root = bit.tld_path.match(/(\/home\/[^\/]*\/bits\/[^\/]*)/)[1];
              }
            else
              {
                bit.tld_root = AXIS.Util.uri.chompSlash(bit.tld_folder);
              }
            bit.tld_path_from_root = bit.tld_path.replace(bit.tld_root, "");
            bit.root_url = AXIS.Util.uri.getBitUrl(bit.tld_root);

            bit.name = bit.tld_root.replace( /.*\//, "" ); // get the folder basename

            return bit;

          },

        getBitmarks: function(bitFolder, cb)
          {
            var dav = AXIS.WebDAV;
            var marks = {
              tags: []
            };

            dav.SEARCH({
              asynch: true,
              url: bitFolder,
              props: ["displayname", "resource-id", "lastmodified", "popularity"],
              bitmarks: {
                ns: AXIS.WebDAV.ns.bm,
                names: ["tag", "description"]
              },
              depth: "0",
              callback: function(r) {
                var resp = r.responseXMLObject().multistatus.response;

                var pstat = resp.propstat;
                var bstat = resp.bitmarkstat || [];

                pstat = AXIS.isArray(pstat) ? pstat : [pstat];
                bstat = AXIS.isArray(bstat) ? bstat : [bstat];

                for(var j = 0; j < pstat.length; j++)
                  {
                    if(pstat[j].status.match(/200 OK/))
                      {
                        AXIS.Util.lang.augmentObject(marks, pstat[j].prop);
                      }
                  }

                for(var k = 0; k < bstat.length; k++)
                  {
                    if(bstat[k].status.match(/200 OK/))
                      {
                        var bitmark = bstat[k].bitmark;
                        bitmark = AXIS.isArray(bitmark) ? bitmark : [bitmark];

                        for(var l = 0; l < bitmark.length; l++)
                          {
                            if (bitmark[l].tag)
                              {
                                marks.tags.push(bitmark[l].tag);
                              }
                            else
                              {
                                AXIS.Util.lang.augmentObject(marks, bitmark[l]);
                              }
                          }
                      }
                  }
                cb.call(null, marks)                                
              }
            });
          },


        /**
         * Opens the configure tab for a bit.
         * Arguments :: @bitPath: Top-level-domain Path to the bit
         * Currently this is done by setting a cookie where :
         *  cookie-name : Absolute URL of the bit
         *  cookie-value : "new_copy"
         */
        openConfigureTab: function( bitPath )
          {
            if(!bitPath)
              return;

            var bit = AXIS.Util.bit.bitInfo(AXIS.Util.uri.getBitUrl(bitPath));

            // Create the cookie
            AXIS.Cookies.create(bit.tld_root, "openConfigure", { path: '/apps/' });
            window.location.assign(bit.url + "?bar");
          }

      };
      
    this.uri = 
      {
        basename: function(path)
          {
            return path.replace(/.*\//, "");
          },

        /* get folder containing given URL,
         * will return the URL itself if it ends in '/'
         * otherwise strips all non '/' characters at the end */
        getFolder: function(url) {
          return url.replace(/\/[^\/]*$/,'/');
        },

        getParent: function(url) {
            return url.replace(/\/[^\/]*\/?$/, '/');
        },

        /**
         * Normalizes the url by removing redundant slashes (and trailing slashes) and decodes %20
         *
         * @param    {String} url    The url to normalize
         * @returns                  The normalized url
         * @type     {String}
         */
        normalize: function(url) {
          url = url.replace(/\/\/*/g, "/"); // replace multiple consecutive slashes with one slash
          url = url.replace(/%20/g, " ");   // decode %20
          return AXIS.Util.uri.chompSlash(url);  // remove any trailing slashes and return
        },

        /**
         * Removes trailing slashes from the url if present
         *
         * @param    {String} url    The url
         * @returns                  The url without any trailing slashes
         * @type     {String}
         */
        chompSlash: function(url) {
          return url.replace(/[\/\\]*$/, '');
        },

        getBitUrl: function(tld_path) {
          var bit_info = tld_path.match(/home\/([^\/]+)\/bits\/([^\/]+)(.*)/);
          var bit_url;

          if (bit_info && bit_info[2].match(/^[-a-z0-9]*$/))
            {
              bit_url = AXIS._siteData.hosts.limebits.replace("www", bit_info[2] + "." + bit_info[1]) + bit_info[3].substr(1);
            }
          else
            {
              bit_url = this.getUserUrl(tld_path);
            }

            return bit_url;
        },

        /**
         * Converts the www domain path to a FQDN url for accessing the path without redirects
         *
         * @param    {String} tld_path    The www domain path of the folder 
         * @returns                       The FQDN for accessing that path directly
         * @type     {String}
         */
        getUserUrl: function(tld_path) {
          var user_url, user_info;
          url_info = tld_path.match(/home\/([^\/]+)(.*)/);
          if (url_info)
            user_url = AXIS._siteData.hosts.limebits.replace("www", url_info[1]) + url_info[2].substr(1);
          else
            user_url = AXIS._siteData.hosts.limebits + tld_path.substr(1);
          return user_url;
        },

        /**
         * Converts the FQDN url to a www domain path
         *
         * @param    {String} user_url    The FQDN for accessing that path directly
         * @returns                       The www domain path of the folder
         * @type     {String}
         */
        getTLDPath: function(user_url) {
          var bitParts = user_url.match(/http:\/\/([^\/]+)(\/[^#]*)/);
          var hostname_pref = bitParts[1].replace(AXIS._siteData.hosts.limebits.match(/www(\.[^\/]*)/)[1], "");
          var bitOwner;
          var bitPath = bitParts[2];
          if (hostname_pref.split(".").length == 2)
            {
              bitOwner = hostname_pref.split(".")[1];
              return '/home/' + bitOwner + '/bits/' + hostname_pref.split(".")[0] + bitPath;
            }
          else
            {
              bitOwner = hostname_pref;
              return (bitOwner == 'www') ? bitPath: '/home/' + bitOwner + bitPath;
            }
        },

        findAlternateName: function(name, used_names)
          {
            if (!used_names.indexOf)
            {
              used_names.indexOf = function(item)
              {
                for (var i = 0; i < this.length; i++)
                  if (this[i] === item) return i;
                return -1;
              }
            }
            var new_name, extension;
            if (name.indexOf('.') == -1)
              {
                new_name = name;
                extension = '';
              }
            else
              {
                var name_parts = name.match(/(.*)(\.[^.]*)$/);
                new_name = name_parts[1];
                extension = name_parts[2];
              }

            var start_num = 2;
            var name_parts = new_name.match(/(.*)([\d]+)$/);
            if (name_parts)
              {
                new_name = name_parts[1];
                start_num = parseInt(name_parts[2]) + 1;
              }

            var new_full_name;
            var end_num = start_num + used_names.length + 1;
            for (var i = start_num; i <= end_num; i++)
              {
                var new_full_name = new_name + i + extension;
                if (used_names.indexOf(new_full_name) == -1) break;
              }
            return new_full_name;
          }
      };

    this.xml =
      {
        iterableToArray: function(iterable) {
          if (iterable.toArray) return iterable.toArray();
          var length = iterable.length, results = new Array(length);
          while (length--) results[length] = iterable[length];
          return results;
        },
        getChildrenByNameNS: function(el, name, ns) {
          if (ns == null)
            ns = "DAV:";

          if (typeof el.getElementsByTagNameNS == "function")
            return this.iterableToArray(el.getElementsByTagNameNS(ns, name));
          else {
            el.ownerDocument.setProperty("SelectionLanguage", "XPath");
            el.ownerDocument.setProperty("SelectionNamespaces", "xmlns:pref='"+ns+"'");

            var child_els = el.selectNodes("*//pref:" + name);
            if (child_els.length == 0)
              child_els = el.selectNodes("pref:" + name);

            return this.iterableToArray(child_els);
          }
        },
        getText: function(node)
          {
            var txt = "";
            for (var i = 0; i < node.childNodes.length; i++)
              {
                if (node.childNodes[i].nodeType == AXIS.TEXT_NODE)
                  txt += node.childNodes[i].nodeValue;
              }
            return txt;
          }
      }

    this.lang =
      {
        augmentObject: function(r, s) {
          for(var p in s)
            {
              r[p] = s[p];
            }
        }
      }

    this.json =
      {
        eval: function(text)
          {
            var json_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(text.replace(/"(\\.|[^"\\])*"/g, ''))) && eval('(' + text + ')');
            /*")*/
            return json_object;
          },

        safeEval: function(text)
          {
            /* comes from JSON.parse at http://json.org/json2.js (public domain). */
            var j = {};

            var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
            cx.lastIndex = 0;
            if (cx.test(text))
               {
                 text = text.replace(cx, function (a) {
                   return '\\u' +
                     ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                 });
               }

            if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, '')))
              j = eval('(' + text + ')');

             return j;
          }
      }
  }
/**
 * Copyright 2008, 2009 Lime Labs LLC
 *
 * This file is part of AXIS.
 *
 * AXIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * AXIS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with AXIS.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @fileoverview
 */
function Cookies() {
  /**
   * @constructor
   */
  this.__construct = function() {
  };
    
  this.create = function(name,value,o) {
    var c       = o || {};
        
    name      = encodeURIComponent(name);
    value     = encodeURIComponent(value);
    
    c.domain  = c.domain ? '; domain=' + encodeURIComponent(c.domain) : '';
    c.path    = c.path || "/";

    if(c.days) {
      var date = new Date();
      date.setTime(date.getTime()+(c.days*24*60*60*1000));
      var expires = "; expires="+date.toGMTString();
    } 
    else {
      var expires = "";
    }
        
    try {
      document.cookie = name+"="+value+expires+"; path=" + c.path + c.domain;
      return true;
    }
    catch(e) {
      return false;
    }
  };
      
  this.update = function(name,value,days) {
    /*
     * bridge function to allow more accurate
     * description of script flow
     */
    var n = name  || null;
    var v = value || null;
    var d = days  || null;
        
    return this.create(n,v,d);
  };
      
  this.read = function(name) {
    var nameEQ = encodeURIComponent(name) + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
      var c = ca[i];
      while(c.charAt(0)==' ') {
        c = c.substring(1,c.length);
      }
            
      if(c.indexOf(nameEQ) == 0) {
        return decodeURIComponent(c.substring(nameEQ.length,c.length));
      }
    }
    
    return false;
  };
  
  this.erase = function(name, o) {
    o = o || {};
    o.days = -1;
    return this.create(name,"", o);
  };
};/**
 * Copyright 2008, 2009 Lime Labs LLC
 *
 * This file is part of AXIS.
 *
 * AXIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * AXIS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with AXIS.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @fileoverview
 *
 * @requires  AXIS
 * @requires  Cookies
 */
function User()
  {
    /**
     * @constructor
     */
    this.__construct = function()
      {
      };
    
    /**
     * Loads a client's geodata, via Google's Data API.
     * Note that this is making an XHR call, so the data
     * will be available at some point in the future -- it is likely
     * that you would want to set a callback to inform your application
     * of when this data is available.  
     *
     * @param      {Function}    [cb]    The callback function.
     * @example    AXIS.User.getUserGeoData(function() {
     *                alert(AXIS.User.Data.get('city'));
     *              });  
     */
    this.getUserGeoData = function(cb)
      {
        if(AXIS.Modules)
          {
            /**
             * This also requires the developer having set `useGoogleAPI` argument.
             * Checks and notifications on failure to do that exist in the
             * Module extension. 
             *
             * @see AXIS#Modules#load
             */
            AXIS.Modules.load({
            	provider: 'google',
            	module:   'gdata',
             	version:  '1',

              onload: function(google)
                {
                  console.log(google);
                  var v = google.loader.ClientLocation;
                  if(v)
                    {
                      var a = v.address;
                      var d = AXIS.User.Data;
                  
                      d.set('latitude', v.latitude || '');
                      d.set('longitude', v.longitude || '');
                      d.set('city', a.city || '');
                      d.set('country', a.country || '');
                      d.set('country_code', a.country_code || '');
                      d.set('region', a.region || '');  
    
                      cb && cb();
                    }
                }

            }); 
         }
      };

    this.Data = 
      {
        set: function(k,v)
          {
            if(k && v)
              {
                this[k] = v;  
              }    
          },
          
        get: function(k)
          {
            if(k && this[k])
              {
                return this[k];  
              }
            else
              {
                return false;  
              }
          }
      };
      
    /*
     * Checks if user is logged in by reading cookie ('user');
     * NOTE: use #username if possible.
     *
     * @return    {Mixed} username if logged in; {Boolean} false if not
     */ 
    this.isLoggedIn = function()
      {
        return AXIS.Cookies.read('user');  
      };

    /**
     *  Alias of #isLoggedIn: more readable.
     *
     * @return    {String} username if logged in; 'unauthenticated' if not
     */
    this.username = function()
      {
        return AXIS.Cookies.read('user') || 'unauthenticated';  
      };
      
    this.getBitsFolderName = function()
      {
        return this.isLoggedIn() ? "/home/" + this.username() + "/bits/" : "";
      };
  };/**
 * Copyright 2008, 2009 Lime Labs LLC
 *
 * This file is part of AXIS.
 *
 * AXIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * AXIS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with AXIS.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @fileoverview
 */
function Loader() 
  {
    /**
     * @constructor
     */
    this.__construct = function()
      {
        /*
         * all requests will go into _activeRequests until a locked request.
         * TODO: create locked requests, and request queueing
         */
        this._activeRequests  = [];
        
        AXIS.onDOMReady.subscribe({
          callback: function() {
            this.loadingPanel = document.body;
            this.createLoadingPanel();
          },
          scope: this
        });
          
        /*
         * 1:   Supported by all browsers
         * 2:   Supported by rfc2616 (Opera supports these)
         * 3:   Basic methods, supported by IE.
         * n:   Gecko(Firefox) supports all methods
         *
         * NOTE: methods of higher index include all lower indexed methods
         */
        this.supportedMethods = {
          GET:                1,
          POST:               1,
          HEAD:               2,
          PUT:                2,
          DELETE:             2,
          PROPFIND:           3,
          PROPPATCH:          3,
          MKCOL:              3,
          LOCK:               3,
          UNLOCK:             3,
          COPY:               3,
          MOVE:               3,
          REPORT:             3,
          SEARCH:             3,
          CHECKIN:            3,
          CHECKOUT:           3,
          UNCHECKOUT:         3,
          'VERSION-CONTROL':  3,
          TRACE:              3,
          BIND:               3,
          UNBIND:             3,
          REBIND:             3,
          MKREDIRECTREF:      3,
          OPTIONS:            3                                        
        };
      };
      
    /**
     * Use this method to initiate an XHR call, mainly if you simply want to 
     * fetch a resource using GET.  A more comprehensive API for sending various
     * types of HTTP calls can be found in AXIS#WebDAV.  A direct call using this
     * method would look like:
     *
     * AXIS.Loader.load({url: foo/bar.html});
     * 
     * NOTE: default method is GET; and TRUE is the default for .asynch property.
     */

    this.load = function(a)
      {  
        /*
         * Validate "core" attributes
         */
        if(!!a.url === false)
          {
            new AXIS.Errors.LoaderException('XHR_NO_URL_SENT');
            return false;  
          }
          
        a.method        = (a.method) ? a.method.toUpperCase() : 'GET';
        a.url           = a.url;
        a.breakCache    = !!a.breakCache;
        a.callback      = a.callback      || AXIS.F;
        a.onSuccess     = a.onSuccess     || AXIS.F;
        a.onFailure     = a.onFailure     || AXIS.F;
        a.onBeforeSend  = a.onBeforeSend  || AXIS.F;
        a.onAfterSend   = a.onAfterSend   || AXIS.F;
        a.callId        = a.callId        || AXIS.getUniqueId('xhr_');
        a.passThru      = a.passThru      || [];
        a.asynch        = (a.asynch === undefined) ? false : !!a.asynch;
        a.body          = a.body          || null;
        a.username      = a.username      || null;
        a.password      = a.password      || null;
        a.loadingMsg    = a.loadingMsg    || AXIS.defaultLoadingMsg;
        a.headers       = a.headers       || {};   
        
        /**
         * Keep a non-translated version of the request url in case we want
         * to fetch that info later
         */
        a.origRequestUrl = a.url;

        /**
         * To simplify matters for user, allow the sending of succ/fail codes in
         * basic array format ( [403,412,423] ).  However, we'll want to look
         * them up as keys (codes[200] === true).  So translate here.
         *
         * @see  XHR#build#main
         */
        a.failureCodes  = a.failureCodes  || [];
        for(var f=0; f<a.failureCodes.length; f++)
          {
            a.failureCodes[a.failureCodes[f]] = true;  
          }

        a.successCodes  = a.successCodes  || [];
        for(var f=0; f<a.successCodes.length; f++)
          {
            a.successCodes[a.successCodes[f]] = true;  
          }
          
        /*
         * If the method is not supported (either by the browser or
         * in general) send as a special query
         */
        if(!this.methodSupported(a.method))
          {
            var _oldOnBeforeSend = a.onBeforeSend;
            a.onBeforeSend = function() {
              _oldOnBeforeSend.apply(this, arguments);

              var auth = AXIS.Cookies.read("auth");
              auth = auth ? ("&auth=" + auth) : "";
                  
              a.url += "?webdav-method=" + a.method.toUpperCase() + auth;
              a.method = 'POST';
            };
            //alert('special load');
          }
          
        try
          {
           /*
             * Add to the displayed list of queued xhr objects
             */
            if(this.addRequest(a))
              {
                /**
                 * Start the loading.  There is some particular thinking here
                 * around how to treat synch/asynch calls.  In the case of
                 * asynch, we use the Queue to wait for a response, and
                 * to then handle callbacks and so forth.
                 * We could also use synch in this way, and the framework can
                 * handle that.  However, for those who use synch calls, it may
                 * be more appropriate for, instead of using callbacks, for 
                 * Loader#load to return the response directly.  As the 
                 * AXIS.XHR.send function will be executing the .httpHandle.send()
                 * XHR method, a synch call will block, and since we know that
                 * when the block is cleared we have the response object, simply
                 * call .main() (which does some processing on the response), and
                 * return the result.  Note as well, any callbacks that are set
                 * will still be called *prior* to this returning.  This should
                 * provide the best of both worlds, direct functional responses
                 * (for those who want blocking), and a callback structure (for
                 * those who would prefer a non-blocking, asynchronous model).
                 */
                         
                /**
                 * This gets back the fully formed call object, to be either
                 * queued or not, as described above.
                 */
                var ss = AXIS.XHR.send(a);

                if(a.asynch === false)
                  {
                    return ss.main();
                  }
                else
                  {
                    return AXIS.Queue.add(ss);  
                  }
              }
          }
        catch(e)
          {
            /*
             * The error is probably with the send attempt, so attempt to clear 
             * the View of any "Loading" messages tied to this attempt.
             */
            if(a && a.callId)
              {
                this.clearLoadingPanelItem(a.callId);   
              }
          }
        return false;
      };

    this.methodSupported = function(meth)
      {
        var sm = this.supportedMethods[meth];

        if(sm)
          {    
            if(sm < 2)                      return true;
            if(AXIS.isOpera && (sm < 3))    return true;
            if(AXIS.isIE && (sm < 4))       return true;
            if(AXIS.isMoz || AXIS.isGecko)  return true;
          }
          
        return false;
      };
      
    this.addRequest = function(r)
      {
        if(r.callId && this.notDuplicateCall(r.callId))
          {
            this.updateLoadingPanel(r.callId,r.loadingMsg); 
            this._activeRequests.push(r);
            return true;
          }
        return false;
      };
          
    this.notDuplicateCall = function(callId)
      {
        var aR  = AXIS.Loader._activeRequests;
        var i   = aR.length;
        while(i--)
          {
            if(aR[i].callId == callId)
              {
                return(false);
              }
          }
        return(true);
      };
          
    this.afterResponseCleanup = function(retOb)
      {
        var aR  = AXIS.Loader._activeRequests;
        var i   = aR.length;
        while(i--)
          {
            if(aR[i].callId == retOb.callId)
              {
                /*
                 * Clear acitve request
                 */
                aR.splice(i,1);
              }
          }
        AXIS.Loader.clearLoadingPanelItem(retOb.callId);

        //retOb.callback(retOb);
      };

    this.createLoadingPanel = function()
      {
        try
          {
            var b = document.createElement('div');
            b.id          = AXIS.loadingMsgContainerId;
    
            
            document.body.appendChild(b);
          }
        catch(e){}
      };
      
    this.updateLoadingPanel = function(id,msg)
      {
        if(AXIS.settings('showLoadingInfo'))
          {
            try
              {
                var e = document.createElement('div');
                e.setAttribute('id',id);
                e.className = AXIS.loadingMsgItemClass;
                e.appendChild(document.createTextNode(msg));
                 
                var f = document.getElementById(AXIS.loadingMsgContainerId);
                f.style.width     = '100%';    
                f.appendChild(e);
              }
            catch(e){}
          }
      };
      
    this.clearLoadingPanelItem = function(id)
      {
        var z = document.getElementById(id);
        if(z && z.parentNode)
          {
            var pn = z.parentNode;
            pn.removeChild(z);
    
            if(pn.childNodes.length == 0)
              {
                var f = document.getElementById(AXIS.loadingMsgContainerId).style;
                f.width = '0%';
              }
          }
      };
  };
/**
 * Copyright 2008, 2009 Lime Labs LLC
 *
 * This file is part of AXIS.
 *
 * AXIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * AXIS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with AXIS.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @fileoverview
 *
 * The main routines for XHR.  The main purpose is to provide methods to build 
 * and enhance the transport package, make the call, check status and throw
 * transport errors. 
 *
 * @throws   Error     XHR_401
 * @throws   Error     XHR_423
 * @throws   Error     XHR_500
 * @requires AXIS
 */
function XHR()
  {
    /**
     * @constructor
     */
    this.__construct = function()
      {
        /**
         * The headers that will be sent with every request, unless overridden
         *
         * @see #send
         */
        this.defaultHeaders = 
          {
            'X-Requested-With':   'XMLHttpRequest',
            'Accept':             'text/javascript, text/plain, text/html, text/xml, application/javascript, application/json,  application/xml, */*'
          };

        /**
         * Caching information and storage for GET calls.
         * 
         * @see #send
         * @see #build#main
         */
        this.cache            = {};
        
        
        /**
         * The receipt of a status code *not* present in this list results
         * in the call being treated as failed.
         *
         * NOTE: the `reshuffling` done on this Array following definition.
         *
         * @see #build
         */
        this.statusCodes = 
          [
            100, // Continue
            102, // Processing (WebDAV) (RFC 2518)  
            
            200, // OK
            201, // Created
            202, // Accepted
            203, // Non-Authoritative Information (since HTTP/1.1)
            204, // No Content
            205, // Reset Content
            206, // Partial Content
            207, // Multi-Status (WebDAV)
            208, // Already reported.
            300, // Multiple Choices
            301, // Moved Permanently
            302, // Found
            303, // See Other (since HTTP/1.1)
            304, // Not Modified
            305, // Use Proxy (since HTTP/1.1)
            307, // Temporary Redirect (since HTTP/1.1)
            
            400, // Bad Request
            401, // Unauthorized
            402, // Payment Required
            403, // Forbidden -- Note Opera will often return this instead of 401
            404, // Not Found  
            405, // Method Not Allowed
            406, // Not Acceptable
            407, // Proxy Authentication Required
            408, // Request Timeout
            409, // Conflict
            410, // Gone
            411, // Length Required
            412, // Precondition Failed
            413, // Request Entity Too Large
            414, // Request-URI Too Long
            415, // Unsupported Media Type
            416, // Requested Range Not Satisfiable
            417, // Expectation Failed
            422, // Unprocessable Entity (WebDAV) (RFC 4918)
            423, // Locked (WebDAV) (RFC 4918)
            424, // Failed Dependency (WebDAV) (RFC 4918)
            426, // Upgrade Required (RFC 2817)
            449, // Retry With
            
            500, // Internal Server Error
            501, // Not Implemented
            502, // Bad Gateway
            503, // Service Unavailable
            504, // Gateway Timeout
            505, // HTTP Version Not Supported
            506, // Loop detected (Bind Draft)
            507, // Insufficient Storage (WebDAV) (RFC 4918)
            509, // Bandwidth Limit Exceeded
            510  // Not Extended (RFC 2774)
          ];
          
        /**
         * Create another accessor, creating object properties from values
         * (allowing checks for statusCodes[sc])
         */
        var i = this.statusCodes.length;
        while(i--)
          {
            this.statusCodes[this.statusCodes[i]] = this.statusCodes[i];  
          }
      };
      
    /**
     * It is often the case that an API will set status code handlers
     * for HTTP responses (on404, on200, etc).  As well, it is common
     * for several of these to be set per response.  This function aims
     * to make that process easier, allowing the developer to send a status
     * code and either a handler function or an AXIS.Errors code (which is
     * then translated into a function which fires an error notification).  As
     * well, the developer can set more than one of these to set at a time. 
     * Function must be called status:function||code pairs, such as:
     *           AXIS.XHR.setStatusHandlerForResponse(xmlhttprespobj,
     *             {
     *               200: function(){ // do something },
     *               404: 'AXIS_ERROR_CODE'
     *             }
     * Notice as well that should there already is a handler, it is not overridden.
     *
     * @param    {Object}  o   The returned object from AXIS#XHR#send
     * @param    {Object}  i   The handler info
     *
     * @see AXIS#WebDAV
     */
    this.setStatusHandlerForResponse = function(o,i)
      {
        var f = i || {};
        
        var n = function(t,ns,nf)
          {
            t['on' + ns]  = t['on' + ns] || (AXIS.isString(nf[ns]) ? function()
              {
                new AXIS.Errors.XHRException(nf[ns]).report();    
              } : nf[ns]);
          }
        
        for(var s in f)
          {
            n(o || {},s,f);
          }
      };
    
    /**
     * Use this to override the default headers for XHR object, or to set others.
     */
    this.setRequestHeader = function(xhr,nm,hd)
      {
        xhr.httpHandle.setRequestHeader(nm, hd);
      };
    
    /**
     * Will set default headers for the call object (see #XHR). NOTE that
     * should an equivalent header be set in the call object, the default will
     * of course not override it.
     */        
    this.setDefaultHeaders = function(xhr) 
      {
        var ch = xhr.headers;
        
        for(var h in this.defaultHeaders)
          {
            if(!ch[h])
              {
                this.setRequestHeader(xhr, h, this.defaultHeaders[h]);
              }
          }
      };
    
    /**
     * Constructs an XHR object.  It is built to be attached to AXIS#Queue
     *
     * {@link AXIS#Queue}
     * @return    An extended XHR object
     * @type      Object
     */
    this.build = function(a)
      {
        var xob = 
          {
            httpHandle: window.ActiveXObject ? new ActiveXObject("Msxml2.XMLHTTP") : new XMLHttpRequest(),

            /**
             * Interprets the .status property of this.httpHandle, adjusting
             * for various quirks, only allowing acceptable codes, warning if
             * errors happen.
             *
             * @see     #main
             * @return  False on error, a number if successful
             * @type    Mixed
             */
            getStatus: function()
              {
                var hh = this.httpHandle;
                var st = hh.status;

                /*
                 * TODO: testing xbrow
                 */
                if((!st && location.protocol == 'file:') || st)
                  {
                    
                    /**
                     * Check for IE oddity of creating status `1223` when
                     * correct is `204`. Simply return reset value
                     */
                    if(st == 1223)
                      {
                        return 204;  
                      }
                    
                    /** 
                     * An unfortunate hack for Safari && Opera.
                     * Safari sets a status of 404 when receiving 401 responses.  
                     * Opera will set a status of 403 when receiving 401.
                     * Safari also destroys headers.
                     * Our auth (401) pages send HTML w/ `<title>401</title>`. 
                     * The solution is to determine if, based on responseText,
                     * we have received a 401.  Override whatever status is
                     * sent should this be the case.  This is done for all
                     * browsers, to avoid ongoing browser-specific forking. As
                     * Chrome's implementation of Webkit doesn't have the same
                     * bug, the hope is that it is known and will go away.
                     */
                    if((st == 404 || st == 403) && hh.responseText.indexOf('<title>401</title>') !== -1)
                      {
                        return 401;     
                      }

                    /**
                     * Only return on acceptable status codes
                     */
                    if(AXIS.XHR.statusCodes[st])
                      {
                        return st;
                      }
                  }
                return false;
              },
              
            getResponseHeader: function(h)
              {
                if(h && AXIS.isString(h))
                  {
                    var rh = this.httpHandle.getResponseHeader(h);  
                    
                    /**
                     * Special case: In cases where the returned body is gzipped, the 
                     * server will append a suffix '-gzip' to the Etag.  This causes 
                     * trouble if this suffixed Etag is used later to run update checks,
                     * such as when we want to check if a file has been updated since the
                     * current local representation was loaded. If we set a header precondition
                     * (If-Match: Etag) on a PUT, the Etag reported to PUT will *not* have a 
                     * suffix, and as such will *always* be different than the original 
                     * suffixed Etag, even if the file itself has not changed.  So... we strip
                     * out '-gzip' from Etag requests.
                     */
                    if(rh !== null && h == 'Etag')
                      {
                        return rh.replace('-gzip','');  
                      }
                      
                    return rh;
                  }
                return null;
              },
            
            lifespan:         a.lifespan || AXIS._maxXHRLifespan,
            onBeforeTimeout:  a.onBeforeTimeout || function()
              {
                new AXIS.Errors.XHRException('XHR_REQUEST_TIMEOUT');  
              },
            
            /**
             * The XHR object should be attached to the AXIS.Queue, and
             * will wait for the readyState to hit `4`.
             *
             * .readyState values:
             * 0: uninitialized
             * 1: loading
             * 2: loaded
             * 3: interactive
             * 4: complete
             * 
             * {@link Queue}
             * @type  Boolean
             */
            main: function(inf)
              { 
                if(this.httpHandle.readyState === 4) 
                  {     
                                    
                    var T = this;
                    AXIS.Shell.log('axis','debug','Received ', T);
                    /**
                     * Ensure that we snip this instance off the queue, in case
                     * of some error happening where the main function fails
                     * to return false. NOTE: relevant only to asynch calls (Queue)
                     */
                    this.die && this.die();

                    /**
                     * Handles the loading notifications cleanup
                     */
                    AXIS.Loader.afterResponseCleanup(T);
       
                    var stat = T.getStatus();

                    T.responseText = T.httpHandle.responseText || ''; 
                    T.responseXML = '';
                    
                    if(T.httpHandle.responseXML)
                      {
                        T.responseXML = T.httpHandle.responseXML;
                        T.serializedXML = function()
                          {
                            return AXIS.isIE ? T.responseXML.xml : (new XMLSerializer()).serializeToString(T.responseXML);
                          }
                      }
                    if(stat == 304)
                      {
                        /**
                         * Not modified response is 304; in those cases we want to send back
                         * the cached version, if any, to the callback.  So we first make sure we handle
                         * the 304 (to replace current response with cached response).  The callback also
                         * needs to be modified, in order to provide caching for fresh responses.
                         */
                         //alert('304: '+T.url);

                         try
                           {
                             var cac              = AXIS.XHR.cache[T.url];
                             
                             T.responseText       = cac.responseText;
                             T.responseXML        = cac.responseXML;
                           }
                         catch(e)
                           {
                             alert('cache error');
                           }
                      }
                    else if(stat === 200 && T.method === 'GET') // only cache successful GET's
                      {                        
                        /**
                         * On first load, cache info.
                         * Fetch the Etag and use to store match flag.
                         * Unfortunately response header not always available on all browsers.
                         */
                        try
                          {
                            T.__ETAG__ = T.getResponseHeader("Etag");
                          }
                        catch(e)
                          {
                            T.__ETAG__ = '"0"'; 
                          }
                          
                        AXIS.XHR.cache[T.url]  = T;
                      }
                    
                    /**
                     * Call the response processor, if any.  Note that this will be returned
                     * as the second argument of a response handler.  
                     */ 
                    var procResp =  T.responseProcessor 
                                    ? T.responseProcessor.call(T.scope, T) 
                                    : false;
                      
                    /**
                     * NOTE how we call onXXX handlers prior to other response handlers.
                     */
                    T['on' + stat] && T['on' + stat].call(T.scope, T, procResp);

                    /**
                     * If a status code that the user has defined as a failure code is
                     * caught, fire the #onFailure handler.  If status is not returned or
                     * a > 400 level Http status code is returned, we understand this
                     * as being a failure, and we fire the #onFailure handler.
                     */
                    if(T.failureCodes[stat] || stat === false || stat > 400) 
                      {
                        T.onFailure.call(T.scope, T, procResp);
                      }
                    /**
                     * If not a failure, check success status, and fire any 
                     * relevant success handlers.
                     */
                    else if(  ((stat > 199)
                            &&(stat < 209))
                            || stat == 304
                            || T.successCodes[stat])
                      { 
                        T.onSuccess.call(T.scope, T, procResp);
                      }

                    /**
                     * NOTE: .callback is always called whether success or failure, useful
                     * for creating a single handler.
                     */
                    T.callback.call(T.scope, T, procResp);
   
                    return procResp || T;  
                  } 
                return true;
              }
          };

        /**
         * Now attach request args to the returned object
         */
        for(var p in a)
          {
            xob[p] = a[p];  
          }

        /**
         * 204's in IE cause trouble... mainly, an httpHandle.send does
         * not return at point of send. So we force the readystate handler
         * into the duty of calling #main.
         */
        xob.httpHandle.onreadystatechange =   AXIS.isIE && 
                                              a.asynch === false && 
                                              a.method === 'PUT' 
                                                ? function() { xob.main() } 
                                                : AXIS.F;
        
        return(xob);
      };
    
    this.send = function(a)
      {
        var CB        = this.build(a);
        var targUrl   = CB.url;
        AXIS.Shell.log('axis','debug','Sending ',CB);
        /**
         * To break cache we're just adding a random query. Check if
         * request has query fragment, and if so, append, if not, create.
         */
        if(CB.breakCache)
          {
            targUrl = (CB.url.indexOf('?') !== -1) 
                      ? CB.url + AXIS.getUniqueId('&') 
                      : CB.url + AXIS.getUniqueId('?');
          }

        /**
         * This scope will be applied to the response handlers, if any.
         * Default scope is the call object itself.
         *
         * @see #build#main
         */
        CB.scope = CB.scope || CB; 

        CB.httpHandle.open(CB.method, targUrl, CB.asynch, CB.username, CB.password);
        
        this.setDefaultHeaders(CB);
        
        /*
         * GET requests should not send content-type header
         */
        if(CB.method == 'GET') 
          {
            if(CB.headers["Content-Type"]) 
              {
                delete CB.headers["Content-Type"];
              }

            /**
             * Using Etag to check for changes in file, for cacheing.  See #build#main
             */
            if(this.cache[CB.url])
              {
                CB.headers["If-None-Match"] = this.cache[CB.url].__ETAG__;
              }
          }

        /**
         * Override default headers with any sent in call object
         */
        if(CB.headers)
          {
            for(var z in CB.headers)
              {
                this.setRequestHeader(CB, z, CB.headers[z]);
              }
          }
        
        CB.onBeforeSend.call(CB.scope,CB);

        CB.httpHandle.send(CB.body);  

        CB.onAfterSend.call(CB.scope,CB);

        return(CB);
      };       
  };
/**
 * Copyright 2008, 2009 Lime Labs LLC
 *
 * This file is part of AXIS.
 *
 * AXIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * AXIS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with AXIS.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @fileoverview
 *
 * This is a collection of WebDAV methods.  This is a core class, part of the
 * AXIS framework.  All functions or calls which have to do with WebDAV operations
 * are included here.
 *
 * @href  http://greenbytes.de/tech/webdav/rfc4918.html 
 * 
 * Dead Properties
 * ---------------
 * creationdate
 * displayname
 * getcontentlanguage
 * getcontentlength
 * getcontenttype
 * getetag
 * getlastmodified
 * lockdiscovery
 * resourcetype
 * supportedlock
 *
 * @requires   AXIS
 * @requires   Loader
 * @throws     DAV_MOVE_403
 * @throws     DAV_MOVE_404
 * @throws     DAV_MOVE_409
 * @throws     DAV_MOVE_412
 * @throws     DAV_COPY_403
 * @throws     DAV_COPY_404
 * @throws     DAV_COPY_409
 * @throws     DAV_COPY_412
 * @throws     DAV_PUT_NO_BODY
 * @throws     DAV_DELETE_404
 * @throws     DAV_BAD_PROPSET_ARGS
 * @throws     DAV_BAD_PROPREMOVE_ARGS
 * @throws     DAV_LOCK_412
 * @throws     DAV_UNLOCK_400
 * @throws     DAV_UNLOCK_403
 * @throws     DAV_UNLOCK_409
 * @throws     DAV_NO_RANGE
 *
 */

function WebDAV()
  {
    /**
     * @constructor
     */
    this.__construct = function()
      {
        /**
         * namespaces that should be useful to users
         * use like AXIS.WebDAV.ns.lb
         */
        this.ns = {
          d: 'DAV:',
          lb: 'http://limebits.com/ns/1.0/',
          bm: 'http://limebits.com/ns/bitmarks/1.0/'
        };

        this.currentProperties  = [];

        /**
         * In seconds.
         * 
         * @see #LOCK
         */
        this._defaultLOCKTimeout      = 86400;
        
        /**
         * Only write is supported.
         * 
         * @see #LOCK
         */
        this._defaultLOCKType         = 'write';
        
        /**
         * @see #LOCK
         */
        this._defaultLOCKScope        = 'exclusive';
        
        /**
         * @see #LOCK
         */
        this._defaultLOCKDepth        = 'infinity';
        
        this.PRINCIPAL_ALL =                '1';
        this.PRINCIPAL_AUTHENTICATED =      '2';
        this.PRINCIPAL_UNAUTHENTICATED =    '3';
        this.PRINCIPAL_SELF =               '4';
        this.PRINCIPAL_PROPERTY =           '5';
        this.PRINCIPAL_USER =               '6';
        this.PRINCIPAL_GROUP =              '7';
        this.PRINCIPAL_HREF =               '8';
        
        
        AXIS.Errors.registerCode('DAV_ACE_CONFLICT', "An ace conflict occured");
        AXIS.Errors.registerCode('DAV_ACE_INVALID_PRIVILEGES', "Privileges argument is invalid.");
        AXIS.Errors.registerCode('DAV_ACL_CANNOT_REMOVE_INHERITED_ACE', "Inherted aces can only be removed on the ancestor.");
        AXIS.Errors.registerCode('DAV_ACL_CANNOT_REMOVE_PROTECTED_ACE', "Protected aces cannot be removed.");
        AXIS.Errors.registerCode('DAV_ACL_CORRUPTED', "The acl has been corrupted. Have you been editing it directly? ");
        AXIS.Errors.registerCode('DAV_ACL_CHANGED', "The resource's acl has changed since you last fetched it. Try again and use a lock if possible.");

            var search = this.SEARCH;
            search._prop_to_xml = function (prop) {
                if (prop.constructor != Array) {
                    prop = [prop];
                    prop.unshift("DAV:") // set dav as default if no namespace is present
                }
                return '<R:' + prop[1] + ' xmlns:R="' + prop[0] + '"/>';
            };
            // logical operators
            var logicalOps = ["and", "not", "or"];
            var logialGenerator = function(oper) {
                search[oper] = function() {
                    return "<" + oper +">" + Array.prototype.join.call(arguments, "")  + "</" + oper + ">";
                }
            }
            for (var i=0; i < logicalOps.length; i++) {
                logialGenerator(logicalOps[i])
            };
  
            // comparison operators
            var comparisonOps = ["gt", "lt", "eq", "gte", "lte", "gti", "lti", "like", "is_defined", "is_collection", "is_bit"];
            var comparisonGenerator = function(oper) {
                // OK, so the search only searches for <prop>s. We need to add search support based on bitmarks
                // searchCriteria currently has two members: @name : name of the tag whose value is to be searched
                //                                          @ns : namespace for the tag
                search[oper] = function(prop, literal, searchCriteria) {
                    if(searchCriteria) {
                        startTag = "S:" + searchCriteria.name + " xmlns:S='" + searchCriteria.ns + "'";
                        endTag = "S:" + searchCriteria.name;
                    }
                    else {
                        startTag = "prop";
                        endTag = "prop";
                    }
                    
                    if (oper === "is_defined")
                        return "<is-defined><" + startTag + ">" + search._prop_to_xml(prop) + "</" + endTag + "></is-defined>"; 
                    if (oper === "is_collection")
                        return "<is-collection/>";
                    if (oper == "is_bit") 
                        return "<is-bit/>";
                    if (literal !== undefined) {
                        if (oper.indexOf("i") === (oper.length - 1)) {
                            oper = oper.slice(0, -1);
                            literal = '<typed-literal xsi:type="xs:integer" \
                                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \
                                xmlns:xs="http://www.w3.org/2001/XMLSchema">' + literal + "</typed-literal>";
                        } else {
                            literal = "<literal>" + literal + "</literal>";
                        }
                    }
                    return "<" + oper + "><" + startTag + ">" + search._prop_to_xml(prop) + "</" + endTag + ">" + literal + "</" + oper + ">";
                }
            }
            for (var i=0; i < comparisonOps.length; i++) {
                comparisonGenerator(comparisonOps[i])
            };
      };

    /**
     * MAIN WEBDAV METHODS         
     * These methods are passed an argument object, defined below.  The only absolute
     * requirement for ALL methods is .url.  See individual methods for specifics.
     *
     * url          ::  {String} a resource url, and MKCOL name.
     * targetUrl    ::  {String} target, when MOVEing or COPYing.
     * asynch       ::  {Boolean} if true, an asynchronous call; defaults to false.
     * callId       ::  {String} a unique id for this call.  defaults to a random id.
     *                    NOTE: The main purpose of this id is to prevent multiple identical
     *                    xhr calls -- until a call with id[x] has returned, subsequent
     *                    calls with id[x] will simply be ignored.
     * callback     ::  {Function} a handler to receive the xhr result object.
     * headers      ::  {Array} an assoc array of name/value pairs. [See XHR for defaults].
     * propName     ::  {String} with .propPatch, propFind, name of property being accessed.
     * propValue    ::  {String} with .propPatch, propFind, value of property being accessed.
     * body         ::  {String} content body to be sent with request (PUT).
     * acl          ::  {Array}  acl to be set in this request (.setACL).
     */
    
    /**
     * Locks a resource.  
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_LOCK
     * @throws   DAV_LOCK_412
     */
    this.LOCK = function(dav)
      {
        var d = dav || {};
        
        d.url           = d.url       || false;
        d.method        = 'LOCK';
        d.timeout       = d.timeout   || 'Second-' + this._defaultLOCKTimeout;
        d.depth         = d.depth     || this._defaultLOCKDepth;
        d.lockscope     = d.lockscope || this._defaultLOCKScope;
        d.locktype      = d.locktype  || this._defaultLOCKType;
        d.ifExists      = d.ifExists === undefined ? false : true;
        
        d.headers       = {
                            Timeout:          d.timeout,
                            Depth:            d.depth
                          };
                          
        /**
         * If-Match will stop a lock from being taken on an unmapped
         * url -- which would normally create an locked, empty, resource.
         */
        if(d.ifExists)
          {
            d.headers['If-Match'] = '*';
          }
          
        /**
         * Create the <owner> block.  If the user is logged in,
         * this becomes '/home/usernamehere'; otherwise, it is unauthenticated.
         */
         
        var user = AXIS.User.username();
        var ublk = (user) ? '<D:owner>/home/' + user + '</D:owner>' : '<D:unauthenticated>';
        
        d.owner   = (user) ? '/home/' + user : 'unauthenticated';
        
        d.body          = 
          '<?xml version="1.0" encoding="utf-8" ?>\
            <D:lockinfo xmlns:D="DAV:">\
              <D:lockscope>\
                <D:' + d.lockscope + '/>\
              </D:lockscope>\
              <D:locktype>\
                <D:' + d.locktype + '/>\
              </D:locktype>\
              ' + ublk + '\
            </D:lockinfo>'; 
            
        return this.send(d);
      };
      
    /**
     * Attempts to unlock a resource
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_UNLOCK
     * @throws   DAV_UNLOCK_400
     * @throws   DAV_UNLOCK_403
     * @throws   DAV_UNLOCK_409
     */
    this.UNLOCK = function(dav)
      {
        var d = dav || {};
        
        d.method      = 'UNLOCK';
        d.headers       = {
                            'Lock-Token': '<' + d.locktoken + '>'
                          };
                          
        AXIS.XHR.setStatusHandlerForResponse(d, 
          {
            400:  'DAV_UNLOCK_400~~' + d.url,
            403:  'DAV_UNLOCK_403~~' + d.url,
            409:  'DAV_UNLOCK_409~~' + d.url
          });
          
        return this.send(d);
      };
    
    /**
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#rfc.section.9.4
     */  
    this.HEAD = function(dav)
      {
        var d       = dav || {};
        d.method    = 'HEAD';
        
        return this.send(d);
      };
    
    /**
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#rfc.section.9.4
     */  
    this.GET = function(dav)
      {        
        var d       = dav || {};
        d.method    = 'GET';
          
        return this.send(d);
      };
    
    /**
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_POST
     */
    this.POST = function(dav)
      {
        var d       = dav || {};
        d.method    = 'POST';
        
        return this.send(d);
      };
     
    /**
     * NOTE: If no depth header is sent, header is set to depth:0.
     *
     * @type   {Mixed}
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_PROPFIND
     */  
    this.PROPFIND = function(dav)
      {
        var d     = dav || {};
        d.method  = 'PROPFIND';
        
        /*
         * Need to ensure that there is header information sent. Mainly,
         * for a PROPFIND there must be a depth property.
         *
         * Some possible other headers:
         * Apply-To-Redirect-Ref: T
         */
        d.headers = d.headers || {};
        d.headers['Depth'] = d.headers['Depth'] || 0;
        
        return this.send(d);
      };
      
      /**
       * Does a PROPPATCH on a resource.  
       * Takes an array of properties to set and remove
       *
       * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_PROPPATCH
       * @see    #setProperty
       * @see    #removeProperty
       *
       * @example:
       *  setProperties = [{
       *     name: "displayname",
       *     value: "foo.html"   
       *  }, {
       *     ns: "http://www.limebits.com/ns/1.0"
       *     name: "Author",
       *     value: "Fitzgerald"
       *  }];
       *  AXIS.WebDAV.PROPPATCH({url: someURL, setProperties: setProperties});
       */
    
    this.PROPPATCH = function(dav)
      {
        var d              = dav || {};
        d.method           = 'PROPPATCH';
        d.setProperties    = dav.setProperties || [];
        d.removeProperties = dav.removeProperties || [];
      
        d.body             = '<?xml version="1.0" encoding="utf-8" ?>\
                                <D:propertyupdate xmlns:D="DAV:">';
      
        var grouper = function(nm)
          { 
            var props   = d[nm + "Properties"];
            var block   = '<D:' + nm + '><D:prop>';
            for (var i = 0; i < props.length; i++)
              {
                var prop = props[i];
                if (prop.name)
                  {
                    block += "<P:" + prop.name + " xmlns:P=\"" + (prop.ns || "DAV:") + "\">" + prop.value + "</P:" + prop.name + ">"; 
                  }
              }
            block += '</D:prop></D:' + nm + '>';
            return block;
          }
      
        d.body += grouper("set");
        d.body += grouper("remove");
      
        d.body += '</D:propertyupdate>';
        return this.send(d); 
      };
          
    /**
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_MKCOL
     * @param    {Object}  dav     An object containing options for this call.
     * @throws   DAV_MKCOL_405
     */
    this.MKCOL = function(dav)
      {
        var d         = dav || {};
        d.method      = 'MKCOL';    

        AXIS.XHR.setStatusHandlerForResponse(d, 
          { 
            405: 'DAV_MKCOL_405' 
          });
          
        return this.send(d);  
      };
    
    /**
     * Deletes a resource based on sent url.
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_DELETE
     * @throws   DAV_DELETE_404
     */
    this.DELETE = function(dav)
      {
        var d         = dav || {};
        d.method      = 'DELETE';    
        d.headers     = dav.headers || {};

        AXIS.XHR.setStatusHandlerForResponse(d, 
          { 
            404: 'DAV_DELETE_404' 
          });
        
        return this.send(d);  
      };
    
    /**
     * Puts a file (writes the content of the sent .body to a url)
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @throws   DAV_PUT_NO_BODY
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_PUT
     */
    this.PUT = function(dav)
      {
        var d         = dav || {};
        d.method      = 'PUT'; 
        d.headers     = dav.headers || {};
        
        /**
        if(!d.headers['Content-Type'])
          {
            d.headers['Content-Type'] =  'text/html'
          };
         **/
         
         
        /**
         * There must be a body for a PUT, but it is allowed to be 
         * empty (an empty string).  Check for an undefined body.
         */
        if(d.body === undefined)
          {
            new AXIS.Errors.DAVException('DAV_PUT_NO_BODY');  
            return false;
          }
        
        return this.send(d);
      };
    
    /**
     * Will copy a file.  The call object expects you to send a .destination
     * string (the new resource url).  Optionally, you may send a .overwrite 
     * string (`T` || `F`), indicating what to do re: overwrites.  This defaults
     * to `F`. An optional smart copy feature choosing alternate names for you if
     * the destination url is taken.
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @throws   DAV_COPY_403
     * @throws   DAV_COPY_404
     * @throws   DAV_COPY_409
     * @throws   DAV_COPY_412
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_COPY
     */
    this.COPY = function(dav)
      {
        var d         = dav || {};
        d.method      = 'COPY';
        d.origUrl     = d.url;
        
        var depth = (d.headers && d.headers.Depth) ? d.headers.Depth : 'infinity';
        
        if(d.destination === undefined)
          {
            new AXIS.Errors.DAVException('DAV_COPY_NO_DESTINATION');
            return false;  
          }
       
        d.headers     = {
                          Destination:  d.destination,
                          Overwrite:    d.overwrite ? 'T' : 'F',
                          Depth:        depth
                        };

        AXIS.XHR.setStatusHandlerForResponse(d, 
          { 
            403:  'DAV_COPY_403',
            404:  'DAV_COPY_404',
            409:  'DAV_COPY_409',
            412:  !d.tryAlternateNames ? 'DAV_COPY_412' : function() {
              /* Smart copy handler. Tries to find alternate unused name at destination */
              if (d.overwrite) return 'DAV_COPY_412';
              var dst_folder = AXIS.Util.uri.getFolder(d.destination);
              var dst_name = AXIS.Util.uri.basename(d.destination);
              AXIS.WebDAV.PROPFIND({
                url: dst_folder,
                asynch: d.asynch,
                headers: {
                  'Depth': '1'
                },
                callback: function(r) {
                  var responses = r.responseXMLObject().multistatus.response;
                  var children = [];
                  for (var i = 1; i < responses.length; i++)
                    {
                      children.push(AXIS.Util.uri.basename(responses[i].href));
                    }
                  var alternate_name = AXIS.Util.uri.findAlternateName(dst_name, children);
                  return AXIS.WebDAV.COPY({
                    url: d.origUrl,
                    asynch: d.asynch,
                    destination: dst_folder + alternate_name,
                    alternateName: alternate_name,
                    callback: d.callback,
                    on201: d.on201
                  });
                }
              })
            }
          });
          
        /**
         * Need to do more stuff here, reading response for reason
         */
        d.on207 - d.on207 || function()
          {
            // do something here.  
          }
                        
        return this.send(d);
      };
      

    /**
     * Will move a file.  The call object expects you to send a .destination
     * string (the new resource url).  Optionally, you may send a .overwrite 
     * string (`T` || `F`), indicating what to do re: overwrites.  This defaults
     * to `F`.
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @throws   DAV_MOVE_403
     * @throws   DAV_MOVE_404
     * @throws   DAV_MOVE_409
     * @throws   DAV_MOVE_412
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#METHOD_MOVE
     */
    this.MOVE = function(dav)
      {
        var d         = dav || {};
        d.method      = 'MOVE';
     
        if(d.destination === undefined)
          {
            new AXIS.Errors.DAVException('DAV_MOVE_NO_DESTINATION');
            return false;  
          }
       
        d.headers     = {
                          Destination:  d.destination,
                          Overwrite:    d.overwrite ? 'T' : 'F',
                          Depth:        d.depth || 'infinity'
                        };
          
        AXIS.XHR.setStatusHandlerForResponse(d, 
          { 
            403: 'DAV_MOVE_403',
            404: 'DAV_MOVE_404',
            409: 'DAV_MOVE_409',
            412: 'DAV_MOVE_412'
          });
        
        /**
         * Need to do more stuff here, reading response for reason
         */
        d.on207 - d.on207 || function()
          {
            // do something here.  
          }
                        
        return this.send(d);
      };
      
    this.VERSION_CONTROL = function(dav) 
      {
        var d = dav || {};
        d.method = "VERSION-CONTROL";
        this.send(d);
      };
    
    this.CHECKOUT = function(dav) 
      {
        var d = dav || {};
        d.method = "CHECKOUT";
        this.send(d);
      };
      
    this.UNCHECKOUT = function(dav) 
      {
        var d = dav || {};
        d.method = "UNCHECKOUT";
        this.send(d);
      };
    
    this.CHECKIN = function(dav) 
      {
        var d = dav || {};
        d.method = "CHECKIN";
        this.send(d);
      };
    
    /**
     *   Search ported from search.js
     *
     * @param    {Object}  dav     An object containing options for this call.
     *
     * @param    {Object} dav.bitmarks :: An object containing the bitmarks to be obtained 
     *                                  with the resources themselves
     *
     *                    dav.bitmarks.ns:        {String}      Namespace for the bitmarks
     *                    dav.bitmarks.bitmarks:  {Array}       List of bitmarks to fetch
     *
     */
    this.SEARCH = function(dav) 
      {
        // utility
        var _prop_to_xml = this.SEARCH._prop_to_xml;
        
        // options
        var d = dav || {};
        
        d.props         = d.props || ["allprop"],
        d.depth         = d.depth || 1,
        d.where         = d.where || null,
        d.limit         = d.limit || null,
        d.offset        = d.offset || null,
        d.orderby       = d.orderby || null,
        d.bitmarks      = dav.bitmarks || null;
        
        d.url           = d.url;
        d.method        = "SEARCH";
        
        
        /*
          xml builder functions
        */
        
        // TODO  this could probably be abstracted out a bit
        var builder = {
            
          defaultNS : AXIS.WebDAV.ns.bm,
            
          _propXML: function() {
            var props = d.props;
            if (props[0] == 'allprop') {
              return '<allprop/>';
            } else {  
              var propXML = '';
              for (var i=0; i < props.length; i++) {
                propXML += _prop_to_xml(props[i]);
              }
              return '<prop>' + propXML + '</prop>';
            }
          },
          
          _bitmarkXML: function() {
              
              /*
               * bitmark xml is of the form : 
               * <lb:bitmark>
               *    <bm:name/>
               *    <bm:description/>
               *    <bm:tag/>
               * </lb:bitmark>
               */
              
              var retString = '';
              var bmarks = d.bitmarks;
              
              if( bmarks ) {
                  
                  var bitmarkXML = [];
                  
                  var bitmarkNS = d.bitmarks.ns || this.defaultNS;
                  
                  bitmarkXML.push("<lb:bitmark xmlns:lb='" + bitmarkNS + "'>");
                  
                  var bitmarkTemplate = "<lb:name/>";
                  
                  var bitmarkNames = bmarks.names;
                                    
                  for(var i = 0; i < bitmarkNames.length; i++) {
                      bitmarkXML.push( bitmarkTemplate.replace(/name/g, bitmarkNames[i]) );
                  }
                  
                  bitmarkXML.push("</lb:bitmark>");
                  
                  retString = bitmarkXML.join("");
              }
              
              return retString;

          },
          
          _whereXML: function() {
            var whereXML = '';

            if(d.where) {
                whereXML = '<where>' + d.where + '</where>';
            }

            return whereXML;
          },

          _orderbyXML: function() {
            var orderbyXML = '';

            if (d.orderby) {
                orderbyXML += '<orderby><order>';
                orderbyXML += '<prop>' + _prop_to_xml(d.orderby) + '</prop>';
                if (d.order) {
                    orderbyXML += '<' + d.order + '/>';
                }
                orderbyXML += '</order></orderby>';
            }

            return orderbyXML;
          },

          _limitXML: function() {
            var limitXML = '';

            if(d.limit) {
                limitXML = '<limit><nresults>' + d.limit + '</nresults></limit>';
            }

            return limitXML;
          },

          _offsetXML: function() {
            var offsetXML = '';

            if(d.offset) {
                offsetXML = '<offset>' + d.offset + '</offset>';
            }

            return offsetXML;
          }
        }
        
        
        d.body = dav.body || '<?xml version="1.0"?> \
          <searchrequest xmlns="DAV:"> \
            <basicsearch> \
              <select>' + builder._propXML() + builder._bitmarkXML() + '</select> \
              <from> \
                <scope> \
                  <href>' + d.url + '</href> \
                  <depth>' + d.depth + '</depth> \
                </scope> \
              </from> \
              ' + builder._whereXML() + ' \
              ' + builder._orderbyXML() + ' \
              ' + builder._limitXML() + ' \
              ' + builder._offsetXML() + ' \
            </basicsearch> \
          </searchrequest> \
        ';
        
        return this.send(d);
      };
          
    /**
     * Helper methods.
     * Functionality that prepares particular flavours of DAV
     * calls.  The requirement of all of these methods is that
     * they ultimately return the response from a standard
     * DAV method, as defined above.
     */
     
    /**
     * Retrieves a range of bytes from a resource.
     *  
     * @see http://www.faqs.org/rfcs/rfc2616.html
     */
    this.getByteRange = function(dav)
      {
        var d     = dav || {};
        d.method  = 'GET';
        
        /**
         * Must have a byte range...
         */
        if(!d.byteRange)
          {
            new AXIS.Errors.DAVException('DAV_NO_RANGE');
          }  
          
        d.headers             = d.headers || {};
        d.headers['Range']    = 'bytes=' + d.byteRange;
        
        /**
         * A 206 response (partial content) is what you expect;
         * A 416 response (requested range not satisfiable) is of course an error.
         */
        d.failureCodes  = [416];
        
        return this.send(d);
      };  
     
    /**
     * @href http://greenbytes.de/tech/webdav/rfc3253.html#METHOD_REPORT
     * @href http://greenbytes.de/tech/webdav/rfc3744.html#rfc.section.9.1
     * @param    {Object}  dav     An object containing options for this call.
     */
    this.REPORT = function(dav)
      {
        var d         = dav || {};
        d.method      = 'REPORT';    

        return this.send(d);  
      };

    /**
     * @href http://greenbytes.de/tech/webdav/rfc3253.html#REPORT_version-tree
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @see #REPORT
     * @return  The response from #REPORT
     * @type  Object
     */
    this.getVersionTreeReport = function(dav)
      {
        var d     = dav || {};
        d.body    = '<?xml version="1.0" encoding="utf-8" ?>\
                      <D:version-tree xmlns:D="DAV:">\
                        <D:prop>\
                          <D:version-name/>\
                          <D:creator-displayname/>\
                          <D:successor-set/>\
                          <D:getlastmodified/>\
                        </D:prop>\
                      </D:version-tree>';
                      
        d.headers =  {
                       'Depth': 0
                     };

        return this.REPORT(d);
      };
    
    /**
     * @href http://greenbytes.de/tech/webdav/rfc3253.html#REPORT_expand-property
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @see #REPORT
     * @return  The response from #REPORT
     * @type  Object
     */
    this.getExpandPropertyReport = function(dav)
      {
        var d     = dav || {};
        d.body    = '<?xml version="1.0" encoding="utf-8" ?>\
                      <D:expand-property xmlns:D="DAV:">\
                        <D:property name="version-history">\
                          <D:property name="version-set">\
                            <D:property name="creator-displayname"/>\
                            <D:property name="activity-set"/>\
                          </D:property>\
                        </D:property>\
                      </D:expand-property>';

        return this.REPORT(d);
      };

    /**
     * @href http://greenbytes.de/tech/webdav/rfc3744.html#REPORT_acl-principal-prop-set
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @see #REPORT
     * @return  The response from #REPORT
     * @type  Object
     */
    this.getAclPrincipalPropSetReport = function(dav)
      {
        var d     = dav || {};
        d.body    = '<?xml version="1.0" encoding="utf-8" ?>\
                      <D:acl-principal-prop-set xmlns:D="DAV:">\
                        <D:prop>\
                          <D:displayname/>\
                        </D:prop>\
                      </D:acl-principal-prop-set>';
                      
        d.headers =  {
                       'Depth': 0
                     };

        return this.REPORT(d);
      };

    /**
     * @href http://greenbytes.de/tech/webdav/rfc3744.html#REPORT_principal-match
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @see #REPORT
     * @return  The response from #REPORT
     * @type  Object
     */
    this.getPrincipalMatchReport = function(dav)
      {
        var d     = dav || {};
        d.body    = '<?xml version="1.0" encoding="utf-8" ?>\
                      <D:principal-match xmlns:D="DAV:">\
                        <D:principal-property>\
                          <D:owner/>\
                        </D:principal-property>\
                      </D:principal-match>';
                      
        d.headers =  {
                       'Depth': 0
                     };

        return this.REPORT(d);
      };

    /**
     * @href http://greenbytes.de/tech/webdav/rfc3744.html#REPORT_principal-search-property-set
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @see #REPORT
     * @return  The response from #REPORT
     * @type  Object
     */
     
    this.getPrincipalSearchPropertySetReport = function(dav)
      {
        var d     = dav || {};
        d.body    = '<?xml version="1.0" encoding="utf-8" ?>\
                      <D:principal-search-property-set xmlns:D="DAV:"/>';
                      
        d.headers =  {
                       'Depth': 0
                     };

        return this.REPORT(d);
      };

    /**
     * Sets a property on a resource.  Expects a property name.  You may also
     * send an array of properties to set.
     *
     * NOTE: This is essentially an alias to the PROPPATCH method, and expects the
     * arguments defined for that method for *setting* values.  The one thing it
     * does add is the ability to send a single (not array) setValues value, which
     * is promptly converted into an array and sent along.
     *  
     * @param    {Object}  dav     An object containing options for this call.
     * @see      #PROPPATCH
     * @throws   DAV_BAD_PROPSET_ARGS
     */
    this.setProperty = function(dav)
      {
        var d     = dav || {};
  
        if(d.setProperties)
          {
            d.setProperties = (AXIS.isArray(d.setProperties)) ? d.setProperties : [d.setProperties];  
          }
        else
          {
            new AXIS.Errors.DAVException('DAV_BAD_PROPSET_ARGS');
            return false;
          }
          
        return this.PROPPATCH(d);
      };
      
    /**
     * Removes a property on a resource.  Expects a property name.  You may also
     * send an array of properties to remove.
     *
     * NOTE: This is essentially an alias to the PROPPATCH method, and expects the
     * arguments defined for that method for *removing* values. The one thing it
     * does add is the ability to send a single (not array) removeValues value, which
     * is promptly converted into an array and sent along.
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @see      #PROPPATCH
     * @throws   DAV_BAD_PROPREMOVE_ARGS
     */
    this.removeProperty = function(dav)
      {
        var d     = dav || {};
        
        if(d.removeProperties)
          {
            d.removeProperties = (AXIS.isArray(d.removeProperties)) ? d.removeProperties : [d.removeProperties];  
          }
        else
          {
            new AXIS.Errors.DAVException('DAV_BAD_PROPREMOVE_ARGS');
            return false;
          }

        return this.PROPPATCH(d);
      };
      
    this.getProperty = function(dav) 
      {
        var d       = dav || {};
        var props   = d.properties || [];
        var xml     = "";
        var prop;
        
        /**
         * Allowed to send a single property object, or array
         */
        props = AXIS.isArray(props) ? props : [props];
        
        for(var i=0; i < props.length; i++) 
          {
            prop = props[i];
            
            /**
             * Also want to allow the sending of a simple property string
             * if only sending a property name. Instead of:
             *   [{name: 'propname'}]
             * send:
             *   ['propname']
             */
            prop      = AXIS.isObject(prop) ? prop : {name:prop};
            prop.ns   = prop.ns || 'DAV:';
            
            xml += "<P:" + prop.name + " xmlns:P='" + prop.ns + "'/>";
          }
        
        d.body      = '<?xml version="1.0" encoding="utf-8" ?>\
     	                <propfind xmlns="DAV:">\
     	                  <prop>\
     	                    ' + xml + '\
     	                  </prop>\
       	              </propfind>';

       	return this.PROPFIND(d)
      };
      
    /**
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#rfc.section.9.1.4  
     *
     * @param   {Object}  dav  Contains the arguments necessary for call.  At least .url.
     * @see #PROPFIND
     * @return  The response from #PROPFIND
     * @type  Object
     */  
    this.getPropertyNames = function(dav)
      {
        var d         = dav || {};
        d.body        = '<?xml version="1.0" encoding="utf-8" ?>\
           	              <propfind xmlns="DAV:">\
           	                <propname/>\
           	              </propfind>';
	            
        return this.PROPFIND(d);
      };
      
    /**
     * Does a PROPFIND with <allprop>.  Allows inclusion (<include>).
     *
     *  DAVObject {
     *              .url: 'foo.html',
     *              .includes: [
     *                'supported-live-property-set',
     *                'supported-report-set'
     *              ]
     *            }
     *
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#rfc.section.9.1.5
     * @href http://greenbytes.de/tech/webdav/rfc4918.html#rfc.section.9.1.6 
     *
     * @param   {Object}  dav Contains the arguments necessary for call.  At least #url.
     * @see #PROPFIND
     * @return  The response from #PROPFIND
     * @type  Object
     */  
    this.getAllProperties = function(dav)
      {
        var d         = dav || {};     
        d.body        = '<?xml version="1.0" encoding="utf-8" ?>\
           	              <D:propfind xmlns:D="DAV:">\
           	                <D:allprop/>';
        
        if(d.includes && AXIS.isArray(d.includes))
          {
            d.body    +=  '<D:include>'; 
            for(var i=0; i < d.includes.length; i++)
              {
                d.body += '<D:' + d.includes[i] + '/>';
              }
           	d.body    +=  '</D:include>';
          }              
        d.body        += '</D:propfind>';

        return this.PROPFIND(d);
      };
      
    /**
     * Gets all properties on a resource.  A resouce can either be a single "file", 
     * or a collection resource (folder).  For a collection, this would fetch you
     * a collection ("directory") listing, with each "sub" resource described in the 
     * same way it would have been had its properties been requested via this method. 
     *
     * Because this dual nature of resources may be strange or ambiguous to some,
     * and due to the very real possibility that the resource type of the url passed 
     * to this call is not known in advance, we add a useful object with semi-parsed
     * data to the return object, so that the developer can easily determine the
     * type of resource returned, as well as all bound resources in the case
     * of collections.
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @see #PROPFIND
     * @return  The response from #PROPFIND
     * @type  Object
     */
    this.readFolder = function(dav)
      {
        var d         = dav || {};

        var withCups  = !!dav.withCups;

        d.body        = '<?xml version="1.0" encoding="utf-8" ?>\
           	              <propfind xmlns="DAV:">\
           	                <allprop/>';
        
        if(withCups)
          {
            d.body  +=  '   <include>\
                              <current-user-privilege-set/>\
           	                </include>';
          }
          
        d.body      +=  '</propfind>';
        
        d.headers     =  {
                            'Depth': 1
                         };

        /**
         * Ensure that our new properties are set in the return object by 
         * creating this proxy callback which passes on the info.
         */
        var c2    = d.responseProcessor || AXIS.F;
        d.responseProcessor  = function(r)
          {
            var resObBuild = function(resOb)
              {
                /**
                 * Check the response.  If with Cups, then each response
                 * object will contain two propstat responses, meaning that
                 * response.propstat will be an Array; if not with Cups, then
                 * it is not.  Check for this, and build the return object
                 * with added Cups info, if required.
                 */
                var rPS       = resOb.propstat;
                var wC        = AXIS.isArray(rPS);
                var rP        = wC ? rPS[0] : rPS;
                var rC        = wC ? rPS[1] : rPS;
                
                var ret =
                  {
                    properties:   rP.prop,
                    href:         resOb.href,
                    type:         rP.prop.resourcetype ? 'collection' : 'file'
                  };
                  
                if(wC)
                  {
                    var cOb   = function(c)
                      {
                        this.privileges = {};
                        
                        for(w=0; w < c.length; w++)
                          {
                            /**
                             * An unfortunate consequence of the
                             * way that responseXMLObject() works...
                             */
                            for(var s in c[w])
                              {
                                this.privileges[s] = true;
                              }
                          }
                        
                        this.hasPrivilege = function(p)
                          {
                            p = p || 'x';
                            
                            return this.privileges.hasOwnProperty(p); 
                          };
                      };
                    
                    ret.Cups = new cOb(rC.prop['current-user-privilege-set'].privilege);
                  }
                
                return ret;
              }

            try
              {
                var st      = r.responseXMLObject().multistatus.response;
                var res     = AXIS.isArray(st) ? st : [st];
                   
                /**
                 * The [0] element is the folder info itself.
                 */
                r.folder = resObBuild(res[0]);
                
                r.folder.children = [];
                              
                /**
                 * Build the result list, pushing the resource object's propstat.prop(s). 
                 * Note that we are adding the #href and #type properties to each resource
                 * object.  The resource at [0] index is the original called resource, so
                 * we start at [1].
                 */
                for(var x=1; x < res.length; x++)
                  {
                    r.folder.children.push(resObBuild(res[x]));
                  }
              }
            catch(e){};

            c2(r);
          }
        
        return this.PROPFIND(d);
      };
      
    this.emptyFolder = function(dav)
      {
        var d = dav || {};
   
        if(AXIS.isString(d.url) === false)
          {
            return;  
          }
        
        var f = this.readFolder({
          url: d.url,
          onSuccess: function(r) 
            {
              var fL = r.folder.children;
              
              for(var x=0; x < fL.length; x++)
                {
                  AXIS.WebDAV.DELETE({
                    url: fL[x].href
                  });
                }
            }
        });
      };
      
    /**
     * Checks if a file exists. Note that this is a forced synchronous
     * call, a PROPFIND, that returns an object containing information about
     * resource:
     *
     * {String}   resourcetype    One of 'collection' or 'file'.
     * {String}   contenttype     'text/html', 'applications/javascript'...
     * {Boolean}  success         Whether file was found. 
     */
    this.fileInfo = function(dav)
      {
        var d   = dav || {};
                
        return AXIS.WebDAV.getProperty({
          url:        d.url,
          asynch:     !!d.asynch, // note that this defaults to false
          properties: [ 'getcontenttype',
                        'resourcetype'
                      ],
          callback:   d.callback || AXIS.F,
          responseProcessor: function(r) {
          
            var rX, txt;
            
            r.success       = false;
            r.contenttype   = null;
            r.resourcetype  = 'file';
            r.Caller        = r
            
            if(r.getStatus() === 207)
              {
                /**
                 * Fetch correct nodes from returned document.
                 */
                var rType = AXIS.Util.xml.getChildrenByNameNS(r.responseXML.documentElement, 'resourcetype');
                
                var cType = AXIS.Util.xml.getChildrenByNameNS(r.responseXML.documentElement, 'getcontenttype');
    
                /**
                 * #resourcetype set to 'collection' or 'file'.
                 */
                if(rType[0].firstChild)
                  {
                    if(rType[0].firstChild.nodeName === 'D:collection')
                      {
                        r.resourcetype = 'collection';  
                      }
                    else
                      {
                        r.resourcetype = 'file';  
                      }
                  }
                else
                  {
                    txt = AXIS.Util.xml.getText(rType[0]);
                    r.resourcetype =  txt === 'collection' 
                                      ? 'collection' 
                                      : 'file'
                  }
                
                r.contenttype = cType[0] ? AXIS.Util.xml.getText(cType[0]) : '';
                  
                r.success = true;
              }
          }
        }); 
      };
      
    this.mkcolParents = function(dav) 
      {
        var self = this;
        var d = dav || {};
        var parentUrl = d.url.replace(/\/[^\/]*\/?$/, '/');

        var d2 = AXIS.merge(dav, {
            on409: function() {
                self.mkcolParents({ 
                    url: parentUrl,
                    onSuccess: function() {
                        self.MKCOL(d);
                    }
                });
            }
        });

        self.MKCOL(d2);
    }
            
/***********************************
 * START ACL
 ***********************************/

    var Acl = function(aceElements)
      {
        if (AXIS.isIE)
          {
            var acl = [];
            AXIS.Util.lang.augmentObject(acl, this);
            acl.parseAceElements(aceElements);

            acl.indexOf = function(e, i) 
              {
                i = i || 0;

                if (i < 0) 
                  {
                    i = this.length + (i % this.length);
                  }

                for (; i < this.length; i++) 
                  {
                    if (this[i] === e) 
                      {
                        return i;
                      }
                  }

                return -1;
              }
            return acl;
          }

        this.parseAceElements(aceElements);
      }

    Acl.prototype = new Array;

    AXIS.Util.lang.augmentObject(Acl,
      {
        printPrincipal: function(prin,p)
          {
            var pretty = p || false;
            var p_str = pretty ? '' : '<D:principal>';
            var t;
            
            switch(prin[0])
              {
              case AXIS.WebDAV.PRINCIPAL_ALL:
                p_str += pretty ? 'All' : '<D:all/>';
                break;
              case AXIS.WebDAV.PRINCIPAL_AUTHENTICATED:
                p_str += pretty ? 'Authenticated' : '<D:authenticated/>';
                break;
              case AXIS.WebDAV.PRINCIPAL_UNAUTHENTICATED:
                p_str += pretty ? 'UnAuthenticated' : '<D:unauthenticated/>';
                break;
              case AXIS.WebDAV.PRINCIPAL_SELF:
                p_str += pretty ? 'Self' : '<D:self/>';
                break;
              case AXIS.WebDAV.PRINCIPAL_PROPERTY:
                if(prin[1][1] == "DAV:")
                  {
                    t = "<D:" + prin[1][0] + "/>";
                  }
                else
                  {
                    t = "<" + prin[1][0] + " xmlns='" + prin[1][1] + "'/>";
                  }
                  
                if(pretty)
                  {
                    p_str += t;
                  }
                else
                  {
                    p_str += '<D:property>' + t + '</D:property>';
                  }
                break;
              case AXIS.WebDAV.PRINCIPAL_USER:
                t = AXIS._siteData.hosts.limebits + "users/" + prin[1];
                if(pretty)
                  {
                    p_str += t;
                  }
                else
                  {
                    p_str += '<D:href>' + t + '</D:href>';
                  }
                break;
              case AXIS.WebDAV.PRINCIPAL_GROUP:
                t = AXIS._siteData.hosts.limebits + "groups/" + prin[1];
                if(pretty)
                  {
                    p_str += t;  
                  }
                else
                  {
                    p_str += '<D:href>' + t + '<D:href>';
                  }
                break;
              case AXIS.WebDAV.PRINCIPAL_HREF:
                p_str += pretty ? prin[1] : '<D:href>' + prin[1] + '</D:href>';
                break;
              }
            p_str += "</D:principal>";
            return p_str;
          },

        printPrivileges: function(privs)
          {
            var p_str = "";
            for (var i = 0; i < privs.length; i++)
              {
                var priv = privs[i];
                p_str += "<D:privilege>";
                if (priv[0] == "DAV:")
                  p_str += "<D:" + priv[1] + "/>";
                else
                  p_str += "<" + priv[1] + " xmlns='" + priv[0] + "'/>";
                p_str += "</D:privilege>";
              }
            return p_str;
          },

        /**
         * Get an array of privilge strings from the corresponding XML elements
         *
         * @param    {Array}       an array of D:privilege XML elements
         */
        getPrivilegesFromElements: function(elems)
          {
            var privs = [];
            for (var i = 0; i < elems.length; i++)
              {
                var j;
                for (j = 0; j < elems[i].childNodes.length && elems[i].childNodes[j].nodeType != AXIS.ELEMENT_NODE; j++);
              
                var priv = elems[i].childNodes[j].nodeName;
                priv = priv.split(":").pop();
                var priv_ns = elems[i].childNodes[j].namespaceURI;

                privs.push([priv_ns, priv]);
              }

            return privs;
          }
      });

    AXIS.Util.lang.augmentObject(Acl.prototype, 
      {
        parseAceElements: function(aces_el) {
          this.protectedAces = [];
          this.editableAces = [];
          this.inheritedAces = [];

          this.protectedAces.makeAce = this.editableAces.makeAce = this.inheritedAces.makeAce = Acl.prototype.makeAce;
          this.protectedAces.findAces = this.editableAces.findAces = this.inheritedAces.findAces = Acl.prototype.findAces;

          for (var i = 0; i < aces_el.length; i++)
            {
              var ace_el = aces_el[i];
              var principal = this.getPrincipalFromElem(AXIS.Util.xml.getChildrenByNameNS(ace_el, "principal")[0]);

              var grant;
              var inherited = false;
              var isProtected = false;
              if (AXIS.Util.xml.getChildrenByNameNS(ace_el, "protected").length)
                isProtected = true;

              var inherited = AXIS.Util.xml.getChildrenByNameNS(ace_el, "inherited")[0];
              inherited = inherited ? AXIS.Util.xml.getText(AXIS.Util.xml.getChildrenByNameNS(inherited, "href")[0]) : false;

              grant = AXIS.Util.xml.getChildrenByNameNS(ace_el, "grant").length ? true : false;

              var privs = Acl.getPrivilegesFromElements(AXIS.Util.xml.getChildrenByNameNS(ace_el, "privilege"));

              var ace = this.makeAce(grant, privs, principal, isProtected, inherited);

              if (isProtected)
                this.protectedAces.push(ace);
              else if (inherited)
                this.inheritedAces.push(ace);
              else
                this.editableAces.push(ace);
            }

          for (var i = 0; i < this.protectedAces.length; i++)
            this.push(this.protectedAces[i]);
          for (var i = 0; i < this.editableAces.length; i++)
            this.push(this.editableAces[i]);
          for (var i = 0; i < this.inheritedAces.length; i++)
            this.push(this.inheritedAces[i]);

          this._origACL = this.printXML(true);
        },

        /**
         * Gets an internal representation of the principal element in XML.
         *
         * @param    {Object}  el     A principal element in an xml doc.
         * @return   An internal represention of the element using an array.
         * @href http://greenbytes.de/tech/webdav/rfc3744.html#ace.principal
         */
        getPrincipalFromElem: function(el)
          {
            var principal = null;
            var href = AXIS.Util.xml.getChildrenByNameNS(el, "href", "DAV:");
            if (href.length > 0)
              {
                href = AXIS.Util.xml.getText(href[0]);
                if (href.indexOf(AXIS._siteData.hosts.limebits + "users/") == 0)
                  principal = [AXIS.WebDAV.PRINCIPAL_USER, AXIS.Util.uri.basename(href)];
                else if (href.indexOf(AXIS._siteData.hosts.limebits + "groups/") == 0)
                  principal = [AXIS.WebDAV.PRINCIPAL_GROUP, AXIS.Util.uri.basename(href)];
                else
                  principal = [AXIS.WebDAV.PRINCIPAL_HREF, href];
                return principal;
              }

            if (AXIS.Util.xml.getChildrenByNameNS(el, "all").length == 1)
              principal = [AXIS.WebDAV.PRINCIPAL_ALL];
            else if (AXIS.Util.xml.getChildrenByNameNS(el,"authenticated").length == 1)
              principal = [AXIS.WebDAV.PRINCIPAL_AUTHENTICATED];
            else if (AXIS.Util.xml.getChildrenByNameNS(el, "unauthenticated").length == 1)
              principal = [AXIS.WebDAV.PRINCIPAL_UNAUTHENTICATED];
            else if (AXIS.Util.xml.getChildrenByNameNS(el, "self").length == 1)
              principal = [AXIS.WebDAV.PRINCIPAL_SELF];
            else
              { 
                var prop_el = AXIS.Util.xml.getChildrenByNameNS(el, "property")[0];
                if (prop_el)
                  {
                    var j;
                    for (j = 0; j < prop_el.childNodes.length && prop_el.childNodes[j].nodeType != AXIS.ELEMENT_NODE; j++);
                    prop_el = prop_el.childNodes[j];

                    principal = [AXIS.WebDAV.PRINCIPAL_PROPERTY, [prop_el.nodeName.split(":").pop(), prop_el.namespaceURI]];
                  }
              }
            return principal;
          },

        /**
         * Creates an internal representation of an ace
         *
         * @param    {Boolean}        grant         If true, creates a grant ace. If false, creates a deny ace.
         * @param    {Array/String}   privs         An array of privileges to be managed by this ace. Each privilege is itself an array of the form ["namespace", "privilege"]. For convenience, if the namespace is "DAV:", the privilege can be represented by the string "privilege". If providing exactly one privilege, and it is in the "DAV:" namespace, this parameter can be the string "privilege". Egs: [["http://limebits.com/ns/1.0/", "read-private-properties"]]; [["DAV:", "read"], "write-content"]; "write-content"; 
         * @param    {Acl.prin_t}     principal     Principals affected by this ace
         * @param    {Boolean}        isProtected (optional)  If true, create an ace marked as protected. Defaults to false
         * @param    {String}         inherited (optional)    A string containing the href of the resource this ace is inherited. Defaults to creating an uninherited ace.
         * @return   {Object}         An object representing the ace
         * @href http://greenbytes.de/tech/webdav/rfc3744.html#PROPERTY_acl
         */
        makeAce: function(grant, privs, principal, isProtected, inherited)
          {
            isProtected = isProtected ? true : false;
            inherited = inherited ? inherited : false;

            if (privs instanceof Array)
              {
                for (var i = 0; i < privs.length; i++)
                  {
                    var priv = privs[i];
                    if (!(priv instanceof Array))
                      privs[i] = ["DAV:", priv];
                  }
              }
            else if (typeof privs === 'string')
              privs = [["DAV:", privs]];
            else
              new AXIS.Errors.DAVException('DAV_ACE_INVALID_PRIVILEGES');

            privs.hasPriv = function(priv, priv_ns)
              {
                if (priv_ns == null)
                  priv_ns = "DAV:";

                for (var i = 0; i < privs.length; i++)
                  {
                    var p = privs[i];
                    if (priv_ns == p[0] && priv == p[1] )
                      return true;
                  }
                return false;
              }

            if (!(principal instanceof Array))
              principal = [principal];

            return {
              principal: principal,
              isProtected: isProtected,
              inherited: inherited,
              grant: grant,
              privs: privs
            };
          },

        findAces: function(grant, privs, principal)
          {
            var ace = this.makeAce(grant, privs, principal);
            var matchingAces = [];
            for (var i = 0; i < this.length; i++)
              {
                if (ace.grant == null || this[i].grant == ace.grant)
                  {
                    if (Acl.printPrincipal(this[i].principal) == Acl.printPrincipal(ace.principal))
                      {
                        for (var j = 0; j < ace.privs.length; j++)
                          if (this[i].privs.hasPriv(ace.privs[j][1], ace.privs[j][0]))
                            matchingAces.push(this[i]);
                      }
                  }
              }
            return matchingAces;
          },

        isGrantedRead: function(principal)
          {
            var matchingAces = this.findAces(null, ['read', 'all'], principal);
            if (matchingAces.length)
              return matchingAces[0].grant;

            return false;
          },

        /**
         * Checks if a provided ace conflicts with any of the existing protected or editable aces
         *
         * @param    {Object}         ace         Object containing the details of the ace that needs to be added
         * @throws   DAV_ACE_CONFLICT
         * @href http://greenbytes.de/tech/webdav/rfc3744.html#rfc.section.8.1.3
         */
        checkAceConflict: function(ace)
          {
            for (var i = 0; i < this.length; i++)
              {
                if (this[i].inherited && !this[i].isProtected)
                  continue;

                if (this[i].grant ? !ace.grant : ace.grant)
                  {
                    if (Acl.printPrincipal(this[i].principal) == Acl.printPrincipal(ace.principal))
                      {
                        for (var j = 0; j < ace.privs.length; j++)
                          if (this[i].privs.hasPriv(ace.privs[j][1], ace.privs[j][0]))
                            new AXIS.Errors.DAVException('DAV_ACE_CONFLICT~~' + "Ace " + this.printACE(ace)+ " conflicts with" + (this[i].isProtected ? " protected" : "") + " ace " + this.printACE(this[i]));
                      }
                  }
              }
          },

        /**
         * Checks if a provided ace conflicts with any of the existing protected or editable aces
         *
         * @param    {Boolean}        grant         If true, adds a grant ace. If false, adds a deny ace.
         * @param    {Array/String}   privs         see @param privs of makeAce 
         * @param    {Acl.prin_t}     principal     see @param principal of makeAces
         * @param    {Integer}        index (optional)   The position in the acl at which to add the ace. The index must be within the editable aces
         * @return   {Integer}                      The position at which the ace was actually added
         * @throws   DAV_ACE_CONFLICT
         * @href http://greenbytes.de/tech/webdav/rfc3744.html#rfc.section.8.1.3
         */
        addAce: function(grant, privs, principal, index)
          {
            var ace = this.makeAce(grant, privs, principal);
            this.checkAceConflict(ace);

            if (index == null || index < this.protectedAces.length) /* Insert at the head of the editable aces */
              index = this.protectedAces.length;
            else if (index > this.protectedAces.length + this.editableAces.length) /* Insert at the tail of the editable aces */
              index = this.protectedAces.length + this.editableAces.length;

            this.editableAces.splice(index - this.protectedAces.length, 0, ace);
            this.splice(index, 0, ace);

            return index;
          },

        /**
         * Removes the editable ace at a given index
         *
         * @param    {Integer}   index       The index of the ace which is to be removed
         * @throws   DAV_ACL_CANNOT_REMOVE_PROTECTED_ACE
         * @throws   DAV_ACL_CANNOT_REMOVE_INHERITED_ACE
         * @throws   DAV_ACL_CORRUPTED
         */
        removeAce: function(index)
          {
            if (this[index].isProtected)
              new AXIS.Errors.DAVException('DAV_ACL_CANNOT_REMOVE_PROTECTED_ACE');

            if (this[index].inherited)
              new AXIS.Errors.DAVException('DAV_ACL_CANNOT_REMOVE_INHERITED_ACE');

            var edIndex = index - this.protectedAces.length;

            if (this[index] != this.editableAces[edIndex])
              new AXIS.Errors.DAVException('DAV_ACL_CORRUPTED');

            this.editableAces.splice(edIndex, 1)
            return this.splice(index, 1)[0];
          },

        /**
         * Removes all the editable aces in this acl
         *
         * @throws   DAV_ACL_CORRUPTED
         */
        clearEditableAces: function()
          {
            this.splice(this.protectedAces.length, this.editableAces.length);
            this.editableAces.splice(0, this.editableAces.length);
            if (this.length != this.protectedAces.length + this.inheritedAces.length)
              new AXIS.Errors.DAVException('DAV_ACL_CORRUPTED');
          },

        printACE: function(ace)
          {
            var aceXML = "";
            aceXML += "<D:ace>";
            aceXML += Acl.printPrincipal(ace.principal);
            aceXML += "<D:" + (ace.grant ? "grant" : "deny") + ">";
            aceXML += Acl.printPrivileges(ace.privs);
            aceXML += "</D:" + (ace.grant ? "grant" : "deny") + ">";
            if (ace.isProtected)
              aceXML += "<D:protected/>";
            else if (ace.inherited)
              aceXML += "<D:inherited><D:href>" + ace.inherited + "</D:href></D:inherited>";
            aceXML += "</D:ace>";
            return aceXML;
          },

        /**
         * Print the aces of the acl
         *
         * @param   {Boolean}    editableOnly (optional)   If true, outputs only the editable aces.
         * @return  {String}     The acl property as a string
         */
        printXML: function(editableOnly)
          {
            var aclArray = editableOnly ? this.editableAces : this;
            var aclXML = '<D:acl xmlns:D="DAV:">';

            for (var i = 0; i < aclArray.length; i++)
              aclXML += this.printACE(aclArray[i]);
            aclXML += "</D:acl>";
            return aclXML;
          }
      });
    this.Acl = Acl;

    /**
     * Gets the Current User Privilege Set for a resource.
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @href http://greenbytes.de/tech/webdav/rfc3744.html#PROPERTY_current-user-privilege-set
     */
    this.getCUPS = function(dav)
      {
        var d     = dav || {}

        d.body    = '<?xml version="1.0" encoding="utf-8" ?>\
                    <D:propfind xmlns:D="DAV:">\
                      <D:prop>\
                        <D:current-user-privilege-set/>\
                      </D:prop>\
                    </D:propfind>';

        /**
         * On a successful response, we create a simple accessor for CUPS, extending
         * the response with an .allows method, letting the developer now check
         * directly if a user has a given priviledge by checking value of 
         * response.allows('write'), for example.
         */  
        d.responseProcessor = function(r)
          {
            var ps = AXIS.Util.xml.getChildrenByNameNS(r.responseXML.documentElement, "propstat", "DAV:")[0];
            var st = AXIS.Util.xml.getChildrenByNameNS(ps, "status", "DAV:")[0].firstChild.nodeValue;

            if (st.indexOf("200 OK") < 0)
              return null;

            var privs = Acl.getPrivilegesFromElements(AXIS.Util.xml.getChildrenByNameNS(ps, "privilege"));

            privs.hasPriv = function(priv, priv_ns)
              {
                if (priv_ns == null)
                  priv_ns = "DAV:";

                for (var i = 0; i < privs.length; i++)
                  {
                    var p = privs[i];
                    if (priv_ns == p[0] && priv == p[1] )
                      return true;
                  }
                return false;
              }

            r.privs = privs;
            r.allows = privs.hasPriv; // backward compatibility
            return privs;
          };

        return this.PROPFIND(d);
      };

    /**
     * Gets the ACL for a resource.
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @href http://greenbytes.de/tech/webdav/rfc3744.html#rfc.section.5.5.5
     */
    this.getACL = function(dav)
      {
        var d     = dav || {};
        
        d.body    = '<?xml version="1.0" encoding="utf-8" ?>\
                      <D:propfind xmlns:D="DAV:">\
                        <D:prop>\
                          <D:acl/>\
                        </D:prop>\
                      </D:propfind>';
                      
        /**
         * On a successful response, we create an array of ACE's, to facilitate
         * access for the developer.  Could probably do more here.
         */  
        d.responseProcessor = function(r)
          {
            var ps = AXIS.Util.xml.getChildrenByNameNS(r.responseXML.documentElement, "propstat")[0];
            var st = AXIS.Util.xml.getChildrenByNameNS(ps, "status")[0].firstChild.nodeValue;

            /* FIXME: handle this error better */
            if (st.indexOf("200 OK") < 0)
              return null;

            var acl = new AXIS.WebDAV.Acl(AXIS.Util.xml.getChildrenByNameNS(ps, "ace"));

            r.aces = acl;
            return acl;
          }

        return this.PROPFIND(d);
      };


    /**
     * Attempts to set the ACL on a resource
     *
     * @param    {Object}  dav     An object containing options for this call.
     * @href http://greenbytes.de/tech/webdav/rfc3744.html#METHOD_ACL
     * @throws   DAV_ACL_CHANGED
     */
    this.setACL = function(dav)
      {
        var d = dav || {};
        d.method      = 'ACL';
        return this.getACL(
          {
            url: d.url,
            asynch: d.asynch,
            callback: function(r, newAcl)
              {
                if (newAcl.printXML() != d.acl._origACL)
                  new AXIS.Errors.DAVException("DAV_ACL_CHANGED");

                var acl = d.acl.printXML(true);

                d.body        = '<?xml version="1.0"?>' + acl;
                return AXIS.WebDAV.send(d);
              }
          });
      };

/*******************************
 * END ACL
 *******************************/

    
    /**
     * Does checks on send arguments, prepares a proper  
     * call object, and returns it.  NOTE that since this object is a DAV-specific
     * preparation of an XHR call, and will ultimately be sent as an XHR call,  
     * the object is further (more strongly) validated within Loader.load().
     *
     * @private
     * @param   {Object}  dav    This is passed through by each DAV method, and
     *                            is the original arg object sent by user
     * @return  A properly prepared call.
     * @type  Object
     */
    this._prepare = function(dav)
      {
        dav.asynch        = (dav.asynch === undefined) ? false : !!dav.asynch;
        dav.loadingMsg    = dav.loadingMsg || 'DAV::' + dav.method + '::' + dav.url;
        
        /**
         * Make sure sent object has .headers set; if not, empty object.
         */
        dav.headers       = dav.headers || {};

        /*
         * Gives the returned object the ability to fetch the
         * response in object form (allowing for easy iteration)
         */
        dav.responseXMLObject = this._responseXMLObject;
        
        /**
         * Do some pruning of empty space if we can.
         */
        dav.body && dav.body.replace(/(\>\s*\<)/g, "><");
        
        return dav;
      };
      
    /**
     * Makes the Loader call, passing the DAV call object.
     *
     * @param   {Object}  dav A WebDAV call object.
     * @return  Reference to call object in AXIS.Queue if successful; false if not
     * @type  Object
     */
    this.send = function(dav)
      {
        var p = this._prepare(dav || {});
        if(p)
          {
            return AXIS.Loader.load(p);
          }
        
        return false;
      };
    
    /**
     * Function attached as a public method of an XML response.
     * Freely distributable under the terms of an MIT-style license.
     *
     * @private
     * @see #prepare.
     * @author Shinichi Tomita <shinichi.tomita@hotmail.com>
     * @modified Sandro Pasquali - repackaged as XHR response handler
     */
    this._responseXMLObject = function(withAtts) 
      {
        if(this.httpHandle && this.httpHandle.responseXML)
          {
            var o = dom2obj(this.httpHandle.responseXML.documentElement);
            var obj = {};
            obj[o.tag] = o.value;
            return obj; 
          }
        else
          {
            return false;  
          }
          
        function dom2obj(elem) 
          {
            if(elem.nodeType == AXIS.TEXT_NODE) 
              {
                return elem.data;
              }
            else if(elem.nodeType == AXIS.ELEMENT_NODE) 
              {
                var obj = {};
                var hasprops = false;
                for(var i=0; i<elem.attributes.length; i++) 
                  {
                    hasprops = true;
                    var attr = elem.attributes[i];
                    
                    if(typeof withAtts == "undefined")
                      {
                        if(attr.nodeName.match(/^xmlns/)) 
                          {
                            if (elem.attributes.length == 1) 
                              {
                                hasprops = false;
                              }
                            continue;
                          }
                      }
                      
                    obj[attr.nodeName] = attr.nodeValue;
                  }
                for(var i=0; i<elem.childNodes.length; i++) 
                  {
                    var childObj = dom2obj(elem.childNodes[i]);
                    if(childObj.tag) 
                      {
                        hasprops = true;
                        if(obj[childObj.tag]) 
                          {
                            if(AXIS.isArray(obj[childObj.tag]) === false) 
                              {
                                obj[childObj.tag] = [ obj[childObj.tag] ];
                              }
                            obj[childObj.tag].push(childObj.value);
                          } 
                        else 
                          {
                            obj[childObj.tag] = childObj.value;
                          }
                      } 
                    else 
                      { // text content
                        // don't fill the obj with empty linebreaks
                        // - wil
                        if (!childObj.match(/^\n$/i)) 
                          {
                            obj['#'] = childObj;
                          }
                      }
                  }
                if(!hasprops) 
                  {
                    obj = obj['#'] ? obj['#'] : null;
                  }
                return { tag : elem.tagName.replace(/^\w+:/, ''), value : obj };
              }
            return {};
          }
      };
  };
/**
 * Copyright 2008, 2009 Lime Labs LLC
 *
 * This file is part of AXIS.
 *
 * AXIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * AXIS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with AXIS.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @fileoverview
 *
 * @requires  AXIS
 * @requires  WebDAV
 * @requires  Cookies
 * @requires  User
 */
function Login()
  {
    /**
     * @constructor
     */
    this.__construct = function()
      {
        this.forceLogin       = false;
        this.redirReturnTo    = window.location.href;
        this.authCookie       = false;
        this._authState       = null;
        this._loginFrameWindow = {};
        this._returnTo        = location.protocol + "//" + location.host + "/!lime/root/logout";
        this._baseHomePath    = "http://limebits.com/apps/finder/#folder=/home/";
        
        AXIS.Errors.registerCode('LB_LOGOUT_INVALID_SIGNOUT', "Couldn't log out of all domains");

        this.onAuthUpdate = AXIS.CustomEvent.create();
        
        this.beforeLogout = AXIS.CustomEvent.create();
        
        /*
         * Fetch site/user login data and store. Note that this is
         * synchronous.
         */     
        var resp = AXIS.WebDAV.GET({
                  			method:       'GET',
                     	  url:          '/!lime/root/lib/site.json',
                     	  asynch:				false,
                     	  callId:       'LOGIN_FETCH_DATA'
                     	});

        AXIS._siteData = eval('('+resp.responseText+')');

        if(AXIS.settings('noLogin') === false)
          {
            AXIS.onDOMReady.subscribe({
              callback: function() {
                this._createAutoLoginFrame();
                this.login();
              },
              scope: this
            });
          }
      };

    this._authStateUpdate = function(state)
      {
        if (state === this._authState)
          return;

        this._authState = state;

        this.onAuthUpdate.fire(state);
      }
      
    /*
     * The login/auth check, executed on every page
     */
    this.login = function()
      {    //alert('logging in');
        
       	/*
       	 * This is a non-cached resource. Ensures that we have the latest cookies.
       	 */
    		AXIS.WebDAV.GET({
    			method:       'GET',
       	  url:          '/!lime/root/logout',
       	  asynch:       false,
       	  callId:       'LOGIN_CHECK'
       	});
       	
        if(AXIS.Cookies.read('auth') && !AXIS.Cookies.read('user'))
	        {
	          AXIS.Cookies.erase('auth');
	        }
       
        // return if already logged in
        if(AXIS.User.isLoggedIn())
          {
            return this._authStateUpdate(AXIS.Cookies.read('user'));
          }

        AXIS.Login.setAuthCookie(
          function()
            {
              AXIS.Login._submitAutoLoginFrame();
            });                 

        return true;
      };
    
    this.logout = function()
      {
        AXIS.Login.beforeLogout.fire({
          args: [] 
        });

        if (location.protocol + "//" + location.host + "/" == AXIS._siteData.hosts.limebits)
          {
            AXIS.Login._submitAutoLogoutFrame();
          }
        else
          {
            AXIS.Cookies.erase('auth');
            AXIS.Cookies.erase('user');
            this._authStateUpdate(false);
          }
      };

    this.setAuthCookie = function(cb)
      {
        this.authCookie = AXIS.Cookies.read('auth');
        if(!this.authCookie) 
          {
            AXIS.WebDAV.GET(
              {
                method:       'GET',
                url:          '/!lime/root/authonly',          	    
           	on401:        function(r)
           	  {
           	    AXIS.Login.authCookie = AXIS.Cookies.read('auth');
           	    //alert(r.responseText);
           	  },
           	callback:     function(r)
                  {
           	    AXIS.Login.authCookie = AXIS.Cookies.read('auth');
                    cb();
                  },
           	asynch:       false, 
           	callId:       'GET_AUTH_COOKIE'
              }
            );
          }
      };

    /**
     * Creates the autologin frame in DOM
     *
     * @see #_loadFrameHandler
     */
    this._createAutoLoginFrame = function()
      {
        try
          {
            var loginFrame = document.createElement('iframe');
            loginFrame.style.display = 'none';
            var frm = document.body.appendChild(loginFrame);
    
            if (frm.contentDocument && frm.contentDocument.defaultView)
              this._loginFrameWindow = frm.contentDocument.defaultView;
            else
              this._loginFrameWindow = frm.contentWindow;
            this._loginFrameEl = frm;
    
            this._loginFrameWindow.location.replace("about:blank");
    
            AXIS.attachEvent('load',this._loadFrameHandler,frm);
          }
        catch(e){}
      };
     
    /**
     * When the login frame (referenced by #_loginFrameWindow; @see #_createAutoLoginFrame)
     * has its src changed, this is the handler fired when given src is loaded.
     *
     * @private
     * @see #_createAutoLoginFrame
     */
    this._loadFrameHandler = function()
      {
        var loc   = AXIS.Login._loginFrameWindow.location;
        var hsh;
        
        try 
          {
            hsh   = loc.hash;
            if (hsh === undefined || loc.pathname != '/!lime/root/logout')
              throw "error";
          } catch(e) { return; }
            
        hsh       = hsh.split('#')[1] || false;
        
        //alert('loc: ' + loc + ' - ' + 'hash: ' + hsh);
        
        var cook  = AXIS.Cookies.read('user');

        switch(hsh)
          {
          case false:
            if (cook)
              {
                AXIS.Login._authStateUpdate(cook);
                break;
              }
          case 'noauth':
          case 'noallow':
            // TODO: look into handling no_allow with a modal iframe popup

            if(AXIS.Login.forceLogin)
              {
                AXIS.Login._redirectToAccess();
              }
            else
              {
                AXIS.Login._authStateUpdate(false);
              }
            break;
          case "signout":
            AXIS.Cookies.erase('auth');
            AXIS.Cookies.erase('user');
            AXIS.Login._authStateUpdate(false);
            break;
          case "nosignout":
            new AXIS.Errors.LoginException('LB_LOGOUT_INVALID_SIGNOUT');
            break;
          default:
            break;
          }
      };
    
    /**
     * Submits the login frame created with #_createAutoLoginFrame
     *
     * @see #_loadFrameHandler
     */
    this._submitAutoLoginFrame = function() 
      {
        //alert('submitting frame');
        AXIS.Login._loginFrameWindow.location.replace(AXIS._siteData.hosts.secure + 'secure/auth_validator.html#auth_cookie=' + AXIS.Login.authCookie  + '&return_to=' + escape(AXIS.Login._returnTo));
      };

    this._redirectToAccess = function(join)
      {
        AXIS.Login.authCookie = AXIS.Cookies.read('auth');
        top.location = AXIS._siteData.hosts.secure + 'secure/access.html#auth_cookie=' + AXIS.Login.authCookie + (join ? ('&sign_up=' + join) : '') + (AXIS.Login.redirReturnTo ? ('&return_to=' + escape(AXIS.Login.redirReturnTo)) : '');
      }
         
    this._submitAutoLogoutFrame = function() 
      {
        AXIS.Login.authCookie = AXIS.Cookies.read('auth');
        AXIS.Login._loginFrameWindow.location.replace(AXIS._siteData.hosts.secure + 'secure/auth_validator.html#logout=1&www_auth_cookie=' + AXIS.Login.authCookie + '&return_to=' + escape(AXIS.Login._returnTo));
      }

    /*
     * @return    {String} full path to user home folder
     */
    this.homePath = function()
      {
        return this._baseHomePath + AXIS.User.isLoggedIn();
      };
  };
/**
 * Copyright 2008, 2009 Lime Labs LLC
 *
 * This file is part of AXIS.
 *
 * AXIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * AXIS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with AXIS.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @fileoverview
 *
 * AXIS is the core framework that all other objects register with.  It is
 * inherited as the prototype of any class attached via its #register method.
 * This is an essential library, and nothing will work without it.  It is 
 * expected that the core of your build will be the AXIS supplemented by several
 * registered classes.  
 *
 * The goal of the library is to make the caretaking of large javascript applications
 * easier.  At the most basic level, this involves making it easy to add new code
 * as classes and objects without creating namespace conflicts, and to do so in a way
 * which is natural and easy to follow.  
 *
 * @throws  Error   AXIS_ERR_REG_FAIL
 * @throws  Error   AXIS_INCLUDE_SCRIPT_NO_SRC
 * @throws  Error   AXIS_INCLUDE_CSS_NO_HREF
 */ 

var $AXIS = function()
  {      
    /**
     * Check if this is a combined/minified version of the AXIS library.  The check
     * is for existence of XHR extension.  Loose, but unlikely that the combined
     * AXIS will not have this extension, as it is necessary for most everything.
     */
    this.minFName = (typeof XHR == 'function') ? 'AXIS.combined.js' : 'AXIS.js';
       
    /**
     * Set up environment info
     */
    this.uA =                 window.navigator.userAgent.toLowerCase();
    this.browserVersion =     parseFloat(this.uA.match(/.+(?:rv|it|ml|ra|ie)[\/: ]([\d.]+)/)[1]);
    this.isSafari =           /webkit/.test(this.uA);
    this.isOpera =            /opera/.test(this.uA);
    this.isIE =               /msie/.test(this.uA) && !/opera/.test(this.uA);
    this.isMoz =              /mozilla/.test(this.uA) && !/(compatible|webkit)/.test(this.uA);
    this.isWebKit =           /AppleWebKit/.test(this.uA);
    this.isGecko =            /Gecko/.test(this.uA) && (/KHTML/.test(this.uA) === false);
    this.isKHTML =            /KHTML/.test(this.uA);
    this.isMobileSafari =     !!this.uA.match(/Apple.*Mobile.*Safari/);
    
    this.isMac =              /mac/.test(this.uA);
    this.isWindows =          /win/.test(this.uA);
    this.isLinux =            /linux/.test(this.uA);
    this.isUnix =             /x11/.test(this.uA);
    
    this.isIPhone =           /iPhone/.test(this.uA);
    this.isIPod =             /iPod/.test(this.uA);

    /**
     * The shortcut path to the root directory of system (containing such key folders
     * as /css, /library, /home, libraries/ etc)
     */
    this.ROOT   = function()
      {
        return '/!lime/root/';
      };
      
    /**
     * Returns the path to a given script. 
     *
     * @param    {String}    [s]   The script filename.  Defaults to this.minFName
     * @type     {String}
     * @example: AXIS.PATH('AXIS.js')
     * @see      #initialize
     */
    this.PATH = function(s) {
      var script = s || this.minFName;
      var src = this.getScriptSrc(s);
      if(src) {
        return src.replace(new RegExp(script + ".*"), '');
      }
      
      /**
       * Script not found, path unknown
       */
      return '';
    };

    /**
     * Returns a reference to the SCRIPT element whose source contains
     * the string argument.
     *
     * @param    {String}    s     The string to search for in the .src attributes
     *                              of the SCRIPTS collection.  Normally, this would
     *                              be a js filename, like AXIS.js
     */
    this.getScriptSrc = function(s) {
      var script = s    || this.minFName;
        
      var scripts = document.getElementsByTagName("script");    
      for(var i=0; i < scripts.length; i++) {
        var src = scripts[i].getAttribute("src");
        if(src && src.match(script)) {
          return src;
        }
      }
      return '';
    };
          
    /**
     * General null function, used variously.
     */
    this.F  = function()
      {
        return false;
      };
    
    /**
     * @see #attachEvent, #detachEvent
     */
    this.IE_EVENTS = [];
    
    /**
     * Interface to the Shell object, which handles ! requests.  The main purpose
     * is to allow the Shell object code itself to be loaded only when needed. Code
     * is loaded once first call to #run is received.
     *
     * @see #onHashChange
     * @see shell/Shell.js
     */
    this.Shell = 
      {
        run: function(cmd) 
          {
            var r = function()
              {
                AXIS.Shell._run(cmd || false);
              };
              
            if(AXIS.isUndefined(this._run))
              {
                AXIS.includeScript({
                  src:    AXIS.PATH() + '/shell/Shell.js',
                  onload: r
                });
              }
            else
              {
                r();
              }
          },
          
          log: function() {}
      };

    /**
     *
     * @private
     * @see #settings
     * @see #initialize
     */
    this._settings =         [];
    
    /**
     * Accessor for #_settings.
     *
     * @param      {String}    q   The query property
     * @returns                    The query property value, or false.
     * @type       {Mixed}
     */
    this.settings = function(q)
      {
        if(q && this._settings[q])
          {
            return this._settings[q];  
          }   
        return false;
      };
      
    /**
     * @private
     */
    this._notificationsEnabled  = true;
    
    /**
     * Site data file, used variously, mainly for Login
     *
     * @private
     * @see #initialize
     * @see __build__.js
     */  
    this._siteData  =           {};
   
    /**
     * The css files which every page gets
     *
     * @private
     */
    this._generalCSS  =         [this.ROOT() + 'css/AXIS.css'];
    
    /**
     * These are the objects which together w/ AXIS represent the base framework.  
     * You may add other scripts.
     * NOTE: order is important.  Be sure to leave Login.js as the very last.  
     *
     * In order to be included in AXIS.combined.js, be sure to put your script on a separate
     * line and have your line be of the form "this.PATH() + 'yourscript.js'".
     * 
     * Please do not remove the BEGIN LIMEBITS_.... line below.  It is used by our Rakefile
     * to identify where to start parsing out the names of the scripts to include in
     * AXIS.combined.js
     *
     * BEGIN LIMEBITS_SITE_AXIS_COMBINED_FRAMEWORK_LIST
     *
     * @private
     */
     
    this._framework =  [
      this.PATH() + 'Util.js',
      this.PATH() + 'Cookies.js',
      this.PATH() + 'User.js',
      this.PATH() + 'Loader.js',
      this.PATH() + 'XHR.js',
      this.PATH() + 'WebDAV.js',
      this.PATH() + 'Login.js'
     ];
    /* END LIMEBITS_SITE_AXIS_COMBINED_FRAMEWORK_LIST */
                      
    /**
     * Error handling/debugging for the AXIS is enabled by loading the
     * `Errors` extension.  Errors are reported in this system by adding
     * commands like: new AXIS.Errors.AXISException('exception info') to
     * your code.  It wouldn't be reasonable to expect these commands to
     * be removed from the code when not in debugging mode.  However, they
     * make no sense unless the Errors extension is loaded: if it is not
     * loaded, AXIS.Errors does not exist, resulting in an non-existent
     * method javascript error.  The below exists, therefore, to "catch"
     * those debug commands when the Errors extension is not enabled. 
     * Note that the methods of this dummy class do nothing.
     */
    this.Errors = 
      {
        createExceptionType:      function(ex) 
          {
            AXIS.Errors[ex] = function()
              { 
                this.report = function(){};
              };
          },
        registerCode:             this.F,
        setReportingLevel:        this.F
      };
    
    this.createCoreErrorTypes = function()
      {  
        /**
         * @see #Errors
         */
        AXIS.Errors.createExceptionType('AXISException');
        AXIS.Errors.createExceptionType('XHRException');
        AXIS.Errors.createExceptionType('LoginException');
        AXIS.Errors.createExceptionType('LoaderException');
        AXIS.Errors.createExceptionType('DAVException');
      };
      
    /**
     * Interface to the Shell object, which handles ! requests.  The main purpose
     * is to allow the Shell object code itself to be loaded only when needed. Code
     * is loaded once first call to #run is received.
     *
     * @see #onHashChange
     * @see shell/Shell.js
     */
    this.Shell = 
      {
        run: function(bang) 
          {
            var r = function()
              {
                /**
                 * Parse out the request. Simply want to ensure that the shell
                 * receives a string command, and an array of arguments. Format is:
                 * !command:arguments&split&on&ampersand.
                 *
                 * If arguments are sent, the regex match will return a captured group
                 * array, where command is [2] and arguments are [3]. No arguments or other
                 * failure of outer capturing group we get null, which means no args (no :)
                 */
                var i = bang.match(/^((.*):{1}(.*))$/);
                var c = i === null ? bang  : i[2];
                var a = i === null ? [] : i[3].split('&'); 
                
                AXIS.Shell._run(c,a);
              };
              
            if(AXIS.isUndefined(this._run))
              {
                AXIS.includeScript({
                  src:    AXIS.PATH() + '/shell/Shell.js',
                  onload: r
                });
              }
            else
              {
                r();
              }
          },
          
          log: function() {}
      };
      
    /**
     * A reference to every script include object is registered here, indexed by its .id.
     *
     * @private
     * @see #includeScript
     */                      
    this._registeredScripts =    [];
    
    /**
     * A reference to every css include object is registered here, indexed by its .id.
     *
     * @private
     * @see #includeCSS
     */                      
    this._registeredStyleSheets =    [];
        
    /**
     * The # of ms that an XHR request will poll for readystate 4 before dying.
     *
     * @private
     * @see Queue#add
     * @see XHR#build
     */
    this._maxXHRLifespan  =       15000;
    
    /**
     * The # of ms that a notification stays visible prior to fading.
     *
     * @private
     * @see #showNotification
     */
    this._notificationFadeDelay = 20000;

    /**
     * ID and CLASS attributes of the DOM containers for messages (both
     * notifications and loading messages).  These should be defined in base.css. 
     *
     * @see Loader#load
     * @see Loader#createLoadingPanel
     * @see Loader#updateLoadingPanel
     * @see Loader#clearLoadingPanelItem
     * @see #showNotification
     */
    this.loadingMsgContainerId  =           'AXIS_loading_panel';
    this.loadingMsgItemClass  =             'AXIS_loading_panel_item';
    this.defaultLoadingMsg  =               'Loading...';
    this.notificationContainerId  =         'AXIS_notification_container';
    this.notificationItemClass  =           'AXIS_notification';
    this.notificationCloseButtonClass =     'AXIS_notification_close_button';
    
    /**
     * Ms fade runs in #fadeTo
     *
     * @see #fadeTo
     */
    this.defaultFadeSpeed  =         500; 
    
    /**
     * Readable Node values
     */
    this.ELEMENT_NODE =                   1;
    this.ATTRIBUTE_NODE =                 2;
    this.TEXT_NODE =                      3;
    this.CDATA_SECTION_NODE =             4;
    this.ENTITY_REFERENCE_NODE =          5;
    this.ENTITY_NODE =                    6;
    this.PROCESSING_INSTRUCTION_NODE =    7;
    this.COMMENT_NODE =                   8;
    this.DOCUMENT_NODE =                  9;
    this.DOCUMENT_TYPE_NODE =             10;
    this.DOCUMENT_FRAGMENT_NODE =         11;
    this.NOTATION_NODE =                  12;
    
    /**
     * Escape CSS selector
     */
    this.escapeCSS = function(s) 
      {
        return s.replace(/([.:/%* >+~])/g, "\\$1");
      };
      
    /**
      merge properties of primary object with secondary 
      */
    this.merge = function(primary, secondary) 
      {
        var result = {};
        for(var p in primary) 
          {
            result[p] = primary[p];
          }
  
        for(var p in secondary) 
          {
            result[p] = secondary[p];
          }
  
        return result;
       };

    /**
     * Will parse any string sent as one having a possible querystring -- that a `?`
     * character exists after which there are query arguments, in the format one
     * would expect with standard http querystrings.  If such a querystring is found,
     * it will be parsed.  An example:
     *
     * general.js?specialArgs=one+two+three&moreArgs=foobar
     *
     * - `specialArgs` will be a new index in returned array;
     * - `+` is coverted to space ` `, so value of `specialArgs` is
     *   a string with spaces: `one two three`;
     * - `moreArgs` is another key in returned array, value `foobar`.
     *
     * @param      {String}      qst     A string.
     * @returns                          An array filled as described above.
     * @type       {Array}
     * @see      #initialize
     */
    this.fetchBuildArguments = function() 
      {
        var ret = [];
        var i, j, src, s, args;
        
        var scripts = document.getElementsByTagName("*");    
        for(i=0; i < scripts.length; i++) 
          {
            src = scripts[i].src;
            
            if(src && src.match(this.minFName)) 
              {
                args = scripts[i].getAttribute('arguments');
                args = args ? args.split('+') : [];
                ret.extensions  = scripts[i].getAttribute('extensions') || '';
                ret.libraries   = scripts[i].getAttribute('libraries') || '';
                
                for(j=0; j < args.length; j++) 
                  {
                    s = args[j].split('=');
                    ret[s[0]] = s[1] || true;
                  }
                
                this._settings = ret; 
                break;
              }
          }
      };

    /**
     * Extension of the AXIS is done by registering classes via this function. Any
     * class so registered inherits (via prototype chain) this method, allowing the
     * registered class to further register `subclasses`.
     *
     * @param   {String}  scr   A String representation of the class to be registered
     */
    this.register = function(scr) 
      {
        /**
         * Do not re-register. NOTE: The Errors extension is a special case.  AXIS
         * has a "dummy" Errors object, which handles bug reports quietly when
         * the actual Errors extension is not requested.  So, if the user has 
         * requested the Errors extension, given below, it won't actually be loaded,
         * as there already exists the mentioned dummy Errors object.  
         */
        if(AXIS.hasOwnProperty(scr) && (scr != 'Errors')) 
          {
            return this[scr];  
          }

        if(window[scr]) 
          {
            /**
             * Set prototype of object definition to caller
             */
            window[scr].prototype = this;    
            
            /**
             * Add new object to this collection
             */
            this[scr] = new window[scr];

            /**
             * If `createGlobals` has been passed via query, set global.  
             * NOTE: What you are doing by creating globals is creating a shortcut
             * to any registered framework object in the global namespace.  For example,
             * if you register an object `MyStuff`, which when registered is now 
             * accessbible via `AXIS.MyStuff`, you will also be able to access it
             * via `$$Mystuff`.  This should be ok, but in general globals can cause
             * conflicts, so it is up to you to make sure you aren't creating collisions.
             *
             * @see #PATH
             * @see #initialize
             */
            if(this.settings('createGlobals')) 
              {
                window['$$' + scr] = this[scr];
              }
    
            /**
             * Mark original class def for cleanup
             */
            window[scr] = null;
    
            /**
             * Call constructor, if any.
             */
            this[scr].__construct && this[scr].__construct();
    
            return this[scr];
          }
        
        return null;
      };

    /**
     * Namespace storage. 
     *
     * @param  {String}    ns      The namespace, form of `chain.like.this`, which 
     *                              creates $AXIS.chain.like.this namespace.
     * @type   {Mixed}             NS ref if successful; false if not.  
     */
    this.createNamespace  =  function(ns) 
      {              
        var a = arguments;
        var x, y, f, i, z;
        
        if(a.length === 0)
          {
            return false;  
          }
                
        /**
         * Allowing for multiple namespace strings to be sent
         */
        for(i=0; i < a.length; i++) 
          {
            x = a[i].split(".");
            y = $AXIS;
        
            for(z=0; z < x.length; z++) 
              {
                /**
                 * Simply adding to the chain, and repointing y to the new node.
                 * NOTE that pre-existing nodes are preserved.
                 */
                y = y[x[z]] = y[x[z]] || {};
              }
          }
        
        /**
         * Note that in case of multiple NS strings, only the first
         * resolved NS will be returned.
         */
        return eval('$AXIS.' + ns);
      };
        
    /**
     * Shows a notification message.  The behaviour is as follows:
     * 1. Show notification and set its fading behaviour.  The default of the
     *    AXIS is to have an absurdly large delay before fading (24 hours), which
     *    means the user will not miss the notification if away from desk.  You
     *    can change this value via .setNotificationDelay().
     * 2. Each notification is given a dismiss button ('OK') to its rightmost, and
     *    clicking this button will get rid of not only the current notice, but
     *    ALL notices. This follows the logic of next behaviour.
     * 3. Any click on the screen will terminate all existing notices.
     *
     * NOTE that the notification is only shown after content is ready.
     *
     * @param      {Object}      v       The notification info object:
     *    {
     *      content   {String}    The content of the message. Can be HTML.
     *      button    {Boolean}   Whether to show a close button. Default true.
     *      onDismiss {Function}  A function to execute when notification dismissed.
     *      type      {String}    The type of notification (TODO)
     *    }
     */
    this.showNotification = function(v) 
      {
        if(v && v.content && this._notificationsEnabled) 
          {
            AXIS.onDOMReady.subscribe({
              callback: function() 
                {
                  var b           = v.button || true;
                  var t           = v.type || 'default';
                  b               = b ? '<input class="' + AXIS.notificationCloseButtonClass + '" type="button" value="OK" onclick="this.parentNode.nClose()" />' : '';
                  var n           = AXIS.find(AXIS.notificationContainerId);
                  var d           = n.appendChild(document.createElement('div'));
                  d.id            = AXIS.getUniqueId('notification_');
                  d.className     = AXIS.notificationItemClass;
                  d.innerHTML     = v.content + b;
        
                  var f = AXIS.fadeTo({
                    'element':    d,
                    startDelay:   AXIS._notificationFadeDelay,
                    deleteOnEnd:  true
                  });
                      
                  d.nClose = function(e) { 
                    AXIS.detachEvent('click',d.nClose);
                    f.forceFade();  
                    v.onDismiss && v.onDismiss();
                  }
                      
                  /**
                   * This event will force a close of all visible notifications.
                   * You can change the event, or simply comment this out.
                   */  
                  AXIS.attachEvent('dblclick',d.nClose,n);
                }
            });
          }
      };

    /**
     * Creates a unique id, suitable for id="" usage, and elsewhere
     * @param   {String}  pref  An optional prefix.  defaults to 'id_'
     * @return  A unique id
     * @type    String
     */
    this.getUniqueId  = function(pref) 
      {
        var d = new Date;
        return (pref || 'id_') + parseInt(Math.random(d.getTime())*Math.pow(10,10));
      };

    /** 
     * onDOMReady
     * Copyright (c) 2009 Ryan Morr (ryanmorr.com)
     * Licensed under the MIT license.
     *
     * Cosmetic changes to cut bytes.  Also: Original code had legacy
     * browsers hijacking window.onload. This has been removed.
     */
    this._onContentReady = function()
      {
      	var ready, timer;
      	var D = document;
      	var hasFired = false;
      	
      	var onStateChange = function(e)
        	{
        	  // moz && opera
        		if(e && e.type == "DOMContentLoaded")
          		{
          			dR();
          		}
        		else if(D.readyState)
        		  {
        		    // safari & ie
          			if((/loaded|complete/).test(D.readyState))
            			{            			    
            				dR();
            			//IE, courtesy of Diego Perini (http://javascript.nwbox.com/IEContentLoaded/)
            			}
          			else if(!!D.documentElement.doScroll)
          			  {
            				try
              				{
              					ready || D.documentElement.doScroll('left');
              				}
            				catch(e)
              				{
              					return;
              				}

            				dR();
          			  }
        		  }
        	};
      	
      	var dR = function()
        	{
            if(!D.body)
              {
                return;  
              }	
        	  
        		if(!ready)
          		{
          			ready = true;
          			
          			/**
                 * Create the notification element.  
                 *
                 * @see #showNotification
                 */
                var n   = D.body.appendChild(D.createElement('div'));
                n.id    = AXIS.notificationContainerId;
                                  
                AXIS.onDOMReady.fire();
          			
          			/**
          			 * DOM cleanup
          			 */
          			if(D.removeEventListener)
          			  {
          				  D.removeEventListener("DOMContentLoaded", onStateChange, false);
          				}
          			D.onreadystatechange = null;
          			clearInterval(timer);
          			timer = null;
          		}
        	};
      	
      	// Mozilla & Opera
      	if(D.addEventListener)
      	  {
      		  D.addEventListener("DOMContentLoaded", onStateChange, false);
      		}
      	// IE
      	D.onreadystatechange = onStateChange;
      	// Safari & IE
      	//timer = setInterval(onStateChange, 5);
      };
    
    /**
     * Sets up a system whereby the hash fragment is watched, and any changes are
     * broadcast to subscribers of AXIS#onHashChange.  NOTE: To disable the watcher,
     * simply pass `disableHashWatcher` argument to AXIS.
     *
     * @see #initialize
     */
    this.createHashWatcher = function()
      {
        AXIS.onHashChange = AXIS.CustomEvent.create();
        
        AXIS.onHashChangeWatcher = AXIS.Queue.add({    
          currentHash:  null,  
          lastHash:     null,    
          command:      null,
          main:         function() {  
            var splt, cmd, z, x, s;
            
            var args          = {};
            var u             = AXIS.parseUrl();
            var hsh           = u.fragment;
            var bang          = u.url.lastIndexOf('!');
            this.currentHash  = hsh;
            
            if(AXIS.settings('disableHashWatcher')) 
              {
                return false;  
              }
            
            if(!!hsh && this.lastHash !== hsh)
              {
                /**
                 * Don't want to change lastHash if command.
                 */
                if(bang === -1)
                  {
              	    this.lastHash   = hsh;
                  }
                  
              	/**
              	 * We now have a command, and any arguments.  Pass
              	 * this to the hash handler.
              	 */
              	splt  = hsh.split('&');
              	cmd   = splt.shift();
              	
              	/**
              	 * Store, so that apps can use #onHashChangeWatcher reference
              	 * to check for latest command.
                 */
              	this.command = cmd;
                
                /**
                 * Create args object.
                 */
                for(x=0; x < splt.length; x++)
                  {
                    s = splt[x].split('=');
                    args[s[0]] = s[1];
                  }
              	  
              	/**
              	 * Commands that begin with a ! are internal.  Others
              	 * are passed on to listeners.
              	 */
              	if(bang !== -1)
              	  {
              	    /**
              	     * Because this framework may well run alongside other code that
              	     * itself might be checking for hash changes, want to clear away
              	     * the command, to avoid corrupting such data.
              	     */
		                window.location.replace(u.url.substring(0,bang));

		                /**
		                 * Now send command to shell.
		                 */
		                AXIS.Shell.run(u.url.substring(bang +1,u.url.length));  
              	  }
              	else
              	  {  
                  	AXIS.onHashChange.fire({
                  	  command:  cmd,
                  	  args:     args
                  	});
                  }
              }
            return true;
          }
        });
      };
      
    /**
     * Determines paths to files needed to satisfy AXIS directes `extensions` and
     * `libraries`. Expects an array reference to push these results onto.
     *
     * NOTE: The filenames given in attributes `libraries` and `extensions` are
     * by default expected to exist within the folder containing the AXIS.  It is likely
     * that you may want to create custom extensions and libraries which will not
     * exist in the AXIS folder.  To do that, you simply prepend a tilde(~) to the 
     * library/extension name, followed by the path (either relative, or absolute).
     * Such as:
     *          libraries="array+string+~mylibrary/foo/bar/file.js+~http://www.foo.com/bar.js
     *
     * @param   {Array}     ret   An array to push resolved paths onto  
     * @param   {String}    qN    A directive string ("foo+bar...");
     * @param   {String}    aP    Added subpath (ie 'libraries', which means axis/libraries/)
     * @see     #initialize
     */
    this.buildPathForDirective = function(ret,qN,aP) 
      {
        var f, i;
        var a       = qN ? qN.split('+') : [];
        var aPath   = aP ? aP + '/' : '';

        for(i=0; i < a.length; i++) 
          {
            if(a[i].charAt(0) == '~')
              {
                f = a[i].substring(1,a[i].length);
              }
            else
              {
                f = AXIS.PATH() + aPath + a[i] + '.js';  
              }
        
            ret.push(f); 
          }
      };

    /**
     * Upon instantiation of AXIS object (see bottom of file), a call to #initialize
     * is made, which:
     * - Sets a timeout to handle framework loading timeouts;
     * - Calls #PATH on AXIS.js, which is done to fetch any query args regarding
     *   requested extensions to AXIS;
     * - Add any extensions to the core #_framework array;
     * - Load all core files, including extensions, and when they are loaded, load
     *   the initialization script (__build__.js), which does registration of objects,
     *   fetches user domain data, fetches user data via google api, and calls #start.
     *   
     * @see          __build__.js
     * @throws       AXIS_FRAMEWORK_LOAD_TIMEOUT
     */
    this.initialize = function() 
      { 
        AXIS.createCoreErrorTypes();
        
        /**
         * Fired when the AXIS is loaded, initialized... ready
         *
         * @see AXIS#start
         */
        AXIS.onReady = AXIS.CustomEvent.create({
          name: 'onReady'
        });
          
        /**
         * Fires when the DOM is ready for manipulation
         *
         * @see AXIS#_onContentReady
         */
        AXIS.onDOMReady = AXIS.CustomEvent.create({
          name: 'onDOMReady'
        });
          
        /**
         * Fires when the window has loaded (window.onload)
         */
        AXIS.onWindowReady = AXIS.CustomEvent.create({
          name: 'onWindowReady'
        });
        
        /**
         * Fires just prior to the window being unloaded (user leaves or refreshes).
         * NOTE: Opera does not support this, and Chrome only seems to fire on refresh.
         */
        AXIS.onBeforeUnload = AXIS.CustomEvent.create({
          name: 'onBeforeUnload'
        });
        
        AXIS.attachEvent('beforeunload',function() {
          AXIS.onBeforeUnload.fire();
        }, window);
        
        /**
         * Fires when the window is resized NOTE how we are
         * attaching an event, below.
         */
        AXIS.onResize = AXIS.CustomEvent.create({
          name: 'onResize'
        });
        AXIS.attachEvent('resize',function() {
          AXIS.onResize.fire();
        },window);
        
        /**
         * Fires when the window is scrolled. NOTE how we are
         * attaching an event, below.
         */
        AXIS.onScroll = AXIS.CustomEvent.create({
          name: 'onScroll'
        });
        AXIS.attachEvent('scroll',function() {
          AXIS.onScroll.fire();
        },window);
          
        /**
         * We want to check here if this is minified.  When minified, the #_framework
         * files are NOT to be loaded.  However, the #_framework array is still needed,
         * as it will be added to, below, if there are extensions, etc, to load.  When
         * minified, the #_framework files are added to the top of this file (>cat). We
         * don't want to load them again.  So we clear the #_framework array if minified.
         * The check is for the XHR object; it could be any essential file.
         */
        if(typeof XHR == 'function') 
          {
            AXIS._framework = [];      
          }
          
        var AF = AXIS._framework;
          
        /**
         * Load any general CSS
         */
        for(var f=0; f < this._generalCSS.length; f++) 
          {
            this.includeCSS({
              id:   'css_' + f,
              href: this._generalCSS[f]
            });
          }
        
        /**
         * Get any query args first
         */
        AXIS.fetchBuildArguments();
          
        /**
         * Add the google api loader on request, simply by adding to the framework array. 
         * Note: need to pass `useGoogleAPI` to AXIS arguments.
         *
         * @see AXIS#Modules#load
         * @see AXIS#User#getUserGeoData
         */
        if(AXIS.settings('useGoogleAPI')) 
          {
            // ABQIAAAABH14nUM9IOGSATH59A8PIxTtVJmlcGkc8uAjvGT8FSkFC9SscxRd4KeXJgXC39BF8yapmiOggBEOdg

            AF.push('http://www.google.com/jsapi?key=ABQIAAAAL888oCL6bdlp-RuWkSBsthQXxHUepxJBTAuGj9Pcf4C4H-lDUxRnOXsQgDZxtSvcHhv84_sjei_pWQ');
          }
        
        /**
         * Uses the YUI reset css
         */
        if(AXIS.settings('CSSReset'))
          {
            this.includeCSS({
              id:   'CSS_RESET',
              href: this.ROOT() + 'css/reset.css'
            });
          }  
          
        /**
         * Check if user wants notifications turned off
         */
        if(AXIS.settings('disableNotifications')) 
          {
            AXIS._notificationsEnabled = false;
          }
          
        var AP  = AXIS.PATH();
        var lib = AXIS.settings('library');
            
        /**
         * Augment framework list with extensions, libraries.
         */        
        AXIS.buildPathForDirective(AF, AXIS.settings('extensions'));
        AXIS.buildPathForDirective(AF, AXIS.settings('libraries'),'libraries');
  
        /**
         * When loading an extension `Extension` there is expected to exist a file, `Extension.js`,
         * which contains a constructor function named `Extension`.  That `Extension` constructor
         * is intantiated and attached to the AXIS (via #register) as AXIS.Extension.  At which point
         * the constructor function `Extension` is "destroyed" by having its value set to null 
         * (Extension = null).  There is a possibility, though unlikely, that the name of a 
         * constructor function for an extension has already been defined in the DOM prior to 
         * the AXIS loading process. That is, what if `Extension` had already been defined prior 
         * to AXIS loading process?  In that case we want to store the original value, do our 
         * business with extensions, and then replace the original value once `Extension` has been 
         * instantiated.  So, prior to doing our object registrations, we store any existing values
         * here, and replace them after instantiation (in __build__.js).
         *
         * @see __build__.js
         */
        AXIS.createNamespace('__TVAR');
        $AXIS.__TVAR = [];
        for(var i=0; i < AF.length; i++) 
          {
            var nm  = AF[i].split('/');
            nm      = nm[nm.length-1].split('.')[0];
            $AXIS.__TVAR[nm] = window[nm] || false;            
          }
            
        var build = function() 
          {
            if((AXIS.minFName == 'AXIS.js') || (AXIS.isIE && AXIS._framework.length)) 
              {
                AXIS.includeScript(AP + '__build__.js');
              }
            else 
              {
/**
 * Begins the startup process for the AXIS.  Mainly, registers any extensions that
 * were passed as query args, executes anything stored in the AXIS <script> block,
 * restores any global vars overwritten in the load process, and tells the AXIS to start.
 *
 * @param    {Object}    [exts]      Extensions that need to be registered.    
 */

(function(){

var fi,oI,exts,libs,dlist;
var fs            = AXIS._framework;
var AP            = AXIS.PATH();
var extNames      = {};

/**
 * Go through the framework list, strip out paths and such, 
 * and get an array of object names.  At this point, check for dependencies.
 * After we have established dependencies, initiate the load of those, if any,
 * and when we have all our shizzle ready, register the objects and start
 * the AXIS.
 */
for(var r=0; r < fs.length; r++)
  {
    /**
     * Strip out the filename; lose all path info, and extension.
     */
    fi = fs[r].substring(fs[r].lastIndexOf('/')+1, fs[r].lastIndexOf('.'));

    /**
     * If this file exists in the AXIS folder, then this is an extension.
     */
    if(fs[r].indexOf(AXIS.PATH() + fi)!=-1)
      {
        extNames[fi] = fs[r].src;
      }
  }

var rObs = extNames || {}; 

/**
 * If this is a combined AXIS, these extensions are already loaded.  They
 * won't exist.
 */
 
rObs.Util         = 1;
rObs.Cookies      = 1;
rObs.User         = 1;
rObs.Loader       = 1;
rObs.XHR          = 1;
rObs.WebDAV       = 1;
rObs.Login        = 1;

/**
 * Register the objects.  If Errors object is requested, register prior
 * to all other objects so that they may set error handlers.
 */
if(rObs.hasOwnProperty('Errors'))
  {
    AXIS.register('Errors');
    delete(rObs.Errors);  
  }
  
for(var n in rObs)
  {
    AXIS.register(n);
  }
           
/**
 * Replace any vars that have been replaced
 *
 * @see #initialize
 */
for(var p in $AXIS.__TVAR)
  {
    window[p] = $AXIS.__TVAR[p] || null;
  }   

delete $AXIS.__TVAR;

AXIS.start();
    
})();
              }
          }
  
        /**
         * Final initialization of the framework is done by the code in
         * __build__.js.  This file will be loaded and executed following
         * successful inclusion of core script group.  
         */
        if(AF.length > 0) 
          {
            AXIS.includeScriptGroup(AF,function() {
              build();
            });
          }
        else 
          {
            build();
          }
          
        AXIS.createHashWatcher();
      };
      
    /**
     * Load any system css, various initializations. This starts the system.
     *
     * @see #initialize
     * @see #Queue
     */
    this.start  = function() 
      {  
        this._onContentReady();
          
        /**
         * Set window.onload handling
         */
        this.attachEvent('load',function() {
          AXIS.onWindowReady.fire();
        },window);
            
        this.Queue.start();
  
        this.onReady.fire();
      };
      
    /**
     * Allows the execution of a single function following the load of a
     * group of scripts.  Unlike #includeScriptChain, this method can load 
     * all scripts asynchronously (faster), with any code dependent on these 
     * scripts executing only following load of entire group.
     *
     * @param   {Boolean}   group   An array of scripts.
     * @see #_includeScript
     */
    this.includeScriptGroup = function(group, fc) 
      {
        var finalCall = fc || AXIS.F;
        if(AXIS.isArray(group) && (group.length > 0)) 
          {
            var grpCount = group.length;
            var grp = function() 
              {
                --grpCount;
                if(grpCount < 1) 
                  {
                    finalCall();
                  }   
              };
                  
            for(var i=0; i < group.length; i++) 
              {
                if(AXIS.isString(group[i])) 
                  {
                    this._includeScript({
                      src:      group[i],
                      onload:   grp
                    });
                  }
                else 
                  {
                    this._includeScript({
                      id:         group[i].id,
                      src:        group[i].src,
                      method:     group[i].method || false,
                      register:   group[i].register || false,
                      onload:     grp
                    });
                  }
              }
          }
        else 
          {
            /**
             * No members of group.  It is possible there will still be a finalCall() set.
             * This won't fire via normal operation, above, which relies on the group
             * being non-empty.  So, fire the finalCall() here, if any.
             */  
            finalCall();
          }
      };

    /**
     * Takes passed script array, and chains inclusions, so that
     * script[0] is certain to be loaded prior to script[1]...script[n].
     *
     * @param   {Boolean} chain   An array of scripts.
     * @see #_includeScript
     */
    this.includeScriptChain = function(chain) 
      {
        if(chain && (chain.constructor === Array) && (chain.length > 0)) 
          {
            /**
             * Shift off the first script object.  This gives us the current
             * script to load, and the remaining collection.  Get current script's
             * onload (if any), store it, and create a new onload handler that
             * fires stored onload handler (first!), and then simply passes the
             * remaining collection to this script, which forces ordering.
             * Then include current script.
             */
            var d     = chain.shift();
            var z     = d.onload || AXIS.F;
            d.onload = function() 
              {
                z();
                AXIS.includeScriptChain(chain);      
              }  
            
            AXIS._includeScript(d);  
          }
      };
      
    /**
     * Takes script definitions passed, and includes script in page. Public
     * interface to #_includeScript.  Mainly sorts argument types.
     *
     * @public
     * @param   {Mixed}   ob      The loading object
     * @see _includeScript
     */
    this.includeScript  = function(ob) 
      {
        if(ob) 
          {
            if(AXIS.isArray(ob) && (ob.length > 0)) 
              {
                for(var x=0; x < ob.length; x++) 
                  {
                    AXIS._includeScript(ob[x]);  
                  }
                return true;
              }
            
            else if(AXIS.isObject(ob)) 
              {
                AXIS._includeScript(ob);
                return true;
              }
            
            else if(AXIS.isString(ob)) 
              {
                AXIS._includeScript({
                  src: ob
                });
                return true;
              }
          }
        
        return false;
      };
    
    /**
     * Includes a script via DOM HEAD insert if the document is loaded
     * or via document.write if not.
     *
     * @param   {Object} ob - The loading object:
     *                          {
     *                            [id] -- The id for the <script> tag
     *                            src  -- The src value of the <script> tag
     *                            [type] -- Default is 'text/javascript'
     *                            [charset] -- character set, default utf-8
     *                            [onload] -- To fire when script loaded
     *                            [method] -- 'write' || 'insert'
     *                          }
     * @private
     * @see #contentReady
     * @throws  Error   AXIS_INCLUDE_SCRIPT_NO_SRC
     */
    this._includeScript = function(ob) 
      {   
        var onloadStr   = '';
        var rscr        = AXIS._registeredScripts;
        
        ob.onload       = ob.onload || AXIS.F;
        
        try 
          {  
            if(AXIS.isUndefined(ob.src) || ob.src.toString() == "") 
              {
                throw new AXIS.Errors.AXISException('AXIS_INCLUDE_SCRIPT_NO_SRC');  
              }
              
            /**
             * Check if we have already loaded this script, and if we have, exit.
             * NOTE that we still fire the onload handler for the script, if any.
             */
            for(var q in rscr)
              {
                if(rscr[q].src === ob.src)
                  {
                    ob.onload();
                    
                    return true;  
                  }
              }
              
            ob.id         = ob.id || AXIS.getUniqueId('script_'); 
            ob.charset    = ob.charset || 'utf-8';
            ob.type       = ob.type || 'text/javascript';
              
            /**
             * Get the basename, in case this is an extension that needs registration.
             * ie. 'foo/bar/file.js' > 'file'
             */
            ob.baseName = ob.src.substring(ob.src.lastIndexOf('/')+1, ob.src.lastIndexOf('.'));
              
            /**
             * If before DOM loaded, write directly (assume we are building the head).
             */
            if(ob.method === 'write' || (AXIS.onDOMReady.hasFired() === false)) 
              {
                
                if(ob.register) 
                  {
                    onloadStr += "AXIS.register('" + ob.baseName + "');";
                  }
              
                onloadStr +=   'AXIS._registeredScripts["' + ob.id + '"].onload();';
                
                /**
                 * We store a copy of the call object mainly to store the onload
                 * handler reference (if any) to be called when written script loads.
                 * Stored on DOM insert as well, below.
                 */
                AXIS._registeredScripts[ob.id] = ob;
            
                document.write('<' + 'script' + ' id="' + ob.id + '" type="' + ob.type + '" src="' + ob.src + '" charset="' + ob.charset + '"> </' + 'script' + '>' + '<' + 'script' + ' type="text/javascript">' + onloadStr + '</' + 'script' + '>');
            
                return true;
              }
              
            /**
             * If the DOM is loaded, append to HEAD script collection.
             */
            var hT    = document.getElementsByTagName('head')[0]; 
            var s     = document.createElement('script'); 
            s.id      = ob.id; 
            s.type    = ob.type;
            s.src     = ob.src; 
            s.charset = ob.charset;
          
            if(ob.onload) 
              {
            		s.onload = s.onreadystatechange = function() 
              		{
              			if( !this.__loaded__  
              			    && (  !this.readyState 
              			          || this.readyState == "loaded" 
              			          || this.readyState == "complete"
              			        )
              			  ) 
              			  {
              			    /**
              			     * A flag to indicate that this script has loaded... see
              			     * above for info on why.
              			     */ 
                			  this.__loaded__ = true;
                					      
                				/**
                				 * If this is an extension, and immediate registration is requested,
                				 * do that here, prior to firing onload.  
                				 */
                				if(ob.register) 
                  				{
                  				  AXIS.register(ob.baseName);
                  				}
                					      
                				ob.onload();
              		    }
              		};
              } 
                
            hT.appendChild(s);
            AXIS._registeredScripts[ob.id] = ob;
          }
        catch(e) 
          {
            e.report();
            return false;
          }
      };
    
    /**
     * Include CSS file (.css) in <head> of document.
     *
     * @param   {String}  ob - The include object
     *                          {
     *                            [id] -- The id for the <css> tag
     *                            href  -- The src value of the <css> tag
     *                            [media] -- Default is 'screen'
     *                          }
     * @throws  Error   AXIS_INCLUDE_CSS_NO_HREF
     */
    this.includeCSS = function(ob) 
      {
        var ss = AXIS._registeredStyleSheets;

        try 
          {
            if(ob && ob.href.toString() == "") 
              {
                throw new AXIS.Errors.AXISException('AXIS_INCLUDE_CSS_NO_HREF');  
              }
                                        
            var hT    = document.getElementsByTagName('head')[0]; 
            var css   = document.createElement('link'); 
            css.id    = ob.id || AXIS.getUniqueId('css_'); 
            css.rel   = 'stylesheet'; 
            css.type  = 'text/css';
            css.href  = ob.href; 
            css.media = ob.media || 'screen';
            
            /**
             * Don't reload the style sheet if already exists.
             */
            for(var q in ss)
              {
                if(ss[q].href === ob.href)
                  {
                    return true;  
                  }  
              }
    
            AXIS._registeredStyleSheets[css.id] = ob;
            hT.appendChild(css);
            return true;
          }
        catch(e) 
          {
            e.report();
            return false;
          }
      };


    /**
     * Regular Expressions library.  Used variously.     
     *
     */
    this.Regexes  = 
      {
        /**
         * #parseUrl Regex from the reverseHTTP javascript layer.
         * http://github.com/tonyg/reversehttp/blob/master/priv/www/httpd.js
         *
         * @sandro :  modified capturing group for .parseUrl fragment(#), changing:
         *              (#(\w*)) (matching on word characters)
         *      to :    (#(.*)) (any single character)
         *
         * @see #parseUrl
         */
        parseUrl:            /^((\w+):)?(\/\/((\w+)?(:(\w+))?@)?([^\/\?:]+)(:(\d+))?)?(\/?([^\/\?#][^\?#]*)?)?(\?([^#]+))?(#(.*))?/,
        text:           /^[\S\ ]{1,}$/, // a non null string, spaces included
        oneChar:        /^[^\s]{1,}$/, // a non null string, no spaces
        varchar:        /^[\S\ ]{0,1000}$/,
        datetime:       /^([0-9]{2,4})-([0-1][0-9])-([0-3][0-9]) (?:([0-2][0-9]):([0-5][0-9]):([0-5][0-9]))?$/,
        date:           /^([0-9]{2,4})-([0-1][0-9])-([0-3][0-9])$/,
        time:           /^([0-2][0-3]):([0-5][0-9]):([0-5][0-9])$/,
        integer:        /^\d+$/,
        year:           /^[+-]?\d+$/,
        email:        /^([\w_\-\.]+)@((\[[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.)|(([\w\-]+\.)+))([a-zA-Z]{2,4}|[\d]{1,3})(\]?)$/,
        zip:            /^[\d]{5}$/,
        zip4:           /^[\d]{5}-[\d]{4}$/,
        DBFieldName:    /^[_$a-zA-Z]\S{0,99}$/,
        
        allDigits:      /^\d+$/,
        allAlpha:       /^[a-zA-Z]+$/,
        username:       /^[A-Za-z-_!:~\w]{5,63}$/,
        password:       /^[A-Za-z-_!:~\w]{5,255}$/,
        JSONP:          /^[\s\u200B\uFEFF]*([\w$\[\]\.]+)[\s\u200B\uFEFF]*\([\s\u200B\uFEFF]*([\[{][\s\S]*[\]}])[\s\u200B\uFEFF]*\);?[\s\u200B\uFEFF]*$/
      };

    /**
     * Management class for the Queue
     */
    this.Queue  = 
      {   
        /**
         * This is the array which contains the queue.
         *
         * @private
         */  
        _queue:   [],
          
        /**
         * This is the reference to the timer which runs the queue.
         *
         * @see  #start
         */
        _timer:   null,
      
        /**
         * Adds an object to the Queue.  These objects must contain a .main()
         * method, which is called on each run of the queue.  If .main returns
         * true, it will be kept on and continue to run; returning false (or 
         * returning nothing) results in the object being popped off the queue.
         * Note the special attributes attached (and updated on each execution)
         * to the object.  These are accessed within your .main function via 
         * `this` operator. They are:
         * __TIMESTART__    : The time that the object was attached (timestamp).
         * __TIMECURRENT__  : The current time at execution.
         * __TIMELAST__     : The time of immediately previous execution.
         * __ITERATIONS__   : The number of times that the routine has run, inclusive.
         * __LASTXINDEX__   : The position of object in queue at current execution (zero(0)-base).
         *
         * @see AXIS#fadeTo.
         * @return  The modified and stacked object
         * @type    Object
         */
        add: function(obj) 
          {
            var ob = obj || {};
            var d                 = new Date();
            var gt                = d.getTime();
            ob.__TIMESTART__      = gt;
            ob.__TIMECURRENT__    = gt;
            ob.__TIMELAST__       = gt;
            ob.__ITERATIONS__     = 0;
            ob.__LASTXINDEX__     = null;
            ob.lifespan           = obj.lifespan        || 10000000000;
            ob.maxIterations      = ob.maxIterations    || 10000000000;
            ob.onBeforeDie        = ob.onBeforeDie      || AXIS.F;
            ob.main               = ob.main             || AXIS.F;
                
            /**
             * This is what to call should you want to terminate a process.
             */
            ob.die                = function(idx) 
              {
                this.onBeforeDie(this);
                AXIS.Queue.remove(idx || this);
              };
    
            /**
             * Execute the main method immediately, and if it is to remain
             * on the queue, add it.
             */
            ob.main() && AXIS.Queue._queue.unshift(ob);
    
            return ob;
          },
  
        /**
         * This is the queue walker.  Runs the #main of each object in queue,
         * handles the detachment (or not) and updates the internal special vars.
         *
         * @see #Queue#start
         */
        walk: function() 
          {  
            var q = AXIS.Queue._queue;
            var c = q.length;
            var d = new Date();
            var qc;
     
            while(c--) 
              {
                qc = q[c];
                qc.__ITERATIONS__++;
                qc.__TIMELAST__     = qc.__TIMECURRENT__;
                qc.__TIMECURRENT__  = d.getTime();     
                qc.__TIMETOTAL__    = qc.__TIMECURRENT__ - qc.__TIMESTART__;
                qc.__LASTXINDEX__   = c;
      
                /**
                 * Check for death conditions
                 */
                if( (qc.lifespan < qc.__TIMETOTAL__) || 
                    (qc.maxIterations < qc.__ITERATIONS__) || 
                    !!qc.main() === false) 
                  {
                    qc.die(c);  
                  }            
              }
          },
            
        /**
         * Used to check whether a particular object exists in the Queue
         * 
         * @param   {Object}  A Queue object reference
         * @type  Boolean
         */
        exists: function(r) 
          {
            var i = this._queue.length;
            while(i--) 
              {
                if(this._queue[i] === r) 
                  {
                    return true;  
                  }  
              }
            
            return false;
          },
  
        /**
         * Allows the killing of any objects with the given property/value.
         * Note that this will kill ALL objects which satisfy
         * the property/value condition!
         *
         * @param   {String}  p   The property name.
         * @param   {Mixed}   v   The property value.
         *
         * @return  The # killed.
         * @type  Number
         */
        killByPropertyValue: function(p,v) 
          {
            var q = this._queue;
            var i = q.length;
            var h = 0;
    
            while(i--) 
              {
                if(q[i][p] && q[i][p] === v) 
                  {
                    q[i].die();
                    ++h;
                  }  
              }
                  
            return h;
          },
           
        /**
         * Removes sent object instance, if any
         *
         * @param      {Mixed}     r     A Queue object, or a Queue index.
         * @type       {Boolean}
         */
        remove: function(r) 
          {
            /**
             * Removal involves marking for cleanup, not simply deleting. The aim
             * is to kill only via using the natural destructor, #die.
             */
            
            /**
             * If passed an index, use that. Note that we check if
             * the object at given index 
             */
            if(r && AXIS.isNumber(r)) 
              {
                try 
                  { 
                    this._queue[r].main = AXIS.F;
                    return true;  
                  } 
                catch(e) 
                  {
                    return false;
                  }
              }
            
            /**
             * Find and remove object
             */
            var i = this._queue.length;
            while(i--) 
              {
                if(this._queue[i] === r) 
                  {
                    this._queue[i].main = AXIS.F;     
                    return true;
                  }  
              }
            
            return false;
          },
          
        /**
         * Destroys all objects in the Queue
         */
        clear: function() 
          {
            var i = this._queue.length;
            for(var x = i; x >= 0; x--) 
              {
                this.remove(x);  
              }
            return i;
          },
            
        start: function() 
          {
            AXIS.Queue._timer = setInterval(AXIS.Queue.walk,1);
          },
            
        stop: function() 
          {
            clearInterval(AXIS.Queue._timer);
          }
      };

    /**
     * Object facilitating document.createElement() and similar
     * types of document element creation and manipulation needs.
     *
     * @param    {Mixed}     t     1. A DOM element;
     *                              2. A DOM element Id;
     * @param    {Object}    p     properties, all optional, in form:
     *                        {
     *                          html:   html to insert,
     *                          class:  class attribute,
     *                          style:  {
     *                                    width:  '20px',
     *                                    height: '5px'
     *                                  },
     *                          events: {
     *                                    'click':  function() {
     *                                                // do something
     *                                              }
     *                                  }
     *
     *                          // ...and any other DOM attributes.
     *                          id:     'idstring',
     *                          title:  'some title'
     *                        },                         
     */
    this.Element = 
      {
        extensions: [],
        
        create: function(t,p) 
          {
            if(!t || AXIS.onDOMReady.hasFired() === false) 
              {
                return false;
              }
                
          	/**
          	 * Build the element, attach it to the document.
          	 */
          	var el = AXIS.isString(t) ? document.createElement(t) : t;
          	  
            if(p) 
              {
                var pp, ob, tmp;
                for(var prop in p) 
                  {
                    pp = p[prop];
                    ob = AXIS.isObject(pp);
                    switch(prop) 
                      {
                        
                        case 'html':
                          el.innerHTML = pp;
                        break;
                                
                        case 'class':
                          el.className = pp;
                        break;
                        
                        case 'style':
                          if(ob) 
                            {
                              for(var s in pp) 
                                {
                                  el.style[s] = pp[s];  
                                }
                            }
                        break;
                        
                        case 'events':
                          if(ob) 
                            {
                              for(var e in pp) 
                                {
                                  AXIS.attachEvent(e, pp[e], el);
                                }
                            }
                        break;
                        
                        case 'append':
                          if(AXIS.isArray(pp)) 
                            {
                              for(var e in pp) 
                                {
                                  if(AXIS.isElement(pp[e]))
                                    {
                                      el.appendChild(pp[e]);
                                    }
                                }
                            }
                        break;
          
                        default:
                          try
                            {
                              el.setAttribute(prop, '' + pp);
                            }
                          catch(e)
                            {                      
                              el[prop] = pp;
                            }
                        break;  
                      }
                  }  
              }
            
            document.body.appendChild(el);
            
            return el;
          },
          
        extend: function(o) 
          {
            var ob    = o || {};
            var name  = !AXIS.isUndefined(o.name) && AXIS.isString(o.name) ? o.name : false;
            var fn    = !AXIS.isUndefined(o.func) && AXIS.isFunction(o.func) ? o.func : false;
            
            if(!name || !fn) 
              {
                return;  
              }

            this.extensions[name] = fn;  
            
            /**
             * Anything other than IE can use the HTMLElement prototype
             */
            if(AXIS.isIE === false) 
              {
                HTMLElement.prototype[name] = fn;
                return;
              }
    
            var _createElement = document.createElement;
            document.createElement = function(tag) 
              {
                var e = _createElement(tag);
                if(e) { e[name] = fn; }
              	
              	return e;
              }
            
            var _getElementById = document.getElementById;
            document.getElementById = function(id) 
              {
                var e = _getElementById(id);
              	if(e) { e[name] = fn; }
                return e;
              }
            
            var _getElementsByTagName = document.getElementsByTagName;
            document.getElementsByTagName = function(tag) 
              {
              	var a = _getElementsByTagName(tag);
              	var z = a.length;
              	while(z--) 
                	{
                		a[z][name] = fn;
                	}
              	
                return a;
              }
          }
      };
      
    /**
     * Creates a subscribable event.
     */
    this.CustomEvent = 
      {    
        /**
         * Going to store references to created events to allow the firing
         * of custom event objects without access to the original reference.  Note
         * that this is only done for created custom events that are passed a #name
         */
        events: [],
        
        /**
         * This is a special method that allows the firing of a custom event
         * identified by the #name attribute passed when the event was created.
         *
         * @see #create
         */
        fire: function(nm,aob) 
          {
            if(nm && this.events[nm]) 
              {
                this.events[nm].fire(aob || false);
              }
          },
        
        /**
         * This is a special method that allows the subscribing to a custom event
         * identified by the #name attribute passed when the event was created.
         *
         * @see #create
         */
        subscribe: function(nm,sob) 
          {
            if(nm && this.events[nm] && sob) 
              {
                this.events[nm].subscribe(sob);
              }
          },
        
        hasFired: function(nm, wlf) 
          {
            return  (nm && this.events[nm]) 
                      ? this.events[nm].hasFired(wlf || false) 
                      : null;
          },
          
        /**
         * Creates a custom event.  It is expected that the object returned by this
         * method will be stored somewhere permanent, and fired when necessary, ie:
         *
         * var myCustomEvent = AXIS.CustomEvent.create({...});
         * myCustomEvent.fire();
         *
         * By using the optional #name attribute, you can also identify this event
         * for use without having a permanent reference, ie:
         *
         * var myCustomEvent = AXIS.CustomEvent.create({name: 'myName'});
         * AXIS.CustomEvent.fire('myName');
         *
         * @param    {Object}      ob    Object of form:
         *              {
         *                {String}  [name]        Name of event.
         *                {Object}  [scope]       Scope to fire subscrber handler in. Default window.
         *                {Boolean} [forceWait]   See notes for #subscribe, below.
         *              }
         */
        create: function(ob) 
          {
            var d         = ob || {};
    
            var evOb = function() 
              {
                this._subscribers   = [];
                this._hasFired      = false;
                this._lastFiredArgs = false;
                this._scope         = d.scope || window;
                this._name          = d.name || '';
                this._forceWait     = d.forceWait || false;
              };
                  
            evOb.prototype = 
              {
                /**
                 * Subscribes to an event.
                 *
                 * @param      {Object}      ob    Object of form:
                 *                {
                 *                  {Function}  callback    Function to call when event fires.
                 *                  {Object}    [object]    An object which is available to #fire
                 *                  {Boolean}   [wait]      Wait for next firing (see docs below)
                 *                  {Mixed}     [scope]     Scope to fire subscrber handler in. Default window.
                 *                }
                 */
                subscribe: function(ob) 
                  {
                    var b         = ob || {};
                    b.callback    = b.callback  || AXIS.F;
                    b.object      = b.object    || {};
                    b.wait        = b.wait || false;
                    b.scope       = b.scope || this._scope;
                    
                    /**
                     * Prepare the scoped callback. See below and #fire.
                     */
                    b.callback    = AXIS.curry(b.callback, b.scope);  
                      
                    this._subscribers.push(b);  
        
                    /**
                     * At the point of subscription an event may have already fired.
                     * The default behaviour is, if already fired, to fire the 
                     * callback for this subscription immediately.  This is useful in 
                     * situations like DOMReady or WindowLoaded, which will fire
                     * once only, and if subscribed to multiple times it is expected
                     * the developer wants the subscription to immediately fire.  However,
                     * it is also the case that some subscriptions want to wait for 
                     * the next event, and are indifferent to, or misled by, immediate
                     * firing.  In the latter case, the subscriber can set the .wait
                     * property of the subscription argument to true, avoiding immediate
                     * firing.  NOTE: The #create method also allows forcing a wait on
                     * on subscriptions, which forces second behaviour of second case.
                     *
                     * @see #create
                     */
                    if(this._hasFired && (b.wait === false && this._forceWait === false)) 
                      {
                        b.callback({
                          name:   this._name,
                          data:   this._lastFiredArgs,
                          object: b.object
                        });

                        return true;
                      }
                    return false;
                  },
                
                /**
                 * Destroys all subscribers with given callback.
                 *
                 * @param    {Function}    cb    The callback function for given subscriber.
                 * @param    {Object}      [ob]  The options object subscribed with.
                 * @returns                      The number of unsubscribes done.
                 * @type     {Number}
                 */
                unsubscribe: function(cb,ob) 
                  {
                    var fnd = 0;
                    if(cb) 
                      {
                        for(var f=0; f < this._subscribers.length; f++) 
                          {
                            if(this._subscribers[f].callback === cb) 
                              {
                                /**
                                 * Filter by subscriber object, if requested
                                 */
                                if(ob && this._subscribers[f].object.toString() !== ob.toString()) 
                                  { 
                                    continue;
                                  }
                                
                                ++fnd;
                                this._subscribers.splice(f,1); 
                              }
                          }
                      }
                    return fnd;
                  },
                
                /**
                 * Destroys all subscribers for this event
                 */
                unsubscribeAll: function() 
                  {
                    this._subscribers = [];
                    return true;
                  },
                  
                fire:           function(a) 
                  {
                    var ss;
                  
                    /**
                     * @see  #subscribe
                     * @see  #hasFired
                     */
                    this._hasFired        = true;
                    this._lastFiredArgs   = a;
                     
                    for(var f=0; f < this._subscribers.length; f++) 
                      {
                        ss = this._subscribers[f];
                        
                        ss.callback({
                          name:   this._name,
                          data:   this._lastFiredArgs,
                          object: ss.object
                        });
                      }
                  },
                
                /**
                 * Whether or not this event has fired at least once.  By
                 * passing wLastF argument, will still return false if has
                 * not fired, but will pass the arguments passed by the last
                 * #fire execution, if any.  As this is still truthy, it's 
                 * a simple way to get more info in one call, but be careful.
                 */
                hasFired: function(wlf) 
                  {
                    return wlf ? this._lastFiredArgs : this._hasFired;  
                  }
              };
            
            var ce = new evOb;
            
            /**
             * If we're passed a #name, store the custom object for later reference
             */
            if(AXIS.Regexes.oneChar.test(ce._name)) 
              {
                if(this.events[ce._name]) 
                  {
                    new AXIS.Errors.AXISException('CUST_EVENT_NAME_DUPLICATE');
                    return;
                  }
                
                AXIS.CustomEvent.events[ce._name] = ce;
              }
            
            return ce;
          }
      };

    /**
     * An accurate way of checking whether a given value is an Array.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isArray  = function(a) 
      {
        return  !!a && Object.prototype.toString.apply(a) === '[object Array]';
      };
      
    /**
     * Whether a given value is an Object.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isObject = function(a) 
      {
        return !!a && Object.prototype.toString.call(a) === '[object Object]';  
      };
      
    /**
     * Whether a given value is a Function.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isFunction = function(a) 
      {
        return !!a && a.constructor === Function;  
      };
      
    /**
     * Whether a given value is a String.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isString = function(a) 
      {
        return  typeof a !== 'undefined' && 
                a !== null && 
                a.constructor === String;  
      };
      
    /**
     * Whether a given value is a Number.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isNumber = function(a) 
      {
        return  typeof a !== 'undefined' && 
                a !== null && 
                a.constructor === Number;  
      };
    
    /**
     * Whether a given value is a Boolean.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isBoolean = function(a) 
      {
        return  typeof a !== 'undefined' && 
                a !== null && 
                a.constructor === Boolean;  
      };
      
    /**
     * Whether a given value is a Regular Expression.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isRegExp = function(a) 
      {
        return !!a && a.constructor === RegExp;  
      };
      
    /**
     * Whether a given value is an DOM element.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isElement = function(a) 
      {
        return  typeof HTMLElement === 'object' 
                ? !!a && a instanceof HTMLElement 
                : !!a && typeof a === 'object' && 
                  a.nodeType === 1 && 
                  typeof a.nodeName === 'string';
      };
      
    /**
     * Whether a given value is undefined.
     *
     * @param     {Mixed}     a     The value to check
     * @type      {Boolean}
     */
    this.isUndefined = function(a) 
      {
        return typeof a === 'undefined';
      };
    
    /**
     * Breaks a url into its component parts.
     *
     * @param   {String}    [url]   The url to parse. Default is document location.
     */
    this.parseUrl = function(url)
      {
        url = url || window.location.href;
        var m = url.match(AXIS.Regexes.parseUrl);

        return {
          'url'         : m[0],
          'protocol'    : m[2],
          'username'    : m[5],
          'password'    : m[7],
          'host'        : m[8]  || "",
          'port'        : m[10],
          'pathname'    : m[11] || "",
          'querystring' : m[14] || "",
          'fragment'    : m[16] || ""
         };
      };
    
    this.clone = function(obj, deep)
      {
        var c;
        
        /**
         * Non objects aren't passed by reference, so just send it back.
         */
        if(obj === null || typeof obj !== 'object')
          {
            return obj;
          }
          
        c = new obj.constructor(); 
    
        for(var p in obj)
          {
            c[p] = deep ? this.clone(obj[p]) : obj[p];
          }
        
        return c;
      };
    
    /** 
     * document.getElementById() shortcut. 
     * @param       {String}    id    An id attribute of a document element
     * @returns                       element or null
     */
    this.find     = function(id) 
      {
        return id ?  document.getElementById(id) : null;  
      };
    
    /** 
     * Returns the text inside of an element, if any. 
     * @param       {Mixed}    el     An element id string, or an element reference.
     * @type        {String}
     */
    this.getText  = function(el) 
      {
        var t =   AXIS.isString(el) 
                  ? AXIS.find(el) 
                  : AXIS.isElement(el) 
                    ? el 
                    : false;
        if(t) 
          {
            return el.innerText || el.textContent;
          }
        
        return '';
      };
            
    /**
     * A general event attaching script.  
     *
     * @param   {String}    ev    The event name -- NOTE: sans 'on': `onclick` == `click`
     * @param   {Function}  f     A function to attach as event handler
     * @param   {Object}    ob    An element to attach to. Default to `document`
     */
    this.attachEvent  = function(ev,f,ob) 
      {
        var obj = ob || document;
        if(obj.addEventListener) 
          {
            obj.addEventListener(ev, f, false);
          }
        else
          {  
            /**
             * IE needs some special treatment.  There are three things.
             * First, we need to attach any DOM Element extensions which
             * have been registered: @see #Element#extend. Other browsers 
             * allow modification of HTMLElement.prototype, so this is not 
             * necessary for them.  Second, IE doesn't properly provide the 
             * `this` context to an event handler, so we create a `wrapper` 
             * function that will properly contextualize the handler.
             * Related to second point, we also set the `target` property of the
             * event object returned to handler (ie. handler(e){}) to the 
             * element to maintain consistency with the common .target property
             * provided by other browser implementations. And finally, we need
             * to store this new handler wrapping function so that when a
             * #detachEvent request is made, we can use this lookup to find
             * the relevant wrapper function which was used.
             *
             * @see #detachEvent
             * @see #Element#extend
             */
            var nf = function(el) 
              {
                el.target = window.event.srcElement || document;
                for(var w in AXIS.Element.extensions)
                  {
                    el.target[w] = AXIS.Element.extensions[w];
                  }  

                f.call(ob,el);
              }
            
            AXIS.IE_EVENTS['IE' + ev + f] = nf;
            obj.attachEvent('on'+ev, nf);
          } 
      };  

    /**
     * A general event detaching script.  
     *
     * @param   {String}    ev    The event name -- NOTE: sans 'on': `onclick` == `click`
     * @param   {Function}  f     A function to remove as event handler
     * @param   {Object}    ob    The element to detach from. Default to `document`
     */
    this.detachEvent  = function(ev,f,ob) 
      {
        var obj = ob || document;

        if(obj.removeEventListener)
          {                                       
            obj.removeEventListener(ev, f, false);
          }
        else
          { 
            /**
             * @see #attachEvent
             */
            var id = 'IE' + ev + f;
            f = AXIS.IE_EVENTS[id] || f;   
            obj.detachEvent('on'+ev, f);  
            delete AXIS.IE_EVENTS[id];                  
          } 
      };
      
    /**
     * Fires a bound event on a given element.  
     *
     * @param   {String}    ev    The event name -- NOTE: sans 'on': `onclick` == `click`
     * @param   {Function}  f     A function to remove as event handler
     * @param   {Object}    ob    The scope of the element. Default to `document`
     */
    this.fireEvent = function(ev,el,ob)
      {
        var obj = ob || document;
        if(document.createEvent)
          {
            var evt = obj.createEvent("HTMLEvents");
            evt.initEvent(ev, true, true );
            return !el.dispatchEvent(evt);
          }
        else
          {
            var evt = obj.createEventObject();
            el.fireEvent('on' + ev, evt);
          }
      };
      
    /**
     * Stops event from triggering any event handlers set on surrounding elements.
     * NOTE: This does *not* stop the default action for the event, if any.
     * 
     *  @param {Event} e   The event object
     */
    this.stopPropagation = function(e)
      {
        e.cancelBubble = true;
        if(e.stopPropagation) 
          {
            e.stopPropagation();
          }
      };
      
    /**
     * Stops the default behaviour of the event from happening.  For example,
     * if you have a checkbox with an onclick handler, you could handle the
     * click, then .preventDefault(), and the checkbox would not be checked
     * (which is the default behaviour of clicking on a checkbox).
     * NOTE: This does *not* stop propogation of the event.
     * 
     *  @param {Event} e   The event object
     */
    this.preventDefault = function(e)
      {
        e.returnValue = false;
        if(e.preventDefault) 
          {
            e.preventDefault();
          }
        return false;
      };
       
    /**
     * Curry a function.
     *
     * @param    {Function}    fnc     The function to curry.
     * @param    {Object}      [scp]   The scope to execute in. Defaults to window.
     *
     * @return   The curried function, or null function on error.
     * @type     {Function} 
     */
    this.curry  = function(fnc, scp) 
      {
        if(fnc) 
          {
            var _scp = scp || window;
            var args = [].slice.call(arguments,2)
            return function() {
              return fnc.apply(_scp, args.concat([].slice.call(arguments,0)));
            };  
          }
        else 
          {
            return AXIS.F;  
          }
      };
      
    /**
     * Changes the opacity of an element over time.
     *
     * @param   {Object}  ob   Object in this form:
     *                          element -- Either an object reference, or element id.
     *                          [startOpacity]  The opacity to set the object to
     *                          [endOpacity]  The opacity to be achieved
     *                          [time]  Ms fade runs for
     *                          [startDelay] Ms prior to beginning of fade
     *                          [deleteOnEnd] Whether to remove the object from DOM
     *                                        collection when endOpacity is reached.    
     */
    this.fadeTo = function(ob) 
      {
        var el                = (typeof ob.element == 'object') 
                              ? ob.element : document.getElementById(ob.element);
        var startOpacity      = (ob.startOpacity === undefined) ? 100 : ob.startOpacity;
        var endOpacity        = (ob.endOpacity === undefined) ? 0 : ob.endOpacity;
        var time              = (ob.time === undefined) ? -AXIS.defaultFadeSpeed : -ob.time;
        var delta             = Math.abs(startOpacity - endOpacity);
        var startDelay        = (ob.startDelay === undefined) ? 0 : ob.startDelay;
        var deleteOnEnd       = (ob.deleteOnEnd === undefined) ? false : !!ob.deleteOnEnd;
        var onComplete        = ob.onComplete || AXIS.F;
          
        var curOp             = startOpacity;
        
        /**
         * Lose any previous fades on this element
         */
        AXIS.Queue.killByPropertyValue('_fade_el',el);
          
        var doOpacity = function(newop) 
          {
            curOp              = Math.abs(newop);
            el.style.opacity   = curOp/100;
            el.style.filter    = 'alpha(opacity=' + curOp + ')';
          };
          
        doOpacity(startOpacity);
          
        return AXIS.Queue.add({
          _forceFade:   false,
          _fade_el:     el,
          forceFade:    function() 
            {
              /**
               * Forcing a fade.  As there will normally be a start delay,
               * we need to eliminate that, starting as if the fade
               * is starting right now.  Set start time to now and delay to zero.
               */
              var d                 = new Date();
              this.__TIMESTART__    = d.getTime();
              startDelay            = 0;
              this._forceFade       = true;
            },
            
          main: function() 
            { 
              var elapsed = this.__TIMECURRENT__ - this.__TIMESTART__;
    
              if(this._forceFade || (elapsed > startDelay)) 
                {
                  var dec   = ((elapsed - startDelay) / time) * delta;
      
                  doOpacity(startOpacity + dec);
      
                  if(Math.abs(dec) >= Math.abs(startOpacity - endOpacity)) 
                    {
                      doOpacity(endOpacity);
                              
                      if(deleteOnEnd === true && el.parentNode) 
                        {
                          el.parentNode.removeChild(el);
                        }
                      onComplete();  
                      return false;  
                    }
                }            
              return true;
            }
        });
      };
  };
  
/**
 * Build custom library creation API into AXIS prototype.  
 */
$AXIS.prototype = new function()
  { 
    /**
     * @see #store
     */      
    this.$$     = [];
      
    /**
     * The iff/endiff truth state
     *
     * @see #iff
     * @see #endiff
     */
    this.$$if   = true;
    
    /**
     * An if/iff construct. Can be passed one or two arguments, each of which may 
     * be of any type, including functions.  Two cases, 1 or 2 arguments, A or B:
     * 
     * A: A Function argument is evaluated in the scope of AXIS.$, or used as sent.
     *    The processed argument  is then "cast" to a boolean,  creating the truth 
     *    condition for an `if` block.
     * B: Function arguments are evaluated in the scope of AXIS.$, or used as sent.
     *    No casting is done; the truth condition of an `iff` block is
     *    the result of comparison (arg1 === arg2).
     *
     * @see #$elsif
     * @see #$endif
     */
    this.$if = function()
      {
        var a = arguments;
        var f = function(arg)
          {
            return  AXIS.isFunction(arg)
                    ? arg.apply(AXIS.$)
                    : arg;
          };
          
        var t1 = f(a[0]);
        var t2 = f(a[1]);
        
        AXIS.$$if = (a.length === 1) 
                    ? !!t1
                    : (a.length === 2)
                      ? t1 === t2
                      : false;
                      
        return this;
      }; 
    
    /**
     * @see #$if
     */
    this.$elsif = function()
      {
        return this.$$if === false ? this.$if(arguments[0],arguments[1]) : this;  
      };
    
    /**
     * @see #$if
     */
    this.$endif = function()
      {
        AXIS.$$if = true;
        
        return this;
      };

    this.scope  = function(sc, build)
      {
        AXIS.$ = build ? new window[sc] : sc;
        
        return this;
      };
    
    /**
     * Stores a value at a given namespace.  If no key value is given,
     * will store value at ns.$.  If nothing is sent, stored this.$$.
     * 
     * @example   f('my.namespace.here','that') === $AXIS.my.namespace.here.that;
     *            f('other.namespace')          === $AXIS.other.namepspace.$;
     *            f()                           === AXIS.$$
     * @param     {String}    [nm]  A namespace.
     * @param     {String}    [k]   An specific key in the namespace.
     */
    this.store = function(nm,k)
      { 
        if(arguments.length === 0)
          {
            AXIS.$$ = AXIS.clone(AXIS.$);
          }
        else if(nm)
          {
            var ns = AXIS.createNamespace(nm);
            ns[k || '$'] = AXIS.clone(AXIS.$);
          }
        
        return this;
      };
      
    this.restore = function()
      {
        AXIS.scope(AXIS.$$);
        
        return this;
      };
  
    this.run = function(f)
      {
        var e;
        f = AXIS.isArray(f) ? f : [f];
        
        for(var w=0; w < f.length; w++)
          {
            e = f.apply(AXIS.$, Array.prototype.slice.call(arguments, 0));
            if(e !== undefined)
              {
                AXIS.$ = e;  
              }
          }
          
        return this;
      };

    this.extend = function(a)
      {
        if( AXIS.isObject(a) === false ||  
            AXIS.isString(a.name) === false || 
            AXIS.isFunction(a.func) === false)
          {
            alert("AXIS.extend() Arguments malformed. Probably no #name, no #func, or both missing.");
            return false;
          }

        var methName    = a.name;
        var chainType   = a.expects || 'Array';
        var func        = a.func;
        var ns          = a.namespace || false;
        
        var cts         = '[object ' + chainType + ']';
        var cins        = new window[chainType];
        var op          = Object.prototype.toString;
        var ap          = Array.prototype.slice;
        
        var onBefore    = AXIS.isFunction(a.onBefore) ? a.onBefore : false;
        var onAfter     = AXIS.isFunction(a.onAfter) ? a.onAfter : false;

        var args, callDesc, R, lastR;
        
        if((ns && ns.charAt(0) === '$') || methName.charAt(0) === '$')
          {
            alert("AXIS.extend() Method or Namspace names cannot begin with dollar sign($)");
            return false;  
          }
        
        /**
         * This is the business function, attached to a namespace if requested.
         */
        var aFunc = function()
          { 
            args = ap.call(arguments,0);
            
            /**
             * If we're in a failed `iff` condition, return this.
             *
             * @see #iff
             * @see #endiff
             */
            if(AXIS.$$if === false)
              {
                return this;  
              }

            /**
             * Unscoped? Create one based on chainType
             */
            !AXIS.$ && AXIS.scope(chainType,1);
            
            callDesc = 
              {
                'name':     methName,
                'func':     func,
                'scope':    AXIS.$,
                'args':     args
              };
            
            onBefore && onBefore.call(null, callDesc);
            
            /**
             * Ensure that the function executes in the scope it expects.
             * Only update scope value if something is returned.
             */
            var R = func.apply(op.apply(AXIS.$) === cts ? AXIS.$ : cins, args); 
            
            if(R !== undefined)
              {
                /**
                 * If the last operation was destructive, we store the
                 * current (pre-change) $, so it can be recovered.
                 */
                if(lastR !== R)
                  {
                    lastR = AXIS.$$ = AXIS.clone(AXIS.$);
                  }
                
                AXIS.$ = R;
              }
            
            onAfter && onAfter.call(null, callDesc);
            
            return this;
          };
        
        /**
         * Check if a namespace was sent, create it if it doesn't exist,
         * and add named(methName) method to the namespace collection.  Notice
         * how if no namespace is sent, `this` (AXIS) is the namespace.
         */
        if(ns)
          {
            if(this[ns] === undefined)
              {
                var f           = function(){}
                f.prototype     = this;
                this[ns]        = new f;
                this[ns].$if    = this.$if; 
                this[ns].$elsif = this.$elsif; 
                this[ns].$endif = this.$endif; 
              } 
              
            if(this[ns].hasOwnProperty(methName))
              {
                alert('AXIS.' + ns + '.' + methName + ' < Method exists.  No changes made.');
                return false;
              }
              
            this[ns][methName] = aFunc;
          }
        else
          {
            if(this[methName])
              {
                alert('AXIS.' + methName + ' < Method exists.  No changes made. Try creating a namespace.');
                return false;
              }
            this[methName] = aFunc;  
          }
        return true;
      };
  };


/**
 * Create AXIS object, and initialize.
 *
 * @see AXIS#createNamespace
 */
var AXIS  = new $AXIS;
/**
 * Now we re-use $AXIS var, as a namespace container.
 * @see AXIS#createNamespace
 */
$AXIS = {};

AXIS.initialize();

