vapour's blog

14Jun/12Off

Fiddler监听Firefox、Chrome中的http请求

Fiddler在前端调试中经常用到,尤其是线上脚本调试和ie6/ie7中的脚本调试。这篇文章不是介绍怎么使用Fiddler调试脚本,而是介绍使用Fiddler中遇到的问题。

Fiddler是一个运行在本地电脑8888端口的HTTP代理,可以监听任何经过Fiddler代理的HTTP请求,不止是浏览器的http请求,可以监听你电脑上所有软件的HTTP请求。

因为Firefox的安全机制,需要安装FiddlerHook插件后手动开启,才能使用Fiddler。如果Firefox安装了代理插件(例如AutoProxy),会和Fiddler发生冲突,有两种解决方法:

  1. 禁用代理插件后重启浏览器,就可以正常使用。
  2. 这里以AutoProxy为例子。在AutoProxy中增加一个Fiddler代理 127.0.0.1:8888,将其设置成默认代理,再把Autoproxy设置成全局代理,就可以正常使用Fiddler了。

Chrome浏览器如果安装了代理插件,解决方法同上。

Tagged as: 1 Comment
19Apr/12Off

跨域之window.postMessage

window.postMessage是一种跨域交互方法,支持通过iframe和window.open页面之间的跨域通信。经测试IE7+、Firefox、Opera、Chrome、Safari均支持window.postMessage。

vapour.net/message.htm:

<!doctype html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>window.postMessage</title>
</head>

<body>
<iframe id="ifrBlog" src="http://vapour.cn/iframe.htm"></iframe>

<script type="text/javascript">
(function (){
    var ifr = document.getElementById('ifrBlog').contentWindow;
    
    window.onload = function(){
        ifr.postMessage('Hello, I am vapour, from vapour.net', 'http://vapour.cn');
    };
   
    if (window.addEventListener) {
        window.addEventListener('message', function(ev){
            alert(ev.data);
            //console.log(ev.data, ev.origin, ev.source);
        }, false);
    } else {
        window.attachEvent('onmessage', function(ev){
            alert(ev.data);
            //console.log(ev.data, ev.origin, ev.source);
        });
    }
    
})();
</script>
</body>
</html>

 

vapour.cn/iframe.htm:

<!doctype html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>window.postMessage</title>
</head>

<body>
<script type="text/javascript">
(function (){
    if (window.addEventListener) {
        window.addEventListener('message', function(ev){
            alert(ev.data);
            //console.log(ev.data, ev.origin, ev.source);
            ev.source.postMessage('Hi, this is vapour.cn', 'http://vapour.net');
        }, false);
    } else {
        window.attachEvent('onmessage', function(ev){
            alert(ev.data);
            //console.log(ev.data, ev.origin, ev.source);
            ev.source.postMessage('Hi, this is vapour.cn', 'http://vapour.net');
        });
    }
})();
</script>
</body>
</html>

 

参考:

13Apr/12Off

jsVelocity – An JavaScript Template Engine of Velocity

模板引擎以前一直在使用自己模仿mustache语法实现的轻量级模板系统。由于使用习惯问题,一直喜欢velocity的语法,于是最近按照velocity语法重新开发了一个模板系统jsVelocity,已经放到了github上。支持#set、#if、#foreach、#macro,并且提供了简单的模板管理系统。

下面是一个helloword程序:

Template.define('t1', [
    '#set(${name}="vapour")',
    '#if(${name})',
    '<div>${name}</div>',
    '#else',
    '<div>${web}</div>',
    '#end',
    '<ul>',
    '#foreach(${web} in ${website})',
    '<li>${velocityCount} ${web.name} : ${web.url} #macro(isGoogle ${web.name})</li>',
    '#end',
    '</ul>'
]);
    
