我的技术和生活

  • Windows 8 64bit,Chrome 18,display:none的<input type=”file” />无法通过JS手工触发click事件的方法来打开选择文件的窗口。添加到页面内也不行,不知道是不是所有不显示的标签都不会广播click事件,回头验证下。

  • Window 8,使用管理员账户启用Chrome,会无法从桌面拖动内容到网页当中去。

  • 漫游暹粒 – 酒店

    漫游暹粒 – 酒店

    我和媳妇都是酒店控,对旅游中的酒店尤其在意;不过因为手头并不宽裕,所以选酒店时着实下了一番功夫,最后老婆选定两家:Petit Villa Boutique & Spa(以下简称Petit)和Golden Banana(后面简称GB)。虽说暹粒的消费水平较高,但是酒店的价位相较富国岛来说,却要便宜一些,可能因为小旅社(Guest House)太多了吧。从Agoda订的Petit,450/晚,从Booking订的GB,650/晚。

    还是按老传统,先上照片吧。我们对Petit满意得多,而且头几天兴致高,所以Petit的照片也就多一些,GB的照片就少多了。(点开图片,有详细描述哦)

    接下来,按时间顺序,先说Petit吧。Petit是我们此行最好的酒店,性价比很高。装修风格可以照片,偏古朴、清幽,蓝色的游泳池不仅增加了神秘感,也使得整体风格没有陈旧下去。酒店不大,中间一条线是大门>迎接处>餐厅>游泳池>吊床,将整个酒店一分为二,两边都是客房。客房很少,只有7间,不过可以我们看到已经开始加盖二楼了,可能是有点供不应求吧。

    Petit的游泳池24小时开放,但是房间的隔音很好,所以并不会听到噪音。酒店的服务人员穿着也很讲究,男的都是白衬衣+黑裤子,头发梳得一丝不苟;女子有穿白衬衣蓝裙子,饭店服务员稍有不同。不过看起来都很舒服。还有一个服务生在学中文,他会用简单的中文跟我们对话,老婆还教给他一个词:“靠谱”。

    Petit的饭菜很好吃,种类也很丰富,美中不足就是上得太慢。我们推测有两个原因:厨子不够;严格要求顺序。他们家很讲究,吃一顿饭用3~4种勺子,对应不同的餐食;沙拉吃完上才会主菜,主菜吃完自动上甜品。相比之下,老市场的餐厅让人失望,尤其是穷游攻略上推荐的几家。

    这里的马杀鸡也给了我们意料不到的惊喜。吴哥窟旅游很累,2天下来腰腿疼痛的厉害,于是我们就各要了一个钟头的按摩,加油12刀,不要油10刀。按完发现比之国内同档次的好很多,手法到位,力道均匀,而且时间绝对够数,绝不缺斤短两。后来每天我们都要按一次,还给媳妇开了个Spa的荤。如果大家以后要去,千万不要错过。

    要说不足呢,就是酒店位置比较偏僻了,一般的托托车司机都不知道路。总之,如果大家要去暹粒游玩,强烈推荐这家酒店。

    接下来说说GB。因为Petit珠玉在前,所以入住GB并没给我们带来什么惊喜,当然,期待的大浴盆也没让我们失望。GB比起Petit来大了不少,我至少看到4个庭院。我们住的是最靠里的一个。

    GB的饭不太好吃,虽然也不贵。游泳池限时开放,而且房间隔音不好,我们的房间就在小瀑布旁边,在屋子里会听到哗哗的声音。不知为何,我们这座院子里都是男的,女的很少;而坐托托车经过别的院子时,就看到里面有外国MM裸胸丁字裤比基尼晒日光浴!是在让人无法对这家酒店感到满意啊……他们家的服务员打扮也很粗旷,一水的黑皮男,背心+短裤,完全不像Petit里档次很高的样子。

    GB的按摩比Petit略贵,而且还有外面OEM的。不过尝试了下,26+刀的也不比10刀的好到哪儿去。我们打算如果将来再来暹粒,就在Petit做90分钟的,哈哈。

    GB的浴盆还是很过瘾,如果不考虑走光的话,可以泡的很舒服。这点还是没让我们失望,他们家的热水供应比Petit好很多,Petit可能是太阳能的,夜里和早上都不太好。

    最后总结一下,暹粒的酒店比较便宜,性价比都很高。GB也不错,只不过比起Petit来,缺乏亮点——当然,如果能订到泳池边都是比基尼裸胸晒日光浴MM的那个院子,还是值得一去的。

  • Backbone.js经验两则

    Backbone.js经验两则

    使用HTML5,实现从桌面拖拽到网页

    使用HTML5新增加的API,可以很方便的实现拖拽, 包括从桌面拖拽到网页上(部分浏览器比如Chrome还可以把东西从网页拖拽到桌面),这个操作不在今天的讨论之内,可以参考:NATIVE HTML5 DRAG AND DROP这篇文章,讲得足够详细了。

    当我试图在Backbone框架上使用这个功能的时候,问题出现了。开始我没多想,直接这么写的:

    var myView = Backbone.View.extend({
      events: {
        "drop img": "img_dropHandler"
      },
      img_dropHandler: function (event) {
        var reader = new FileReader();
        var img = event.target;
        reader.onload = function (event) {
          $(img).attr('src', event.target.result);
        }
        result.readAsDataURL(event.dataTransfer.files[0]);
      }
    });
    

    结果运行时提示我,event对象没有dataTransfer属性。我用的是最新版本的Chrome,理应是对HTML5支持最好的,而且文中也说代码在Firefox和Chrome下运行通过。后来检查了一下event,发现是f.Event,似乎原本应该是MouseEvent或者Event什么的。于是我把属性展开,看到了originalEvent这个属性,是MouseEvent;再展开originalEvent,就看到了dataTransfer属性,里面有期望中的所有属性。看来是Backbone并没有直接使用原始事件,而是封装了一层再广播。(更正)事件代理是通过jQuery来做的,jQuery在这里把原始事件封装了一层再进行转播,导致原始事件的属性没有完全复制。解决方法很简单,多写几个字母就行了:

    // 其它地方都一样
    result.readAsDataURL(event.originalEvent.dataTransfer.files[0]);
    

    Model中数组的处理

    先看一段代码:

    var ModelClass = Backbone.Model.extend({
      defaults: {
        contents: []
      }
    });
    var model1 = new ModelClass();
    var arr = model1.get('contents');
    arr[0] = 'haha';
    var model2 = new ModelClass();
    console.log(model2.get('contents'));
    

    大家猜猜结果是什么?竟然是“[‘haha’]”!这个我只能认为是Backbone.js的bug了。解决方法是先复制一个数组,对数据操作后再赋值回去,如下:

    var model1 = new ModelClass();
    var arr = model1.get('contents').concat();
    arr[0] = 'haha';
    model1.set('contents', arr);
    
  • 漫游暹粒 – 概述

    漫游暹粒 – 概述

    Petit迎宾处夜景
    Petit迎宾处夜景

    去年从富国岛回来后,我和我媳妇合计了下,觉得这种旅游真好,我们也能负担,于是就义无反顾的加入了出境游大军。不过,相比那些旅行达人,工作半年玩半年,我们俩的档次低很多,平时还要老实上班,只是计划每年总要出去耍几次。后来知道亚航、港龙定期有打折机票促销,旅程可以提前一年安排,开销会更低,出去耍的想法就更强烈了。 (更多…)

  • 启程,目标暹粒

    自从去年从富国岛回来,我就爱上了出国游。所以今年准备再出去玩两次。

    小学的时候,跟我爸走南闯北,去过西安、张家界、上海;高中大学期间跟同学一起玩了青岛、黄山、连云港。工作后,因为有了老婆,多了一家人,而假期反而少了,所以有点时间不是郑州就是重庆。去年蜜月,当我们登上越南富国岛,一下子重新找到了旅游的感觉。

    近些年国内旅游市场很乱,不管多大品牌,该出事儿还是要出事儿。我觉得跟我们信仰缺失有关。出了国发现,越南人并不贫穷,人家很富足,家家门口挂国旗;小小富国岛上N所学校,基本每个村落都有;岛上租摩托押金都不要,路边店喝冷饮、补车胎,该要多少是多少。跟同事说要出国玩,她说,她也想出国玩,但是出去一怕不安全,二怕被宰——她是海南人,我反对地域歧视,可是三亚那些宰人的店,一顿饭钱够我东南亚玩一礼拜了……

    不吐槽了~~暹粒,吴哥窟是历史名城,去之前做了不少功课。这次相信会玩得更好。明天出发,广州中转,大概晚饭时间到暹粒。我们选了两家酒店,都是比较豪华的——跟团的别想了——行程安排的比较松散,找点时间写代码,尝试下封闭开发,哈哈。

    啊,对了,姆依姆依这几天只能委托堂哥堂嫂照顾了,希望一切无碍。可惜不能带狗出去玩,太可惜了。

    照片攻略随后奉上。

  • CMDN Club 16期归来

    下午去参加了“CMDN Club 16期——跨平台开发框架PhoneGap入门与实践”,有一些收获,整理记录下来。

    1. 会上着重讲了Android和iOS下使用plugin扩展PhoneGap功能的方法。
      1. Android上使用prompt实现。js通过prompt发送请求,WebView侦听到prompt后,解析并进行处理。处理完之后,将返回的数据放在内建的XHR服务器上;而应用在发出请求后,就在不断轮询,得到数据后返回给预先设定的callback。
      2. 比较特殊的地方在于,Android支持同步和异步两种形式,一般推荐使用异步,这样进程就不会被锁死,用户体验得以保障。
      3. iOS版则是建立一个iframe,把请求格式化后放进iframe的src。WebView接触到特殊标记的url就会进行处理,得到返回值之后,将js交给WebView处理。因为使用了iframe,所以操作都是异步无阻塞的。
    2. PhoneGap默认提供了很多本地api的封装,不过想要达到native的标准,自己补充插件是必不可少的。
    3. PhoneGap已经捐赠给Apache基金会,以后叫Cordova,代码组织会更加规范,而且会向AMD方向发展。看来研究下Require.js也很有必要。
    4. Weinre是个调试利器,非常适合调试移动设备,不仅是PhoneGap,日常开发也会很有用。
    5. Adobe Shadow是在Weine基础上封装的一个工具,目标是同时调试多台设备,不过目前还处在实验室阶段,将来会是非常好用的产品。持续关注吧。
    6. jQuery Mobile性能不是一般差,看来很有必要研究下Sencha Touch。
    7. www.phonegap.cn是个非官方组织,今天第二位嘉宾就是它的站长,看起来很靠谱的一个人。
    8. 对于我之前提到的防盗链问题,他也没有好建议,只提出一些想法:
      1. 使用插件抓取图片,转换为base64字符串,再显示出来。没记错的话这样会有兼容性问题,而且html也会很难看。
      2. 修改PhoneGap代理所有http请求。这个难度比较大。
    9. 现在没有命令行工具,他们正在开发。希望肉大师第一版完工的时候能用上。
    10. 会上有不少非人类思维的提问者,我感觉嘉宾都很迷茫。
  • 我将一口泡馍送里嘴里,突然意识到,又一个卖盐的兄弟,殉职了。

  • 舒心汤馆(一)

    那天做了三个梦,分别关于阴阳界、泡沫宇宙、社会问题。其中第一个梦也就是关于阴阳界的感觉比较好,温馨鬼故事,所以改写成一篇小说。以下文字纯属虚构,如有雷同,真的是巧合……

    (更多…)

  • 重写Backbone.js的加载动作

    居然在Google里找不到类似的情况,难道只有我一个人会有这种疑问么……

    我在使用Backbone的时候,遇到一个问题:我需要用 Backbone.Model 或者 Backbone.Collection 来加载一些远程数据,一般来说都是静态文件,比如HTML或者XML,既不满足RESTful,也不是JSON;虽然不很符合Backbone的要求,不过因为是静态的,所以我觉得ajax应该都没问题。当我按照这个思路写下去,一般就是这样:

    var MyModelClass = Backbone.Model.extend({
      url: 'config.xml',
      parse: function (response) {
        console.log(response);
      }
    });
    var model = new MyModelClass();
    model.fetch();
    

    但是运行之后,我发现被覆盖的 parse 没有执行。然而查看网络,目标文件已经被正常加载。作为Backbone的初学者,也不知道问题出在哪里。不得已找来源码跟踪,发现Backbone实现 fetch 是委托给 Backbone.sync 方法,但是在实现的时候会把数据格式设置为json:

    // http://documentcloud.github.com/backbone/backbone.js
    // Default JSON-request options.
    var params = {type: type, dataType: 'json'};
    

    而 jQuery 1.4版之后,会对返回的数据格式进行验证,如果不符合就抛出异常。所以当我那些不是 JSON 但声明是 JSON 的数据加载完毕后,jQuery 就会抛出异常,于是覆盖的parse也就不执行了。

    如果远程数据不是JSON,需要覆盖数据加载逻辑时,就应该覆写 .fetch(),比如这样:

    var MyModelClass = Backbone.Model.extend({
      url: 'config.xml',
      fetch: function () {
        $.ajax({
          url: this.url,
          context: this,
          success: this.parse
        });
      }
      parse: function (response) {
        console.log(response);
      }
    });
    
    var model = new MyModelClass();
    model.fecth();
    

    我认为不应该重写 .sync(),因为实际应用中,不同的 Model 和 Collection 可能要提供加载远程数据、加载模板、与 LocalStorage 交互等不同的功能,重写sync也很难满足需要。所以不妨直接在不同的类里面覆盖各自的fetch、save方法。


    这篇文章还有人看,那就更新一下。(2016-11-25)

    Backbone 用的久了,发现它其实设计了更好的办法解决这种问题:调用 .fetch() 的时候传递 options

    // 以下代码通过继承并覆写 .fetch() 方法,告知 jQuery 返回数据类型是 XML
    var MyModel = Backbone.Model.extend({
      fetch: function (options) {
        options = options || {};
        options.dataType = 'xml';
        return Backbone.Model.prototype.fetch.call(this, options);
      },
      parse: function (response) {
        console.log(response); // jQuery 会帮我们分析这个 XML
      }
    });
    

    这种方式比上面的好得多。