android用phonegap实现websocket

由于android浏览器是不支持websocket的,想到了phonegap,包一层外壳使用android的socketChannel进行socket通信。当然,别人已经写好一个插件https://github.com/anismiles/websocket-android-phonegap 支持草案76和75,标准兼容性暂未测试
服务端用的小胖写的GT-WebSocketServer
====分隔线======
X10i/android2.3.3系统模拟器会关掉程序,真机会抛出一个socket连接错误。
miui android2.3.7,android2.1和4.0模拟器测试通过
=====分隔线=====
1,隔一段时间自动断开,服务器端的close事件也是异常的,并没有关闭phonegap连接的session。

待续

backbone.js实例Todo阅读笔记

// 一个backbone的实例,作者:
// [Jérôme Gravel-Niquet](http://jgn.me/). 这个demo使用了
// [LocalStorage adapter](backbone-localstorage.js)
// 你可以在浏览器中演示它.

//当DOM载入完之后加载应用:
$(function() {

    // Todo Model
    // 基础 **Todo** model 拥有 `title`, `order`, 和 `done` 属性.
    var Todo = Backbone.Model.extend({

        // todo的默认属性.
        defaults : {
            title : "empty todo...",
            done : false
        },

        // 保证每个model创建的时候都有`title`.
        initialize : function() {
            if(!this.get("title")) {
                this.set({
                    "title" : this.defaults.title
                });
            }
        },
        // 切换`done`状态.
        toggle : function() {
            this.save({
                done : !this.get("done")
            });
        },
        // 将Todo从*localStorage*和视图中删除.
        clear : function() {
            this.destroy();
        }
    });

    // Todo 列表
    // ---------------

    // 列表存储在*localStorage*中,代替存储在服务器中
    var TodoList = Backbone.Collection.extend({

        // 关联列表的 model.
        model : Todo,

        // 以`"todos"`名字空间保存所有Todo.
        localStorage : new Store("todos-backbone"),

        // 过滤已完成的Todo.
        done : function() {
            return this.filter(function(todo) {
                return todo.get('done');
            });
        },
        // 过滤列表,保留未完成的Todo.
        remaining : function() {
            return this.without.apply(this, this.done());
        },
        // 尽管我们在localStorage中是无序存储的,但是我们还是按顺序读取和保存的
        nextOrder : function() {
            if(!this.length)
                return 1;
            return this.last().get('order') + 1;
        },
        // 按照原来的顺序保存.
        comparator : function(todo) {
            return todo.get('order');
        }
    });

    // 创建一个全局列表 **Todos**.
    var Todos = new TodoList;

    // Todo Item View
    // --------------

    // todo item的DOM元素
    var TodoView = Backbone.View.extend({

        //列表标签.
        tagName : "li",

        // 为单个元素缓存模板.
        template : _.template($('#item-template').html()),

        // 单个元素的DOM事件.
        events : {
            "click .toggle" : "toggleDone",
            "dblclick .view" : "edit",
            "click a.destroy" : "clear",
            "keypress .edit" : "updateOnEnter",
            "blur .edit" : "close"
        },

        // TodoView视图监听 model的事件变化,重新渲染, **Todo** 和 **TodoView** 成一一对应的关系.
        initialize : function() {
            this.model.bind('change', this.render, this);
            this.model.bind('destroy', this.remove, this);
        },
        // 重新渲染单条todo的title.
        render : function() {
            var $el = $(this.el);
            $el.html(this.template(this.model.toJSON()));
            $el.toggleClass('done', this.model.get('done'));

            this.input = this.$('.edit');
            return this;
        },
        // 切换model的`"done"`状态.
        toggleDone : function() {
            this.model.toggle();
        },
        // 切换视图为`"editing"`,显示输入框.
        edit : function() {
            $(this.el).addClass("editing");
            this.input.focus();
        },
        // 关闭`"editing"`视图, 保存改变数据到Todo.
        close : function() {
            var value = this.input.val().trim();

            if(!value)
                this.clear();

            this.model.save({
                title : value
            });
            $(this.el).removeClass("editing");
        },
        // 如果输入 `enter`回车键, 保存编辑项目.
        updateOnEnter : function(e) {
            if(e.keyCode == 13)
                this.close();
        },
        // 移除单条Todo,销毁model.
        clear : function() {
            this.model.clear();
        }
    });

    // The Application
    // ---------------

    // **AppView**是最外层层UI.
    var AppView = Backbone.View.extend({

        // 使用页面上已有的HTML结构.
        el : $("#todoapp"),

        // app底部统计的模板.
        statsTemplate : _.template($('#stats-template').html()),

        // 为新建Todo和清除已完成Todo创建事件代理.
        events : {
            "keypress #new-todo" : "createOnEnter",
            "click #clear-completed" : "clearCompleted",
            "click #toggle-all" : "toggleAllComplete"
        },

        // 初始化的时候绑定 `Todos`列表的事件,add事件会添加到 *localStorage*.
        initialize : function() {

            this.input = this.$("#new-todo");
            this.allCheckbox = this.$("#toggle-all")[0];

            Todos.bind('add', this.addOne, this);
            Todos.bind('reset', this.addAll, this);
            Todos.bind('all', this.render, this);

            this.$footer = this.$('footer');
            this.$main = $('#main');

            Todos.fetch();
        },
        // 重新渲染只是刷新统计,其他不变.
        render : function() {
            var done = Todos.done().length;
            var remaining = Todos.remaining().length;

            if(Todos.length) {
                this.$main.show();
                this.$footer.show();

                this.$footer.html(this.statsTemplate({
                    done : done,
                    remaining : remaining
                }));
            } else {
                this.$main.hide();
                this.$footer.hide();
            }

            this.allCheckbox.checked = !remaining;
        },
        // 添加一个Todo项,并插入到`<ul>`中.
        addOne : function(todo) {
            var view = new TodoView({
                model : todo
            });
            this.$("#todo-list").append(view.render().el);
        },
        // 一次把所有的Todo添加到 **Todos** 列表中.
        addAll : function() {
            Todos.each(this.addOne);
        },
        // 新建一个Todo赋予属性.
        newAttributes : function() {
            return {
                title : this.input.val().trim(),
                order : Todos.nextOrder(),
                done : false
            };
        },
        // 在input中按回车键会新建一个**Todo** model并且保存到*localStorage*.
        createOnEnter : function(e) {
            if(e.keyCode != 13)
                return;
            if(!this.input.val().trim())
                return;

            Todos.create(this.newAttributes());
            this.input.val('');
        },
        // 清除所有已完成Todo models.
        clearCompleted : function() {
            _.each(Todos.done(), function(todo) {
                todo.clear();
            });
            return false;
        },
        toggleAllComplete : function() {
            var done = this.allCheckbox.checked;
            Todos.each(function(todo) {
                todo.save({
                    'done' : done
                });
            });
        }
    });

    // 创建一个 **App**.
    var App = new AppView;
});