var html = Template.render('t1', {
    website: [{
        name: 'google',
        url: 'http://www.google.com/';
    }, {
        name: 'yahoo',
        url: 'http://www.yahoo.com/';
    }, {
        name: 'facebook',
        url: 'http://www.facebook.com/'
    }, {
        name: 'twitter',
        url: 'http://www.twitter.com/';
    }],
    isGoogle: function(name){
        return name === 'google' ? ', is google' : '';
    }
});

模板变量必须是${variable}这种形式。

 

下面是实现代码,包含模板管理、velocity语法解析器、调试模块。

var Template = (function (undef) {
    'use strict';
    var util,
        templates = {},
        SENTENCE = /#([a-z]+)\(([\s\S]+?)\)/,
        VARIABLE = /\$\{\w[\w\.]*\}/g,
        velocity;
    
    util = {
        each: function (arr, cb) {
            var i = 0, l = arr.length;
            for (; i < l; i++) {
                cb(arr[i], i, arr);
            }
        },
        trim: function (str) {
            return str.replace(/^\s+|\s+$/g, '');
        },
        log: function(msg, type){
            Template.log(msg, type);
        }
    };

    velocity = {
        parse: function (str, params) {
            var result;
            params = params || {};

            while ((result = SENTENCE.exec(str)) !== null) {
                switch (result[1]) {
                    case 'set':
                        str = this.parseSet(str, params, result);
                        break;
                    case 'if':
                        str = this.parseIf(str, params, result);
                        break;
                    case 'foreach':
                        str = this.parseForeach(str, params, result);
                        break;
                    case 'macro':
                        str = this.parseMacro(str, params, result);
                        break;
                }
                //break; //debug开发阶段只执行一次
            }
            str = this.replaceVariable(str, params);
            return str;
        },
        parseSet: function (str, params, result) { //parse #set
            var start = result.index,
                end = start + result[0].length,
                expression = result[2];

            if (expression.indexOf('=') > -1) {
                expression = expression.split('=');
                params[expression[0].slice(2, -1)] = this.express(expression[1], params);
            } else {
                util.log('#set syntac error', 'warn');
            }
            str = str.slice(0, start) + str.slice(end);
            return str;
        },
        parseIf: function (str, params, result) { //parse #if
            var flag = this.express(this.velocityToVariable(result[2], params), params),
                start = result.index,
                middle = this.matchElse(str),
                end = this.matchEnd(str);

            if (end === -1) {
                util.log('syntax error,miss #end', 'warn');
                return str;
            }

            //#if(expression) length=5+expression.length
            //#end length=4
            //#else length=5
            if (middle > -1 && middle < end) {
                if (flag) {
                    str = str.slice(0, start) + str.slice(start + result[2].length + 5, middle) + str.slice(end + 4);
                } else {
                    str = str.slice(0, start) + str.slice(middle + 5, end) + str.slice(end + 4);
                }
            } else {
                if (flag) {
                    str = str.slice(0, start) + str.slice(start + result[2].length + 5, end) + str.slice(end + 4);
                } else {
                    str = str.slice(0, start) + str.slice(end + 4);
                }
            }
            return str;
        },
        //#foreach(expression) length=10+expression.length
        //#end length=4
        parseForeach: function (str, params, result) { //parse #foreach
            var start = result.index,
                end = this.matchEnd(str),
                context = str.slice(start + result[2].length + 10, end),
                arr = result[2].split(' in '),
                list, item, html = '';

            if (arr.length !== 2) {
                util.log('syntax error: ' + result[0], 'error');
                return str;
            }

            item = util.trim(arr[0]).slice(2, -1);
            list = this.getVariable(util.trim(arr[1]).slice(2, -1), params);
            
            if (list) {
                util.each(list, function (o, index) {
                    var p = velocity.clone(params);
                    p[item] = list[index];
                    p.velocityCount = index + 1;
                    
                    html += velocity.parse(context, p);
                });
            }
            str = str.slice(0, start) + html + str.slice(end + 4);

            return str;
        },
        //#macro(funcName $variable1 $variable2);
        parseMacro: function(str, params, result){ //parse #macro
            var start = result.index,
                end = start + result[0].length,
                ret = '', func,
                arr = [];
            
            //filter whitespace
            util.each(result[2].split(' '), function(v, index){
                v = util.trim(v);
                if (v !== '') {
                    if (index > 0) {
                        arr.push(velocity.getVariable(v.slice(2, -1), params));
                    } else {
                        arr.push(v);
                    }
                }
            });
            
            func = arr[0];
            if (func) {
                func = params[func];
                if (func) {
                    ret = func.apply(null, arr.slice(1)) || '';
                } else {
                    util.log('#macro ' + arr[0] + ' is undefined', 'error');
                }
            } else {
                util.log('#macro syntax error, macro name is undefined', 'error')
            }
            str = str.slice(0, start) + ret + str.slice(end);
            return str;
        },
        clone: function () { //clone object
            var ret = {},
                i, len, o, p;
            for (i = 0, len = arguments.length; i < len; i++) {
                o = arguments[i];
                for (p in o) {
                    if (o.hasOwnProperty(p) && !ret[p]) {
                        ret[p] = o[p];
                    }
                }
            }
            return ret;
        },
        express: function (expression, params) {
            return eval('(' + expression + ')');
        },
        velocityToVariable: function (str, params) { //velocity to JS
            str = str.replace(VARIABLE, function (all) {
                return 'params.' + all.slice(2, -1);
            });
            return str;
        },
        replaceVariable: function (str, params) { //display velocity variables
            str = str.replace(VARIABLE, function (all) {
                var name = all.slice(2, -1),
                    value = velocity.getVariable(name, params);

                if (value !== undef) {
                    return value;
                } else {
                    util.log('Variables ' + all + ' is undefined', 'warn');
                    return all;
                }
            });
            return str;
        },
        getVariable: function (name, params) {
            var ret, tmp, arr, i, len;
            if (name.indexOf('.') === -1) {
                ret = params[name];
            } else {
                ret = params;
                arr = name.split('.');
                for (i = 0, len = arr.length; i < len; i++) {
                    if (ret[arr[i]] !== undef) {
                        ret = ret[arr[i]];
                    } else {
                        util.log('Variables ' + name + ' is undefined', 'warn');
                        ret = '';
                        break;
                    }
                }
            }
            return ret;
        },
        matchEnd: function (str) { //find the position of #end
            var reg = /#(foreach|if|end)/g,
                start = 0,
                index = -1,
                result;

            while ((result = reg.exec(str)) !== null) {
                switch (result[1]) {
                case 'foreach':
                case 'if':
                    start += 1;
                    break;
                case 'end':
                    start -= 1;
                    if (start === 0) {
                        index = result.index;
                    }
                    break;
                default:
                    break;
                }

                if (index > -1) {
                    break;
                }
            }

            return index;
        },
        matchElse: function (str) { //find the position of #else
            var reg = /#(if|else|end)/g,
                start = 0,
                index = -1,
                result;

            while ((result = reg.exec(str)) !== null) {
                switch (result[1]) {
                case 'if':
                    start += 1;
                    break;
                case 'end':
                    start -= 1;
                    break;
                case 'else':
                    if (start === 1) {
                        index = result.index;
                    }
                    break;
                default:
                    break;
                }

                if (index > -1) {
                    break;
                }
            }
            return index;
        }
    };

    return {
        render: function (name, params) {
            if (typeof templates[name] !== 'string') {
                util.log('Template ' + name + ' not found!', 'warn');
                return '';
            } else {
                return velocity.parse(templates[name], params);
            }
        },
        define: function (name, template) {
            if (typeof template == 'string') { //string
                templates[name] = template;
            } else { //array
                templates[name] = template.join('');
            }
        }
    };
})();

