
 /*
  * Author: Doug Hendricks. doug[always-At]theactivegroup.com
  * Copyright 2007-2008, Active Group, Inc.  All rights reserved.
  *
  * This extension adds EventManager Support to Ext.lib.Ajax (if
  *    Ext.util.Observable is present in the stack)
  ************************************************************************************
  *   This file is distributed on an AS IS BASIS WITHOUT ANY WARRANTY;
  *   without even the implied warranty of MERCHANTABILITY or
  *   FITNESS FOR A PARTICULAR PURPOSE.
  ************************************************************************************

  License: ext-basex is licensed under the terms of the Open Source LGPL 3.0 license.
  Commercial use is permitted to the extent that the code/component(s) do NOT become
  part of another Open Source or Commercially licensed development library or toolkit
  without explicit permission.

  * Donations are welcomed: http://donate.theactivegroup.com
  */

if(Ext.util.Observable){
  Ext.apply( Ext.lib.Ajax ,{

   events:{request     :true,
           beforesend  :true,
           response    :true,
           exception   :true,
           abort       :true,
           timeout     :true,
      readystatechange :true
    }

   /*
     onStatus
     define eventListeners for a single (or array) of HTTP status codes.
   */
   ,onStatus:function(status,fn,scope,options){
        var args = Array.prototype.slice.call(arguments, 1);
        status = [].concat(status||[]);
        Ext.each(status,function(statusCode){
            statusCode = parseInt(statusCode,10);
            if(!isNaN(statusCode)){
                var ev = 'status_'+statusCode;
                this.events[ev] || (this.events[ev] = true);
                this.on.apply(this,[ev].concat(args));
            }
        },this);
   }
   /*
        unStatus
        unSet eventListeners for a single (or array) of HTTP status codes.
   */
   ,unStatus:function(status,fn,scope,options){
           var args = Array.prototype.slice.call(arguments, 1);
           status = [].concat(status||[]);
           Ext.each(status,function(statusCode){
                statusCode = parseInt(statusCode,10);
                if(!isNaN(statusCode)){
                    var ev = 'status_'+statusCode;
                    this.un.apply(this,[ev].concat(args));
                }
           },this);
      }
    ,onReadyState : function(){
         this.fireEvent.apply(this,['readystatechange'].concat(Array.prototype.slice.call(arguments, 0)));
    }

  }, new Ext.util.Observable());

}

 /*
  * Author: Doug Hendricks. doug[always-At]theactivegroup.com
  * Copyright 2007-2008, Active Group, Inc.  All rights reserved.
  *
  * These Ext.lib.Ajax overrides:

    - adds Synchronous Ajax Support ( options.async =false )
    - Permits IE7 to Access Local File Systems using IE's older ActiveX interface
      via the forceActiveX property
    - Pluggable Form encoder (encodeURIComponent is still the default encoder)
    - Corrects the Content-Type Headers for posting JSON (application/json)
      and XML (text/xml) data payloads and sets only one value (per RFC)
    - Adds fullStatus:{ isLocal, isOK, isError, error, status, statusText}
      object to the existing Response Object.
    - Adds standard HTTP Auth support to every request (userId, password config options)
    - options.method prevails over any method derived by the lib.Ajax stack (DELETE, PUT, HEAD etc).
  *
  */


