Arc浏览器

总的来说,Arc浏览器还算挺新颖好用的,但也带来一些问题:

1. 去掉了主页

我一直有在使用一个本地主页,是因为有些复杂逻辑的跳转需要Javascript代码,而不是简单的几个链接。

目前我把主页Pin在第一个位置,前面还有Gmail, Calendar,所以Cmd+3会让我直接看到主页,从主页点击跳转的时候,按住Cmd+Shift键,链接会在新标签页打开。

自然,这要好用需要短暂的适应,形成肌肉记忆。

2. 去掉了书签

这个最大的问题,还是不支持Bookmarklet,解决办法是把Bookmarklet转换成插件,网上有现成的工具,但始终感觉还是有点重。

最后,多学习一下Arc的快捷键,会好用很多。也包括不同的链接打开方式:

  • 点击:原标签页打开,留在原标签页
  • Cmd + 点击:新标签页打开,留在原标签页
  • Cmd + Shift + 点击:新标签页打开,留在新标签页
  • Opt + 点击:Split中打开,留在Split
  • Shift + 点击:Peek中打开,留在Peek
  • Cmd + Opt + 点击:Little Arc中打开,留在Little Arc

What’s wrong with BuzzCounter?

在自己的Blog上展示Buzz,以前一直用的BuzzCounter提供的服务,但BuzzCounter最近开始出问题, 始终显示很老的buzz,不能更新,加之他本来有的一些其他的问题,比如:1. 有广告贴;2. 贴子是通过他服务器中转的,而不是从客户浏览器直接去Google取的。

再加上Google Buzz API现在也RESTful了,更好的是,还支持JSONP,跨域的问题也解决了,所以还是觉得自己写一个比较好。

参见侧边栏和附件,Google ID在buzz.js里的第一行就可以设置。至于颜色、外观啥的,自己改改buzz.css吧。

当然,前提是你的Buzz是Public的。

附件:aXqd Buzz

Camtasia Studio 6

下周要负责录一个内部产品的Tutorial,于是先行用Camtasia Studio来录了一个测试教程,还是蛮好用的。

缺点有:

Smart Focus显得移动过于频繁;

生成Flash预加载缓冲内容过多,将近5%;(不知有没有配置选项)

机器embeded的话筒录下来,感觉完全是另外一个人的声音;

突然发现,”嗯啊哈”的,如此之多,呵呵。

附测试结果:【教程整个很白痴,纯粹测试性质】

http://www.axqd.net/hotkey/ for Hotkey Power on http://www.axqd.net/

PS: 工作后真是没法坚持定期写Blog了,一切随缘吧,想起了又无事,就多写几篇。

Oh…Twitter

云南真是避暑胜地,闭关一周安安静静看看书,其实也挺不错的。

言归正传,一直以来因为公司提供了很多VPN,所以也没有特别关注翻墙的问题。但今天突然发现博客首页的flash twitter插件没法用了。所以干脆就直接换成javascript的,然后开始翻墙。

翻墙基本原理是拿我这台博客服务器做代理,php不太熟,其间还google了一下“php if statement”:

https://blog.axqd.net/wp-content/themes/emplode/axqd.php?callback=TWTR.Widget.receiveCallback&count=50&since_id=2769255170&clientsource=TWITTERINC_WIDGET

<?php
if( $_GET[ ‘since_id’ ] && preg_match( ‘/^[0-9]+$/’, $_GET[ ‘since_id’ ] ) )
  echo file_get_contents("http://twitter.com/statuses/user_timeline/axqd.json?callback=TWTR.Widget.receiveCallback&count=50&clientsource=TWITTERINC_WIDGET&since_id=".$_GET[ ‘since_id’ ]);
else
  echo file_get_contents("http://twitter.com/statuses/user_timeline/axqd.json?callback=TWTR.Widget.receiveCallback&count=50&clientsource=TWITTERINC_WIDGET");
?>