Template.log = (function () {
    'use strict';
    var debug = window.location.href.indexOf('debug') > -1,
        log = function () {
            var div = document.createElement('div');
            div.style.cssText = 'position:fixed;width:100%;bottom:0;height:120px;border-top:2px solid #ccc;backround-color:#efefef;overflow-y:scroll';
            document.body.appendChild(div);

            log = function (msg, type) {
                var d = document.createElement('div');
                d.style.padding = '4px';
                switch (type) {
                case 'warn':
                    d.style.color = 'red';
                    break;
                case 'info':
                    break;
                }
                d.innerHTML = msg;
                div.appendChild(d);
            };
        };
    if (debug) {
        if (window.console && console.log) {
            return function (msg, type) {
                switch (type) {
                case 'info':
                    console.info(msg);
                    break;
                case 'warn':
                    console.warn(msg);
                    break;
                default:
                    console.log(msg);
                    break;
                }

            };
        } else {
            log();
            return log;
        }
    } else {
        return function () {};
    }
})();

 

28Feb/12Off

Math.max() < Math.min()

在firefox(或其他浏览器开发者工具)的命令行中运行:

Math.Max() < Math.min()
// true

对这个结果是不是有点惊讶,我们看下ECMA-262

ECMA-262中关于max和min的规范如下:

15.8.2.11 max([value1[, value2[, ...]]])

Given zero or more arguments, calls ToNumber on each of the arguments and returns the largest of the resulting values.

  • If no arguments are given, the result is -∞
  • If any value is NaN, the result is NaN.
  • The comparison of values to determine the largest value is done as in 11.8.5 except that +0 is considered to be larger than -0.

The length property of the max method is 2.

 

15.8.2.12 min ( [ value1 [ , value2 [ , … ] ] ] )

Given zero or more arguments, calls ToNumber on each of the arguments and returns the smallest of the resulting values.

  • If no arguments are given, the result is +∞.
  • If any value is NaN, the result is NaN.
  • The comparison of values to determine the smallest value is done as in 11.8.5 except that +0 is considered to be larger than -0.

The length property of the min method is 2.

ECMA中规定,当max和min的参数为空时,输出-∞或∞。

google chrome v8引擎Math.max和Math.min代码:

// ECMA 262 - 15.8.2.11
function MathMax(arg1, arg2) { // length == 2
    var length = % _ArgumentsLength();
    if (length == 2) {
        if (!IS_NUMBER(arg1)) arg1 = NonNumberToNumber(arg1);
        if (!IS_NUMBER(arg2)) arg2 = NonNumberToNumber(arg2);
        if (arg2 > arg1) return arg2;
        if (arg1 > arg2) return arg1;
        if (arg1 == arg2) {
            // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be
            // a Smi or a heap number.
            return (arg1 == 0 && ! % _IsSmi(arg1) && 1 / arg1 < 0) ? arg2 : arg1;
        }
        // All comparisons failed, one of the arguments must be NaN.
        return 0 / 0; // Compiler constant-folds this to NaN.
    }
    if (length == 0) {
        return -1 / 0; // Compiler constant-folds this to -Infinity.
    }
    var r = arg1;
    if (!IS_NUMBER(r)) r = NonNumberToNumber(r);
    if (NUMBER_IS_NAN(r)) return r;
    for (var i = 1; i < length; i++) {
        var n = % _Arguments(i);
        if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
        if (NUMBER_IS_NAN(n)) return n;
        // Make sure +0 is considered greater than -0. -0 is never a Smi, +0 can be
        // a Smi or heap number.
        if (n > r || (r == 0 && n == 0 && ! % _IsSmi(r) && 1 / r < 0)) r = n;
    }
    return r;
}