nodejs资源打包下载优化

幸得点睛老辜笔,打包请求双变单!

之前做webslide在线设计的小工具就有一个打包下载的功能。使用了node-native-zip模块,原本实现的思路是分两步:

  1. 发起一次请求在服务端把资源打包成name.zip的文件(打包);
  2. 再发起一个普通的get请求获取name.zip的文件(下载);

代码片断如下:

app.get('/zip/:sid', function(req, res) {
    var archive = new zip();
    archive.addFiles([{
        name : '...',
        path : '...'
    }], function(err) {
        if(err) {
            res.write('0');
            throw err;
        } else {
            var buff = archive.toBuffer();
            fs.writeFile('public/zip/name.zip', buff, function(err) {
                if(err) {
                    res.write('0');
                    throw err;
                } else {
                    console.log('name.zip Finished');
                    res.write('1');

                }
            });
        }
        res.end();
    });
});

然后用普通的http请求name.zip文件下载到本地。

然后来看看第二种方式,设置contentType为application/zip先输出请求头,并且识别为zip文件。然后打包文件一遍读buffer一边输出请求正文,使用了zipstream模块,通过管道输出。
代码片断如下:

app.get('/zip/:sid', function(req, res) {
    var zip = zipstream.createZip({ level: 1 });
    zip.addFile(fs.createReadStream('...'), { name: '...' }, function() {
        zip.finalize(function(written) { console.log(written + ' total bytes written'); });
    });
    res.header('Content-Disposition', 'attachment;filename=' + sid + '.zip');
    res.contentType('application/zip');
    zip.pipe(res);
});

