2009年2月20日星期五

尝试IE8 RC1

其实前不久才升级了IE7,但因为CSS Filter导致ClearType强制关闭的问题,我又不愿意挂上GDI++,一直用得不太舒服。这几天看到IE8 RC1发布的消息,虽然有评测说IE8内存占用率很高,但想想用Maxthon应该影响不大,于是就装上试了试,感觉变化还蛮大的。

UI有变漂亮一点,菜单上多了很多Icon。“管理加载项”对话框Live风格明显,不过总觉得有点粗糙。内存占用似乎是比较高一点,但速度还不错。地址栏跟Chrome一样了,会突出显示顶级域名主体。

新增加功能方面。“加速器”(Accelerators)这个名字怪怪的,作用其实和BHO类似,叫Launcher或许还准确点。SmartScreen的作用机制不明,如果跟IE7反钓鱼功能类似,那其实没什么用。InPrivate隐私保护模式开启时,Cookie、历史纪录、Internet临时文件等内容不会被保存,工具栏和扩展也会被禁用。简而言之,都没多大用处。

比较实在的变化都在最终用户看不到的地方。IE8增加了一个看起来很强的调试工具,随便试了试感觉蛮好用的。JScript和VBScript更新至v5.8,内置了JSON对象,XMLHttp可以处理Timeout事件,以及一个新增的跨域访问对象XDomainRequest。尤其是XDomainRequest,如果Mozilla和Chrome等不跟进的话,网页设计师会很挣扎吧,真是命苦。

接下来就是我比较关心的部分。IE8有三种渲染模式,分别是IE5 Quirks模式、IE7 Strict模式和IE8 Standards模式。为了兼容IE6时代的网页,有一个Quirks模式好理解。但IE7自2006年10月发布至今不到3年,其造成的兼容性问题竟然也需要一个独立的Strict模式来解决,真是有出息。

默认情况下,IE8使用DOCTYPE来决定使用何种渲染模式。下表是不同DOCTYPE时,document.documentMode属性的值,5表示Quirks模式,8表示Standards模式。

Empty 5
<!DOCTYPE HTML> 8
HTML 5
HTML 2.0 5
HTML 3.0 5
HTML 4.0 8
HTML 4.0 Transitional 5
HTML 4.0 Strict 8
XHTML 8
XML 8

如果想为特定页面指定渲染模式,需要在head标签里加一个meta标签,比如想强制使用Standards模式可以写:<meta http-equiv="X-UA-Compatible" content="IE=8" />

IE8的默认渲染模式是IE=EmulateIE8,即根据DOCTYPE判断使用Quirks模式还是Standards模式。类似的还有一个IE=EmulateIE7,目前的Maxthon中默认采用了该模式(可以在设置中勾选“启用 IE8 标准渲染模式”启用EmulateIE8)。

扯这么多,我想说的其实还是CSS Filter导致ClearType强制关闭的问题。在IE8的Quirks模式和Strict模式下,这个问题依然存在,Standards模式下倒是修复了。所以,针对有问题的网页,只要在广告过滤器里把<head>替换为<head><meta http-equiv="X-UA-Compatible" content="IE=8" />即可解决问题。

顺带一提,Standards模式字体渲染效果不错,包括过去一直存在的微软雅黑字体下划线位置错误的问题也一并修复了。

2009年2月10日星期二

如何开启Live Mail的POP3功能

在网上看到Live Mail,也就是曾经的Hotmail、MSN提供POP3支持的消息,一点也不高兴:该功能只对有限的几个国家的用户开放,显然,不包括中国。

好吧,根据以往的经验,试试能不能冒充一下外国友人,享受非国民待遇。因为某些原因,先试了荷兰,不成,接着试了英国和加拿大,还是不成。心想说微软不至于这么小气吧,于是Google之,找到了这里,照做之下还是不成。