// ECMA 262 - 15.8.2.12
function MathMin(arg1, arg2) { // length == 2
    var length = % _ArgumentsLength();
    if (length == 2) {
        if (!IS_NUMBER(arg1)) arg1 = NonNumberToNumber(arg1);
        if (!IS_NUMBER(arg2)) arg2 = NonNumberToNumber(arg2);
        if (arg2 > arg1) return arg1;
        if (arg1 > arg2) return arg2;
        if (arg1 == arg2) {
            // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be
            // a Smi or a heap number.
            return (arg1 == 0 && ! % _IsSmi(arg1) && 1 / arg1 < 0) ? arg1 : arg2;
        }
        // All comparisons failed, one of the arguments must be NaN.
        return 0 / 0; // Compiler constant-folds this to NaN.
    }
    if (length == 0) {
        return 1 / 0; // Compiler constant-folds this to Infinity.
    }
    var r = arg1;
    if (!IS_NUMBER(r)) r = NonNumberToNumber(r);
    if (NUMBER_IS_NAN(r)) return r;
    for (var i = 1; i < length; i++) {
        var n = % _Arguments(i);
        if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
        if (NUMBER_IS_NAN(n)) return n;
        // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be a
        // Smi or a heap number.
        if (n < r || (r == 0 && n == 0 && ! % _IsSmi(n) && 1 / n < 0)) r = n;
    }
    return r;
}

 

注意:

∞ == 1/0 == Infinity == Number.POSITIVE_INFINITY

-∞ == -1/0 == -Infinity == Number.NEGTIVE_INFINITY

 

规范这么规定原因可能是:

You see, the min/max implementations need something to compare to and Infinity and -Infinity are the only safe values to use for that comparison.

 

参考:

min less max

Standard ECMA-262

google chrome v8

Tagged as: , , , No Comments
20Sep/11Off

非字母数字的JS(二)

在2011年的BlackHat DC 2011大会上Ryan Barnett给出了一段关于XSS的示例javascript代码: 

($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__[_+~$]+$_[_]+$$](_/_)

上面这段代码在浏览器中输出alert(1),这段代码中巧妙利用sort函数执行返回window对象来获取window对象,然后执行window['alert'](1)。关于这段代码的详细解释可查看这里

关于上面代码的实现技术原理,可以查看《非字母数字组成的JavaScript》。

由上面的代码,引发思考能不能输出数字(0-9)和全部26个字母,于是进行了下面尝试:

//获取数字
console.log(+[]); //0
console.log(-~[]); //1
console.log(-~-~[]); //2
console.log(-~-~-~[]); //3
console.log(-~-~-~-~[]); //4
console.log(-~-~-~-~-~[]); //5
console.log(-~-~-~-~-~-~[]); //6
console.log(-~-~-~-~-~-~-~[]); //7
console.log(-~-~-~-~-~-~-~-~[]); //8
console.log(-~-~-~-~-~-~-~-~-~[]); //9

通过上面这种方法,可以得到0-9,再组合0-9就可以得到任意数值了。

//获取字母
//[{}]+[] [object Object]
console.log(([{}]+[])[-~[]]); //o
console.log(([{}]+[])[-~-~[]]); //b
console.log(([{}]+[])[-~-~-~[]]); //j
console.log(([{}]+[])[-~-~-~-~[]]); //e
console.log(([{}]+[])[-~-~-~-~-~[]]); //c
console.log(([{}]+[])[-~-~-~-~-~-~[]]); //t

//![]+[] false
console.log((![] + [])[+[]]); //f
console.log((![] + [])[-~[]]); //a
console.log((![] + [])[-~-~[]]); //l
console.log((![] + [])[-~-~-~[]]); //s
console.log((![] + [])[-~-~-~-~[]]); //e

//!![]+[] true
console.log((!![]+[])[+[]]); //t
console.log((!![]+[])[-~[]]); //r
console.log((!![]+[])[-~-~[]]); //u
console.log((!![]+[])[-~-~-~[]]); //e

//({}[(!![]+[])[+[]]])+[] undefined
console.log((({}[(!![]+[])[+[]]])+[])[-~[]]); //n
console.log((({}[(!![]+[])[+[]]])+[])[-~-~[]]); //d
console.log((({}[(!![]+[])[+[]]])+[])[-~-~-~-~-~[]]); //i

