使用 AddJS 为网页自动添加自定义javascript脚本

addJS是一个chrome插件。view in github

在浏览网站的时候,常常会想自己写js操作一下页面的内容(比如:抓图片,提取下载链接,屏蔽广告什么的)。于是,就有了这个插件。

功能:自动在指定的url的网页中添加自定义的javascript脚本并运行。

现在就开始使用AddJS

首先从chrome商店安装AddJS: https://chrome.google.com/webstore/detail/addjs/aoahkpekljaimojhfefaiibfdcnmlhdi?hl=zh-CN

或者下载安装:/assets/download/addjs.zip

至此,AddJS就可以使用了。下面是一些基本功能的使用操作方法:

Read More

命令行推送个人文档至kindle

昨天同事发了一本pdf格式的电子书,以往的方式,我都是登陆自己的qq邮箱,然后发pdf作为附件,推送到自己的kindle接收邮箱上。
突然觉得,其实nodejs应该有这样的命令行工具,可以一条命令搞定,于是在npm上搜索了一下,果然有一个叫“kindle”的包。
但是安装以后,总是推送失败,大概开了下源码,估计是作者使用的nodemailer组件做了不兼容升级导致。

于是,为了以后传文件到kindle时偷个懒,就自己动手写了一个命令行工具,名叫ksend( https://github.com/hanan198501/ksend ),简单好用。 O(∩_∩)O~

使用指南

  1. 安装

    npm install ksend -g
    
  2. 设置默认发送邮箱,格式: 邮箱地址:密码

    ksend --from yourname@qq.com:yourpassword
    
  3. 推送,如下示例,推送 a.pdf 至 hanan@kindle.cn 这个kindle接收邮箱:

    ksend -m hanan@kindle.cn a.pdf
    

    以上命令,参数 -m 表示接收邮箱。自此,完成推送。

如果脚得每次都要敲 -m 接收邮箱 麻烦,可以设置默认接收邮箱:

ksend --to hanan@kindle.cn

这样,以后只需要如下命令即可推送:

ksend a.pdf

也可以同时推送多个文档:

ksend a.pdf b.pdf ../img/photo.jpg /Users/hanan/book/ooxx.txt

查看帮助:

ksend --help

ps: 记得把发送邮箱添加到您的kindle已认可的发件人电子邮箱列表哦。

基于 postMessage 和 localStorage 的跨域本地存储方案

HTML5 的 postMessage 为解决跨域页面通信提供了一套可控的机制, 而 localStorage 则提供了易用简洁的本地存储方案?
这两者结合起来,能否实现跨域本地存储呢 ?

答案是可以的。假设有 a.com 和 b.com 两个页面。我们想通过 a 页面去修改 b 页面的本地数据。 我们需要做如下步奏:

  • 在 a 页面创建一个 iframe ,嵌入 b 页面
  • a 页面通过 postMessage 传递指定格式的消息给 b 页面
  • b 页面解析 a 页面传递过来的消息内容,调用localStorage API 操作本地数据
  • b 页面包装 localStorage 的操作结果,并通过 postMessage 传递给 a 页面
  • a 页面解析 b 页面传递回来的消息内容,得到 localStorage 的操作结果

整个过程如下图:

Read More

如何绕过chrome的弹窗拦截机制

在chrome的安全机制里面,非用户触发的window.open方法,是会被拦截的。举个例子:

var btn = $('#btn');
btn.click(function () {

    //不会被拦截
    window.open('http://cssha.com')
});

上面的代码中,window.open是用户触发的时候,是不会被拦截的,可以正常打开新窗口。再看下面这个:

var btn = $('#btn');
btn.click(function () {

    $.ajax({
        url: 'ooxx',
        success: function (url) {

            //会被拦截
            window.open(url);
        }
    })
});

上面的代码中,用户没有直接出发window.open,而是发出一个ajax请求,window.open方法被放在了ajax的回调函数里,这样的情况是会被拦截的。

那么,当用户点击按钮,发出ajax请求,如何在ajax请求完成后再打开新窗口,又不被拦截呢? 接着往下看:

var btn = $('#btn');
btn.click(function () {

    //打开一个不被拦截的新窗口
    var newWindow = window.open();

    $.ajax({
        url: 'ooxx',
        success: function (url) {

            //修改新窗口的url
            newWindow.location.href = url;
        }
    })
});

上面的代码中,用户安点击按钮的时候,先打开一个空白页,再发ajax请求,在ajax回调里面修改新窗口的.location.href,这样就不会被拦截啦啦啦啦~~~

如何实现一个ECMAScript 6 的promise补丁

Promise最先是CommonJS工作组提出的一种规范之一,目的是为了解决js异步编程过程中,回调嵌套过深的问题。ECMAScript 6 规范化了Promise对象的语言接口,使其成了js的原生对象之一。

如果你还不了解ECMAScript 6 中的Promise对象,建议阅读http://es6.ruanyifeng.com/#docs/promise。ECMAScript 6 中的Promise对象与一些常用的promise库(如jquery,when.js)中的实现略有不同,本文讨论的实现,以ECMAScript 6 为准。
下面,我们来一起学习,如何在不支持ES6的浏览器上简单实现Promise,如有不对的地方,还请多多指正。

首先,我们来看看Promise构造函数。

var Promise = function (fun) {
        var me = this,
            resolve = function (val) {
                me.resolve(val);
            },
            reject = function (val) {
                me.reject(val);
            }
        me._st = 'pending';
        me._rsq = null;
        me._rjq = null;
        (typeof fun === 'function') && fun(resolve, reject);
    },
    fn = Promise.prototype;

构造函数接受一个异步的回调函数,并调用,回调函数的2个参数,分别为异步操作成功和失败时候要调用的改变Promise实例状态的方法。
_st用于存放当前实例的状态,初始值为”pending”,异步操作成功为”resolve”,失败为”reject”。_rsq用于存放异步操作成功的回调,_rjq用于存放异步操作失败的回调。
同时,把Promise.prototype挂在变量fn上,接下我们只要给fn添加方法就可以了。

Read More

Blog迁移到Github

由于Wordpress主机到期,而且wp的markdown插件非常坑爹,经常把本来些的好好的md格式转得乱七八糟。于是花了一天时间,把Blog搬到github来了。使用了Jekyll Bootstrap框架,下面是折腾过程中的一些体会:

  • 文章迁移: wp后台把数据导出为xml文件,再用exitwp转成md。
  • 评论迁移: Jekyll自带的disqus不太符合国情, 于是选择了国产的多说
  • 代码高亮: 使用Pygments
  • 文章摘要: Jekyll自带的显示摘要功能对HTML支持非常不好,我只好全文输出到一个script标签,再通过JS来截取并显示。
  • 搜索文章: 尝试了google站内搜索,但是发现好多冗余的页面也被搜出来了,最后决定自己用JS来实现一个简单的,只针对文章标题进行匹配的搜索功能。

迁移到Jekyll以后, 终于可以愉快地用md写blog了。

发布grunt-cptpl插件

view on Github:https://github.com/hanan198501/grunt-cptpl

grunt-cptpl插件可以将您的文本模板文件编译成javascript文件。在前端开发的时候,模板文本我们一般存放在一个隐藏dom节点,再通过javascript去获取这个节点的内容,将其编译。或者在写javascript的时候,我们手动通过拼接字符串的方式储存模板文本。grunt-cptpl使前端开发也可以像后端一样,把模板文本存放在单独的文件中,使我们的开发工作从繁琐的dom操作和拼串中解放出来,提高我们的开发效率。最主要的,模板文件作为单独文件存放,可以使我们的项目代码逻辑更加清晰,更具可维护性。

grunt-cptpl都做了啥?

grunt-cptpl会读取每个模板文件的文本内容,用指定模板引擎的预编译方法将其包裹起来,生成一个新的javascript文件。这个javascript文件文件里面的内容,其实就是模板引擎的预编译方法调用,传入的参数为模板文件的文本内容。这样我们就有了一个编译好的模板函数,要渲染的时候把数据传给它就好了。

Read More

popup组件中mask层的IE6 bug和兼容办法

我们在写一个popup组件时候,通常会加上一个元素作为mask层,用于遮盖住popup元素以外的页面内容。 而IE6下有个著名的bug就是select元素无法被遮盖,通常的做法,在IE6我们会在mask和和popup里面添加一个iframe来解决,一般的,iframe的css会是这样:

#iframe {
    display: block;
    width: 100%;
    height: 100%;
    _filter: Alpha(opacity=0);
    position: absolute;
    top: 0px;
    left: 0px;
    z-index: -1;
}