最后才发现,原来是我用以测试的Magic Mail Monitor这个程序有问题。可叹我用了他那么些年,直到正式皈依GMail为止。GMail的POP3貌似是独立的,在Web中删除或归档的信件不会反映在POP3连接中,于是把POP3当作检查新邮件的手段就不好使了。

呃,不废话了,以下是开启Live Mail POP3功能的办法:

  • 用浏览器打开:https://account.live.com/?mkt=en-gb
  • 点击“Registered information”
  • 将Home location和Work location的如下项都改掉
    • Country/region: United Kingdom
    • Postal Code: WC1B 3DG
    • Constituent Country: England
    • Time zone: London, United Kingdom - GMT
  • 保存后,点页面最上边的“Mail”进入Live Mail
  • 右边的“选项”,点一下,把界面改成英文

这样就搞定了,似乎并不需要登出登入。在激活了POP3功能之后,可以把你的个人信息改回去。或者干脆就冒充外国人到底,只改Work location。为了在FF中好看点,顺便偷图一张。

EMail客户端需要设置的参数如下:

  • 用户名:SomeBody@hotmail.com
  • 服务器:pop3.live.com
  • SSL:要且一定要
  • 端口号:995

我终于可以用GMail通过POP3来收取Live Mail的信件,实现天下同G了?很遗憾,可以,也不可以。

跟GMail刚开始提供IMAP功能时的毛病一样,Live Mail目前的POP3功能不是8-bit clean的,未经编码的GBK、UTF8通通有问题。用通俗的话来说就是有可能会乱码,所有中文字符显示为问号。因此暂时还必须在Live Mail中保存原件,或者只把POP3功能当检查新邮件的手段用。

目前开放了POP3功能的国家名单中还有日本,所以这个问题微软肯定早就知道了,等着吧。

另外还有一个不大不小的问题,通过POP3收取的信件不包括junk,也没有选项可以进行设置。

在折腾的过程中还顺便发现Live Mail Web端Bug一枚:

当邮件标题为未编码的GBK字符时,如界面语言为中文则显示正常,否则邮件列表界面中会乱码。点开邮件,只要其Content-type正确,在任何语言的界面下,包括邮件标题都显示正常。

不过无所谓了,我只希望POP3的Bug尽快修复,我就可以早一点跟Live Mail界面上那条硕大的广告说再见了。

2009-3-11 12:54 Update:

8bit clean问题已修复。

2009年2月8日星期日

帮人搬家之导入Blogger

在跟我实在不熟的Python奋斗了若干时间后,我才悲哀地发现,Blogger现在对API导入添加了限制。每天通过API发布一定量的帖子后,再发就得输入验证码,我太阳。

还好,通过尝试发现,Blogger GUI中提供的导入功能可以用。不然的话,某人的小500张帖子,还不知道要弄到什么时候去。

用Javascript写这段代码,看起来就乱七八糟的。不过没办法,还是因为Ruby没有好用的HTMLParser。虽然可以在Ruby里可以调用ActiveX COM,但是会有回车换行符混乱的问题,将就用吧。

// 这个函数用来处理内文需要修改的地方
// 如果没什么可改的那就不需要调用
function parseHTML(src) {
  var doc = new ActiveXObject('htmlfile');
  doc.write(src);
  
  var es = doc.getElementsByTagName('img');
  for (var i=0;i<es.length;i++) {
    var s = es[i].src;
    if (s.indexOf('foto.ycstatic.com')>0) {
      es[i].src = imgmap[encodeURIComponent(s)];
    }
  }
  return doc.body.innerHTML;
}