搞定之后,可以选择自己处理这个json对象。不过既然已经有现成的,为什么不用呢?于是比照博客皮肤,在twitter官方配色了一个JS版本的client。

接下来转换资源地址有点麻烦:

先啃容易的骨头,VPN出去,下载相应的静态资源,比如style sheet,js file,还有几个png、gif啥的,然后修改其中一些资源里面的引用地址,再通通一股脑传到服务器上。

接下来改js文件,可惜的是,这个js文件是混淆过的,也不太知道怎么还原。好在混淆器大多像下面这样,在后面搞得像是符号表似的:

‘||||this|function|return||if|var||||||||||||||||||||||||||||||||||||||twtr|false|div|id|else|true|tweet||user|class|net|http|call|results…’

我试着替换其中某些字符串,在本地多尝试几遍,居然找到一个可用的替换。

当然这种方式也有不太爽的地方,细心的你会发现到twitter.com的链接全都被换成了blog.axqd.net。:P

不过反正放这个插件的目的在于展示,而不在于收集reply,所以也就懒得处理这个问题了,呵呵。

一道面试题

最近帮某公司出了一道面试题,由于面试官技术参差不齐。所以,题目要求:上手容易(不然冷场了,面试官不易处理),还有就是不怎么暴露面试官的无知。

题目内容比较open,没有标准答案,只是要求用伪码实现简化版的windows计算器标准模式,相信大家上手应该都不成问题。

然而实际效果,这道题目区分度还是蛮大的。正是因为按键少,上手就写程序,很容易就成为意大利面条式的代码。甚至有人电话面试的时候,没到5分钟,就直接压了电话。

按键设定为只有以下18个:
0 1 2 3 4 5 6 7 8 9 . + – * / = C +/-

由于对一切乱七八遭的输入,都要能够接受,不做异常处理,我们有下面一些约定:
1. 忽略前置0: 000000 => 0;
2. 忽略多个.:1…..23 => 1.23;
3. 多个操作符(包括=)取最后一个(这与windows的版本有细微出入):*/2 => /2;

最后,是一些考量:
1. 有限状态机在这里是比较自然的思路;
2. 初始计算前加入0+的计算,可以与后面的情况合并处理;
3. Clear和+/-可以不进入状态机处理,而提前解决掉;
4. 一般都能把数字”0~9″和”.”、”+-*/”和”=”统一处理;但问题在于,用了大量的if来别扭的处理”=”,其实”=”之所以别扭,就是因为他和”+-*/”最大的不同,在于它是unary operator,而其他的是binary operator。如果考虑到这里,那么对于后续问题。例如,如何加入对于sin/cos/开根号等的支持,也能作出较好的回答;
5. 还有一个小陷阱,除0的考虑;

总之,简单的一道题目,但在面试的特殊环境下,能够较好的区分出应聘者的设计水平。

为了更好的在面试的时候说明题目,我用flex实现了一个界面(因为只起演示作用,所以有个已知的bug就是超长的输入数字,或者回显的超长数字结果,会使程序的显示产生未定义行为)[Updated: 加了个限长为10,现在可以忽略这句话了]。

附件里有代码和状态转换图,有兴趣的可以下来看看。有更好的考量,还望多多指教。

http://www.axqd.net/calculator/

古灵精怪

以前一直很惊叹解析DOC的quirks模式,居然能在如此多错误的情况下容错,保持健壮。但最近看看Macromedia公司的AS2官方类库,才更为惊叹AS脚本解析器的容错能力;
代码里面一堆堆错误,硬是能够正常跑起来,结果放到Eclipse下,一大堆报错,类库编译都通不过,用FDT+Aswing随便个小东西就得改一大堆的错误才能运行,随便举几个例子:
以下摘抄都是官方公布类库:
Directory:
Documents and SettingsAdministratorLocal SettingsApplication DataMacromediaFlash 8zh_cnConfigurationClasses
例一、类型不匹配
不过ASv1本身就是弱类型的,所以不算太错,但Number-Date真不知运行的时候为啥不会错,因为确实没找到转换函数
mxservicesWSDL.as

