zoukankan      html  css  js  c++  java
  • Microsoft Ajax 脚本浅析

             最近有时间下载并在本地安装了 AjaxControlToolkit , 在运行里面的示例时,发现所生
    成的源文件时发现有几个“特别”的地方。因为本人对Microsoft Ajax未曾做过什么研究,因
    此就想看看微软的这个产品中是有什么奥秘。现在就把我所看的源码以及相关的理解记录如下
    (本文以SampleWebSite/DragPanel/DragPanel.aspx为例),以便与大家交流,希望大家多提意
    见。

        代码段1:
        <script type="text/javascript">
            Sys.WebForms.PageRequestManager._initialize('ctl00$SampleContent$ctl00',
      document.getElementById('aspnetForm'));
            Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90);
        </script>

        Sys.WebForms.PageRequestManager._initialize函数可以在这个本文的下载包中的
    ScriptResource_ScriptManager.js中找到,它的代码如下:
       Sys.WebForms.PageRequestManager._initialize =
     function Sys$WebForms$PageRequestManager$_initialize(scriptManagerID, formElement)
        {
           if (Sys.WebForms.PageRequestManager.getInstance()) {
              throw Error.invalidOperation(Sys.WebForms.Res.PRM_CannotRegisterTwice);
           }
           Sys.WebForms.PageRequestManager._instance = new Sys.WebForms.PageRequestManager();
           Sys.WebForms.PageRequestManager.getInstance()._initializeInternal(
      scriptManagerID, formElement);
        }

        其中的 if 分支里应该是为了确保当前的 _instance 唯一或为空 。分支结束后则开始运
    行初始化并运行实例的_initializeInternal函数这个函数的第二个参数formElement就是页面
    表单的ID,如下:
         <form name="aspnetForm" method="post" action="DragPanel.aspx" id="aspnetForm">
        实始化的函数如下(请看注释):
        function Sys$WebForms$PageRequestManager$_initializeInternal(scriptManagerID,
    formElement)
        {

            this._scriptManagerID = scriptManagerID; 
            this._form = formElement;
            this._form._initialAction = this._form.action;

            this._onsubmit = this._form.onsubmit;
            this._form.onsubmit = null;

            //下面这一行代码中的_onFormSubmit事件和相关解释参见
     //http://www.cnblogs.com/JeffreyZhao/archive/2007/04/09/Be_careful_with_loading_script_files_after_an_async_postback.html
            this._onFormSubmitHandler = Function.createDelegate(this, this._onFormSubmit);

     //_onFormElementClick应该是对 element.tagName 为INPUT(submit,image)或BUTTON
     //进行元素值的绑定,并对_postBackSettings进行封装 功能:元素绑定(如panel等)和
     //子元素(如果存在)值数据进行封装,请看代码段:_createPostBackSettings
            this._onFormElementClickHandler = Function.createDelegate(this, this._onFormElementClick);

     //运行 this.dispose();
            this._onWindowUnloadHandler = Function.createDelegate(this, this._onWindowUnload);

            //进行相应的事件绑定
            Sys.UI.DomEvent.addHandler(this._form, 'submit', this._onFormSubmitHandler);
            Sys.UI.DomEvent.addHandler(this._form, 'click', this._onFormElementClickHandler);
            Sys.UI.DomEvent.addHandler(window, 'unload', this._onWindowUnloadHandler);

            this._originalDoPostBack = window.__doPostBack;
                    if (this._originalDoPostBack) {
                window.__doPostBack = Function.createDelegate(this, this._doPostBack);
            }

     //运行_pageLoaded (true)详见代码段:"_pageLoaded ",如果false
            //则运行Sys.Application.raiseLoad()
            this._pageLoadedHandler = Function.createDelegate(this, this._pageLoadedInitialLoad);
            Sys.UI.DomEvent.addHandler(window, 'load', this._pageLoadedHandler);
        }

        其中_onFormSubmit函数就是当页面发生提交操作时执行的函数,里面有生成formBody和
    WebRequest对象的操作,详情不多说了,可参见链接:http://blog.joycode.com/saucer/articles/85745.aspx


       
        接下来看函数_createPostBackSettings代码段:
        function Sys$WebForms$PageRequestManager$_createPostBackSettings(async, panelID, sourceElement)
        {
            return { async:async, panelID:panelID, sourceElement:sourceElement };
        }


        代码段:_pageLoaded
        function Sys$WebForms$PageRequestManager$_pageLoaded(initialLoad) {
            var handler = this._get_eventHandlerList().getHandler("pageLoaded");
            if (handler) {
                handler(this, this._getPageLoadedEventArgs(initialLoad));
            }
            if (!initialLoad) {
                Sys.Application.raiseLoad();
            }
        }


         然后再看一下:Sys.WebForms.PageRequestManager.getInstance()._updateControls 
    它的作用应该是将位于当前panel下的所有数据元素绑定到PageRequestManager的相关属性上,
    这样就可以做局部页面数据的提交和相关操作(如刷新等)。
        可能是因为初始化页面,所以这个函数里的前三个参数将为“[]” ,只有第四个参数是90
    也就是AsyncPostBackTimeOut的属性值(单位:秒)。相关的函数如下:  
        function Sys$WebForms$PageRequestManager$_updateControls(updatePanelIDs,
    asyncPostBackControlIDs, postBackControlIDs, asyncPostBackTimeout)
        {
            if (updatePanelIDs) {
                this._updatePanelIDs = new Array(updatePanelIDs.length);
                this._updatePanelClientIDs = new Array(updatePanelIDs.length);
                this._updatePanelHasChildrenAsTriggers = new Array(updatePanelIDs.length);
                for (var i = 0; i < updatePanelIDs.length; i++) {
                    var realPanelID = updatePanelIDs[i].substr(1);
                    var childrenAsTriggers = (updatePanelIDs[i].charAt(0) === 't');

                    this._updatePanelHasChildrenAsTriggers[i] = childrenAsTriggers;
                    this._updatePanelIDs[i] = realPanelID;
                    this._updatePanelClientIDs[i] = this._uniqueIDToClientID(realPanelID);
                }
                // 设置异步回传过期时间
                this._asyncPostBackTimeout = asyncPostBackTimeout * 1000;
            }
            else {
                this._updatePanelIDs = [];
                this._updatePanelClientIDs = [];
                this._updatePanelHasChildrenAsTriggers = [];
                this._asyncPostBackTimeout = 0;
            }
     ......
        }
       

      
        然后就是下面的代码段了:
        Sys.Application.initialize();
        Sys.Application.add_init(function() {
           $create(AjaxControlToolkit.FloatingBehavior, {"handle":
     $get("ctl00_SampleContent_Panel7"),"id":"ctl00_SampleContent_DragPanelExtender1"},
     null, null, $get("ctl00_SampleContent_Panel6"));
        });

        函数Sys.Application.initialize用于初始化并进行创建组件的处理句柄绑定,代码如下:
        function Sys$_Application$initialize() {
            if(!this._initialized && !this._initializing) {
                this._initializing = true;
                window.setTimeout(Function.createDelegate(this, this._doInitialize), 0);
            }
        }
        其中_doInitialize函数如下:
        function Sys$_Application$_doInitialize() {
            Sys._Application.callBaseMethod(this, 'initialize');

            //getHandler("init")返回值是Sys.Application.add_init函数中传入的处理句柄
            var handler = this.get_events().getHandler("init");
            if (handler) {
                this.beginCreateComponents();
                handler(this, Sys.EventArgs.Empty);
                this.endCreateComponents();
            }
            //创建组件 
            this.raiseLoad();
            this._initializing = false;
        }

        其中raiseLoad 函数如下:
        function Sys$_Application$raiseLoad() {
            var h = this.get_events().getHandler("load");
            var args = new Sys.ApplicationLoadEventArgs(Array.clone(this._createdComponents),
       !this._initializing);
            if (h) {
                h(this, args);
            }

            if (window.pageLoad) {
                window.pageLoad(this, args);
            .....
        }

       
        最后就是Sys.Application.add_init将创建句柄转入了,它里面的函数$create 用于创建相应的
    客户端组件,ASP.NET AJAX 客户端组件对象类型:Sys.UI.Behavior和Sys.UI.Control,它们扩充了
    基本组件的功能,行为类组件继承于Sys.UI.Behavior,控件继承于Sys.UI.Control。
        $create 代码段如下(下载包中的ScriptResource_EventHandlerList文件):

        function Sys$Component$create(type, properties, events, references, element)
        {
            .......

     //FloatingBehavior是否是组件
            if (!type.inheritsFrom(Sys.Component)) {
                throw Error.argument('type', String.format(Sys.Res.createNotComponent,
      type.getName()));
         }

     //FloatingBehavior是否是行为类组件或控件
            if (type.inheritsFrom(Sys.UI.Behavior) || type.inheritsFrom(Sys.UI.Control)) {
            if (!element) throw Error.argument('element', Sys.Res.createNoDom);
            }
            else if (element) throw Error.argument('element', Sys.Res.createComponentOnDom);

            //按指定类型创建相关的组件
            var component = (element ? new type(element): new type());
            var app = Sys.Application;
      
     ......

            //为组类实例绑定相应的属性
            if (properties)
            {
               Sys$Component$_setProperties(component, properties);
            }

            //为组件添加相应的事件名称
            if (events)
            {
              for (var name in events) {
                if (!(component["add_" + name] instanceof Function))
       throw new Error.invalidOperation(String.format(Sys.Res.undefinedEvent, name));

                if (!(events[name] instanceof Function))
      throw new Error.invalidOperation(Sys.Res.eventHandlerNotFunction);

         //注意此处加入了"add_"前缀 ,在FloatingBehavior类中将会有一个move事件,请看代码段
         //FloatingBehavior
                component["add_" + name](events[name]); 
              }
            }

            .....

        return component;
    }
       
         看了创建组件的函数后再看一下要创建的组件类:

         代码段:FloatingBehavior
         AjaxControlToolkit.FloatingBehavior = function(element)
         {
            ...... 
     
            this.add_move = function(handler) {
               this.get_events().addHandler('move', handler);
            }

            this.remove_move = function(handler) {
               this.get_events().removeHandler('move', handler);
            }

            this.get_handle = function() {
               return _handle;
            }

            this.set_handle = function(value) {
               if (_handle != null) {
                $removeHandler(_handle, "mousedown", _mouseDownHandler);           
            }
       
            _handle = value;
            $addHandler(_handle, "mousedown", _mouseDownHandler);       

            ......

            function mouseDownHandler(ev) {
               window._event = ev;
               var el = this.get_element();
           
               if (this.checkCanDrag(ev.target)) {
                _dragStartLocation = CommonToolkitScripts.getLocation(el);
               
                ev.preventDefault();
               
                this.startDragDrop(el);
            }

            ......

            this.initialize = function() {
               AjaxControlToolkit.FloatingBehavior.callBaseMethod(this, 'initialize');
               AjaxControlToolkit.DragDropManager.registerDropTarget(this);

            ......
        }
        这里面mouseDownHandler应该就是处理拖动操作的程序代码了。而在这个类的initialize中有
    DragDropManager.registerDropTarget(this)这一行,它的作用是调用 _wireDropTargetEvents函
    数(下载包中ScriptResource_Drag文件,加入相应的事件处理器如:_dragEnterHandler,
    _dragLeaveHandler等),值得一提的是在DragDropManager(下载包中ScriptResource_DragDropManage
    文件)的_getInstance()函数中出现了如下代码,大家看到了吧,为了实现在不同浏览器下正常工作,
    开发人员在这里做了分别处理:
     _getInstance: function()
            {
              if (!this._instance) {
                if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
                    this._instance = new AjaxControlToolkit.IEDragDropManager();
                }
                else {
                    this._instance = new AjaxControlToolkit.GenericDragDropManager();
                }

         相关的设置大家可以看一下IEDragDropManager和GenericDragDropManager这两个类,我就不
    在这里多聊了。

         最后是:
         AjaxControlToolkit.FloatingBehavior.registerClass('AjaxControlToolkit.FloatingBehavior',
      AjaxControlToolkit.BehaviorBase,
      AjaxControlToolkit.IDragSource,
      AjaxControlToolkit.IDropTarget,
      Sys.IDisposable);

         从字面上看,可以知道FloatingBehavior 的基类就是AjaxControlToolkit.BehaviorBase,因为
    registerClass函数声明如下[下载包中ScriptResource_Application文件]:

        //interfaceTypes接口数组
        function Type$registerClass(typeName, baseType, interfaceTypes)
        {
             ......
            //检查类型名称是否有效
            if (!Type.__fullyQualifiedIdentifierRegExp.test(typeName))
        throw Error.argument('typeName', Sys.Res.notATypeName);
         
            ......
            if (baseType && !baseType.__class)
        throw Error.argument('baseType', Sys.Res.baseNotAClass);

            this.prototype.constructor = this;
            this.__typeName = typeName;
            this.__class = true;

            if (baseType)
            {
            this.__baseType = baseType;
            this.__basePrototypePending = true;
            }
            ......

        分析至此告一段落。因为Microsoft Ajax js 文件如此复杂(值得学习借签的地方确实不少),
    只能看到哪里学到哪里了。因为是初次接触,因此文章中有不当之处希望大家多提宝贵意见,共同
    进步:)

    下载包:
         https://files.cnblogs.com/daizhj/package.rar

         注:下载包中的文件已被重命名,只为分析时方便。详情参见AjaxControlToolkit和相关文档。

         参考文章:
         http://www.cnblogs.com/JeffreyZhao/archive/2007/04/09/Be_careful_with_loading_script_files_after_an_async_postback.html
         http://www.cnblogs.com/fanrong/archive/2007/04/27/729875.html
         http://www.cnblogs.com/hblynn/archive/2007/01/31/635507.html

         http://blog.csdn.net/TheMoment_Rain/archive/2006/12/14/1443128.aspx
         http://blog.joycode.com/saucer/articles/85745.aspx
       
       

  • 相关阅读:
    搭建web攻防环境
    远程控制
    网络攻击
    论文翻译:《PRIMES is in P》——素性测试的确定性多项式时间算法研究
    从Fouier级数到DCT
    同态加密简要概述
    杂谈:探究副词“有点”用于修饰形容词和动词的使用范围
    题解:洛谷P2055/[ZJOI2009] 假期的宿舍(匈牙利算法)
    笔记:二部图最大匹配的匈牙利算法
    杂谈:AI Intro课程作业——卷积神经网络练练手之MNIST数据集
  • 原文地址:https://www.cnblogs.com/daizhj/p/762312.html
Copyright © 2011-2022 走看看