10
8
2015
12

ES6 好棒!

SegmentFault 的通知页里的链接总是会在新标签页打开。这对于我来说就多了一个需要关闭的标签页——我本可以在最后一条新通知时不打开新标签页的。我习惯由我自己来控制链接在哪里打开,会根据情况选择在后台新标签页打开或者在当前标签页打开。SegmentFault 通知页这样子在前台标签页打开对于我来说除了烦人之外便毫无用处

然而 SegmentFault 改来改去,最终似乎还是决定像很多国内网站那样让我这种用户厌烦。罢了,是时候让油猴出场了。这次尽情使用 ES6 好了=w=

// ==UserScript==
// @name          SegmentFault Enhancement
// @namespace     http://lilydjwg.is-programmer.com/
// @description   SegmentFault Enhancement
// @include       http://segmentfault.com/*
// @include       https://segmentfault.com/*
// ==/UserScript==

// jshint moz:true

if(location.pathname === "/user/notifications"){
  let elements = document.querySelectorAll('section.stream-list__item a[target]');
  for(let el of elements){
    el.removeAttribute('target');
  }
}

虽然不是第一次使用let了,但for...of是第一次使用。这感觉好棒!跟写了好长时间的 C 之后发现表达力强大的 Python 一样!

现在的 ECMAScript 越来越像语法怪异的 Python 了呢 ;-)

PS: 在网页里写上在新标签页打开有安全隐患的。

3
9
2014
8

GM 脚本:GMail 日期正常化

我所谓的「正常化」,就是适合人而不是机器读取的格式啦。比如你们说「12/2/14」这是哪天呢……明明可以显示成「20YY年M月DD日 上午H:MM」这样子的(鼠标移过去会出现),但是为什么非要我去移鼠标呢。于是就有了这个很简单的脚本:

// ==UserScript==
// @name           GMail 日期正常化
// @namespace      http://lilydjwg.is-programmer.com/
// @description    将 GMail 中的日期使用更适合人阅读的形式显示
// @include        https://mail.google.com/*
// @grant	   none
// ==/UserScript==

var doit = function(){
  var elements = document.querySelectorAll('span[alt]');
  for(var i=0, len=elements.length; i<len; i++){
    elements[i].textContent = elements[i].getAttribute('alt');
  }
};
document.addEventListener('overflow', doit);
window.addEventListener('focus', doit);

点此安装

Category: 火狐 | Tags: gmail GreaseMonkey
11
13
2013
2

GM 脚本:清除 Google Groups 中的翻译提示

终于忍受不了 Google Groups 网页里到处都是的「将本帖翻译成中文」提示了——

// ==UserScript==
// @name           Google Group 清理
// @namespace      http://lilydjwg.is-programmer.com/
// @description    不用将帖子翻译成中文……
// @include        https://groups.google.com/*
// @grant      none
// ==/UserScript==

