Giter Club home page Giter Club logo

mass-framework's People

Contributors

a289459798 avatar dolymood avatar jinkspeng avatar jslouvre avatar qiangtou avatar qinerg avatar riophae avatar rubylouvre avatar wbpmrck avatar xyhp915 avatar yolio2003 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mass-framework's Issues

为什么需要AMD模块?

模块化系统的基础前提是:

  1. 允许创建被封装的代码片段,也就是所谓的模块
  2. 定义本模块与其他模块之间的依赖
  3. 定义可以被其他模块使用的输出的功能
  4. 谨慎的使用这些模块提供的功能

AMD满足以上需求,并将依赖模块设置为其回调函数的参数从而实现在模块代码被执行前异步的加载这些依赖模块。AMD还提供了加载非AMD资源的插件系统。
虽然有其他的load JavaScript的替代方法,但使用脚本元素来加载JavaScript有特有的优势,包括性能,减少调试(尤其在一些老版本的浏览器上)以及跨域的支持。因此AMD致力于提供基于浏览器开发的最优体验。

AMD格式提供了几个关键的好处。首先,它提供了一种紧凑的声明依赖的方式。通过简单的字符串数组来定义模块依赖,使得开发者能够花很小的代价轻松列举大量模块依赖性。

AMD帮助消除对全局变量的需求。 每个模块都通过局部变量引用或者返回对象来定义其依赖模块以及输出功能。因此,模块不需要引入全局变量就能够定义其功能并实现与其他模块的交互。AMD同时是“匿名的”,意味着模块不需要硬编码指向其路径的引用, 模块名仅依赖其文件名和目录路径,极大的降低了重构的工作量。

通过将依赖性映射为局部变量, AMD鼓励高效能的编码实践。如果没有AMD模块加载器,传统的JavaScript代码必须依赖层层嵌套的对象来“命名”给定的脚本或者模块。如果使用这种方式,通常需要通过一组属性来访问某个功能,这会造成全局变量的查找和众多属性的查找,增加了额外的开发工作同时降低了程序的性能。通过将模块依赖性映射为局部变量,只需要一个简单的局部变量就能访问某个功能,这是极其快速的并且能够被JavaScript引擎优化。

transform

Module.extend('Util', function(util) {
    var dom = util.DOM;
    var setOriginOffset = function(el) {
        el.IESUCKS = {
            width : el.offsetWidth,
            height: el.offsetHeight
        };
    };
    var transformKey = function() {
        style = document.body.style;
        var keys = ['transform', 'msTransform', 'MozTransform', 'webkitTransform', 'OTransform'];
        for (var i = keys.length; i--;) {
            if (keys[i] in style) {
                return keys[i];
            }
        }
        return null;
    }();
    dom.rotateAndScale = function(el, angle, scale) {
        scale = scale || 1;
        angle = angle || 0;

        if(transformKey) {
            var rs = 'rotate(' + angle + 'deg) scale(' + scale + ')';
            el.style[transformKey] = rs;
        }else { ////ie8- Matrix filter .

            var filterType = 'DXImageTransform.Microsoft.Matrix';
            var fullFilterType = 'progid:' + filterType;
            var currentFilter = el.style.filter;
            var filterIndex = currentFilter.indexOf(fullFilterType);
            if(currentFilter == '') {
                el.style.filter = fullFilterType;
            }else if (filterIndex < 0) {
                el.style.filter = currentFilter + ' ' + fullFilterType;
            }

            var filters = el.filters;
            var rad = angle * Math.PI / 180;
            var sinA = Math.sin(rad);
            var cosA = Math.cos(rad);
            var m11 = cosA * scale;
            var m12 = -sinA * scale;
            var m21 = sinA * scale;
            var m22 = cosA * scale;

            filters.item(filterType).M11 = m11;
            filters.item(filterType).M12 = m12;
            filters.item(filterType).M21 = m21;
            filters.item(filterType).M22 = m22;
            filters.item(filterType).SizingMethod = "auto expand";


            if (!el.IESUCKS) {
                setOriginOffset(el)
            }


            var offset = el.IESUCKS;
            var originWidth = offset.width;
            var originHeight = offset.height;

            var scaleOffsetWidth = (originWidth - scale * originWidth) / 2 / scale;
            var scaleOffsetHeight = (originHeight - scale * originHeight) / 2 / scale;
            var coefficient = 14.2;
            var fixedWidth = originHeight / coefficient / scale;
            var fixedHeight = originWidth / coefficient / scale;
            fixedWidth = fixedheight = 0;


            //mlgbd ie 的修正值.都要考虑scale的影响.所以,所有修正系数都要和scale重新计算后再进行修正.
            el.style.marginLeft = (originWidth - el.offsetWidth) / 2 + scaleOffsetWidth - fixedWidth + 'px';
            el.style.marginTop = (originHeight - el.offsetHeight) / 2 + scaleOffsetHeight - fixedHeight + 'px';


        }
    };
});
<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>sidebar</title>
    <style>
      .p{
        width : 200px;height:200px;font-size:1px;
        position:absolute; border:solid 1px #000;
      }
      #fuck{
        border:none;width:100px;height:100px;position:absolute;top:50px; left:50px;background:red
      }
    </style>
</head>
<body>

    <div class="p" style="left:400px;top:400px;">

        <div id="fuck" class="p" style="right:50px;bottom:50px">1</div>
    </div>

    <script>


      var transformKey = function  () {
          style = document.body.style;
          var keys = ['transform', 'msTransform', 'MozTransform', 'webkitTransform', 'OTransform'];
          for(var i = keys.length; i--; ){
            if(keys[i] in style){
              return keys[i];
            }
          }
          return null;
        }();

        var setOriginOffset = function(el) {

          if (!el.IESUCKS) {
            el.IESUCKS = {
              width : el.offsetWidth,
              height : el.offsetHeight
            };
          }

        };

        var rotateAndScale = function  (el, angle, scale) {
                  scale = scale || 1;
                  angle = angle || 0;
                  var rs = 'rotate(' + angle + 'deg) scale(' + scale + ')';

                  if(transformKey){
                    el.style[transformKey] = rs;
                  }else{ ////ie8- Matrix filter .
                    var filterType = 'DXImageTransform.Microsoft.Matrix';
                    var fullFilterType = 'progid:' + filterType;
                    var currentFilter = el.style.filter;
                    var filterIndex = currentFilter.indexOf(fullFilterType);
                    if(currentFilter == ''){
                      el.style.filter = fullFilterType; 
                    }else if(filterIndex < 0){
                      el.style.filter = currentFilter + ' ' + fullFilterType;
                    }




                    var filters = el.filters;
                    var rad = angle * Math.PI / 180;
                    var sinA = Math.sin(rad);
                    var cosA = Math.cos(rad);
                    var m11 = cosA * scale;
                    var m12 = -sinA * scale;
                    var m21 = sinA * scale;
                    var m22 = cosA * scale;

                    filters.item(filterType).M11 = m11;
                    filters.item(filterType).M12 = m12;
                    filters.item(filterType).M21 = m21;
                    filters.item(filterType).M22 = m22;
                    filters.item(filterType).SizingMethod="auto expand";

                    if(!el.IESUCKS){

                      setOriginOffset(el)

                    }
                    var offset = el.IESUCKS;
                    var originWidth = offset.width;
                    var originHeight = offset.height;
                    var scaleOffsetWidth = (originWidth - scale * originWidth) / 2 / scale;
                    var scaleOffsetHeight = (originHeight - scale * originHeight ) / 2 / scale;
                    var coefficient = 14.2;
                    var fixedWidth = originHeight / coefficient / scale;
                    var fixedHeight = originWidth / coefficient / scale;

                    //mlgbd ie 的修正值.都要考虑scale的影响.所以,所有修正系数都要和scale重新计算后再进行修正.

                    var marginLeft = (originWidth - el.offsetWidth)/2 + scaleOffsetWidth - fixedWidth;
                    var marginTop = (originHeight - el.offsetHeight)/2  + scaleOffsetHeight - fixedHeight;
                    el.style.marginLeft = marginLeft + 'px';
                    el.style.marginBottom = -marginTop + 'px'

                  } 
        };
        var el = document.getElementById('fuck');
        var angle = 0;
        document.onclick  = function  () {
          timer = setInterval(function  () {
            angle += 10;
              if(angle === 360){
                angle = 0;
                clearInterval(timer);
              }
              rotateAndScale(el, angle, 1);
          }, 50)
        };



    </script>

</body>
</html>

然后,更**的地方在于..ie8 和ie6的行为是相反的.....
贵妃】教主Franky
``html
<!doctype html>