Ext.apply( Ext.lib.Ajax ,
{
  /* set True as needed, to coerce IE to use older ActiveX interface */
  forceActiveX:false,

  /* Global default may be toggled at any time */
  async       :true,

  createXhrObject:function(transactionId)
        {
            var obj={  status:{isError:false}
                     , tId:transactionId}, http;
            try
            {
              if(Ext.isIE7 && !!this.forceActiveX){throw("IE7forceActiveX");}
              obj.conn= new XMLHttpRequest();
            }
            catch(eo)
            {
                for (var i = 0; i < this.activeX.length; ++i) {
                    try
                    {
                        obj.conn= new ActiveXObject(this.activeX[i]);

                        break;
                    }
                    catch(e) {
                    }
                }
            }
            finally
            {
                obj.status.isError = typeof(obj.conn) == 'undefined';
            }
            return obj;

        }

        /* Replaceable Form encoder */
    ,encoder : encodeURIComponent

    ,serializeForm : function(form) {
                if(typeof form == 'string') {
                    form = (document.getElementById(form) || document.forms[form]);
                }

                var el, name, val, disabled, data = '', hasSubmit = false;
                for (var i = 0; i < form.elements.length; i++) {
                    el = form.elements[i];
                    disabled = form.elements[i].disabled;
                    name = form.elements[i].name;
                    val = form.elements[i].value;

                    if (!disabled && name){
                        switch (el.type)
                                {
                            case 'select-one':
                            case 'select-multiple':
                                for (var j = 0; j < el.options.length; j++) {
                                    if (el.options[j].selected) {
                                        if (Ext.isIE) {
                                            data += this.encoder(name) + '=' + this.encoder(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
                                        }
                                        else {
                                            data += this.encoder(name) + '=' + this.encoder(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
                                        }
                                    }
                                }
                                break;
                            case 'radio':
                            case 'checkbox':
                                if (el.checked) {
                                    data += this.encoder(name) + '=' + this.encoder(val) + '&';
                                }
                                break;
                            case 'file':

                            case undefined:

                            case 'reset':

                            case 'button':

                                break;
                            case 'submit':
                                if(hasSubmit == false) {
                                    data += this.encoder(name) + '=' + this.encoder(val) + '&';
                                    hasSubmit = true;
                                }
                                break;
                            default:
                                data += this.encoder(name) + '=' + this.encoder(val) + '&';
                                break;
                        }
                    }
                }
                data = data.substr(0, data.length - 1);
                return data;
    }
    ,getHttpStatus: function(reqObj){

            var statObj = {  status:0
                    ,statusText:''
                    ,isError:false
                    ,isLocal:false
                    ,isOK:false
                    ,error:null};

            try {
                if(!reqObj){throw('noobj');}
                statObj.status = reqObj.status;

                statObj.isLocal = !reqObj.status && location.protocol == "file:" ||
                           Ext.isSafari && reqObj.status === undefined;

                statObj.isOK = (statObj.isLocal || (statObj.status > 199 && statObj.status < 300));
                statObj.statusText = reqObj.statusText || '';
               } catch(e){ //status may not avail/valid yet (or called too early).
                         }

            return statObj;

     }
    ,handleTransactionResponse:function(o, callback, isAbort){

        callback = callback || {};
        var responseObject=null;

        if(!o.status.isError){
            o.status = this.getHttpStatus(o.conn);
            /* create and enhance the response with proper status and XMLDOM if necessary */
            responseObject = this.createResponseObject(o, callback.argument, isAbort);
        }

        if(o.status.isError){
         /* checked again in case exception was raised - ActiveX was disabled during XML-DOM creation?
          * And mixin everything the XHR object had to offer as well
          */
           responseObject = Ext.apply({},responseObject||{},this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false)));

        }

        responseObject.options = o.options;
        responseObject.fullStatus = o.status;

        if(!this.events || this.fireEvent('status_'+o.status.status ,o.status.status, o, responseObject, callback, isAbort) !== false){

             if (o.status.isOK && !o.status.isError) {
                if(!this.events || this.fireEvent('response',o, responseObject, callback, isAbort) !== false){
                    if (callback.success) {
                        callback.success.call(callback.scope||null,responseObject);
                    }
                }
             } else {
                  if(!this.events || this.fireEvent('exception',o ,responseObject, callback, isAbort) !== false){
                    if (callback.failure) {
                        callback.failure.call(callback.scope||null,responseObject);
                    }
                  }
             }
        }

        if(o.options.async){
            this.releaseObject(o);
            responseObject = null;
        }else{
            this.releaseObject(o);
            return responseObject;
        }

    },

    createResponseObject:function(o, callbackArg, isAbort){
        var obj = {responseXML   :null,
                   responseText  :'',
                   getResponseHeader : {},
                   getAllResponseHeaders : ''
                   };

        var headerObj = {},headerStr='';

        if(isAbort !== true){
            try{  //to catch bad encoding problems here
                obj.responseText = o.conn.responseText;
                obj.responseStream = o.conn.responseStream||null;
            }catch(e){
                o.status.isError = true;
                o.status.error = e;
            }

            try{
                obj.responseXML = o.conn.responseXML || null;
            } catch(ex){}

            try{
                headerStr = o.conn.getAllResponseHeaders()||'';
            } catch(ex){}
        }

        if(o.status.isLocal){

           o.status.isOK = !o.status.isError && ((o.status.status = (!!obj.responseText.length)?200:404) == 200);

           if(o.status.isOK && (!obj.responseXML || obj.responseXML.childNodes.length === 0)){

                var xdoc=null;
                try{   //ActiveX may be disabled
                    if(typeof(DOMParser) == 'undefined'){
                        xdoc=new ActiveXObject("Microsoft.XMLDOM");
                        xdoc.async="false";
                        xdoc.loadXML(obj.responseText);
                    }else{
                        var domParser=null;
                        try{  //Opera 9 will fail parsing non-XML content, so trap here.
                            domParser = new DOMParser();
                            xdoc = domParser.parseFromString(obj.responseText, 'application\/xml');
                        }catch(ex){}
                        finally{domParser = null;}
                    }
                } catch(exd){
                    o.status.isError = true;
                    o.status.error = exd;
                }
                obj.responseXML = xdoc;
            }
            if(obj.responseXML){
                var parseBad =  (obj.responseXML.documentElement && obj.responseXML.documentElement.nodeName == 'parsererror') ||
                            (obj.responseXML.parseError || 0) !== 0 ||
                            obj.responseXML.childNodes.length === 0;
                if(!parseBad){
                    headerStr = 'Content-Type: ' + (obj.responseXML.contentType || 'text\/xml') + '\n' + headerStr ;
                }
            }
        }
        var header = headerStr.split('\n');
        for (var i = 0; i < header.length; i++) {
            var delimitPos = header[i].indexOf(':');
            if (delimitPos != -1) {
                headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
            }
        }

        obj.tId = o.tId;
        obj.status = o.status.status;
        obj.statusText = o.status.statusText;
        obj.getResponseHeader = headerObj;
        obj.getAllResponseHeaders = headerStr;
        obj.fullStatus = o.status;

        if (typeof callbackArg != 'undefined') {
            obj.argument = callbackArg;
        }

        return obj;
    },
    setDefaultPostHeader:function(contentType){
        this.defaultPostHeader = contentType;
    },

    setDefaultXhrHeader:function(bool){
        this.useDefaultXhrHeader = bool||false;
    },
    request : function(method, uri, cb, data, options) {

        options = Ext.apply({
               async    :this.async || false,
               headers  :false,
               userId   :null,
               password :null,
               xmlData  :null,
               jsonData :null }, options||{});

        if(!this.events || this.fireEvent('request', method, uri, cb, data, options) !== false){

               var hs = options.headers;
               if(hs){
                    for(var h in hs){
                        if(hs.hasOwnProperty(h)){
                            this.initHeader(h, hs[h], false);
                        }
                    }
               }
               if(options.xmlData){
                    this.initHeader('Content-Type', 'text/xml', false);
                    method = 'POST';
                    data = options.xmlData;
               } else if(options.jsonData){
                    this.initHeader('Content-Type', 'application/json', false);
                    method = 'POST';
                    data = typeof options.jsonData == 'object' ? Ext.encode(options.jsonData) : options.jsonData;
               } else if(data && this.useDefaultHeader){
                    this.initHeader('Content-Type', this.defaultPostHeader);
               }
                //options.method prevails over any derived method.
               return this.makeRequest(options.method || method, uri, cb, data, options);
        }
        return null;

    },

    makeRequest:function(method, uri, callback, postData, options){
        var o = this.getConnectionObject();

        if (!o || o.status.isError) {
                return Ext.apply(o,this.handleTransactionResponse(o, callback));
        } else {
                o.options = options;
                try{
                    o.conn.open(method, uri, options.async, options.userId, options.password);
                    o.conn.onreadystatechange=this.onReadyState ?
                           this.onReadyState.createDelegate(this,[o],0):Ext.emptyFn;
                } catch(ex){
                    o.status.isError = true;
                    o.status.error = ex;

                    var r=this.handleTransactionResponse(o, callback);
                    return Ext.apply(o,r);
                }

                if (this.useDefaultXhrHeader) {
                    if (!this.defaultHeaders['X-Requested-With']) {
                    this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
                    }
                }

                if (this.hasDefaultHeaders || this.hasHeaders) {
                    this.setHeader(o);
                }

                if(o.options.async){ //Timers for syncro calls won't work here, as it's a blocking call
                    this.handleReadyState(o, callback);
                }

                try{
                  if(!this.events || this.fireEvent('beforesend', o, method, uri, callback, postData, options) !== false){
                       o.conn.send(postData || null);
                  }
                } catch(ex){}

                return options.async?o:Ext.apply(o,this.handleTransactionResponse(o, callback));
            }
    }

   ,abort:function(o, callback, isTimeout){
            if (this.isCallInProgress(o)) {
                o.conn.abort();
                window.clearInterval(this.poll[o.tId]);
                delete this.poll[o.tId];
                if (isTimeout) {
                    delete this.timeout[o.tId];
                }
                if(this.events){
                    this.fireEvent(isTimeout?'timeout':'abort', o, callback)
                }

                this.handleTransactionResponse(o, callback, true);

                return true;
            }
            else {
                return false;
            }
    }

    ,clearAuthenticationCache:function(url) {
       // Default to a non-existing page (give error 500).
       // An empty page is better, here.
       url || ( url = '.force_logout');
       try{

         if (Ext.isIE) {
           // IE clear HTTP Authentication
           document.execCommand("ClearAuthenticationCache");
         }
         else {
           // create an xmlhttp object
           var xmlhttp;
           if( xmlhttp = this.createXhrObject()){
               // prepare invalid credentials
               xmlhttp.conn.open("GET", url , true, "logout", "logout");
               // send the request to the server
               xmlhttp.conn.send("");
               // abort the request
               xmlhttp.conn.abort();
               xmlhttp.conn = null;
               xmlhttp = null;
           }
         }
       } catch(e) {
         // There was an error
         return;
       }
     }
});