function parseXML(path) {
  var xml = new ActiveXObject('MSXML2.DOMDocument.3.0');
  xml.load(path);
  
  var ret = "<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href='http://www.blogger.com/styles/atom.css' type='text/css'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7617775710611731452.archive</id><updated>2009-02-08T18:10:51.127+08:00</updated><title type='text'>幺贰和叁</title><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://otnth.blogspot.com/feeds/archive'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7617775710611731452/archive'/><link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='http://www.blogger.com/feeds/7617775710611731452/archive'/><link rel='alternate' type='text/html' href='http://otnth.blogspot.com/'/><author><name>小八</name><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator>";
  var es = xml.getElementsByTagName('feed/entry');
  for (var i=0;i<es.length;i++) {
    ret += '<entry><id>' + es[i].getElementsByTagName('id')[0].firstChild.nodeValue + '</id>';
    
    ret += '<published>' + es[i].getElementsByTagName('published')[0].firstChild.nodeValue + '</published>';
    ret += '<updated>' + es[i].getElementsByTagName('updated')[0].firstChild.nodeValue + '</updated>';
    
    ret += "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/blogger/2008/kind#post'/>";
    var cats = es[i].getElementsByTagName('category');
    for (var j=0;j<cats.length;j++) {
      if (cats[j].getAttribute('scheme') != 'http://www.google.com/reader/') {
        ret += "<category scheme='http://www.blogger.com/atom/ns#' term='" + cats[j].getAttribute('term') + "'/>";
      }
    }

    ret += '<title type="text">' + es[i].getElementsByTagName('title')[0].firstChild.nodeValue + '</title>';

    s = '<div class="oldpost_ycool">' + es[i].getElementsByTagName('summary')[0].firstChild.nodeValue.replace(/\.{3}$/, '')  + '</div>';
    //s = parseHTML(s);
    ret += '<content type="html"><![CDATA[' + s + ']]></content>';
    
    ret += "<author><name>小八</name></author>";
    
    ret += "<thr:total>0</thr:total></entry>";
  }
  ret += '</feed>';
  
  var xn = new ActiveXObject('MSXML2.DOMDocument.3.0');
  xn.async = false;
  xn.loadXML(ret);
  
  if (xn.parseError != 0) {
    var oError = xn.parseError;
    throw new Error("An error occurred:\n错误代码: "
      + oError.errorCode + "\n"
      + "行数: " + oError.line + "\n"
      + "列数: " + oError.linepos + "\n"
      + "原因: " + oError.reason);
  } else {
    xn.save(path + '.txt');
  }
}


function main() {
  var fso = new ActiveXObject('Scripting.FileSystemObject');
  var fd = fso.GetFolder('.');
  var fc = new Enumerator(fd.Files);
  for (;!fc.atEnd();fc.moveNext()) {
    var s = String(fc.item());
    if (s.substr(s.length-4) == '.xml') {
      parseXML(s);
    }
  }
}

main();

帮人搬家之导入图片至Picasa

Blog搬家,文字部分其实很好处理,麻烦的是图片。如果BSP下手狠一点直接删掉,而且本地又没有备份的话,那就全完了。

歪酷到目前为止相册都还没动,不知道是没检查到还是怎样,总之现在还能抓就是了。

因为Ruby没有好用的HTMLParser类,所以以下代码用的是Javascript。由于我不确定歪酷服务器上的图片文件名是否唯一,所以直接将URL处理后作为文件名保存。

function parseHTML(src) {
  var doc = new ActiveXObject('htmlfile');
  doc.write(src);
  
  var es = doc.getElementsByTagName('img');
  for (var i=0;i<es.length;i++) {
    var s = es[i].src;
    if (s.indexOf('foto.ycstatic.com')>0) {
      var c = 'wget -O ' + encodeURIComponent(s) + ' ' + s;
      // 批处理文件需要
      c = c.replace('%', '%%');
      
      WScript.echo(c);
    }
  }
}

function parseXML(path) {
  var xml = new ActiveXObject('MSXML2.DOMDocument.3.0');
  xml.load(path);
  
  var es = xml.getElementsByTagName('feed/entry/summary');
  for (var i=0;i<es.length;i++) {
    parseHTML(es[i].firstChild.nodeValue);
  }
}