if (src != undefined)
{
var start = new Date();
this.wsdl.log.logInfo("Received WSDL document from the remote service", Log.VERBOSE);
this.parseXML(src);
this.loaded = true;
//加入此行,真不知 Math.round(new Date().getMilliseconds())错在哪里T T
var end = new Date();
//106 var parseTime = Math.round(new Date()) - start;
var parseTime = Math.round((end.getMilliseconds()) - start.getMilliseconds());
this.wsdl.log.logInfo("Parsed WSDL XML [" + parseTime + " millis]", Log.VERBOSE);
}

例二、变量名重复定义
例三、缺少结尾分号
mxservicesWebServiceProxy.as

// Shut off the __resolve
this.service.__resolve = function(operationName) {
var callback = new PendingCall();
callback.genSingleConcurrencyFault = function()
{
clearInterval(this.timerID); // only once
//192对应例二 var fault = new SOAPFault("Client.NoSuchMethod",
var innerFault = new SOAPFault("Client.NoSuchMethod", "Couldn't find method '" + operationName + "' in service!");
this.__handleFault(innerFault);
this.onFault(innerFault);
//197对应例三
};
callback["timerID"] = setInterval(function() { callback.genSingleConcurrencyFault(); }, 50); // 5 ms
return callback;
};

例四、相等与赋值乱用
mxservicesWSDL.as

// per WSDL 1.1 spec, if message is not named default to op Name plus suffix by mode:
if (message.name == undefined)
{
if (mode == SOAPConstants.MODE_IN)
{
message.name = operationName + "Request";
}
else
{
//598message.name == operationName + "Response";
message.name = operationName + "Response";
}
}

例五、不知为何
mxservicesNameSpace.as

//18
static var _doc = new XML();
//65不知为何必须使用this来访问static成员,反正少了编不过
var node = this._doc.createTextNode(value);

关于Single Sign On的一些设想

单点登陆,始终有其吸引力。然而就实现而言,结合栋力无限现有情况,比较困难。

主要原因在于栋力无限门户下属各个子站点,不仅有自己开发的,也有直接使用的他人开发的开源项目的。特别对于后者,如果花大力气,对其进行修改,使其使用我们自身的passport,一旦其有所升级、打patch等变动,就会导致大量的重复劳动。

在现有条件下,是否存在一种花费最小代价的Single Sign On实现方式?换句话说,使得插接到passport的各个子站点,所作的修改尽量的少?

最近有一个设想,就是如果想要尽可能少的修改各个子站点,实现passport的通用性,唯有通过模拟各个子站点自身的登陆行为。

法1、填写username password,并post到其form的action链接里面;
法2、直接在用户本机写入cookie文件;

两种方式其实都可以。具体一点说:

第一阶段试验目标:

1、登陆passport.dormforce.net后,passport修改用户登陆状态信息为已登录(并以Web Services或其他方式对外公开)。

2、passport调用放在各个子站点自身程序里的一个模板页面,那个页面实现的唯一功能就是采用法1或法2模拟登陆本子站点,若有必要,在这里,还可以增加对Session,Application等变量的支持;(由于开源,可以跟踪入内,找到其登陆代码,copy过来修改一下即可)(调用的时候,最好使用AJAX等技术后台加载,防止页面频繁刷新,提高用户体验)

3、passport调用放在各个子站点自身程序里的一个模板页面,那个页面实现的唯一功能就是采用法1或法2登出本子站点,若有必要,在这里,还可以增加对Session,Application等变量的支持;(由于开源,可以跟踪入内,找到其登出代码,copy过来修改一下即可)(调用的时候,最好使用AJAX等技术后台加载,防止页面频繁刷新,提高用户体验)

至此,应该能够实现在passport登陆后,畅通访问各个子站点;在passport登出后,退出所有子站点;

第二阶段试验目标:

1、通过用户登陆状态信息等其他验证安全的手段,确保放在各个自站点的模拟登陆页面安全。