<title>sidebar</title> <style> .p{ width : 200px;height:200px;font-size:1px; position:absolute; border:solid 1px #000; } #fuck{ border:none;width:100px;height:100px;position:absolute;background:red } </style>
<div class="p" style="left:400px;top:400px;">

    <div id="fuck" class="p" style="right:50px;bottom:50px">1</div>
</div>

<script>


  var transformKey = function  () {
      style = document.body.style;
      var keys = ['transform', 'msTransform', 'MozTransform', 'webkitTransform', 'OTransform'];
      for(var i = keys.length; i--; ){
        if(keys[i] in style){
          return keys[i];
        }
      }
      return null;
    }();

    var setOriginOffset = function(el) {

      if (!el.IESUCKS) {
        el.IESUCKS = {
          width : el.offsetWidth,
          height : el.offsetHeight
        };
      }

    };

    var rotateAndScale = function  (el, angle, scale) {
              scale = scale || 1;
              angle = angle || 0;
              var rs = 'rotate(' + angle + 'deg) scale(' + scale + ')';

              if(transformKey){
                el.style[transformKey] = rs;
              }else{ ////ie8- Matrix filter .
                var filterType = 'DXImageTransform.Microsoft.Matrix';
                var fullFilterType = 'progid:' + filterType;
                var currentFilter = el.style.filter;
                var filterIndex = currentFilter.indexOf(fullFilterType);
                if(currentFilter == ''){
                  el.style.filter = fullFilterType; 
                }else if(filterIndex < 0){
                  el.style.filter = currentFilter + ' ' + fullFilterType;
                }




                var filters = el.filters;
                var rad = angle * Math.PI / 180;
                var sinA = Math.sin(rad);
                var cosA = Math.cos(rad);
                var m11 = cosA * scale;
                var m12 = -sinA * scale;
                var m21 = sinA * scale;
                var m22 = cosA * scale;

                filters.item(filterType).M11 = m11;
                filters.item(filterType).M12 = m12;
                filters.item(filterType).M21 = m21;
                filters.item(filterType).M22 = m22;
                filters.item(filterType).SizingMethod="auto expand";

                if(!el.IESUCKS){

                  setOriginOffset(el)

                }
                var offset = el.IESUCKS;
                var originWidth = offset.width;
                var originHeight = offset.height;
                var scaleOffsetWidth = (originWidth - scale * originWidth) / 2 / scale;
                var scaleOffsetHeight = (originHeight - scale * originHeight ) / 2 / scale;
                var coefficient = 14.2;
                var fixedWidth = originHeight / coefficient / scale;
                var fixedHeight = originWidth / coefficient / scale;

                //mlgbd ie 的修正值.都要考虑scale的影响.所以,所有修正系数都要和scale重新计算后再进行修正.

                var marginLeft = (originWidth - el.offsetWidth)/2 + scaleOffsetWidth - fixedWidth;
                var marginTop = (originHeight - el.offsetHeight)/2  + scaleOffsetHeight - fixedHeight;
                //  el.style.marginLeft = marginLeft + 'px';
                //el.style.marginTop = marginTop   + 'px'

              } 
    };
    var el = document.getElementById('fuck');
    var angle = 0;
    document.onclick  = function  () {
      timer = setInterval(function  () {
        angle += 10;
          if(angle === 360){
            angle = 0;
            clearInterval(timer);
          }
          rotateAndScale(el, angle, 1);
      }, 50)
    };



</script>
【贵妃】教主Franky
2014/4/6 12:27:09
你看 现在ie6 往左上偏 ,ie8往右下偏

就是因为用的right, bottom定位...

类工厂的改进

带刀在用change事件代理出错,发现是类工厂的属性描述符出问题了

$.defineProperty 方法备份

 
var defineProperty = Object.defineProperty
    //IE8的Object.defineProperty只对DOM有效
    if (defineProperty) {
        try {
            defineProperty({}, 'a',{
                get:function(){}
            });
        } catch (e) {
            defineProperty = null;
        }
    }

    if (defineProperty) {
        // Detects a bug in Android <3.2 where you cannot redefine a property using
        // Object.defineProperty once accessors have already been set.
        (function() {
            var obj = {};
            defineProperty(obj, 'a', {
                configurable: true,
                enumerable: true,
                get: function() { },
                set: function() { }
            });
            defineProperty(obj, 'a', {
                configurable: true,
                enumerable: true,
                writable: true,
                value: true
            });
            if(obj.a !== true){
                defineProperty = null;
            }
        })();
    }
    $.defineProperty = defineProperty || function(){
        $.log("defineProperty is noop method")
    }

大数运算

From Firebug console in Firefox 3.6.3

>>> 100==99.99
false
>>> 100==99.999
false
>>> 100==99.9999
false
>>> 100==99.99999
false
>>> 100==99.999999
false
>>> 100==99.9999999
false
>>> 100==99.99999999
false
>>> 100==99.999999999
false
>>> 100==99.9999999999
false
>>> 100==99.99999999999
false
>>> 100==99.999999999999
false
>>> 100==99.9999999999999
false
>>> 100==99.99999999999999
false
>>> 100==99.999999999999999
true

新式的模板结构

传统的字符串模块都要处理转义的问题,但我这新方法通过传参手段消灭了这步骤

核心原理

function a(){
      var b = ["1,2,3"]
      return Function("b", "function test(){}; return function(data){ test(); return  b }")(b)
}
a()+""

文档和库的文件各种不同步

AJAX这个东西……(做APP的明显最重要,怎么可以没有文档)
进来看到你那些示例代码还好,真的用起来时还得去把你的源代码一个个看过去……然后才发现要引用的不是avalon.js也不是mass_merge.js而是mass.js (rubylouvre.github.io里面版本够老的……)。
然后又发现你帮助文档中,有

require("more/avalon, ready" ......

坑的是源代码的more目录下压根就没有avalon.js这个文件……,然后呢有看到竟然抓取到

http://rubylouvre.github.io/more/locale/zh-cn.js 

这个文件……我瞬间无语了,想来你这帮助文档是在哪个时代的也是不重要了……

avalon模块:依赖链

依赖链是MVVM的核心之一,由$.observable, $.observableArray与$.computed组成。它们都返回一种叫field的可读写函数,来操作用户的数据!

avalon模块的内建适配器

visible bindings,通过第一个传参的真假值决定其是否显示。

        <div data-bind="visible: shouldShowMessage">
            当shouldShowMessage的传参为true时,你会看到这段文字
        </div>

        <script type="text/javascript">
            $.require("ready,more/avalon", function(){
                var viewModel = {
                    shouldShowMessage: $.observable(true) // Message initially visible
                };
                $.applyBindings(viewModel);
                setTimeout(function(){
                    viewModel.shouldShowMessage(false); // ... now it's hidden
                },1000)
                setTimeout(function(){
                    viewModel.shouldShowMessage(true); // ... now it's visible again
                },2000)
            });
        </script>

MVVM模式的实践——avalon模块

JS代码

           $.require("ready,more/avalon", function(){

                $.applyBindings({
                    people: [
                        { firstName: 'Bert', lastName: 'Bertington' },
                        { firstName: 'Charles', lastName: 'Charlesforth' },
                        { firstName: 'Denise', lastName: 'Dentiste' }
                    ]
                });
            
            })

对应HTML代码:

       <ul data-bind="foreach: people" id="ddd">
            <li data-bind="text: $index+firstName">
                XXXX
            </li>
        </ul>

最后页面会变成这样:

  • 0Bert
  • 1Charles
  • 2Denise

发现没啥人气啊

你这个到底是啥东西,说得迷迷糊糊的,感觉不出来你这个有啥啊

在IE下取得元素的属性值

      "@ie:get": function( node, name ){
            //固有属性
            var str = node.outerHTML.replace(node.innerHTML, "")
            var re = /\s+([\w-]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g, obj = {}, t;
            while (t = re.exec(str)) { //属性值只有双引号与无引号的情况
                if(t[2].charAt(0) == '"'){//自定义属性
                     obj[ t[1].toLowerCase() ] =  t[2].slice(1, -1)
                }else{  //固有属性
                     obj[ t[1] ] = t[2] || ""
                }
            }
            return obj[ name ];
        },

toggle 事件升级

每来是这样的

toggle: function(/*fn1,fn2,fn3*/){
            var fns = Array.apply([],arguments), i = 0;
            return this.click(function(e){
                var fn  = fns[i++] || fns[i = 0, i++];
                fn.call( this, e );
            })
        },

这些切换用的点击事件是一次性的,不能添加新的切换用点击事件,现改成以下:

        toggle: function(/*fn1,fn2,fn3*/){
            var fns = Array.apply([],arguments)
            return this.each(function( el ){
                var array =  $._data(el, "toggle_click");
                if(!array){
                    array =  $._data(el, "toggle_click",fns.concat())
                    $(el).click(function(e){
                        var fn =  array.shift();
                        fn.call(el, e)
                        array.push(fn)
                    })
                }else{
                    $.Array.merge(array,fns)
                }
            })
        },

模块标识

本文将介绍模块加载系统最重要也是最有用的东西——模块标识。亦有人称之为模块ID,目的是通过它找到磁盘中的JS文件或CSS文件。

模块标识是用$.require与define方法中

模块有以下几种形式

  1. 带协议开头的,如http: https: about: file:等完整URL,框架就会直接拿它加载JS或CSS文件
  2. 相对于根目录的,这里的根目录不是指网站的根目录(虚拟目录,当然你可以通过配置手段指定为它),它默认是指引用框架种子模块(mass.js)的那个目录,以“/”结束 。在框架中我们可以通过$.core.base访问到该值。相对于根目录的模块标识是指不以协议开头,不以“/”开头,不以“.”开头,并且不是别名的字符串,如"more/store",那么
$.require("more/store", fn)// 相当访问 $.core.base + "more/stroe" +".js"

3 相对兄弟模块,以“./”开头,比如aaa.js与bbb.js是在同一目录,我们在aaa.js中访问bbb.js,只要

define("aaa", ["./bbb"], fn)/

4相对父模块,以“../”开头,可以不断往上找,加多个“../../aaa.js”

5别名模块标识。这个类似于node.js的原生模块的概念,方便随处访问。mass Framework是强制以$开头,并且不能带特殊字符串,长度大于2的字符串。这个东西,有利于当我们在把一整个目录到别处,位于其他目录的模块可以不改一个字符也能访问能他们。在mass Framework中,所有核心模块都拥有别名。如果你的模块非常成熟,并且许多模块都依赖于它,建议把它做成别名模块。而且这东西也有利我们进行文件合并。我们可以通过$.core.alias对象访问到它们。

avalon 新版浏览器兼容方面的小问题

使用avalon 新版本,在各种浏览器下测试,发现如下几个小问题:
1、IE6下,offset() 方法获取的位置不准。
2、ms-mouseenter 或 ms-mouseleave 事件在 chrome 下触发不准确。

测试页面如下:
< html xmlns="http://www.w3.org/1999/xhtml" >
< head >
< meta http-equiv="Content-Type" content="text/html; charset=utf-8" / >
< title >ie6 test< /title >
< style type="text/css" >
< !--
#list {
border: 1px solid #0000FF;
width: 100px;
right: 20px;
line-height: 30px;
float: right;
margin-top: 20px;
}
#msg {
float: left;
width: 200px;
}
-- >
< /style >
< script src="js/avalon.js" >< /script >
< /head >

< body ms-controller="body" >
< div id="msg" >
X:{{x}} Y:{{y}} < br/ >
mouseIn: {{inli}}
< /div >
< div id="list" >
< ul ms-each-ll="list" >
< li ms-mouseenter="mouseEnter" ms-mouseleave="mouseLeave" >{{ll.title}}< /li >
< /ul >
< /div >

< script >
avalon.ready(function() {
avalon.define("body", function(vm) {
vm.x = 0;
vm.y = 0;
vm.inli = "false";

    vm.list = [{title:"111111"},{title: "222222"},{title: "333333"},{title: "444444"},{title: "555555"}];
    vm.mouseEnter = function(e) {
        vm.inli = "true";
        var offset = avalon(e.currentTarget).offset();
        vm.x = offset.left;
        vm.y = offset.top;
    }
    vm.mouseLeave = function(e) {
        vm.inli = "false";
    }
});
avalon.scan();

});
< /script >
< /body >
< /html >

关于原生API与类库框架

打个比方,原生API相当于沙石,像prototype.js ,jquery等基础库相当于提供了砖瓦,
而框架的一些插件(特指存在界面的)提供了地板,窗户,烟囱,
像YUI,EXT,dojo,它们的UI库则是一个个未装修的房子。
对于用户而言,越是接近成品的东西就越受欢迎。
像应用与游戏就是一一个精装房与公园与王国。

保存avalon v6的parser

新的版本将不支持严格模式

//将绑定属性的值或插值表达式里面部分转换一个函数compileFn,里面或包含ViewModel的某些属性
    //而它们分分种都是setter, getter,成为双向绑定链的一部分
    var regEscape = /([-.*+?^${}()|[\]\/\\])/g;

    function escapeRegExp(target) {
        //将字符串安全格式化为正则表达式的源码
        return target.replace(regEscape, "\\$1");
    }
    var isStrict = (function() {
        return !this;
    })();

    function insertScopeNameBeforeVariableName(e, text, scopes, names, args, random) {
        var ok = false;
        if (W3C) { //判定是否IE9-11或者为标准浏览器
            ok = e instanceof ReferenceError;
        } else {
            ok = e instanceof TypeError;
        }
        //opera9.61
        //message: Statement on line 810: Undefined variable: nickName
        //opera12
        //Undefined variable: nickName
        //safari 5
        //Can't find variable: nickName
        //firefox3-20  chrome
        //ReferenceError: nickName is not defined
        //IE10
        //“nickName”未定义 
        //IE6 
        //'eee' 未定义 
        if (ok) {
            if (window.opera) {
                var varName = e.message.split("Undefined variable: ")[1];
            } else {
                varName = e.message.replace("Can't find variable: ", "")
                        .replace("“", "").replace("'", "");
            }
            varName = (varName.match(/^[\w$]+/) || [""])[0]; //取得未定义的变量名
            for (var i = 0, scope; scope = scopes[i++]; ) {
                if (scope.hasOwnProperty(varName)) {
                    var scopeName = scope.$id + random;
                    if (names.indexOf(scopeName) === -1) {
                        names.push(scopeName);
                        args.push(scope);
                    }
                    //这里实际还要做更严格的处理
                    var reg = new RegExp("(^|[^\\w\\u00c0-\\uFFFF_])(" + escapeRegExp(varName) + ")($|[^\\w\\u00c0-\\uFFFF_])", "g");
                    return text.replace(reg, function(a, b, c, d) {
                        return b + scopeName + "." + c + d; //添加作用域
                    });
                }
            }

        }
    }
    var doubleQuotedString = /"([^\\"\n]|\\.)*"/g;
    var singleQuotedString = /'([^\\'\n]|\\.)*'/g;

    function parseExpr(text, scopeList, data) {
        var names = [],
                args = [],
                random = new Date - 0,
                val;
        if (!isStrict) { //如果不是严格模式
            //取得ViewModel的名字
            scopeList.forEach(function(scope) {
                var scopeName = scope.$id + "" + random;
                if (names.indexOf(scopeName) === -1) {
                    names.push(scopeName);
                    args.push(scope);
                }
            });
            text = "var ret" + random + " = " + text + "\r\n";
            for (var i = 0, name; name = names[i++]; ) {
                text = "with(" + name + "){\r\n" + text + "\r\n}\r\n";
            }
        } else {
            var singleFix = random + 1;
            var doubleFix = singleFix + 1;
            var singleHolder = [];
            var doubleHolder = [];
            var loop = true;
            //抽取掉所有字符串
            text = text.replace(singleQuotedString, function(a) {
                singleHolder.push(a);
                return singleFix;
            }).replace(doubleQuotedString, function(b) {
                doubleHolder.push(b);
                return doubleFix;
            });
            do { //开始循环
                try {
                    var fn = Function.apply(Function, names.concat("return " + text));
                    var val = fn.apply(fn, args);
                    loop = false;
                } catch (e) {
                    text = insertScopeNameBeforeVariableName(e, text, scopeList, names, args, random);
                    loop = typeof text === "string";
                }
            } while (loop);
            if (text) {
                if (singleHolder.length) {
                    text = text.replace(new RegExp(singleFix, "g"), function() {
                        return singleHolder.shift();
                    });
                }
                if (doubleHolder.length) {
                    text = text.replace(new RegExp(doubleFix, "g"), function() {
                        return doubleHolder.shift();
                    });
                }
                text = "var ret" + random + " = " + text;
            } else {
                data.compileFn = function() {
                    return "";
                };
                return "";
            }
        }
        if (data.filters) {
            var textBuffer = [],
                    fargs;
            textBuffer.push(text, "\r\n");
            for (var i = 0, f; f = data.filters[i++]; ) {
                var start = f.indexOf("(");
                if (start !== -1) {
                    fargs = f.slice(start + 1, f.lastIndexOf(")")).trim();
                    fargs = "," + fargs;
                    f = f.slice(0, start).trim();
                } else {
                    fargs = "";
                }
                textBuffer.push(" if(filters", random, ".", f, "){\r\n\ttry{ret", random,
                        " = filters", random, ".", f, "(ret", random, fargs, ")}catch(e){};\r\n}\r\n");
            }
            text = textBuffer.join("");
            names.push("filters" + random);
            args.push(avalon.filters);
            delete data.filters; //释放内存
        }
        data.compileArgs = args;
        try {
            text += "\r\nreturn ret" + random;
            var fn = Function.apply(Function, names.concat(text));
            val = fn.apply(fn, args);
            data.compileFn = fn; //缓存,防止二次编译
        } catch (e) {
            data.remove = false;
            data.compileFn = function() {
                return "";
            };
            val = "";
        }
        textBuffer = names = null; //释放内存
        return val;
    }