function main() {
  var fso = new ActiveXObject('Scripting.FileSystemObject');
  var fd = fso.GetFolder('.');
  var fc = new Enumerator(fd.Files);
  for (;!fc.atEnd();fc.moveNext()) {
    var s = String(fc.item());
    if (s.substr(s.length-4) == '.xml') {
      parseXML(s);
    }
  }
}

main();

上传至Picasa的代码倒没什么可解释的,只需要留意一点:每个Picasa相册最多只能容纳500张图片。

require 'net/https'
require 'uri'
require 'rexml/document'
require 'FileUtils'

def getAuth(email, passwd)
  uri = URI.parse('https://www.google.com/accounts/ClientLogin')
  
  req = Net::HTTP::Post.new(uri.path)
  req.set_form_data({'Email'=>email, 'Passwd'=>passwd, 'service'=>'lh2'})
  
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  res = http.start {|h| h.request(req)}
  
  case res
  when Net::HTTPSuccess
    res.body.split("\n").each { |s|
      return s if s[0,5] == 'Auth='
    }
  else
    puts res.body
    res.error!
  end
end

def ul2Picasa(auth, albumid, title, summary, filename, ftype='image/jpeg')
  uri = URI.parse("http://picasaweb.google.com/data/feed/api/user/default/albumid/#{albumid}")
  
  body = <<EOF
Media multipart posting
--END_OF_PART
Content-Type: application/atom+xml

<entry xmlns='http://www.w3.org/2005/Atom'>
  <title>#{title}</title>
  <summary>#{summary}</summary>
  <category scheme="http://schemas.google.com/g/2005#kind"
    term="http://schemas.google.com/photos/2007#photo"/>
</entry>
--END_OF_PART
Content-Type: #{ftype}

#{File.open(filename, 'rb') {|f| f.read()}}
--END_OF_PART--
EOF

  
  req = Net::HTTP::Post.new(uri.path)
  req.body = body
  req.set_content_type('multipart/related; boundary="END_OF_PART"')
  req['MIME-version'] = '1.0'
  req['Authorization'] = "GoogleLogin #{auth}"
  req['Content-Length'] = body.length
  
  http = Net::HTTP.new(uri.host, uri.port)
  res = http.start {|h| h.request(req)}
  
  case res
  when Net::HTTPSuccess
    xmldoc = REXML::Document.new(res.body)
    yield xmldoc.get_elements('entry/content')[0].attributes.get_attribute('src').to_s
  else
    puts res.body
    res.error!
  end
end

email = 'USERNAME@gmail.com'
passwd = 'USERPASSWORD'

# 该值可通过收工创建相册并查看源代码获得
albumid = 'NNNNNNNNNNNNNNNNNN'

# 把上传完成的文件移动到该目录
mvdir = '@up'

auth = getAuth(email, passwd)

# 作纪录备用
flog = File.open('piclog.txt', 'w')
Dir.glob('*.jpg') { |fn|
  ul2Picasa(auth, albumid, fn, '', fn) { |url|
    puts fn
    flog.puts fn + "\t" + url.sub(/(\/[^\/]+)$/, '/s800\1')
    FileUtils.move(fn, mvdir)
  }
}
flog.close

帮人搬家之导出Google Reader缓存

某人的Blog原来放在歪酷上,因为歪酷上榜第七批,所以。

目前歪酷被关Blog的帖子部分无论前台后台均无法访问,日后是否会提供下载不得而知。好在某人的Blog自从2006起就有人(敝人)在Google Reader上订阅,歪酷的Feed也是全文输出,所以能救一部分回来。

但是借助Google Reader缓存搬迁Blog的法子也有不小的局限性:

  • 原Blog提供了全文Feed输出;
  • 只能取回自第一次有人在Reader订阅之后发布的文;
  • 如修改过不包含在Feed中的旧文,则修改部分无法取回;

本想用Javascript写代码,但因为XMLHTTP不能自定义Cookie(ServerXMLHTTP好像可以,不过我没试),所以改用Ruby。

require 'net/https'
require 'uri'