/*
forEach Iteration
  based on previous work by: Dean Edwards (http://dean.edwards.name/weblog/2006/07/enum/)
  Gecko already supports forEach for Arrays : see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
*/

/* Fix for Opera, which does not seem to include the map function on Array's */

Ext.applyIf( Array.prototype,{
   map : function(fun,scope){
    var len = this.length;
    if(typeof fun != "function"){
        throw new TypeError();
    }
    var res = new Array(len);

    for(var i = 0; i < len; i++){
        if(i in this){
        try{res[i] = fun.call(scope||this, this[i], i, this);}catch(e){}
        }
    }
        return res;
     }
  ,forEach : function(block, scope) {
    var i=0,length = this.length;
    while(i < length){
       try{block.apply(scope||this, [this[i], i++, this]);}catch(e){}
      }
   }
});

   // generic enumeration
Ext.applyIf(Function.prototype,{
   forEach : function(object, block, context) {
        context = context || object;
        for (var key in object) {
        if (typeof this.prototype[key] == "undefined") {
            try{block.apply(context, [object[key], key, object]);}catch(e){}
        }
       }

      }
});

   // character enumeration
Ext.applyIf(String.prototype,{
   forEach : function(block, context) {
        var str = this.toString();
        context = context || this;
        var ar = str.split("")||[];
    ar.forEach( function(chr, index) {
         try{block.apply(context,[ chr, index, str]);}catch(e){}
    },ar);
   }
});

   // globally resolve forEach enumeration