经过尝试,暂时只能得到a b c d e f i j l n o r s t u共15个字母。欢迎补充

10Aug/11Off

轻量级模板系统

开发问卷系统时,开发了一个简单的模板管理功能。实现了简单的变量替换、循环、自定义函数、并提供了循环变量tmCount(从0开始)和tmNumber(从1开始)。不多说,先上代码:

var templateManager = (function () {
    var templates = {},
        tmp,
        supplant = {
            init: function (str, params) {
                var value, prop, tmp, len;
                for (prop in params) {
                    if (params.hasOwnProperty(prop)) {
                        value = params[prop];
                        switch (Object.prototype.toString.call(value)) {
                            case '[object Number]':
                            case '[object String]':
                                tmp = {};
                                tmp[prop] = value;
                                str = supplant.replaceString(str, tmp);
                                break;
                            case '[object Function]':
                                str = supplant.replaceFunc(str, prop, value, params);
                                break;
                            case '[object Array]':
                                str = supplant.replaceArray(str, prop, value);
                                break;
                            default:
                                break;
                        }
                    }
                }
                return str;
            },
            replaceString: function (str, params) {
                var prop;
                for (prop in params) {
                    if (params.hasOwnProperty(prop)) {
                        str = str.split('{' + prop + '}').join(params[prop]);
                    }
                }
                return str;
            },
            replaceFunc: function (str, prop, func, item) {
                return str.split('{' + prop + '}').join(func(item));
            },
            replaceArray: function (str, prop, data) {
                var i = 0,
                    left,
                    middle,
                    right,
                    s = '{#' + prop + '}',
                    e = '{/' + prop + '}',
                    len = data.length,
                    start = 0,
                    end = 0;
                    
                start = str.indexOf(s);
                end = str.indexOf(e, start);
                left = str.slice(0, start);
                middle = str.slice(start + s.length, end);
                right = str.slice(end + e.length);
                if (len > 0 && start > -1) {
                    for (; i < len; ++i) {
                        data[i].tmCount = i;
                        data[i].tmNumber = i + 1;
                        if (data[i].content !== null) { //如果content属性不等于null
                            left += supplant.init(middle, data[i]);
                        } else {
                            left += data[i].replace;
                        }
                    }
                    str = left + right;
                    
                    if (str.indexOf(s) > -1) {//如果包括多个循环
                        str = supplant.replaceArray(str, prop, data);
                    }
                }
                
                if (len == 0 && start > -1) {
                    str = left + right;
                }
                
                return str;
            }
        };
    
    return {
        render: function (name, params) {
            if (typeof templates[name] !== 'string') {
                throw 'Template ' + name + ' not found!';
            }
            tmp = name;
            return supplant.init(templates[name], params);
        },
        defineTemplate: function (name, template) {
            if (typeof template == 'string') {
                templates[name] = template;
            } else { //数组
                templates[name] = template.join('');
            }
        }
    };
})();

上面代码中定义一个templateManager对象用来管理模板,提供了两个接口:

  1. defineTemplate 用来定义模板
  2. render 用来渲染模板

内部私有的supplant对象实现模板引擎,提供了替换变量、替换循环、替换函数的功能。

文档和示例:

//替换变量
templateManager.defineTemplate('profile', '姓名:{name},blog:{blog}');
templateManager.render('profile', {name: 'vapour', blog: 'http://dovapour.info/'});
//结果 姓名:vapour,blog:http://dovapour.info/