如果popup组件需要一个功能是:点击mask的时候,隐藏popup。 所以,我们会给mask加上click事件来完成这件事。但是,这个办法在IE6下却行不通,不管吧click添加在mask元素,还是添加到mask的iframe里面的document对象,都无法出发click事件。

由于mask只有一个iframe,而iframe里面又没有然后内容,在IE下其实我们点击mask的时候是相当于点击了mask的iframe的window里。解决的办法是在mask里面再添加一个可以被“点击”到的元素,这个元素撑满mask。这样鼠标点击这个元素时候,事件自然会冒泡的mask上,保证了在IE6下mask的click也能正常被触发。

<div id="mask">
    <div style="height: 100%"></div>
    <iframe id="iframe"></iframe>
</div>

重温CSS 3D

最近开始折腾一些CSS3的东西,发现以前走马观花看过的CSS 3D相关的属性几乎忘记的差不多了,今天就来通过一个小立方体的效果来温习下:

首先,我们来实现HTML布局,用一个div#arana作为“展台元素”,存放整个立方体容器div#box,div#box里面有6个子节点div.face,代表这个立方体的6个面。

<div id="arena">
    <div id="box">
        <div class="face">1</div>
        <div class="face">2</div>
        <div class="face">3</div>
        <div class="face">4</div>
        <div class="face">5</div>
        <div class="face">6</div>
    </div>