第二种方式的好处是:

  1. 请求立即下载,不需要二次请求;
  2. 数据只在内存中操作,不进行硬盘的IO操作;
  3. 下载是实时的,不需要缓存数据,更不需要维护硬盘的缓存区;
  4. 管道会自动限速,以速度慢的那一头为准;
  5. 可以并发成千上万的下载量,每个时间点只有少来资源被占用。

参考文献:Why node.js streams are awesome

关于box-flex的一些讨论结果

今天在群里和@6key同学还有@qbaty同学一起讨论了一下关于box-flex的宽度设置问题。

@6key同学提出的最原始的代码如下:

<!DOCTYPE html>
<html>
<head>
    <title>box</title>
    <style type="text/css">
        .box {
            /* basic styling */
            width: 300px;
            height: 95px;
            border: 1px solid #555;
            font: 14px Arial;

            /* flexbox setup */
            display: -webkit-box;
            -webkit-box-orient: horizontal;

            display: -moz-box;
            -moz-box-orient: horizontal;

            display: box;
            box-orient: horizontal;
        }

        .box > div {
            -webkit-box-flex: 1;
            -moz-box-flex: 1;
            box-flex: 1;
            /*width:100px;*/
            -moz-transition: width 0.7s ease-out;
            -o-transition: width 0.7s ease-out;
            -webkit-transition: width 0.7s ease-out;
            transition: width 0.7s ease-out;
        }

            /* our colors */
        .box > div:nth-child(1){ background : #FCC; }
        .box > div:nth-child(2){ background : #CFC; }
        .box > div:nth-child(3){ background : #CCF; }

        .box > div:hover {
            width:200px;
        }
    </style>
</head>
<body>
<div class="box">
    <div>deuxssssss</div>
    <div>deux</div>
    <div>deux</div>
</div>
</body>
</html>

demo地址:http://debug.cnodejs.net/html/1328843547100.html

问题是当.box > div的内容宽度不一样的时候,三个div并不是均分。

解决方式在.box > div中加上宽度即可,任何有效数值都行。只需保证.box下的子div宽度相等即可

但是问题又来了1动画没有了,2鼠标移上去div的宽度很怪异;

讨论得出的解决方案是:

  1. .box > div的宽度单位要和.box > div:hover的宽度单位一致,要么都是%要么都是px,否则没有动画;
  2. .box > div的宽度值相等,可以随意设置,每列就可以等宽;
  3. 根据实践得出,当3个div的宽度不一致(例如hover的时候)只有当3个div的总宽=box的宽度才回正确显示。如果3个div大于或者小于box宽度,三个div都会按照一定规则缩放,具体规则还有待研究。动手试试:http://debug.cnodejs.net/1328861122371

debug–在线代码调试工具

工具地址:http://debug.cnodejs.net
这个工具主要是为群里面同学们的提问发代码用的,webapp开发可以方便js调试。
主要功能点

  • 分享代码,仅支持html,javascript,css;
  • 代码高亮
  • 生成可运行的html文件
  • 支持手机访问运行页面,生成二维码文件地址;
  • 支持手机访问的console信息打印在pc的控制台,需要添加一段js,并且调用wa.log()

项目地址:https://github.com/xiaoqiang/debug