function doit() {
  console.log('cleaning up...');
  var divs = document.evaluate('//div[@class="gux-confirm-panel-c"]/div/a[@class="GFLL15SM5B"]/parent::div/parent::div', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

  for(var i=0, len=divs.snapshotLength; i<len; i++){
    var div = divs.snapshotItem(i);
    div.parentNode.removeChild(div);
  }
}

var timer;

document.body.addEventListener('overflow', function(e){
  if(timer){
    clearTimeout(timer);
  }
  timer = setTimeout(doit, 500, false);
});

点击安装备用地址

Category: 火狐 | Tags: google 火狐 GreaseMonkey
11
4
2013
5

GM 脚本:Scrum for Trello

没找到这个家伙的 Firefox 版本。想它只是改改 Trello 的 UI 什么的,于是要来 crx 文件,直接把它的 JavaScript 文件扒出来了(顺便也发现了它的 GitHub 地址,不过已经晚了)。

按这里安装

移植过程中解决的问题:

  • PNG 和 CSS 等外部文件。两个小的 PNG 图片,我直接使用 Data URI 内嵌了。CSS 我用 yuicompressor 压缩之后也通过脚本加入了。
  • jQuery 1.7.1 与 Trello 所使用的 2.0.0 冲突了。改成直接使用页面上已有的 jQuery,顺便还省个不小的依赖。在 GreaseMonkey 中得unsafeWindow.jQuery来访问加上@grant: none指示
  • .live()方法没了……按文档改用.on()。共四处改动。.on()在替代.live()时语法不一样要小心不要弄错了。
  • 匹配 Board 名的正则失效了,修之。

PS: 火狐按住Ctrl键可以创建多个选区,在可视化编辑器里可以同时调整多个地方的样式,真方便呢=w=

Category: 火狐 | Tags: 火狐 GreaseMonkey
10
12
2013
24

GM 脚本:在 Disqus 中提示需要登录

Disqus 越来越受欢迎,然而,非 Disqus 用户评论越来越艰难

一开始,和 WordPress 一样,名字、电邮、网站。想要新评论通知?好呀,使用 Facebook、Twitter 或者 Google+ 登录下就好。

后来,「Twitter 用户,创建个 Disqus 帐号吧!」不想要 Disqus 帐号,那就不要登陆了,也甭想推广自己的博客,填上电邮地址显示个头像吧。当然,为了迫使你们登陆,名字和电邮信息也不像一般博客是记住的。下次继续填,继续勾选「以访客身份发布」。

现在,花了不少时间和心思写完很不错的评论,双击填名字的文本框填名字时,却经常发现刚展开的部分里那个「以访客身份发布」复选框没有了。「对不起,必须登录才能在此博客留言哦亲。」Holy shhhhhhhhhhhhhit!

此 GreaseMonkey 脚本为防止最后一种情况的发生,在你动手写下评论的时候明确告诉你不登录你的评论是发不出去的

不过,由于我现在取不到自己的 Disqus 帐号密码,所以不确定登录 Disqus 帐号之后这个脚本能否正确检测到。欢迎反馈!

点击安装

脚本全文如下:

// ==UserScript==
// @name        Disqus login required reminder
// @namespace   http://lilydjwg.is-programmer.com/
// @description Remind you if you can't post your comments because you aren't logged in
// @include     http://disqus.com/embed/comments/*
// @include     https://disqus.com/embed/comments/*
// @version     1
// ==/UserScript==

var check = function(){
  var el = document.querySelector('input[name="author-guest"]');
  if(!el){
    setTimeout(check, 100, false);
    return;
  }
  if(el.style.display == 'none'){
    console.log("login required");
    var msg = document.getElementsByClassName('placeholder')[0];
    msg.textContent = '需要登录 / Login Required!';
    msg.parentNode.addEventListener('blur', function(){
      var msg = document.getElementsByClassName('placeholder')[0];
      msg.textContent = '需要登录 / Login Required!';
    });
  }
};

setTimeout(check, 100, false);

点击安装


附:我始终认为,不管登陆评论能给用户和自己带来多大的好处,只要文章允许评论,来访者应当能够以最小成本发表评论并且署名。也就是,不需要注册,不需要登录,你就可以评论。最好支持 Gravatar 头像,最好支持链接到自己的网站,最好支持被回复时 Email 提醒。实际上本博客非登录用户需要填写验证码我已经很不爽了,只是 Chito 这个博客程序提供的另一种反垃圾策略——使用 Akismet——我这边已经坏掉了。

所以我越来越敬佩 WordPress。

Category: 火狐 | Tags: 博客 火狐 网页 GreaseMonkey
8
20
2012
7

GM 脚本:修正 github

从某时起,Github 和 Linux 一样,开始有着越来越多的 bug 和让人不舒服的地方。本文所附的 GreaseMonkey 脚本修正以下问题:

  • 项目首页默认下载文件格式是 zip 而不是 gzip
  • 新建项目后,从已有项目创建的提示命令使用 HTTPS 而不再是 SSH 协议。这直接导致 git 向用户询问用户名和密码,而不使用用户已经上传并确认的密钥。

Google Code 后来也加入了 git 支持,但是我极少使用。为什么呢?因为我讨厌输入用户名和密码!虽然 Github 没有像 Google Code 那样给你生成个随机密码,但这种麻烦且不安全的方式能避免我就决不容忍。你的密码会比密钥还长吗?你使用密钥时需要输入或者显示密钥的内容吗??

// ==UserScript==
// @name           github fixes
// @namespace      http://lilydjwg.is-programmer.com/ 
// @description    下载默认 gzip 格式,新建项目时使用 ssh 协议
// @include        https://github.com/*
// @version	   1.4
// @grant          none
// ==/UserScript==
 
var dl = document.querySelector('[icon_class=octicon-cloud-download]');
if(!dl){
  dl = document.querySelector('a[title^=Download]');
}
if(dl){
  dl.title = dl.title.replace('zip', 'gzip');
  dl.href = dl.href.replace(/archive\/[^\/]+.zip/, 'tarball/master');
  var infotext = dl.childNodes[dl.childNodes.length-1];
  infotext.textContent = infotext.textContent.replace('ZIP', 'GZIP');
}

var repourl = document.querySelectorAll('.js-live-clone-url');
var re = /https:\/\/github\.com\/([^\/]+)\/(.*)/;
var span, m;
var i, len;
for(i=0, len=repourl.length; i<len; i++){
  span = repourl[i];
  m = re.exec(span.textContent);
  if(m){
    span.textContent = 'git@github.com:'+m[1]+'/'+m[2];
  }
}

点此安装

2012年9月9日更新:跟随 github 的更新,修正修改默认下载的格式失败的问题。

2012年11月27日更新:跟随 github 更新,使用带版本号的下载链接地址。

2013年5月17日更新:跟随 github 更新,更新 CSS 选择器。

2013年6月18日更新:支持 新版界面

Category: 版本控制 | Tags: github GreaseMonkey
8
2
2012
14

GM 脚本:桌面浏览器登录招商银行手机版,及 mitmproxy 的初次使用

招商银行网银需要控件,只支持 Windows 和 Mac。但是手机版不需要安装任何软件可直接登录。通过桌面浏览器访问https://mobile.cmbchina.com/MobileHtml/Login/LoginA.aspx可以看到登录界面,但登录时被拒绝,弹出警告「为了您的资产安全,请用手机访问手机银行!」。更改 UserAgent 失败。通过 Firebug 发现其 POST 数据中包含从 JavaScript 取到的navigator.UserAgentscreen.widthscreen.heightnavigator.platform的值,以 XML 发送给服务器。于是尝试修改之。

这次使用 privoxy 不行了,因为是 HTTPS 加密连接,privoxy 看不到内容。于是用上了前不久才发现的工具mitmproxy,一个支持 SSL 的中间人代理,并支持交互、命令行和脚本化查看、编辑功能。在看了下请求数据后,按i输入要中断的请求的模式-u LoginA,在请求 URL 包含LoginA字样时中断以进行人工编辑。使用j键移动到停下来的橙色请求上:

中断浏览器请求

按回车显示详细信息,按e进行编辑,f选择编辑表单域,编辑完成后退回到请求列表界面,按a继续,再按a接受响应信息。

编辑POST表单

经过多次 Google 和编辑尝试,招行终于不再要求我使用手机访问了。不过很显示,我不能每次登录都使用 mitmproxy 手工编辑对不?于是写了个 GreaseMonkey 脚本。

此脚本用到了unsafeWindow,也就是页面本身中的那个window对象,而不是被GreaseMonkey wrap 过的。这样才能修改页面中定义的函数。注意据说这样做有安全风险。详见 GreaseMonkey Wiki

点击此处安装此脚本早已失效

Category: 火狐 | Tags: GreaseMonkey 火狐 mitmproxy
3
3
2012
6

GM 脚本:github 默认下载格式改为 gzip

不知道为什么,新版 github 在项目首页上只提供 zip 归档格式的下载,要下载 gzip 包还需要点击 download 到新页面去。可是我就是不喜欢 zip 格式嘛——

// ==UserScript==
// @name           github 下载默认 gzip 格式
// @namespace      http://lilydjwg.is-programmer.com/ 
// @description    把默认的 zip 下载按键改成 gzip 的
// @include        https://github.com/*
// ==/UserScript==
 
var dl = document.querySelector('.btn-download');
if(dl){
  dl.title = dl.title.replace('zip', 'gzip')
  dl.href = dl.href.replace('/zipball/', '/tarball/');
  var icon = dl.querySelector('.icon');
  if(icon){
    icon.nextSibling.textContent = 'GZIP';
  }
}
Category: 火狐 | Tags: GreaseMonkey 火狐 linux github
1
10
2012
0

GM 脚本:MediaWiki 脚注 tooltip

MediaWiki 使用脚注插件后就多了脚注功能。可无奈这插件把网页当成纸质书了,脚注得点击跳转后才能看到内容。我不胜其烦,遂作此脚本。只对我自己的 wiki 和英文维基百科启用了,因为另一个常去的 MediaWiki 站点——中文维基百科有个导航Popup小工具更好用。我还是一如既往地没有使用 jQuery。

// ==UserScript==
// @name           MediaWiki 脚注 tip
// @namespace      http://lilydjwg.is-programmer.com/
// @include        http://localhost/wiki/*
// @include        https://en.wikipedia.org/wiki/*
// ==/UserScript==

var showTip = function(evt){
  var el = evt.target;
  var left = el.offsetLeft;
  var top = el.offsetTop;
  var tip = document.getElementById('gm-tip');
  //not el.href here; we need the original one
  var tipTextEl = document.getElementById(el.getAttribute('href').substring(1));
  tip.innerHTML = tipTextEl.textContent.substring(2);
  tip.style.top = (top+5) + 'px';
  tip.style.left = (left+25) + 'px';
  tip.style.display = 'block';
};

var hideTip = function(){
  var el = document.getElementById('gm-tip');
  if(el){
    el.style.display = "none";
  }
};

var cites = document.querySelectorAll('.reference > a');
// var cites = document.querySelectorAll('a[href^="#cite_note-"]');
for(var i=0, len=cites.length; i<len; i++){
  cites[i].addEventListener("mouseover", showTip, false);
  cites[i].addEventListener("mouseout", hideTip, false);
}

var setup = function(){
  el = document.createElement('div');
  el.setAttribute('id', 'gm-tip');
  el.style.display = 'none';
  el.style.position = 'absolute';
  el.style.zIndex = '100';
  el.style.border = '1px #1e90ff solid';
  el.style.backgroundColor = 'rgba(115, 201, 230, 0.75)';
  el.style.padding = '0.2em 0.5em';
  var parentEl = cites[0].offsetParent;
  parentEl.appendChild(el);
};
if(cites.length > 0){
  setup();
}
10
13
2011
2

GM 脚本:在 Chito 后台评论列表中显示评论者的地址位置

GreaseMonkey 代码如下:

// ==UserScript==
// @name           is-programmer 后台评论地理位置显示
// @namespace      http://lilydjwg.is-programmer.com/
// @description    通过 JSONP 查询 IP 地址对应的地理位置并显示
// @include        http://*.is-programmer.com/admin/comments*
// @include        http://*.is-programmer.com/admin/messages*
// ==/UserScript==

var qurl = function(ips){
  return 'http://localhost:2000/queryip?q=' + ips.join(',') + '&cb=?';
};

var letsJQuery = function(){
  var ip_header = document.evaluate('//th[@class="helpHed" and text()="IP"]', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
  $(ip_header).after('<th class="helpHed">地址</th>');
  var ip_cells = document.getElementsByClassName('comment_ip_col');
  var ips = [];
  var i;
  for(i=0, len=ip_cells.length; i<len; i++){
    ips.push(ip_cells[i].textContent);
  }
  $.getJSON(qurl(ips), function(data){
    var ans = data.ans;
    for(i=0, len=ip_cells.length; i<len; i++){
      $(ip_cells[i]).after('<td class="comment_addr_col">'+ans[i]+'</td>');
    }
  });
};

function GM_wait(){
  if(typeof unsafeWindow.jQuery == 'undefined') {
    window.setTimeout(GM_wait, 500);
  }else{
    $ = unsafeWindow.jQuery;
    letsJQuery();
  }
}

GM_wait();

光有这个脚本是不够的,因为没有 IP 地址数据库。我不想像这样用 chrome 权限调用子进程之类的手段,而是从本地 HTTP server 取得数据,这样以后可以方便地扩展。HTTP server 使用 Python 的 tornado 框架写成,名字是“Web Service Provider”的缩写:

#!/usr/bin/env python3
# vim:fileencoding=utf-8

from subprocess import getoutput
from functools import lru_cache
import json

import tornado.web
import tornado.httpserver
from tornado.options import define, options

@lru_cache()
def lookupip(ip):
  return getoutput('cip ' + ip).replace('CZ88.NET', '').strip() or '-'

class IPHandler(tornado.web.RequestHandler):
  def get(self):
    q = self.get_argument('q').split(',')
    addr = []
    for ip in q:
      a = lookupip(ip)
      if 'illegal' in a:
        a = '(错误)'
      elif '\n' in a:
        a = ''
      addr.append(a)

    ans = {
      'ans': addr,
    }
    cb = self.get_argument('cb', None)
    if cb:
      self.set_header('Content-Type', 'text/plain; charset=utf-8')
      self.write('%s(%s)' % (cb, json.dumps(ans, ensure_ascii=False)))
    else:
      self.write(ans)

def main():
  define("port", default=2000, help="run on the given port", type=int)

  tornado.options.parse_command_line()
  application = tornado.web.Application([
    (r'/queryip$', IPHandler),
  ],
    debug=True,
  )
  application.listen(options.port)
  tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
  try:
    main()
  except KeyboardInterrupt:
    pass

用的是 Python 3.2,我很喜欢它的lru_cache装饰器。

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com