</div>

最外层的div#arena的作用类似于一个“展台”,我们可以想象,在“展台”上,我们需要拜访一台摄像机,来拍摄这个立方体。那么,这个摄像机,我们称之为“视点”,视点和立方体的距离,就是div#arana的-webkit-perspective属性值, 它和我们在画画的时候“透视点”的概念有点像,只不过,“透视点”在屏幕的后方,而“视点”在屏幕的前方。在立方体旋转,偏移的时候,我们还需要知道一个“基点”,3D变换的元素, 都会基于这个点来变化,“基点”通过-webkit-perspective-origin属性来定义,的默认值是50% 50%,分别代表“基点”距离元素本身左边和顶部的距离。

#arena {
    -webkit-perspective: 400;
    -webkit-perspective-origin: 50% 50%;
}

第二个容器div#box实际上就是我们的立方体,通过-webkit–transition属性,定义了立方体在变化时候的过度动画效果。-webkit-transform-style属性用于定义3d元素的透视效果,它有2个值:其两个参数,flat|preserve-3d. 前者flat为默认值,表示平面的;后者preserve-3d表示3D透视。这个属性应该用在作为立方体的容器元素上,而不是它里面的6个作为6个面的子元素。

#box {
    position: relative;
    margin: 100px auto;
    height: 200px;
    width: 200px;
    -webkit-transition: -webkit-transform 2s linear;
    -webkit-transform-style: preserve-3d;
}

定义6个“面”元素共同的普通css属性,注意,这里通过position: absolute使6面的“基点”位置保持一致。

.face {
    color: #FFF;
    font-size: 30px;
    font-weight: bold;
    position: absolute;
    background: rgba(50, 50, 50, 0.7);
    width: 160px;
    height: 160px;
    padding: 20px;
    border: 1px solid #333;
}

通过-webkit-transform属性分别设置六个面的偏移和旋转位置,使其刚好围成一个立方体。-webkit-transform属性有多种方法(函数),rotateX()、rotateY()、rotateZ()分别用于设置元素基于XYZ轴的旋转角度。translateX()、translateY()、translateZ()则分别用于设置元素偏离XYZ轴的距离。

.face:nth-child(1) {-webkit-transform: translateZ(100px);}
.face:nth-child(2) {-webkit-transform: rotateY(90deg) translateZ(100px);}
.face:nth-child(3) {-webkit-transform: rotateY(180deg) translateZ(100px);}
.face:nth-child(4) {-webkit-transform: rotateY(270deg) translateZ(100px);}
.face:nth-child(5) {-webkit-transform: rotateX(90deg) translateZ(100px);}
.face:nth-child(6) {-webkit-transform: rotateX(270deg) translateZ(100px);}

最后,我们通过js绑定keyup事件,来改变div#box的-webkit-transform值来达到旋转的目的。

var xAngle = 0, yAngle = 0;
document.addEventListener('keydown', function(e){
    switch (e.keyCode) {
        case 65: // left
            yAngle -= 90;
            break;
        case 87: // up
            xAngle += 90;
            break;
        case 68: // right
            yAngle += 90;
            break;
        case 83: // down
            xAngle -= 90;
            break;
    };
    var box = document.getElementById('box');
    box.style.webkitTransform = "rotateX("+xAngle+"deg) rotateY("+yAngle+"deg)";
}, false);