var forEach = function(object, block, context) {
        context = context || object;
    if (object) {
        var resolve = Object; // default
        if (object instanceof Function) {
            // functions have a "length" property
            resolve = Function;
        } else if (object.forEach instanceof Function) {
            // the object implements a custom forEach method so use that
            object.forEach(block, context);
            return;
        }
        resolve.forEach(object, block, context);
    }
};

if(Ext.util.Observable){
   /*
    * @class Ext.ux.ModuleManager
    * Version:  RC-1
    * Author: Doug Hendricks. doug[always-At]theactivegroup.com
    * Copyright 2007-2008, Active Group, Inc.  All rights reserved.
    *
    ************************************************************************************
    *   This file is distributed on an AS IS BASIS WITHOUT ANY WARRANTY;
    *   without even the implied warranty of MERCHANTABILITY or
    *   FITNESS FOR A PARTICULAR PURPOSE.
    ************************************************************************************

    License: Ext.ux.ModuleManager is licensed under the terms of the Open Source
    LGPL 3.0 license.  Commercial use is permitted to the extent
    that the code/component(s) do NOT become part of another Open Source or Commercially
    licensed development library or toolkit without explicit permission.

    Donations are welcomed: http://donate.theactivegroup.com

    License details: http://www.gnu.org/licenses/lgpl.html


   Sample Usage:

    YourApp.CodeLoader = new Ext.ux.ModuleManager({modulePath:yourBasePath });
    YourApp.CodeLoader.on({
            'beforeload':function(manager, module, response){

                //return false to prevent the script from being evaled.
                return response.fullStatus.isLocal ||
                    module.contentType.toLowerCase().indexOf('javascript') !== -1;

            }
            ,scope:YourApp.CodeLoader
            });

    //Create a useful 'syntax' for you App.

    YourApp.needs = YourApp.CodeLoader.load.createDelegate(YourApp.CodeLoader);
    YourApp.provide = YourApp.CodeLoader.provides.createDelegate(YourApp.CodeLoader);

    YourApp.needs('ext/layouts','js/dragdrop','js/customgrid','style/custom.css');

*/

  (function(){

    Ext.ux.ModuleManager = function(config){

        Ext.apply(this,
            config||{},
            { modulePath:function(){  //based on current page
                   var d= location.href.indexOf('\/') != -1 ? '\/':'\\';
                   var u=location.href.split(d);
                   u.pop(); //this page
                   return u.join(d) + d;
                    }()
             });

        Ext.ux.ModuleManager.superclass.constructor.call(this);
        this.addEvents({
                /**
                * @event loadexception
                * Fires when any exception is raised
                * returning false prevents any subsequent pending module load requests
                * @param {Ext.ux.ModuleManager} this
                * @param {String} module -- the module name
                * @param {Object} error -- An error object containing: httpStatus, httpStatusText, error object
                */
                "loadexception" : true,

                /**
               * @event alreadyloaded
               * Fires when the ModuleManager determines that the requested module has already been loaded
               * @param {Ext.ux.ModuleManager} this
               * @param {String} module -- the module name
               */
                "alreadyloaded" : true,

               /**
                * @event load
                * Fires when the retrieved content has been successfully evaled into the current context.
                * @param {Ext.ux.ModuleManager} this
                * @param {String} module -- the module name
                */
                "load" : true,

                /**
                 * @event beforeload
                 * Fires when the request has successfully completed and just prior to eval
                 * returning false prevents the content (of this module) from being loaded (eval'ed)
                 * @param {Ext.ux.ModuleManager} this
                 * @param {String} module -- the module name
                 * @param {Object} response - the Ajax response object
                 * @param {String} theScript -- the source String to be evaled.
                 */
                "beforeload" : true,

                /**
                 * @event complete
                 * Fires when all module load request have completed (successfully or not)
                 * @param {Ext.ux.ModuleManager} this
                 * @param {Boolen} success
                 * @param {Array} modules -- the modules now available as a result of (or previously -- already loaded) the last load operation.
                 */
                "complete" : true
        });


    };

    Ext.extend(Ext.ux.ModuleManager, Ext.util.Observable,{

     disableCaching: false

    ,modules : {}

    ,method:'GET'

    ,asynchronous : false

    ,loaded:function(name){
        return this.modules[name] && this.modules[name].loaded===true;
    }

    /* A mechanism for modules to identify their presence */
    ,provides : function(){
        Ext.each(arguments,function(module){
           var modName = module.trim().split('\/').pop().toLowerCase()
              ,fullName   = module.indexOf('.') !== -1 ? module.trim() : module.trim() + '.js';

           this.modules[modName] || (this.modules[modName] =
             {
                 name       :modName.trim()
                ,fullName   :fullName.trim()
                ,extension  :fullName.split('.').pop().trim()
                ,path       :''
                ,url        :''
                ,loaded     :true
                ,contentType    :''
                });

        },this);

    }
    ,load:function(){

      var result = true
         ,keepItUp = true
         ,StopIter = "StopIteration"
         ,options = {async:this.asynchronous,headers:this.headers||false}
         ,evaled = []
         ,callback = {
            success:function(response){
                var module     = response.argument.module
               ,moduleName = response.argument.module.name;

               try{
                     module.contentType = response.getResponseHeader['Content-Type'] || '';

                     if(this.fireEvent('beforeload', this, module, response) !== false){
                        this.currentModule = moduleName;
                        module.loaded = true;
                        var exception = this.globalEval( response.responseText );
                        if(exception===true){

                            evaled.push(moduleName);
                            this.modules[moduleName] = module;
                            try{
                              this.fireEvent('load', this, module);
                            }catch(ex){}

                        } else {
                        //coerce to actual module URL
                         throw Ext.applyIf({fileName:module.url ,lineNumber:exception.lineNumber||0 },exception );

                        }
                     }

                }catch(ex) {
                   keepItUp = this.fireEvent('loadexception', this, module, {
                        error         :ex
                       ,httpStatus    :response.status
                       ,httpStatusText:response.statusText
                       });

                      result = false;

                }

           }
           ,failure:function(response){
               var module = response.argument.module;
               module.contentType = response.getResponseHeader['Content-Type'] || '';

            keepItUp = this.fireEvent('loadexception', this, module,{
                    error         :response.fullStatus.error
                   ,httpStatus    :response.status
                   ,httpStatusText:response.statusText
                   });

            result = false;

           }
           ,scope:this
       };

       /* Iterate the desired modules in there implied dependency order */
       try{

         Ext.each(arguments , function(module){
             //strip relative path leaving module name
             var moduleName = module.trim().split('\/').pop().toLowerCase()
                ,fullModule = module.indexOf('.') !== -1 ? module : module + '.js';

             if(!this.loaded(moduleName) || this.forced){

               var moduleObj = {
                         name       :moduleName.trim()
                        ,fullName   :fullModule.trim()
                        ,extension  :fullModule.split('.').pop().trim()
                        ,path       :this.modulePath
                        ,url        :this.modulePath + fullModule
                        ,loaded     :false
                        ,contentType    :''
                       };

               if(this.method == 'GET' && this.disableCaching === true){
                    fullModule += '?_dc=' + (new Date().getTime());
               }

               Ext.apply(callback,{argument:{module:moduleObj}});

               Ext.lib.Ajax.request(this.method||'GET',this.modulePath + fullModule, callback,null,options);
             } else {
               keepItUp = this.fireEvent('alreadyloaded', this, this.modules[moduleName]);
               evaled.push(moduleName);
             }
             if(keepItUp===false){throw StopIter;}
          },this);

       } catch(ex){
        if (ex != StopIter)
            {throw ex;}
       }

      this.fireEvent('complete', this, result, evaled);
      this.forced = false;
      return result;
    }

    ,globalEval: function( data , scope, context ) {
        scope || (scope = window);

        data = String(data||"").trim();

        if(data.length===0){return false;}
        try{
            if(scope.execScript){
                // window.execScript in IE fails when scripts include HTML comment tag.
                scope.execScript(data.replace(/^<!--/,"").replace(/-->$/, ""));
            }else if (Ext.isSafari){
                // safari doesn't provide a synchronous global eval
                scope.setTimeout(data, 0);
            }else{
                // context (target namespace) is only support on Gecko.
                eval.call(scope,data,context || null);
            }
            return true;
        }catch(ex){return ex;}

        }
    });

  }());
}