def getSID(email, passwd)
  uri = URI.parse('https://www.google.com/accounts/ClientLogin')

  req = Net::HTTP::Post.new(uri.path)
  req.set_form_data({'Email'=>email, 'Passwd'=>passwd});
  
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  res = http.start {|h| h.request(req)}
  
  case res
  when Net::HTTPSuccess
    res.body.split("\n").each { |s|
      return s if s[0,4] == 'SID='
    }
  else
    res.error!
  end
end

def parseXML(src)
  m = src.match('([^<]+)')
  if m then
    return m[1]
  else
    return ''
  end
end
    
def exportGRCache(feedurl, email, passwd, fn = '000001', c = '')
  sid = getSID(email, passwd)
  
  begin
    # n尽量大一点,方便后续处理
    url = 'http://www.google.com/reader/atom/feed/' + feedurl + '?n=100'
    url += '&c=' + c if c != ''
    uri = URI.parse(url)
    
    headers = { 'Cookie' => sid, }
    puts 'DL ' + url
    res = Net::HTTP.start(uri.host, uri.port) { |h| h.get(uri.request_uri, headers) }
    
    puts 'Save ' + fn + '.xml ...'
    f = File.new(fn + '.xml', 'w')
    fn.next!
    f.write(res.body)
    
    c = parseXML(res.body)
  end while c != ''
end

# Google Reader中订阅的Feed地址
url = 'http://BLOG.DOMAIN.com/PATH.xml'

# Google Account,Reader API目前必须要验证才能用
email = 'USERNAME@gmail.com'
passwd = 'USERPASSWORD'

exportGRCache(url, email, passwd)

2009年2月5日星期四

禁止WLM自动运行

Windows Live Messenger在每次登陆Live.com的时候都会自动运行,很烦人。网上多方寻觅不得其解,只好自己写办法。

用IE浏览某个网站就执行一个本地程序,怎么想都是ActiveX最可疑。经过尝试,在IE菜单栏中选择“工具”、“Internet选项”,“程序”页中点击“管理加载项”,禁用下图红框中的Windows Live项目即可解决问题。

经过如上设置,用IE登陆Live.com时Messenger是不会自动运行了,但Maxthon或TheWorld却还是照旧。IE中禁用的项在Maxthon中却可以加载,不知这算不算是一个Bug。

试了试直接将“C:\Program Files\Windows Live\Messenger\msgsc.14.0.8050.1202.dll”改名,但一运行WLM就会修复。只好用老办法了,直接屏蔽,将下述内容导入注册表即可:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{E1771B7F-98BE-407F-BA67-AA16ADA5D0C5}]
"Compatibility Flags"=dword:00000400

ActiveX Compatibility项目就是一众“IE插件免疫”工具用到的注册表项,将对应CLSID项中的Compatibility Flags设置为1024即位禁用,改为0则为启用。

Update:

想了想,如果要在Maxthon内部屏蔽该控件应该也是可以的。在JScript中调用ActiveX控件,必然要传递ProgID参数建立ActiveXObject对象,那么只要将注册表里查到的"MSNMessenger.Hotmail2Control"替换为空字符串就行了。

2009年2月3日星期二

Bug In Blogger HTML Gadget

在Blogger中添加一个HTML/Javascript Gadget,贴入以下代码:

<script type="text/javascript">
function do_something() {
  var a = document.getElementsByTagName('div');
  for (var i=0;i<a.length;i++) {
    if (a[i].className=='XX') a[i].className='OO';
  }
}
</script>

保存以后再点修改,会发现代码变成了这个样子:

<script type="text/javascript">
function do_something() {
  var a = document.getElementsByTagName('div');
  for (var i=0;i<a.length;i++) (a[i].classname="XX" ) a[i].classname="OO" ;
 {
 if }
}
></script>

折腾了老半天,我发现把for语句改成如下两种样式都没问题:

<script type="text/javascript">
for (var i=0;i<(a.length);i++) { };
for (var i=a.length-1;i>=0;i--) { };
</script>

抓狂。