readme.md中的文档链接失效了

readme.md中下面这一行的文档链接失效了,点击链接出现404.

它大部分文档已经转移到newland.js项目之下,我们可以在这里访问得到它!

模块的定义

遵循AMD定义方式

define(id? , deps?  factory)

第一个是模块名或叫模块ID,字符串,可省略
第二个依赖列表,为数组,可省略,如果第二个参数是布尔,并且为true,它省去进一步加载此模块,这只用于补丁模块
第三个是模块工厂,不能为undefine,一般为函数,但可以是对象

define({}) 

define([],function(){}) 

define(function(){}) 

define("aaa",[],function(){}) 

 

mass Framework的模块建议写成这样

//=========================================
// 类工厂模块 by 司徒正美
//==========================================
define("class", ["lang"], function(){
$.log("已加载类工厂模块",7)
var
unextend = $.oneObject(["_super","prototype", 'extend', 'implement' ]),
rconst = /constructor|_init|_super/,
classOne = $.oneObject('Object,Array,Function');
//......略
})
/**
change log:
2011.7.11 将$["class"]改为$["@class"] v4
2011.7.25
继承链与方法链被重新实现。
在方法中调用父类的同名实例方法,由$super改为supermethod,保留父类的原型属性parent改为superclass v5
2011.8.6
在方法中调用父类的同名实例方法,由supermethod改为_super,保留父类的原型属性superclass改为_super v6
重新实现方法链
fix 子类实例不是父类的实例的bug
2011.8.14 更改隐藏namespace,增强setOptions
2011.10.7 include更名为implement 修复implement的BUG(能让人重写toString valueOf方法) v7
2012.1.29 修正setOptions中$.Object.merge方法的调用方式
2012.2.25 改进setOptions,可以指定在this上扩展还是在this.XXX上扩展
2012.2.26 重新实现方法链,抛弃arguments.callee.caller v8
2012.7.22 v10 大幅简化,去掉defineProperty与方法链
*/

上面注明模块名,作者,其他贡献者,必要的注释,解释它是干什么的

 $.log("已加载类工厂模块",7)

是方便我们在调试时打印看的,平时我们把$.log.level调到5就看不到这些调试信息

最下方是更改日志

清明节后大升级

代码的重构是永无止境,你总会发现更好的写法。

蓦然回首,发现ecma, target, dispatcher模块已经不见了,取而代之,是时刻准备抛弃的补丁模块(lang_fix,css_fix,event_fix)。
就像是热气球吊篮上的碇子,没有它们,气球会升得更高。

这一轮重构中,语言链对象实现得更加漂亮,事件系统变得更轻快,数据缓存模块支持HTML5的data-*属性,AJAX模块支持XMLHttpRequest Level2

avalon v4的早期实现:

define("mvvm", "$event,$css,$attr".split(","), function($) {

    var prefix = "ms-";
    var avalon = $.avalon = {
        models: {},
        filters: {
            uppercase: function(str) {
                return str.toUpperCase()
            },
            lowercase: function(str) {
                return str.toLowerCase();
            },
            number: function(str) {
                return isFinite(str) ? str : "";
            },
            aaa: function(str) {
                return str + "AAA"
            }
        }
    };
    var blank = " ";
    var obsevers = {};
    var Publish = {};//将函数放到发布对象上,让依赖它的函数
    var expando = new Date - 0;
    var subscribers = "$" + expando;
    /*********************************************************************
     *                            View                                    *
     **********************************************************************/
    var regOpenTag = /([^{]*)\{\{/;
    var regCloseTag = /([^}]*)\}\}/;
    function hasExpr(value) {
        var index = value.indexOf("{{");
        return index !== -1 && index < value.indexOf("}}");
    }
    function forEach(obj, fn) {
        if (obj) {//不能传个null, undefined进来
            var isArray = isFinite(obj.length), i = 0
            if (isArray) {
                for (var n = obj.length; i < n; i++) {
                    fn(i, obj[i]);
                }
            } else {
                for (i in obj) {
                    if (obj.hasOwnProperty(i)) {
                        fn(i, obj[i]);
                    }
                }
            }
        }
    }
    //eval一个或多个表达式
    function watchView(text, scope, scopes, data, callback, tokens) {
        var updateView, target, filters = data.filters;
        var scopeList = [scope].concat(scopes);
        if (!filters) {
            for (var i = 0, obj; obj = scopeList[i++]; ) {
                if (obj.hasOwnProperty(text)) {
                    target = obj;//如果能在作用域上直接找到,我们就不需要eval了
                    break;
                }
            }
        }
        if (target) {
            updateView = function() {
                callback(target[text]);
            };
        } else {
            updateView = function() {

                if (tokens) {
                    var val = tokens.map(function(obj) {
                        return obj.expr ? evalExpr(obj.value, scopeList, data) : obj.value;
                    }).join("");
                } else {
                    val = evalExpr(text, scopeList, data);
                }

                callback(val);
            };
        }
        Publish[ expando ] = updateView;
        updateView();
        delete  Publish[ expando ];
    }
    function evalExpr(text, scopeList, data) {
        console.log(text)
        var uniq = {
            $occoecatio: 1
        }, names = [], args = [];
        
        scopeList.forEach(function(scope) {
            scope.$occoecatio = true;
            forEach(scope, function(key, val) {
                if (!uniq[key]) {
                    names.push(key);
                    args.push(val);
                    uniq[key] = 1;
                }
            });
            delete scope.$occoecatio;
        });
      
        if (data.compileFn) {
            console.log(data.compileFn+"")
            args.push(avalon.filters)
            return data.compileFn.apply(data.compileFn, args);
        }
        if (data.filters) {
            var random = new Date - 0, textBuffer = [], fargs;
            textBuffer.push("var ret", random, "=", text, "\r\n");
            for (var i = 0, f; f = data.filters[i++]; ) {
                var start = f.indexOf("(");
                if (start !== -1) {
                    fargs = f.slice(start + 1, f.lastIndexOf(")")).trim();
                    fargs = "," + fargs;
                    f = f.slice(0, start).trim();
                } else {
                    fargs = "";
                }
                textBuffer.push(" if(filters", random, ".", f, "){\r\n\ttry{ret", random,
                        " = filters", random, ".", f, "(ret", random, fargs, ")}catch(e){};\r\n}\r\n");
            }
            textBuffer.push("\treturn ret", random);
            text = textBuffer.join("");
            names.push("filters" + random);
            args.push(avalon.filters);
            delete data.filters;//释放内存
        } else {
            text = "\treturn " + text;
        }
        try {
            var fn = Function.apply(Function, names.concat(text));
            var val = fn.apply(fn, args);
            data.compileFn = fn;//缓存,防止二次编译
        } catch (e) {
            data.compileFn = function() {
                return "";
            };
            val = "";
        }
        uniq = textBuffer = names = null;//释放内存
        return val;
    }

    var bindingHandlers = avalon.bindingHandlers = {
        //将模型中的字段与input, textarea的value值关联在一起
        "model": function(data, scope, scopes) {
            var element = data.element;
            var tagName = element.tagName;
            if (typeof  modelBinding[tagName] === "function") {
                var array = [scope].concat(scopes);
                var name = data.node.value, model;
                array.forEach(function(obj) {
                    if (!model && obj.hasOwnProperty(name)) {
                        model = obj;
                    }
                });
                model = model || {};
                modelBinding[tagName](element, model, name);
            }
        },
        //抽取innerText中插入表达式,置换成真实数据放在它原来的位置
        //<div>{{firstName}} + java</div>,如果model.firstName为ruby, 那么变成
        //<div>ruby + java</div>
        "text": function(data, scope, scopes) {
            var node = data.node;
            watchView(data.value, scope, scopes, data, function(val) {
                node.nodeValue = val;
            });
        },
        //控制元素显示或隐藏
        "toggle": function(data, scope, scopes) {
            var element = $(data.element);
            watchView(data.value, scope, scopes, data, function(val) {
                element.toggle(!!val);
            });
        },
        //这是一个字符串属性绑定的范本, 方便你在title, alt,  src, href添加插值表达式
        //<a href="{{url.hostname}}/{{url.pathname}}.html">
        "href": function(data, scope, scopes) {
            //如果没有则说明是使用ng-href的形式
            var text = data.value.trim();
            var node = data.node;
            var simple = node.name.indexOf(prefix) === 0;
            var name = data.type;
            if (!simple && /^\{\{([^}]+)\}\}$/.test(text)) {
                simple = true;
                text = RegExp.$1;
            }
            watchView(text, scope, scopes, data, function(val) {
                data.element[name] = val;
            }, simple ? null : scanExpr(data.value));
        },
        //这是一个布尔属性绑定的范本,布尔属性插值要求整个都是一个插值表达式,用{{}}包起来
        //布尔属性在IE下无法取得原来的字符串值,变成一个布尔,因此需要用ng-disabled
        //text.slice(2, text.lastIndexOf("}}"))
        "disabled": function(data, scope, scopes) {
            var element = data.element, name = data.type,
                    propName = $.propMap[name] || name;
            watchView(data.value, scope, scopes, data, function(val) {
                element[propName] = !!val;
            });
        },
        //切换类名,有三种形式
        //1、ms-class-xxx="flag" 根据flag的值决定是添加或删除类名xxx 
        //2、ms-class=obj obj为一个{xxx:true, yyy:false}的对象,根据其值添加或删除其键名
        //3、ms-class=str str是一个类名或多个类名的集合,全部添加
        //http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html
        "class": function(data, scope, scopes) {
            var element = $(data.element);
            watchView(data.value, scope, scopes, data, function(val) {
                if (data.args) {//第一种形式
                    element.toggleClass(data.args.join(""), !!val);
                } else if (typeof val === "string") {
                    element.addClass(val);
                } else if (val && typeof val === "object") {
                    forEach(val, function(cls, flag) {
                        if (flag) {
                            element.addClass(cls);
                        } else {
                            element.removeClass(cls);
                        }
                    });
                }
            });
        },
        //控制流程绑定
        "skip": function() {
            arguments[3].stopBinding = true;
        },
        "if": function(data, scope, scopes) {
            var element = data.element;
            var fragment = element.ownerDocument.createDocumentFragment();
            watchView(data.value, scope, scopes, data, function(val) {
                if (val) {
                    while (fragment.firstChild) {
                        element.appendChild(fragment.firstChild);
                    }
                } else {
                    while (element.firstChild) {
                        fragment.appendChild(element.firstChild);
                    }
                }
            });

        },
        "each": function(data, scope, scopes, flags) {
            var args = data.args, itemName = args[0] || "$data", indexName = args[1] || "$index";
            var parent = data.element;
            var scopeList = [scope].concat(scopes);
            var list = evalExpr(data.value, scopeList, data);
            var doc = parent.ownerDocument;
            var fragment = doc.createDocumentFragment();
            while (parent.firstChild) {
                fragment.appendChild(parent.firstChild);
            }
            function updateListView(method, args, len) {
                var listName = list.name;
                switch (method) {
                    case "push":
                        $.each(args, function(index, item) {
                            updateView(len + index, item);
                        });
                        break;
                    case "unshift"  :
                        list.insertBefore = parent.firstChild;
                        $.each(args, function(index, item) {
                            updateView(index, item);
                        });
                        resetIndex(parent, listName);
                        delete list.insertBefore;
                        break;
                    case "pop":
                        var node = findIndex(parent, listName, len - 1);
                        if (node) {
                            removeView(parent, listName, node);
                        }
                        break;
                    case "shift":
                        removeView(parent, listName, 0, parent.firstChild);
                        resetIndex(parent, listName);
                        break;
                    case "clear":
                        while (parent.firstChild) {
                            parent.removeChild(parent.firstChild);
                        }
                        break;
                    case "splice":
                        var start = args[0], second = args[1], adds = [].slice.call(args, 2);
                        var deleteCount = second >= 0 ? second : len - start;
                        var node = findIndex(parent, listName, start);
                        if (node) {
                            removeViews(parent, listName, node, deleteCount);
                            resetIndex(parent, listName);
                            if (adds.length) {
                                node = findIndex(parent, listName, start);
                                list.insertBefore = node;
                                $.each(adds, function(index, item) {
                                    updateView(index, item);
                                });
                                resetIndex(parent, listName);
                                delete list.insertBefore;
                            }
                        }
                        break;
                    case "reverse":
                    case "sort":
                        while (parent.firstChild) {
                            parent.removeChild(parent.firstChild);
                        }
                        $.each(list, function(index, item) {
                            updateView(index, item);
                        });
                        break;
                }
            }
            var isList = Array.isArray(list[ subscribers ] || {});
            if (isList) {
                list[ subscribers ].push(updateListView);
            }

            function updateView(index, item, clone, insertBefore) {
                var newScope = {}, textNodes = [];
                newScope[itemName] = item;
                newScope[indexName] = index;
                if (isList) {
                    var comment = doc.createComment(list.name + index);
                    if (list.insertBefore) {
                        parent.insertBefore(comment, list.insertBefore);
                    } else {
                        parent.appendChild(comment);
                    }
                }
                for (var node = fragment.firstChild; node; node = node.nextSibling) {
                    clone = node.cloneNode(true);
                    if (clone.nodeType === 1) {
                        scanTag(clone, newScope, scopeList, doc);//扫描元素节点
                    } else if (clone.nodeType === 3) {
                        textNodes.push(clone);
                    }
                    if (list.insertBefore) {
                        parent.insertBefore(clone, list.insertBefore);
                    } else {
                        parent.appendChild(clone);
                    }
                }
                for (var i = 0; node = textNodes[i++]; ) {
                    scanText(node, newScope, scopeList, doc);//扫描文本节点
                }
            }
            forEach(list, updateView);
            flags.stopBinding = true;
        }
    };
    //重置所有路标
    function resetIndex(elem, name) {
        var index = 0;
        for (var node = elem.firstChild; node; node = node.nextSibling) {
            if (node.nodeType === 8) {
                if (node.nodeValue.indexOf(name) === 0) {
                    if (node.nodeValue !== name + index) {
                        node.nodeValue = name + index;
                    }
                    index++;
                }
            }
        }
    }
    function removeView(elem, name, node) {
        var nodes = [], doc = elem.ownerDocument, view = doc.createDocumentFragment();
        for (var check = node; check; check = check.nextSibling) {
            //如果到达下一个路标,则断开,将收集到的节点放到文档碎片与下一个路标返回
            if (check.nodeType === 8 && check.nodeValue.indexOf(name) === 0
                    && check !== node) {
                break
            }
            nodes.push(check);
        }
        for (var i = 0; node = nodes[i++]; ) {
            view.appendChild(node);
        }
        return [view, check];
    }
    function removeViews(elem, name, node, number) {
        var ret = [];
        do {
            var array = removeView(elem, name, node);
            if (array[1]) {
                node = array[1];
                ret.push(array[0]);
            } else {
                break
            }
        } while (ret.length !== number);
        return ret;
    }
    function findIndex(elem, name, target) {
        var index = 0;
        for (var node = elem.firstChild; node; node = node.nextSibling) {
            if (node.nodeType === 8) {
                if (node.nodeValue.indexOf(name) === 0) {
                    if (node.nodeValue == name + target) {
                        return node;
                    }
                    index++;
                }
            }
        }
    }
    //循环绑定其他布尔属性
    var bools = "autofocus,autoplay,async,checked,controls,declare,defer,"
            + "contenteditable,ismap,loop,multiple,noshade,open,noresize,readonly,selected";
    bools.replace($.rword, function(name) {
        bindingHandlers[name] = bindingHandlers.disabled;
    });
    //建议不要直接在src属性上修改,因此这样会发出无效的请求,使用ms-src
    "title, alt, src".replace($.rword, function(name) {
        bindingHandlers[name] = bindingHandlers.href;
    });

    var modelBinding = bindingHandlers.model;
    //如果一个input标签添加了model绑定。那么它对应的字段将与元素的value连结在一起
    //字段变,value就变;value变,字段也跟着变。默认是绑定input事件,
    //我们也可以使用ng-event="change"改成change事件
    modelBinding.INPUT = function(element, model, name) {
        if (element.name === void 0) {
            element.name = name;
        }
        var type = element.type, ok;
        function updateModel() {
            model[name] = element.value;
        }
        function updateView() {
            element.value = model[name];
        }
        if (/^(password|textarea|text)$/.test(type)) {
            ok = true;
            updateModel = function() {
                model[name] = element.value;
            };
            updateView = function() {
                element.value = model[name];
            };
            var event = element.attributes[prefix + "event"] || {};
            event = event.value;
            if (event === "change") {
                $.bind(element, event, updateModel);
            } else {
                if (window.addEventListener) { //先执行W3C
                    element.addEventListener("input", updateModel, false);
                } else {
                    element.attachEvent("onpropertychange", updateModel);
                }
                if (window.VBArray && window.addEventListener) { //IE9
                    element.attachEvent("onkeydown", function(e) {
                        var key = e.keyCode;
                        if (key === 8 || key === 46) {
                            updateModel(); //处理回退与删除
                        }
                    });
                    element.attachEvent("oncut", updateModel); //处理粘贴
                }
            }

        } else if (type === "radio") {
            ok = true;
            updateView = function() {
                element.checked = model[name] === element.value;
            };
            $.bind(element, "click", updateModel);//IE6-8
        } else if (type === "checkbox") {
            ok = true;
            updateModel = function() {
                if (element.checked) {
                    $.Array.ensure(model[name], element.value);
                } else {
                    $.Array.remove(model[name], element.value);
                }
            };
            updateView = function() {
                element.checked = !!~model[name].indexOf(element.value);
            };
            $.bind(element, "click", updateModel);//IE6-8
        }
        Publish[ expando ] = updateView;
        updateView();
        delete Publish[ expando ];
    };
    modelBinding.SELECT = function(element, model, name) {
        var select = $(element);
        function updateModel() {
            model[name] = select.val();
        }
        function updateView() {
            select.val(model[name]);
        }
        $.bind(element, "change", updateModel);
        Publish[ expando ] = updateView;
        updateView();
        delete Publish[ expando ];
    };
    modelBinding.TEXTAREA = modelBinding.INPUT;
    /*********************************************************************
     *                    Collection                                    *
     **********************************************************************/
    //http://msdn.microsoft.com/en-us/library/windows/apps/hh700774.aspx
    //http://msdn.microsoft.com/zh-cn/magazine/jj651576.aspx
    //Data bindings 数据/界面绑定
    //Compatibility 兼容其他
    //Extensibility 可扩充性
    //No direct DOM manipulations 不直接对DOM操作
    function Collection(list, name) {
        var collection = list.concat();
        collection[ subscribers ] = [];
        collection.name = "#" + name;
        String("push,pop,shift,unshift,splice,sort,reverse").replace($.rword, function(method) {
            var nativeMethod = collection[ method ];
            collection[ method ] = function() {
                var len = this.length;
                var ret = nativeMethod.apply(this, arguments);
                notifySubscribers(this, method, arguments, len);
                return ret;
            };
        });
        collection.clear = function() {
            this.length = 0;
            notifySubscribers(this, "clear", []);
            return this;
        };
        collection.sortBy = function(fn, scope) {
            var ret = $.Array.sortBy(this, fn, scope);
            notifySubscribers(this, "sort", []);
            return ret;
        };
        collection.ensure = function(el) {
            var len = this.length;
            var ret = $.Array.ensure(this, el);
            if (this.length > len) {
                notifySubscribers(this, "push", [el], len);
            }
            return ret;
        };
        collection.update = function() {//强制刷新页面
            notifySubscribers(this, "sort", []);
            return this;
        };
        collection.removeAt = function(index) {//移除指定索引上的元素
            this.splice(index, 1);
        };
        collection.remove = function(item) {//移除第一个等于给定值的元素
            var index = this.indexOf(item);
            if (index !== -1) {
                this.removeAt(index);
            }
        };
        return collection;
    }
    /*********************************************************************
     *                            Subscription                           *
     **********************************************************************/
    /*
     为简单起见,我们把双向绑定链分成三层, 顶层, 中层, 底层。顶层是updateView, updateListView等需要撷取底层的值来更新自身的局部刷新函数, 中层是监控数组与依赖于其他属性的计算监控属性,底层是监控属性。高层总是依赖于低层,但高层该如何知道它是依赖哪些底层呢?
     
     在emberjs中,作为计算监控属性的fullName通过property方法,得知自己是依赖于firstName, lastName。
     App.Person = Ember.Object.extend({
     firstName: null,
     lastName: null,
     
     fullName: function() {
     return this.get('firstName') +
     " " + this.get('lastName');
     }.property('firstName', 'lastName')
     });
     
     在knockout中,用了一个取巧方法,将所有要监控的属性转换为一个函数。当fullName第一次求值时,它将自己的名字放到一个地方X,值为一个数组。然后函数体内的firstName与lastName在自身求值时,也会访问X,发现上面有数组时,就放进去。当fullName执行完毕,就得知它依赖于哪个了,并从X删掉数组。
     var ViewModel = function(first, last) {
     this.firstName = ko.observable(first);
     this.lastName = ko.observable(last);
     
     this.fullName = ko.computed(function() {
     // Knockout tracks dependencies automatically. It knows that fullName depends on firstName and lastName, because these get called when evaluating fullName.
     return this.firstName() + " " + this.lastName();
     }, this);
     };
     详见 subscribables/observable.js subscribables/dependentObservable.js
     
     */
    //http://www.cnblogs.com/whitewolf/archive/2012/07/07/2580630.html
    function getSubscribers(accessor) {
        if (typeof accessor === "string") {
            return obsevers[accessor] || (obsevers[accessor] = []);
        } else {
            return accessor[ subscribers ];
        }
    }
    function collectSubscribers(accessor) {//收集依赖于这个域的函数
        if (Publish[ expando ]) {
            var list = getSubscribers(accessor);
            $.Array.ensure(list, Publish[ expando ]);
        }
    }
    function notifySubscribers(accessor) {//通知依赖于这个域的函数们更新自身
        var list = getSubscribers(accessor);
        if (list && list.length) {
            var args = [].slice.call(arguments, 1);
            var safelist = list.concat();
            for (var i = 0, fn; fn = safelist[i++]; ) {
                if (typeof fn === "function") {
                    fn.apply(0, args); //强制重新计算自身
                }
            }
        }
    }
    /*********************************************************************
     *                            Model                                   *
     **********************************************************************/
    $.model = function(name, obj) {
        name = name || "root";
        if (avalon.models[name]) {
            $.error('已经存在"' + name + '"模块');
        } else {
            var model = modelFactory(name, obj, $.skipArray || []);
            model.$modelName = name;
            return avalon.models[name] = model
        }
    };
    var startWithDollar = /^\$/;
    function modelFactory(name, obj, skipArray) {
        var model = {}, first = [], second = [];
        forEach(obj, function(key, val) {
            //如果不在忽略列表内,并且没有以$开头($开头的属性名留着框架使用)
            if (skipArray.indexOf(key) === -1 && !startWithDollar.test(key)) {
                //相依赖的computed
                var accessor = name + key, old;
                if (Array.isArray(val) && !val[subscribers]) {
                    model[key] = Collection(val, accessor);
                } else if (typeof val === "object") {
                    if ("set" in val && Object.keys(val).length <= 2) {
                        Object.defineProperty(model, key, {
                            set: function(neo) {
                                if (typeof val.set === "function") {
                                    val.set.call(model, neo); //通知底层改变
                                } else {
                                    obj[key] = neo;
                                }
                                if (old !== neo) {
                                    old = neo;
                                    notifySubscribers(accessor); //通知顶层改变
                                }
                            },
                            //get方法肯定存在,那么肯定在这里告诉它的依赖,把它的setter放到依赖的订阅列表中
                            get: function() {
                                var flagDelete = false;
                                if (!obsevers[accessor]) {
                                    flagDelete = true;
                                    Publish[ expando ] = function() {
                                        notifySubscribers(accessor); //通知顶层改变
                                    };
                                    obsevers[accessor] = [];
                                }
                                old = val.get.call(model);
                                obj[name] = old;
                                if (flagDelete) {
                                    delete Publish[ expando ];
                                }
                                return old;
                            },
                            enumerable: true
                        });
                        second.push(key);
                    } else {

                    }
                } else if (typeof val !== "function") {
                    Object.defineProperty(model, key, {
                        set: function(neo) {
                            if (obj[key] !== neo) {
                                obj[key] = neo;
                                //通知中层,顶层改变
                                notifySubscribers(accessor);
                            }
                        },
                        get: function() {
                            //如果中层把方法放在Publish[ expando ]中
                            if (!obj.$occoecatio){//为了防止它在不合适的时候收集订阅者,添加$occoecatio标识让它瞎掉
                                collectSubscribers(accessor);
                            }
                            
                            return obj[key];
                        },
                        enumerable: true
                    });
                    first.push(key);
                }
            }
        });
        first.forEach(function(key) {
            model[key] = obj[key];
        });
        second.forEach(function(key) {
            first = model[key];
        });
        return  model;
    }
    /*********************************************************************
     *                           Scan                                     *
     **********************************************************************/
    function scanTag(elem, scope, scopes, doc) {
        scopes = scopes || [];
        var flags = {};
        scanAttr(elem, scope, scopes, flags);//扫描特点节点
        if (flags.stopBinding) {//是否要停止扫描
            return false;
        }
        if (flags.newScope) {//更换作用域, 复制父作用域堆栈,防止互相影响
            scopes = scopes.slice(0);
            scope = flags.newScope;
        }
        if (elem.canHaveChildren === false || !stopScan[elem.tagName]) {
            var textNodes = [];
            for (var node = elem.firstChild; node; node = node.nextSibling) {
                if (node.nodeType === 1) {
                    scanTag(node, scope, scopes, doc);//扫描元素节点
                } else if (node.nodeType === 3) {
                    textNodes.push(node);
                }
            }
            for (var i = 0; node = textNodes[i++]; ) {//延后执行
                scanText(node, scope, scopes, doc);//扫描文本节点
            }
        }
    }
    var stopScan = $.oneObject("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,wbr,script,style".toUpperCase());
    //扫描元素节点中直属的文本节点,并进行抽取
    function scanText(textNode, scope, scopes, doc) {
        var bindings = extractTextBindings(textNode, doc);
        if (bindings.length) {
            executeBindings(bindings, scope, scopes);
        }
    }
    function scanExpr(value) {
        var tokens = [];
        if (hasExpr(value)) {
            //抽取{{ }} 里面的语句,并以它们为定界符,拆分原来的文本
            do {
                value = value.replace(regOpenTag, function(a, b) {
                    if (b) {
                        tokens.push({
                            value: b,
                            expr: false
                        });
                    }
                    return "";
                });
                value = value.replace(regCloseTag, function(a, b) {
                    if (b) {
                        var filters = []
                        if (b.indexOf("|") > 0) {
                            b = b.replace(/\|\s*(\w+)\s*(\([^)]+\))?/g, function(c, d, e) {
                                filters.push(d + e)
                                return ""
                            });
                        }
                        tokens.push({
                            value: b,
                            expr: true,
                            filters: filters.length ? filters : void 0
                        });
                    }
                    return "";
                });
            } while (hasExpr(value));
            if (value) {
                tokens.push({
                    value: value,
                    expr: false
                });
            }
        }
        return tokens;
    }

    function scanAttr(el, scope, scopes, flags) {
        var bindings = [];
        for (var i = 0, attr; attr = el.attributes[i++]; ) {
            if (attr.specified) {
                var isBinding = false, remove = false;
                if (attr.name.indexOf(prefix) !== -1) {//如果是以指定前缀命名的
                    var type = attr.name.replace(prefix, "");
                    if (type.indexOf("-") > 0) {
                        var args = type.split("-");
                        type = args.shift();
                    }
                    remove = true;
                    isBinding = typeof bindingHandlers[type] === "function";
                } else if (bindingHandlers[attr.name] && hasExpr(attr.value)) {
                    type = attr.name; //如果只是普通属性,但其值是个插值表达式
                    isBinding = true;
                }
                if (isBinding) {
                    bindings.push({
                        type: type,
                        args: args,
                        element: el,
                        node: attr,
                        remove: remove,
                        value: attr.nodeValue
                    });
                }
                if (!flags.newScope && type === "controller") {//更换作用域
                    var temp = avalon.models[attr.value];
                    if (typeof temp === "object" && temp !== scope) {
                        scopes.unshift(scope);
                        flags.newScope = scope = temp;
                    }
                }
            }
        }
        executeBindings(bindings, scope, scopes, flags);
    }

    function executeBindings(bindings, scope, scopes, flags) {
        bindings.forEach(function(data) {
            bindingHandlers[data.type](data, scope, scopes, flags);
            if (data.remove) {//移除数据绑定,防止被二次解析
                data.element.removeAttribute(data.node.name);
            }
        });
    }

    function extractTextBindings(textNode, doc) {
        var bindings = [], tokens = scanExpr(textNode.nodeValue);
        if (tokens.length) {
            var fragment = doc.createDocumentFragment();
            while (tokens.length) {//将文本转换为文本节点,并替换原来的文本节点
                var token = tokens.shift();
                var node = doc.createTextNode(token.value);
                if (token.expr) {
                    bindings.push({
                        type: "text",
                        node: node,
                        element: textNode.parentNode,
                        value: token.value,
                        filters: token.filters
                    }); //收集带有插值表达式的文本
                }
                fragment.appendChild(node);
            }
            textNode.parentNode.replaceChild(fragment, textNode);
        }
        return bindings;
    }

    var model = $.model("app", {
        firstName: "xxx",
        lastName: "oooo",
        bool: false,
        array: [1, 2, 3, 4, 5, 6, 7, 8],
        select: "test1",
        color: "green",
        vehicle: ["car"],
        fullName: {
            set: function(val) {
                var array = val.split(" ");
                this.firstName = array[0] || "";
                this.lastName = array[1] || "";
            },
            get: function() {
                return this.firstName + " " + this.lastName;
            }
        }
    });
    $.model("son", {
        firstName: "yyyy"
    });
    $.model("aaa", {
        firstName: "6666"
    });
    scanTag(document.body, model, [], document);
    setTimeout(function() {
        model.firstName = "setTimeout";
    }, 2000);

    setTimeout(function() {
        model.array.reverse()
        // console.log(obsevers.applastName.join("\r\n"))
    }, 3000);
});