2、修改各个子站点的登陆和登出链接,指向passport

至此,应该能够实现我们最初的目标。

我再稍微总结一下:

passport实现的功能就是登陆时,自动登陆各个站点;登出时,自动登出所有站点。这里和通常意义的passport不一样,因为通常意义的passport一般就只有一个cookie。

一个新插入的子站点(不管自身项目还是开源项目),若想使用passport认证,则进行如下操作:

1、找到登陆的代码,套入passport提供的模板页面中;
2、找到登陆和登出的链接,指向passport;

当然,对于第二步来说,仍然比较困难,但是较之完全替换其帐户系统,理论上讲应该轻松许多,这也是我目前能想到的代价最小的实现Single Sign On的方式了。

由于这只是一些设想,跟曾毅和卢浩森聊了一下,据称问题不大,而且曾毅貌似已在音乐站上测试成功第一阶段的目标,所以特贴出来,看看各位达人认为:

1、有可行性么?
2、有没有其他更好更贴近栋力无限自身特点的方式?

多文件批量上传

用一个页面实现文件上传,一般只需要使用HtmlInputFile的runat=server版本就行了,但是现在有些不同的地方:
1、直接上传到数据库的image类型的字段中,而不产生本地文件;
2、多个文件同时上传;
3、上传的文件组最后要同其他相关信息进行关联;

解决方法主要有两种:
1、每次上传一个文件到临时目录,返回并保存相关信息到ViewState,最后提交的时候,统一将文件写入数据库,并根据相关信息做关联;
2、每次上传一个文件到数据库,返回并保存相关信息到ViewState,最后提交的时候,根据相关信息做关联;
3、每次采集HtmlInputFile中的信息到ListBox,最后统一上传多个文件,并做相关关联;

无疑,第三种是其中最好的一种,但是上传多个文件却比较麻烦,本来想在提交的时候,分别多次设置 HtmlInputFile.PostedFile.FileName属性,再多次调用SaveAs进行上传,但在一次提交过程中它不允许重设 FileName属性(事后想想,确实应该不行:P)
后来又想在后台动态添加HtmlInputFile控件并且设其Visible属性为false,结果好像还是不行,因为还是不准自己指定那个怨念的FileName属性

结果:
增加文件按钮:
if( null == m_htmlInputFile )
m_htmlInputFile = new ArrayList();
m_htmlInputFile.Add( FileInputField );

其中FileInputField为System.Web.UI.HtmlControls.HtmlInputFile类型;
并且注意设置m_htmlInputFile这个ArrayList为static,以防每次增加文件后被刷新为null:(
这里其实就每次Add那个Html控件就行了,他们都对应相同的html控件,但是后台的对象每Add一次增加一个:)

提交按钮:
if( null == m_imageList )
m_imageList = new ArrayList();
if( null == m_imageTypeList )
m_imageTypeList = new ArrayList();
foreach( HtmlInputFile hif in m_htmlInputFile )
{
System.Drawing.Image image = System.Drawing.Image.FromStream( hif.PostedFile.InputStream );
m_imageList.Add( image );
m_imageTypeList.Add( hif.PostedFile.ContentType );
}

显示图片:
在GetImage.aspx?ImageID=*页面的Page_Load中
Response.ContentType = m_image.ImageType;
Response.BinaryWrite( m_image.Img );

注意采用BinaryWrite以及设置ContentType为image/pjpeg等

最后在保存文件到后端数据库的时候有点小不爽:
MemoryStream ms = new MemoryStream();
image.Save( ms, System.Drawing.Imaging.ImageFormat.Jpeg );//image是System.Drawing.Image
m_image = new byte[ms.Length];//m_image是byte[]
ms.Position = 0;
//If Too Long, ...
ms.Read( m_image, 0, Convert.ToInt32(ms.Length));

这里ms.Length是long类型,但是同一个类-MemoryStream的Read方法的第三个参数是int型,涉及到一个向下的类型转换,如果Length过大,应该这里会有问题,不知是怎么回事:(