//实现循环
templateManager.defineTemplate('website', [
    '<ul>',
        '{#webs}<li><a href="{url}">{name}<_buff_29_buff_li>{/webs}',
    '<ul>'
]);
templateManager.render('website', {
    webs: [{
        name: '百度',
        url: 'http://www.baidu.com/',
        getLen: function (item) {
            return item.url.length;
        }
    }, {
        name: '淘宝',
        url: 'http://www.taobao.com/',
        getLen: function (item) {
            return item.url.length;
        }
    }, {
        name: '腾讯',
        url: 'http://www.qq.com/',
        getLen: function (item) {
            return item.url.length;
        }
    }, {
        name: '网易',
        url: 'http://163.com/',
        getLen: function (item) {
            return item.url.length;
        }
    }, {
        name: '新浪',
        url: 'http://www.sina.com/',
        getLen: function (item) {
            return item.url.length;
        }
    }]
});
/*
<ul>
    <li><a href="http://www.baidu.com/">百度</a>,url长度21</li>
    <li><a href="http://www.taobao.com/">淘宝</a>,url长度22</li>
    <li><a href="http://www.qq.com/">腾讯</a>,url长度18</li>
    <li><a href="http://163.com/">网易</a>,url长度15</li>
    <li><a href="http://www.sina.com/">新浪</a>,url长度20</li>
<ul>
*/

Tagged as: No Comments
22Jul/11Off

ie中的跨域和ckeditor

在IE中修改了document.domain进行提权后(无论修改前后域名是否不同),iframe都会出现跨域问题。

ckeditor中检查跨域的代码,是根据判断document.domain和window.location.hostname是否相同来来判断跨域的。

isCustomDomain: function () {
   if (!this.ie) return false;
   var g = document.domain,
   h = window.location.hostname;
   return g != h && g != '[' + h + ']';
}

当我们执行在页面执行 document.domain = document.domain 后,执行 isCustomDomain 会返回false,这时ie会因为跨域,报拒绝访问的错误。

所以当我们,在修改document.domain时要进行判断,当新域和旧域不同时再修改document.domain

var oldDomain = document.domain,
    newDomain = 'xxx.com';
if (newDomain != oldDomain) {
   document.domain = newDomain;
}

 

7Jul/11Off

跨浏览器实现全选功能

跨浏览器实现全选功能,IE一行代码就OK,标准浏览器就比较麻烦了,需要使用selection、range、addRange实现。

var selectAll = (function () {
   if (arale.isIE()) { //IE
      return function (win) {
         win = win || window;
         win.document.body.createControlRange().execCommand('SelectAll');
      }
   } else { //标准浏览器
      return function (win) {
         var s, r;
         win = win || window;
         doc = win.document;
         s = win.getSelection();
         r = doc.createRange();

         r.selectNode(doc.body);
         s.addRange(r);
      }
   }
})();

19Apr/11Off

使用动态类型、对象构造而非继承

今天读了OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance,作者提倡使用动态类型(duck typing)、对象构造来代替继承。

关于动态类型的示例代码:

function bad (foo) {
    if ( ! (foo instanceof Bar) ) {
        throw new TypeError("This is an annoying restriction!");
    }
    return foo.baz(foo.quux(10));
}

function good (foo) {
    if ( !foo.baz || !foo.quux ) {
        throw new TypeError("We need foo to have baz and quux methods.");
    }
    return foo.baz(foo.quux(10));
}

下面是作者给出使用动态类型和对象构造的一个操作DOM的示例:

var quacksLike = function (obj, definition) {
    var k, ctor;
    for ( k in definition ) {
        ctor = definition[k];
        if ( ctor === Number ) {
            if ( Object.prototype.toString.call(obj[k]) !== "[object Number]" || isNaN(obj[k]) ) {
                return false;
            }
        } else if ( ctor === String ) {
            if ( Object.prototype.toString.call(obj[k]) !== "[object String]" ) {
                return false;
            }
        } else if ( ! (obj[k] instanceof ctor) ) {
            return false;
        }
    }
    return true;
};
Object.combine = function () {
    var newObj = {},
        i = 0,
        args = Array.prototype.slice.call(arguments),
        len = args.length;
    function copyProperty(k) {
        newObj[k] = args[i][k];
    }
    for ( ; i < len; i++ ) {
        Object.getOwnPropertyNames(args[i]).forEach(copyProperty);
    }
    return newObj;
};
window.$ = (function () {
    var undef, listen, customAPI;
    
    // Private function to turn 'thing' in to an array,
    // optionally slicing the first 'n' elems off.
    function toArray(thing, n) {
        return Array.prototype.slice.call(thing, n || 0);
    }

    // Private function to partially apply the first n arguments
    // to 'fn'.
    function curry (fn) {
        var args = toArray(arguments, 1);
        return function () {
            return fn.apply(this, args.concat(toArray(arguments)));
        };
    }

    // Private function to set the attribute 'attr' of a single
    // DOM node 'node' to 'val'.
    function nodeAttributeSetter (attr, val, node) {
        node.setAttribute(attr, val);
    }

    // Private function to get the value of the attribute 'attr'
    // on the DOM node 'node'.
    function nodeAttributeGetter (attr, node) {
        return node.getAttribute(attr);
    }

    // Do the work to determine the correct method for attaching
    // event listeners only once by using 'quacksLike' and an
    // immediately invoked function.
    listen = (function () {
        var w3c = {
                addEventListener: Function
            },
            ie = {
                attachEvent: Function
            };

        if ( quacksLike(document.body, w3c) ) {
            return function (event, fn, node) {
                node.addEventListener(event, fn, false);
            };
        } else if ( quacksLike(document.body, ie) ) {
            return function (event, fn, node) {
                node.attachEvent("on" + event, fn);
            };
        } else {
            throw new TypeError("Do not know how to attach event listeners.");
        }
    }());

    // This object is a mixin to be composed with the NodeList
    // results of querying the DOM by CSS selector. It provides
    // handy methods for getting and setting attributes,
    // attaching event listeners, and performing sub-queries by
    // CSS on these elements.
    customAPI = {
        attr: function (attr, val) {
            if ( val !== undef ) {
                this.forEach(curry(nodeAttributeSetter, attr, val));
                return this;
            } else {
                return this.length > 0 ? nodeAttributeGetter(attr, this[0]) : "";
            }
        },
        on: function (event, fn) {
            this.forEach(curry(listen, event, fn));
            return this;
        },

        find: function (selector) {
            var res = [];
            this.forEach(function (node) {
                res.push.apply(res, toArray(node.querySelectorAll(selector)));
            });
            return Object.combine(res, customAPI);
        }

    };

    return function (selector, context) {
        context = context || document;
        return Object.combine(Array.prototype, context.querySelectorAll(selector), customAPI);
    };

}());

//The mini DOM library might be used like this:

// The following two forms are equivalent.
// They select the same elements.
$("div").find("p");
$("div p");

// Disable all links.
$("a").on("click", function (event) {
  event.preventDefault();
  alert("Don't leave me all alone!");
});

// Get the id of the first paragraph.
$("p").attr("id");

// Disable all input elementss.
$(input").attr("disabled", "disabled");

// Do stuff with each matched element.
$("div.foo").forEach(function (el) {
  doStuffWith(el);
});

25Mar/11Off

CSS3动画实现mouseLeave和mouseEnter的不同效果

Eric在Inconsistent Transitions 中指出CSS3动画在浏览器中的不一致。

div:hover span {
    transition: 1s transform;
    transform: rotate(270deg);

}

上面代码只在鼠标移动到div上时有过渡动画(transition),在鼠标离开div时没有过渡动画。并给出如下解决文案:

div span {
    transition: 1s transform;
}
div:hover span {

    transform: rotate(270deg);
}

这样,当鼠标进入和离开div时,都有过渡动画效果。其实我们可以进一步实现鼠标进入(mouseEnter)和鼠标离开(mouseLeave)的不同效果。

/*mouseleave*/
div span {

    transition: 3s transform;
}
/*mouseenter*/
div:hover span {

    transform: rotate(270deg);
    transition: 1s transform;

}

这样,就实现了mouseLeave和mouseEnter的不同过渡效果。