parseURL改进

function parseURL(url, parent, ret) {
    //[]里面,不是开头的-要转义,因此要用/^[-a-z0-9_$]{2,}$/i而不是/^[a-z0-9_-$]{2,}
    //别名至少两个字符;不用汉字是避开字符集的问题
    if (/^(mass|ready)$/.test(url)) { //特别处理ready标识符
        return [url, "js"];
    }
    if ($.config.alias[url]) {
        ret = $.config.alias[url];
    } else {
        parent = parent.substr(0, parent.lastIndexOf('/'))
        if (/^(\w+)(\d)?:.*/.test(url)) { //如果用户路径包含协议
            ret = url;
        } else {
            var tmp = url.charAt(0);
            if (tmp !== "." && tmp !== "/") { //相对于根路径
                ret = basepath + url;
            } else {
                tmp = (parent + url).split(/\/?\//g)
                var i = tmp.length
                var up = 0
                var last
                while (--i >= 0) {
                    last = tmp[i]
                    if (last === '.')
                        tmp.splice(i, 1)
                    else if (last === '..') {
                        tmp.splice(i, 1)
                        ++up;
                    }
                    else if (up) {
                        tmp.splice(i, 1)
                        --up;
                    }
                }
                ret = tmp.join('/').replace(":/", "://")
            }
        }
    }
    var ext = "js";
    tmp = ret.replace(/[?#].*/, "");
    if (/\.(css|js)$/.test(tmp)) { // 处理"http://113.93.55.202/mass.draggable"的情况
        ext = RegExp.$1;
    }
    if (ext !== "css" && tmp === ret && !/\.js$/.test(ret)) { //如果没有后缀名会补上.js
        ret += ".js";
    }
    return [ret, ext];
}

改进的灵感源于 https://github.com/curvedmark/roole/blob/master/lib/helper.js

css3 transform 2D的支持改进与实现

早在 v2时,它就加入了对旋转与放大的支持,但由于数学知识的短板一直搞出个矩阵类。而矩阵类是解决IE transform 2D的关键。从上星期决定升级CSS模块开始,就想方设法引进一个矩进类。 个人一开始很看重两个框架 Sylvester.js ,与matrix.js。但它们都太大了,最终还是决定自己搞:

function toFixed(d){
    return  d > -0.0000001 && d < 0.0000001 ? 0 : /e/.test(d+"") ? d.toFixed(7) : d
}
function rad(value) {
    if(isFinite(value)) {
        return parseFloat(value);
    }
    if(~value.indexOf("deg")) {//圆角制。
        return parseInt(value,10) * (Math.PI / 180);
    } else if (~value.indexOf("grad")) {//梯度制。一个直角的100等分之一。一个圆圈相当于400grad。
        return parseInt(value,10) * (Math.PI/200);
    }//弧度制,360=2π
    return parseFloat(value,10)
}
var Matrix = $.factory({
    init: function(rows,cols){
        this.rows = rows || 3;
        this.cols = cols || 3;
        this.set.apply(this, [].slice.call(arguments,2))
    },
    set: function(){//用于设置元素
        for(var i = 0, n = this.rows * this.cols; i < n; i++){
            this[ Math.floor(i / this.rows) +","+(i % this.rows) ] = parseFloat(arguments[i]) || 0;
        }
        return this;
    },
    get: function(){//转变成数组
        var array = [], ret = []
        for(var key in this){
            if(~key.indexOf(",")){
                array.push( key )
            }
        }
        array.sort() ;
        for(var i = 0; i < array.length; i++){
            ret[i] = this[array[i]]
        }
        return  ret ;
    },
    set2D: function(a,b,c,d,tx,ty){
        this.a =  this["0,0"] = a * 1
        this.b =  this["1,0"] = b * 1
        this.c =  this["0,1"] = c * 1
        this.d =  this["1,1"] = d * 1
        this.tx = this["2,0"] = tx * 1
        this.ty = this["2,1"] = ty * 1
        this["0,2"] = this["1,2"] = 0
        this["2,2"] = 1;
        return this;
    },
    get2D: function(){
        return "matrix("+[ this["0,0"],this["1,0"],this["0,1"],this["1,1"],this["2,0"],this["2,1"] ]+")";
    },
    cross: function(matrix){
        if(this.cols === matrix.rows){
            var ret = new Matrix(this.rows, matrix.cols);
            var n = Math.max(this.rows, matrix.cols)
            for(var key in ret){
                if(key.match(/(\d+),(\d+)/)){
                    var r = RegExp.$1, c = RegExp.$2
                    for(var i = 0; i < n; i++ ){
                        ret[key] += ( (this[r+","+i] || 0) * (matrix[i+","+c]||0 ));//X轴*Y轴
                    }
                }
            }
            for(key in this){
                if(typeof this[key] == "number"){
                    delete this[key]
                }
            }
            for(key in ret){
                if(typeof ret[key] == "number"){
                    this[key] = toFixed(ret[key])
                }
            }
            return this
        }else{
            throw "cross error: this.cols !== matrix.rows"
        }
    },
    //http://www.zweigmedia.com/RealWorld/tutorialsf1/frames3_2.html
    //http://www.w3.org/TR/SVG/coords.html#RotationDefined
    //http://www.mathamazement.com/Lessons/Pre-Calculus/08_Matrices-and-Determinants/coordinate-transformation-matrices.html
    translate: function(tx, ty) {
        tx = parseFloat(tx) || 0;//沿 x 轴平移每个点的距离。
        ty = parseFloat(ty) || 0;//沿 y 轴平移每个点的距离。
        var m = (new Matrix()).set2D(1 ,0, 0, 1, tx, ty);
        this.cross(m)
    },
    translateX: function(tx) {
        this.translate(tx, 0)
    },
    translateY: function(ty) {
        this.translate(0, ty)
    },
    scale: function(sx, sy){
        sx = isFinite(sx) ? parseFloat(sx) : 1 ;
        sy = isFinite(sy) ? parseFloat(sy) : 1 ;
        var m = (new Matrix()).set2D( sx, 0, 0, sy, 0, 0);
        this.cross(m)
    },
    scaleX: function(sx) {
        this.scale(sx, 1)
    },
    scaleY: function(sy) {
        this.scale(1, sy)
    },
    rotate: function(angle, fix){//matrix.rotate(60)==>顺时针转60度
        fix = fix === -1 ? fix : 1;
        angle = rad(angle);
        var cos = Math.cos(angle);
        var sin = Math.sin(angle);// a, b, c, d
        var m = (new Matrix()).set2D( cos,fix * sin , fix * -sin, cos, 0, 0);
        return this.cross(m)
    },
    skew: function(ax, ay){
        var xRad = rad(ax);
        var yRad;
 
        if (ay != null) {
            yRad = rad(ay)
        } else {
            yRad = xRad
        }
        var m = (new Matrix()).set2D( 1, Math.tan( xRad ), Math.tan( yRad ), 1, 0, 0);
        return this.cross(m)
    },
    skewX: function(ax){
        return this.skew(ax, 0);
    },
    skewY: function(ay){
        this.skew(0, ay);
    },
 
    // ┌       ┐┌              ┐
    // │ a c tx││  M11  -M12 tx│
    // │ b d ty││  -M21  M22 tx│
    // └       ┘└              ┘
    //http://help.adobe.com/zh_CN/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html
    //分解原始数值,得到a,b,c,e,tx,ty属性,以及返回一个包含x,y,scaleX,scaleY,skewX,skewY,rotation的对象
    decompose2D: function(){
        var ret = {}
        this.a = this["0,0"]
        this.b = this["1,0"]
        this.c = this["0,1"]
        this.d = this["1,1"]
        ret.x = this.tx = this["2,0"]
        ret.y = this.ty = this["2,1"]
 
        ret.scaleX = Math.sqrt(this.a * this.a + this.b * this.b);
        ret.scaleY = Math.sqrt(this.c * this.c + this.d * this.d);
 
        var skewX = Math.atan2(-this.c, this.d);
        var skewY = Math.atan2(this.b, this.a);
 
        if (skewX == skewY) {
            ret.rotation = skewY/Matrix.DEG_TO_RAD;
            if (this.a < 0 && this.d >= 0) {
                ret.rotation += (ret.rotation <= 0) ? 180 : -180;
            }
            ret.skewX = ret.skewY = 0;
        } else {
            ret.skewX = skewX/Matrix.DEG_TO_RAD;
            ret.skewY = skewY/Matrix.DEG_TO_RAD;
        }
        return ret;
    }
});
"translateX,translateY,scaleX,scaleY,skewX,skewY".replace($.rword, function(n){
    Matrix.prototype[n.toLowerCase()] = Matrix.prototype[n]
});
Matrix.DEG_TO_RAD = Math.PI/180;

从这个矩阵类也可以看到,乘法是最重要的,什么translate, scale, skew, rotate都是基于它。唯一不爽的是,它的元素命名有点复杂。当然这是基于乘法运算的需要。由于野心太大,既可以实现2D矩阵,也可以实现3D矩阵,4*3矩阵……在实现过程中,得知矩阵相乘还是有条件的,于是理想主义死亡了。

第二版的矩阵类很简单,就是专攻2D,名字也从$.Matrix收窄为$.Matrix2D。放弃"x,y"这样复杂的元素命名法,改用a, b, c, d, tx, ty命名。基于cross的各种API也自行代码防御与数字转换,容错性大大提高!

    function toFixed(d){//矩阵类第二版
        return  d > -0.0000001 && d < 0.0000001 ? 0 : /e/.test(d+"") ? d.toFixed(7) : d
    }
     function toFloat(d, x){
        return isFinite(d) ? d: parseFloat(d) || x || 0
    }
    //http://zh.wikipedia.org/wiki/%E7%9F%A9%E9%98%B5
    //http://help.dottoro.com/lcebdggm.php
    var Matrix2D = $.factory({
        init: function(){
            this.set.apply(this, arguments);
        },
        cross: function(a, b, c, d, tx, ty) {
            var a1 = this.a;
            var b1 = this.b;
            var c1 = this.c;
            var d1 = this.d;
            this.a  = toFixed(a*a1+b*c1);
            this.b  = toFixed(a*b1+b*d1);
            this.c  = toFixed(c*a1+d*c1);
            this.d  = toFixed(c*b1+d*d1);
            this.tx = toFixed(tx*a1+ty*c1+this.tx);
            this.ty = toFixed(tx*b1+ty*d1+this.ty);
            return this;
        },
        rotate: function( radian ) {
            var cos = Math.cos(radian);
            var sin = Math.sin(radian);
            return this.cross(cos,  sin,  -sin, cos, 0, 0)
        },
        skew: function(sx, sy) {
            return this.cross(1, Math.tan( sy ), Math.tan( sx ), 1, 0, 0);
        },
        skewX: function(radian){
            return this.skew(radian, 0);
        },
        skewY: function(radian){
            return this.skew(0, radian);
        },
        scale: function(x, y) {
            return this.cross( toFloat(x, 1) ,0, 0, toFloat(y, 1), 0, 0)
        },
        scaleX: function(x){
            return this.scale(x ,1);
        },
        scaleY: function(y){
            return this.scale(1 ,y);
        },
        translate : function(x, y) {
            return this.cross(1, 0, 0, 1, toFloat(x, 0), toFloat(x, 0) );
        },
        translateX: function(x) {
            return this.translate(x, 0);
        },
        translateY: function(y) {
            return this.translate(0, y);
        },
        toString: function(){
            return "matrix("+this.get()+")";
        },
        get: function(){
            return [this.a,this.b,this.c,this.d,this.tx,this.ty];
        },
        set: function(a, b, c, d, tx, ty){
            this.a = a * 1;
            this.b = b * 1 || 0;
            this.c = c * 1 || 0;
            this.d = d * 1;
            this.tx = tx * 1 || 0;
            this.ty = ty * 1 || 0;
            return this;
        },
        matrix:function(a, b, c, d, tx, ty){
            return this.cross(a, b, c, d, toFloat(tx), toFloat(ty))
        },
        decompose : function() {
            //分解原始数值,返回一个包含x,y,scaleX,scaleY,skewX,skewY,rotation的对象
            var ret = {};
            ret.x = this.tx;
            ret.y = this.ty;
            ret.scaleX = Math.sqrt(this.a * this.a + this.b * this.b);
            ret.scaleY = Math.sqrt(this.c * this.c + this.d * this.d);

            var skewX = Math.atan2(-this.c, this.d);
            var skewY = Math.atan2(this.b, this.a);

            if (skewX == skewY) {
                ret.rotation = skewY/ Math.PI * 180;
                if (this.a < 0 && this.d >= 0) {
                    ret.rotation += (ret.rotation <= 0) ? 180 : -180;
                }
                ret.skewX = ret.skewY = 0;
            } else {
                ret.skewX = skewX/ Math.PI * 180;
                ret.skewY = skewY/ Math.PI * 180;
            }
            return ret;
        }
    });

    $.Matrix2D = Matrix2D

第二版与初版唯一没有动的地方是decompose 方法,这是从EaselJS抄过来的。而它的作用与louisremi的jquery.transform2的dunmatrix作用相仿,相后者据说是从FireFox源码从扒出来的,但EaselJS的实现明显顺眼多了。至于矩阵类的其他部分,则是从jQuery作者 John Resig的另一个项目Processing.js,不过它的位移与放缩部分有点偷懒,导致错误,于是外围API统统调用cross方法。

但光是有矩阵类是不行的,因此DOM的实现开始时是借鉴useragentman的这篇文章,追根索底,他也是参考另一位大牛的实现。 heygrady 在写了一篇叫《Correcting Transform Origin and Translate in IE》,阐述解题步骤。这些思路后来就被useragentman与louisremi 借鉴去了。但他们俩都在取得变形前元素的尺寸上遇到麻烦,为此使用了矩阵乘向量,然后取四个最上最下最左最右的坐标来求宽高,如此复杂的计算导致误差。因此我框架的CSS模块 v3唯一可做,也唯一能骄傲之处,就是给出更便捷更优雅的求变形前元素的尺寸的解。

下面就是css_fix有关矩阵变换的所有代码,可以看出,数据缓存系统非常重要!

    var ident  = "DXImageTransform.Microsoft.Matrix"

    adapter[ "transform:get" ] = function(node, name){
        var m = $._data(node,"matrix")
        if(!m){
            if(!node.currentStyle.hasLayout){
                node.style.zoom = 1;
            }
            //IE9下请千万别设置  <meta content="IE=8" http-equiv="X-UA-Compatible"/>
            //http://www.cnblogs.com/Libra/archive/2009/03/24/1420731.html
            if(!node.filters[ident]){
                var old = node.currentStyle.filter;//防止覆盖已有的滤镜
                node.style.filter =  (old ? old +"," : "") + " progid:" + ident + "(sizingMethod='auto expand')";
            }
            var f = node.filters[ident];
            m = new $.Matrix2D( f.M11, f.M12, f.M21, f.M22, f.Dx, f.Dy);
            $._data(node,"matrix",m ) //保存到缓存系统,省得每次都计算
        }
        return name === true ? m : m.toString();
    }
    //deg   degrees, 角度
    //grad  grads, 百分度
    //rad   radians, 弧度
    function toRadian(value) {
        return ~value.indexOf("deg") ?
        parseInt(value,10) *  Math.PI/180:
        ~value.indexOf("grad") ?
        parseInt(value,10) * Math.PI/200:
        parseFloat(value);
    }
    adapter[ "transform:set" ] = function(node, name, value){
        var m = adapter[ "transform:get" ](node, true)
        //注意:IE滤镜和其他浏览器定义的角度方向相反
        value.toLowerCase().replace(rtransform,function(_,method,array){
            array = array.replace(/px/g,"").match($.rword) || [];
            if(/skew|rotate/.test(method)){//角度必须带单位
                array[0] = toRadian(array[0] );//IE矩阵滤镜的方向是相反的
                array[1] = toRadian(array[1] || "0");
            }
            if(method == "scale" && array[1] == void 0){
                array[1] = array[0] //sy如果没有定义等于sx
            }
            if(method !== "matrix"){
                method = method.replace(/(x|y)$/i,function(_,b){
                    return  b.toUpperCase();//处理translateX translateY scaleX scaleY skewX skewY等大小写问题
                })
            }
            m[method].apply(m, array);
            var filter = node.filters[ident];
            filter.M11 =  filter.M22 = 1;//取得未变形前的宽高
            filter.M12 =  filter.M21 = 0;
            var width = node.offsetWidth;
            var height = node.offsetHeight;
            filter.M11 = m.a;
            filter.M12 = m.c;//★★★注意这里的顺序
            filter.M21 = m.b;
            filter.M22 = m.d;
            filter.Dx  = m.tx;
            filter.Dy  = m.ty;
            $._data(node,"matrix",m);
            var tw =  node.offsetWidth, th = node.offsetHeight;//取得变形后高宽
            node.style.position = "relative";
            node.style.left = (width - tw)/2  + m.tx + "px";
            node.style.top = (height - th)/2  + m.ty + "px";
          //http://extremelysatisfactorytotalitarianism.com/blog/?p=922
        //http://someguynameddylan.com/lab/transform-origin-in-internet-explorer.php
        //http://extremelysatisfactorytotalitarianism.com/blog/?p=1002
        });

注释里有许多链接,是向先行者致敬的!

在这过程中,还发现许多好东西,一并放出来,以供未来的偷师与转化!

简单介绍一下mass framework吗?

司徒大大,看了您的mass-Framework,这个框架是干什么用的?是node.js web服务器吗?还是前端的框架?
能简单介绍一下它的使用环境和例子吗?node.js入口在哪里呀?

data模块的parseData的改进

此方法是对应jQuery data模块的dataAttr私有方法,将单一的字符串数据转换成多元化的数据。

问题是jQuery内部是使用$.parseJSON进行转换,这就要求对象的键名一定要用双引号括起来相当麻烦。

parseData的改进是,直接使用eval("0,"+data)进行转换,这样一来保存对象就非常简单了,同时我们还可以保存时间对象,函数,正则,NaN等等,非常方便

parseData的加入,让mass Framework现在有五个parseXXX方法了,依次是parseJSON, parseJS,parseXML,parseHTML,parseData

subpub

                (function(window, doc, undef) {

                    var topics = {},
                            subUid = -1,
                            pubsubz = {};

                    pubsubz.publish = function(topic, args) {

                        if (!topics[topic]) {
                            return false;
                        }

                        setTimeout(function() {
                            var subscribers = topics[topic],
                                    len = subscribers ? subscribers.length : 0;

                            while (len--) {
                                subscribers[len].func(topic, args);
                            }
                        }, 0);

                        return true;

                    };

                    pubsubz.subscribe = function(topic, func) {

                        if (!topics[topic]) {
                            topics[topic] = [];
                        }

                        var token = (++subUid).toString();
                        topics[topic].push({
                            token: token,
                            func: func
                        });
                        return token;
                    };

                    pubsubz.unsubscribe = function(token) {
                        for (var m in topics) {
                            if (topics[m]) {
                                for (var i = 0, j = topics[m].length; i < j; i++) {
                                    if (topics[m][i].token === token) {
                                        topics[m].splice(i, 1);
                                        return token;
                                    }
                                }
                            }
                        }
                        return false;
                    };

                    getPubSubz = function() {
                        return pubsubz;
                    };

                    window.pubsubz = getPubSubz();

                }(this, this.document));

AJAX模块的核心**

构建一个伪XHR对象,用于摒弃XMLHttpRequest对象在各浏览器的差异性;

构建一个传送器对象,目的是针对不同的需要使用不同的东西向后端发送数揣,具体有XMLHttpRequest,XDomainRequest,ActiveXObject,script节点(用于JSONP),临时生成iframe中的form(用于上传);

基于$.ajax构建其他便捷方法!

MVVM模式的设计

   $.require("ready,node,attr", function(){
                var personDataSource  = {//这是数据源
                    firstName:"xxxx",
                    lastName:"yyyy",
                    fullName:"rrrr"
                }
                $("#personDataSource [data-bind]").each(function(){
                    var val = this.getAttribute("data-bind");
                    var arr =  val.split(":");
                    var method = arr[0];
                    var v = personDataSource[arr[1].trim()];
                    $(this)[method](v)
                });
            })

HTML部分

       <div id="personDataSource" >
            <p>First name: <span data-bind="text: firstName"></span></p>
            <p>Last name: <span data-bind="text: lastName"></span></p>
            <h2>Hello, <input data-bind="val: fullName"/>!</h2>
        </div>

主要参考框架是Knockout

@带刀 change事件BUG

正美  关于change事件的模拟  你的框架再提个BUG  
事实上在标准浏览器中change事件可以像focusin那样来绑定  
<div id="inputBox">
    <label for="radio" id="label">label</label>
    <input type="radio" id="radio1" name="testRadio" />
    <input type="radio" id="radio2" name="testRadio" />
    <input type="checkbox" id="check1" name="testCheck1" />
    <input type="checkbox" id="check2" name="testCheck2" checked="checked" />
    <input type="text" id="nameInput" />
    <input type="file" id="upFile" />
</div>

$( '#inputBox' ).on( 'change', function(e){
    console.log( 'changed1' );
});

这样绑定 里面的能触发change的表单元素都会触发  我发觉你的框架不支持 

模拟:hover伪类

因为iPhone并没有鼠标指针,所以没有hover事件。那么CSS :hover伪类就没用了。但是iPhone有Touch事件,onTouchStart 类似 onMouseOver,onTouchEnd 类似 onMouseOut。所以我们可以用它来模拟hover。使用Javascript:
?
var myLinks = document.getElementsByTagName('a');
for(var i = 0; i < myLinks.length; i++){
  myLinks[i].addEventListener(’touchstart’, function(){this.className = “hover”;}, false);
  myLinks[i].addEventListener(’touchend’, function(){this.className = “”;}, false);
}
然后用CSS增加hover效果:
?
a:hover, a.hover { /* 你的hover效果 */ }
这样设计一个链接,感觉可以更像按钮。并且,这个模拟可以用在任何元素上。

一个未经验证的精简

mass模块的require方法

 if( dn === cn ){//在依赖都已执行过或没有依赖的情况下
                if( token && !( token in transfer ) ){
                    mapper[ token ].state = 2 //如果是使用合并方式,模块会跑进此分支(只会执行一次)
                    return transfer[ token ] = setup( token, args, callback );
                }else if( !token ){//普通的回调可执行无数次
                    return  setup( token, args, callback );
                }
            }

此处更改为

            if( dn === cn ){//如果需要加载的等于已加载好的
                (mapper[ token ] || {}).state = 2; 
                return transfer[ token ] = setup( token, args, factory );//装配到框架中
            }

getEditScripts方法的推导

  <style>
        .compare{
            border:1px solid #000;
            border-collapse:collapse;
        }
        .compare td{
            border:1px solid #000;
            width:30px;
            height:30px;
            text-align:center;
            background:olive;
        }
        td.zero{
            background:teal;
        }
        td.retain{
            background:orange;
        }
        td.update{
            background:fuchsia;
        }
        td.delete{
            background:red;
        }
        td.add{
            background:lime;
        }
    </style>
<p>ABDEFG &nbsp;   ACEF  </p>
<table class="compare">
    <tr>
        <td class="zero">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
    </tr>
    <tr>
        <td>1</td>
        <td class="retain">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
    </tr>
    <tr>
        <td>2</td>
        <td>1</td>
        <td class="update">1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
    </tr>
    <tr>
        <td>3</td>
        <td>2</td>
        <td>2</td>
        <td class="delete">2</td>
        <td class="retain">2</td>
        <td>3</td>
        <td>4</td>
    </tr>
    <tr>
        <td>4</td>
        <td>3</td>
        <td>3</td>
        <td>3</td>
        <td>3</td>
        <td class="retain">2</td>
        <td class="delete">3</td>
    </tr>
</table>
<ol>
    <li> top > left  cur==next ;  x--    ; cur = left; action = "delete";</li>
    <li> top == left cur==next ;  x-- y--; cur = next; action = "retain";  </li>
    <li> top > left  cur==next ;  x--;     cur = left; action = "retain";  flag="delete";</li>
    <li> top == left cur==next+1 x-- y-- ; cur = next; action = "update";  action=flag; flag=false;</li>
    <li> top == left cur==next+1 x-- y-- ; cur = next; action = "update";  </li>
    <li> top == left cur==next   x-- y-- ; cur = next; action = "retain";  </li>
</ol>
<hr/>
<p> ABDEFG &nbsp;  ACEFF </p>
<table class="compare">
    <tr>
        <td class="zero">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
    </tr>
    <tr>
        <td>1</td>
        <td class="retain">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
    </tr>
    <tr>
        <td>2</td>
        <td>1</td>
        <td class="update">1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
    </tr>
    <tr>
        <td>3</td>
        <td>2</td>
        <td>2</td>
        <td class="delete">2</td>
        <td class="retain">2</td>
        <td>3</td>
        <td>4</td>
    </tr>
    <tr>
        <td>4</td>
        <td>3</td>
        <td>3</td>
        <td>3</td>
        <td>3</td>
        <td class="retain">2</td>
        <td>3</td>
    </tr>
    <tr>
        <td>5</td>
        <td>4</td>
        <td>4</td>
        <td>4</td>
        <td>4</td>
        <td>3</td>
        <td class="update">3</td>
    </tr>
</table>
<ol>
    <li> top == left cur==next+1; x-- y-- ; cur=next;   action="update";  </li>
    <li> top == left cur==next  ; x-- y-- ; cur = next; action = "retain";   </li>
    <li> top > left  cur==next  ; x--;      cur = left; action = "retain";  flag="delete";</li>
    <li> top == left cur==next+1 x-- y-- ;  cur = next; action = "update";  action=flag; flag=false;</li>
    <li> top == left cur==next+1 x-- y-- ;  cur = next; action = "update";  </li>
    <li> top == left cur==next   x-- y-- ;  cur = next; action = "retain";  </li>
</ol>
<hr/>
<p> ABDE  &nbsp;  ABCDEF </p>
<table class="compare">
    <tr>
        <td class="zero">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
    </tr>
    <tr>
        <td>1</td>
        <td class="retain">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
    </tr>
    <tr>
        <td>2</td>
        <td>1</td>
        <td class="retain">0</td>
        <td>1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>3</td>
        <td>2</td>
        <td>1</td>
        <td class="add">1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>4</td>
        <td>3</td>
        <td>2</td>
        <td class="retain">1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>5</td>
        <td>4</td>
        <td>3</td>
        <td>2</td>
        <td class="retain">1</td>
    </tr>
    <tr>
        <td>6</td>
        <td>5</td>
        <td>4</td>
        <td>3</td>
        <td class="add">2</td>
    </tr>
</table>
<ol>
    <li>top <  left cur==next  y-- ;    cur=top;  action="add"; </li>
    <li>top == left cur==next  y-- x--; cur=next; action="retain";  </li>
    <li>top <  left cur==next  y-- ;    cur=top; action="retain"  flag="add"; </li>
    <li>top == left cur=next+1 y--,x--; cur=next; action="update"; action=flag;flag=false; </li>
    <li>top == left cur==next; y--,x--; cur=next; action="retain" </li>
    <li>top == left cur==next; y--,x--; cur=next; action="retain"</li>
</ol>
<hr/>
<p> ABDE  &nbsp;  ABCDEF </p>
<table class="compare">
    <tr>
        <td class="zero">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
    </tr>
    <tr>
        <td>1</td>
        <td class="retain">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
    </tr>
    <tr>
        <td>2</td>
        <td>1</td>
        <td class="retain">0</td>
        <td>1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>3</td>
        <td>2</td>
        <td>1</td>
        <td class="add">1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>4</td>
        <td>3</td>
        <td>2</td>
        <td class="retain">1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>5</td>
        <td>4</td>
        <td>3</td>
        <td>2</td>
        <td class="retain">1</td>
    </tr>
    <tr>
        <td>6</td>
        <td>5</td>
        <td>4</td>
        <td>3</td>
        <td class="add">2</td>
    </tr>
</table>
<ol>
    <li>top < left  cur==next  y-- ;    cur=top;  action="add"; </li>
    <li>top == left cur==next  y-- x--; cur=next; action="retain";  </li>
    <li>top < left  cur==next  y-- ;    cur=top;  action="retain"  flag="add"; </li>
    <li>top== left  cur==next+1 y--,x--;cur=top;  action="update"; action=flag; flag=false; </li>
    <li>top==left   cur==next;y--,x--;  cur=next; action="retain" </li>
    <li>top==left   cur==next;y--,x--;  cur=next; action="retain"</li>
</ol>
<hr/>
<p> AB  &nbsp;  CBGGGG </p>
<table class="compare">
    <tr>
        <td class="zero">0</td>
        <td>1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>1</td>
        <td class="update">1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>2</td>
        <td>2</td>
        <td class="retain">1</td>
    </tr>
    <tr>
        <td>3</td>
        <td>3</td>
        <td  class="add">2</td>
    </tr>
    <tr>
        <td>4</td>
        <td>4</td>
        <td  class="add">3</td>
    </tr>
    <tr>
        <td>5</td>
        <td>5</td>
        <td  class="add">4</td>
    </tr>
    <tr>
        <td>6</td>
        <td>6</td>
        <td class="add">5</td>
    </tr>
</table>
<ol>
    <li>top < left cur==next  y--       ; cur=top; action="add"; </li>
    <li>top < left cur==next  y--       ; cur=top; action="add"; </li>
    <li>top < left cur==next  y--       ; cur=top; action="add"; </li>
    <li>top < left cur==next  y--       ; cur=top; action="add"; </li>
    <li>top == left cur==next x-- y--;    cur=next; action="retain"; </li>
    <li>top==left cur==next+1;y--,x--;    cur=next; action="update"</li>
</ol>
<hr/>
<p>ABCD &nbsp;  ACBD</p>
<table class="compare">
    <tr>
        <td class="zero">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
    </tr>
    <tr>
        <td>1</td>
        <td class="retain">0</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
    </tr>
    <tr>
        <td>2</td>
        <td>1</td>
        <td class="update">1</td>
        <td>1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>3</td>
        <td>2</td>
        <td>1</td>
        <td class="update">2</td>
        <td>2</td>
    </tr>
    <tr>
        <td>4</td>
        <td>3</td>
        <td>2</td>
        <td>2</td>
        <td class="retain">2</td>
    </tr>
</table>
<ol>
    <li>top < left cur==next  y-- ; cur=next; action="add"; </li>
    <li>top < left cur==next  y-- ; cur=next; action="add"; </li>
    <li>top < left cur==next  y-- ; cur=next; action="add"; </li>
    <li>top < left cur==next  y-- ; cur=next; action="add"; </li>
    <li>top == left cur==next x-- y-- ; cur=next; action="retain"; </li>
    <li>top==left cur==next+1;y--,x--;  cur=next; action="update"</li>
</ol>
            //http://www.cnblogs.com/pandora/archive/2009/12/20/levenshtein_distance.html
            //https://gist.github.com/982927
            //http://www.blogjava.net/phyeas/archive/2009/01/10/250807.html
            window.onload = function(){
                //通过levenshtein distance算法返回一个矩阵,matrix[y][x]为最短的编辑长度
                var getEditDistance = function(from, to){
                    var matrix = [], fn = from.length, tn = to.length;
                    // 初始化一个矩阵,行数为b,列数为a
                    var i,j
                    for(i = 0; i <= tn; i++){
                        matrix[i] = [i];//设置第一列的值
                    }
                    for(j = 0; j <= fn; j++){
                        matrix[0][j] = j;//设置第一行的值
                    }
                    // 填空矩阵
                    for(i = 1; i <= tn; i++){
                        for(j = 1; j <= fn; j++){
                            if(to.charAt(i-1) == from.charAt(j-1)){
                                matrix[i][j] = matrix[i-1][j-1];//没有改变
                            } else {
                                matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, //代替 substitution
                                matrix[i][j-1] + 1, // 插入insertion
                                matrix[i-1][j] + 1); //删除 deletion
                            }
                        }
                    }
               
                    return matrix
                };
                //返回具体的编辑步骤
                var getEditScripts = function(from, to, matrix){
                    var x = from.length;
                    var y = to.length;
                    var cost = matrix[y][x];
                    var scripts = [], i = 0;
                    if(x == 0){//如果原数组为0,那么新数组的都是新增的
                        for( ; i < y; i++){
                            scripts[scripts.length] = {
                                action: "add",
                                value: to[i]
                            }
                        }
                        return scripts;
                    }
                    if(y == 0){//如果新数组为0,那么我们要删除所有旧数组的元素
                        for( ; i < x; i++){
                            scripts[scripts.length] = {
                                action: "delete",
                                value: from[i]
                            }
                        }
                        return scripts;
                    }
//把两个JSON合并在一起
                    console.log("原字符串: "+from)
                    console.log("目标字符串: "+to)
                    console.log("最短的编辑长度: "+cost)
                    console.log("比较矩阵: ")
                    console.log(matrix.join("\n"))
                    i =  Math.max(x,y);
                    var flag = true, action;
                    while(scripts.length != i ){
                        var cur = matrix[y][x];
                        var next = matrix[y-1][x-1];
                        var top = matrix[y-1][x];
                        var left = matrix[y][x-1]
                        action = "retain"//top == left && cur == next
                        x--;
                        y--;
                        if( top > left ){
                            action = "delete";
                            y++
                        } else if( top < left ){
                            action = "add";
                            x++
                        }else if( top == left && (cur != next) ){
                            action = "update"
                        }
                        if( /delete|add/.test(action)){
                            if(flag == true ){//第一次用
                                flag = action;// 留着下次用
                            }else{
                                action = "retain";//第一次之后
                            }
                        }else if(action == "update" && /delete|add/.test(flag) ){
                            action = flag;
                            flag = false;
                        }
                        scripts[scripts.length] = {
                            action:  action
                        }
                    }
                    console.log("具体步骤:")
                    console.log(scripts.reverse());
                  
                }
                console.log("===================")
                var old = "ABDEFG", neo = "ACEF"
                var m = getEditDistance( old, neo);
                getEditScripts( old, neo,m)
            }        

mass与newland

请问一下,这个框架是不是全部整合了newLand项目中?还是newLand会一直保持对mass的依赖关系

几个主流库的评论

  • JQuery的优势是轻巧,适合做一些页面修饰,如动态菜单,可以相当程度取代flash做一些动画和特效。在OA等企业级应用采用JQuery基本属于自掘坟墓。
  • Prototype作为最早的Ajax框架,早年的王者,许多迷人的特性,由于对DOM的原型扩展导致现在的进退维谷,惨兮兮的
  • ExtJS, 一纸License绝对可以把稍有远见的技术决策者挡于门外,任何想自己有点核心竞争力的团队,绝不会把基础建立在一个以项目为单位被收费的基础之上。ExtJs通过专业而一致的外观,和可以帮助快速上手的Sample Code,确实有相当大的诱惑力,能让快速开发成为可能,但一但出现问题,维护成本其实相当高。因为有太多的Code你可能都不明白是做什么用的。
    -dojo 完善的包管理机制,不过每升一次级把模块定义都改个底朝天……晕死

有关JSON的安全eval

对于用户写标签中的绑定,如

<span data-bind="class:2355,'22': myItems().count < 5,ttt:function(){alert([1,2,3])}">TEST</span>

mass准备就搬knouckoutjs那一套进行处理。对于JSON的eval,主要问题是现在这个JSON不规范,可以放正则与函数,还有键名没有双引号括起来,其中没有双引号括起来是麻烦的基本。在FF中没有双引号,即使键名为class, float等关键字,它也能安全通过,但IE不行,我们需要手动添加。mass Framework提供了一个normalizeJSON方法干这事。

normalizeJSON里面调用一个parseObjectLiteral方法,先将JSON变成一个键值对数组,如上例变成:

[{ key:"class", value:"2355"},
 { key:"'22'", value:" myItems().count < 5"},
 { key:"ttt", value:"function(){alert([1,2,3])}"}
]

然后逐一加引号:

{'class':2355,'22': myItems().count < 5,'ttt':function(){alert([1,2,3])}}

$.Array.unique

利用es6的新集合对象提高性能

 if(this.Set){
        $.Array.unique = function(target){
            var set = new Set, ret = [], n = target.length;
            for (var i = 0, el; i < n; i++) {
                el = target[i]
                if(!set.has(el)){
                    set.add(el)
                    ret.push(el);
                }
            }
            return ret;
        }
    }

flash加载

      <object class="player" type="application/x-shockwave-flash" data="/medias/player_flv_maxi.swf" width="320" height="240">
                <param name="movie" value="/medias/player_flv_maxi.swf">
                <param name="allowFullScreen" value="true">
                <param name="FlashVars" value="flv=/medias/video.flv&width=320&height=240&showstop=1&showvolume=1&showtime=1&startimage=/medias/startimage_en.jpg&showfullscreen=1&bgcolor1=189ca8&bgcolor2=085c68&playercolor=085c68">
                <p>Video de demonstration.</p>
        </object>

来源:http://flv-player.net/

默写情况下表达式解析失败

第10行的 {{el.text}} 会导致报错,提示 "Uncaught ReferenceError: el is not defined"

<!DOCTYPE html>
<html >
    <head>
        <script src="avalon.js"></script>
    </head>
    <body>
    <ul class="nav navbar-nav" ms-controller="nav_menu" ms-each-el="items">
      <li><a ms-attr-href="el.url">{{el.text}}</a></li>
      <li ms-class="active:el.actived" ms-if="el.children.length==0">
          <a ms-attr-href="el.url">{{el.text}}</a>
      </li>
    </ul>

        <script LANGUAGE="javascript">
            var nav_menu = avalon.define('nav_menu', function(vm){
                vm.items = [
                    {url:'#123', text:'menu 123', actived:true, children:[]},
                    {url:'#123', text:'menu 124', actived:false, children:[
                        {url:'#123', text:'submenu 123', actived:false, children:[]},
                        {url:'#123', text:'submenu 123', children:[]}
                    ]},
                    {url:'#123', text:'menu 125', children:[]},
                ];
            });
        </script>

    </body>
</html>

号召大家加入mass Framework团队

打算建立mass Framework开发团队,专注于UI开发(结合less)
这话我在群里也不说了一两次。光是写业务代码,能力增长也很有限,水平太次也肯定很难进大公司拿高薪。
下面是我在一群说的话
我的技术飞速进步是我加了一年博客,有人说我不如把这么些代码整合起来吧
当时我发现51js的许多大牛也这样做
但做一个库或框架,就不单是1+1=2的问题了
我要发明类,要研究原型,jquery起来后我也开始搞选择器
还要搞动画列队,AJAX
看了许多库,你会知道它们的实现都有区别的
有的库可以追求精致,就省去一点兼容性
有的方法为了与其他方法配合,就要重构自身

但我在搞的时间
玉伯是带了一帮人在搞KISSY啊
因为玉伯出名,他们的团队个个进步飞速
因为他们可以集思广益
我的库用的最多的是abcd贡献的一些小函数与正则
或遇到问题时,得到一点提示与帮助
一下子提升了好多

其他都是我一个人单干的
如果我们这么多人,
如果有三个人以上,我们也可以搞个UI库来卖钱了
差点我们可以在这过程互相学习,DOM的BUG层出不穷,每个人都有忽略点的,因此大家总是能学到东西
语言有人指点与没有指点是差很多的。

我在easyUI与ligerUI呆着,知道这市场有多大 .国内一个叫miniUI的东西,东抄西抄也能买非常高的价值了
对新手来说他们连jquery是什么也不知,因此稍有个出门点的UI框架,能解决他们的手头问题,他们就拿来用了

重要的团队的凝聚力与宣传
当然这只是暂时,我最大的目标是建立像 sencha这样纯JS开发的公司,让mass Framework的核心成员都跑进来发大财。
总不能一辈子给人家打工啊
我觉得就算不能成功开公司,我们这个框架如果有大量的人参考,一定能在国内搞个名堂出来,大家在写框架时互相学习,实力也在位居国内一指。
有喜欢参加者,可以到https://github.com/RubyLouvre fork我的项目,可以在里面提issue或在QQ给我补丁,或你的新模块。我们要尽快搞出个UI库出来。毕竟UI库在项目开发中最有用的。

命名BUG

//error
avalon.define("metroContainer-VModel", [], function(vm) {
....

//ok

avalon.define("metroContainerVModel", [], function(vm) {
....

被这个命名纠结了一个下午,不能有特殊符号!!

unique更简短的实现方式

uniq: function (target) {
    var o = target.reduce(function (a, e) {
        a[e] = true;
        return a
    }, {});
    return Object.keys(o);
}

如果数组特别大话,这个更好些。

在IE下取得元素的属性值

      "@ie:get": function( node, name ){
            //固有属性
            var str = node.outerHTML.replace(node.innerHTML, "")
            var re = /\s+([\w-]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g, obj = {}, t;
            while (t = re.exec(str)) { //属性值只有双引号与无引号的情况
                if(t[2].charAt(0) == '"'){//自定义属性
                     obj[ t[1].toLowerCase() ] =  t[2].slice(1, -1)
                }else{  //固有属性
                     obj[ t[1] ] = t[2] || ""
                }
            }
            return obj[ name ];
        },

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.