読み込んでいます...

很早以前我写过一篇文章关于如何在Firefox开发修改当前页面的内容,但是那篇文章的适用场景在于修改当前页面的Tab下的内容,也就是说,如果你打开多个Tab,同时刷新,只有当前的HTML会被改变,好在那篇文章是讲解如何使用鼠标选择相应的范围更改相应的HTML,如果在那个需求下,则现在的这个就不适用了。

我现在就说说如何在Firefox里面修改多个Tab的页面内容吧。

首先在加载Firefox插件的时候,我们可以给每个Tab的对象加上DomContentLoaded事件响应,并且给真正响应的部分添加事件。

//在onload方法写入下面的事件

gBrowser.addEventListener(DOMContentLoaded,function(event)

{

//Firefox下面的appcontent

var appcontent = document.getElementById(appcontent);

//获得之后就可以为真正的响应部分添加事件

if(appcontent)

{

appcontent.addEventListener(DOMContentLoaded, function(event){pageLoad(event);}, true);

}

} , false);

在获得响应的响应事件之后,我们就需要在pageLoad方法中获取相应的Tab的document对象。这里值得一提的是,在Firefox的document对象中,默认是拿的XUL的document,而不是frame的document。虽然我们可以通过window.content.document或者content.document拿到对象,但是这个地方拿到的仅仅是当前窗口,也就是当前Tab的,如果我们要在所有的Tab加载完毕之后自动的更改内容,就需要拿到每一个document。

我们可以更改pageLoad代码如下。

function pageLoad(event)

{

var doc = event.originalTarget;

if(doc.nodeName != #document || !doc.location)

{

return;

}

//doc is document object

//doc.getElementById(“someid”)

//doc.appendChild(someChild)

}

通过上面的代码我们就拿到了每一个Tab的document对象,拿到之后,我们就可以在相应的方法中使用相应的document更改对应的Tab里的HTML了。

379路过 3评论 Firefox Addon 阅读全文..

原来写过一个File类,这个类的作用就是在Extension中读写本地文件,不过作为浏览器插件,读写本地文件到磁盘就会有一些很不好的问题,这些问题可以总结如下。

  • 写文件要写绝对路径。
  • 不同操作系统有不同的路径。
  • 卸载插件的时候需要手动找到路径删除。

这三点是非常麻烦的,也会造成后期的维护的困难。当然,我们希望的是用户卸载插件的时候连同配置文件一起删除,读写文件也写到安装包路径中。

在Firefox中,有一个XPCOM可以解决这个问题,我们可以通过以下代码获取路径。

const id = test@test.com;

var extension = Components.classes["@mozilla.org/extensions/manager;1"]

.getService(Components.interfaces.nsIExtensionManager)

.getInstallLocation(id)

.getItemLocation(id);

然后,我们可以通过相应的IO类去进行文件的读写,代码如下。

var io = new jguoer.IO();

var file = io.open(extension.path+\\file.txt);

io.write(file,test);

alert(io.read(file));

同样,附上我写的IO类,可以读写文件以及进行一些编码转换,代码我就不贴出来了,好的命名已经解释了一切了。

IO.rar (1.45 kb)

508路过 1评论 Firefox Addon 阅读全文..

我在使用自己的Firefox Package for MAC的时候,发现使用命令的时候会有一个奇怪的错误,错误如下:

  • dyld: Library not loaded: /usr/lib/libsqlite3.dylib
  • Referenced from: /System/Library/Frameworks/Security.framework/Versions/A/Security
  • Reason: Incompatible library version: Security requires version 9.0.0 or later, but libsqlite3.dylib provides version 1.0.0

在网上查了一下,貌似还没有非常好的资料,而且也没有说很好的解决方案,其实这个BUG也不算什么大的BUG,因为Snow Leopard新的系统版本会检查libsqlite这个库的版本,这个错误就是说版本太老了,我们只要更新覆盖一下就行,执行一下下面的命令可修复这个BUG。

mv /Applications/Firefox.app/Contents/MacOS/libsqlite3.dylib /Applications/Firefox.app/Contents/MacOS/libsqlite3.dylib.org

cp /usr/lib/libsqlite3.dylib /Applications/Firefox.app//Contents/MacOS/libsqlite3.dylib

如果依旧有问题,请直接使用firefox-bin开发,而不是使用firefox

这两天开发了一个Firefox Dev Package,用于方便的开发Firefox插件,命令行版,可以方便的和VIM整合在一起。也可以单独的使用命令行进行开发。

MDC(Mozilla Developer Center)官方WIKI请看这里。

FFPackage包括通用的四个命令。

  • ffnew:创建一个firefox项目,参数为ffnew {appname} {appid}
  • ffmake:打包编译firefox插件。
  • ffrun:运行firefox插件。
  • ffprom:Firefox的Profile Manager。

可以看下图(1),(2)命令行下开发,(3)VIM下开发。

可以下载此安装包(请先读官方WIKI或者读README!)。

ffpackage.rar(Zh-cn/En-US) (9.17 mb)

前面我在XPCOM章节里面说了如何去开发一个XPCOM,XPCOM很强大,能够做很多事情,不过有时候为了代码的维护性和优化,我们有时候必须需要定义接口来制定协议。例如当我们有了多个XPCOM的时候,而又需要使用到一些相同的定义或方法,就要自己定义XPCOM的接口。这里还是强调一下,如果不太理解,还是去看如何开发JS的XPCOM

好了,首先我说,我们目的是要定义一个接口,给XPCOM去使用,所以这里我默认各位都会写XPCOM了,OK,首先,我们要写一个idl文件,如果你希望你自己的XPCOM组件能够被其他的XPCOM组件所使用,我们就需要定义接口,就需要些idl文件。在Firefox中,已经定义了很多的idl文件,也就是接口文件,如我们最常用的nsISupports就是一个idl文件,这里我们可以写一个简单的idl文件,用C/C++去写(还支持Java,Python等),代码如下。

#include "nsISupports.idl"
//这里是GUID
[scriptable, uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)]
interface nsIHelloWorld : nsISupports
{
  
string hello();
};

上面我们简单的定义了一个nsIHelloWorld接口,这个借口继承自nsISupports,在写接口的时候,我们必须定义一个GUID,并且保证这个GUID和你任何其他的XPCOM组件不同。

在写完之后,我们需要文件去编译这个idl文件,生成一个xpt文件,这个文件是加密的,其他人员是看不到的。

这些工具我们必须下载Firefox SDK的工具才能使用,所以这里我们可以说已经接触到Firefox里面比较深入的内容了。我们可以下载Gecko-sdk-win32-1.4a,这里有很多工具,同时我们还需要下载buildtools使用一些工具去编译我们的应用。

下载Gecko-sdk-win32-1.4a之后,我们可以看到很多文件夹,这里我们主要使用的是xpcom里面的bin文件夹。在编译的时候,我们还需要将buildtools里的windows\bin\x86文件下的DLL拷贝到xpcom里面的bin文件夹,去编译idl文件生成为xpt。我们需要在命令行下使用,我们使用xpidl文件去编译。命令如下。

rem 下面其中C:盘的路径是xpcom全局idl的路径
xpidl -m typelib -w -v -I "C:\Documents and Settings\jguo\Desktop\gecko-sdk\xpcom\idl" -e test.xpt test.idl

我们可以输入xpidl /?去看帮助命令,但是我还是会解释一下这个命令里的重要的参数的作用。

xpidl -m(mode) typelib(库) -w(提示错误和警告)-I(大写,idl的路径,这个路径是继承的路径,例如最前面的代码继承自nsISupports,这里就要写nsISupports的路径,而不是你编译的idl文件的路径)-e(生成的文件名),最后一个参数为需要编译的idl文件。

我们可以输入以上命令去编译在当前路径下的idl文件并生成为xpt。

前面我也说了,xpt是加密的,看不到代码的,这里我们也可以使用SDK带给我们的工具xpt_dump去看,使用xpt_dump.exe,参数为xpt文件的路径即可看到我们编译后的xpt文件中定义的结构。

既然我们在这里定义了nsIHelloWorld的接口之后,我们也可以将xpt文件放置在components文件夹下,Firefox会自己识别,然后我们在需要使用的XPCOM中使用即可,部分代码如下。

//定义接口的局部变量
const nsIHelloWorld = Components.interfaces.nsIHelloWorld;
//定义当前XPCOM对象
var SomeObject = function()
{}
//
QueryInterface: function(aIID)
{
    
//判断是否继承了这个接口
    if (!aIID.equals(nsIGuoJingTest) && !aIID.equals(nsISupports) && !aIID.equals(nsIHelloWorld))
      
throw Components.results.NS_ERROR_NO_INTERFACE;
    
return this;
}
//继承接口定义的方法

SomeObject.prototype.hello()
{
    
return "something";
}

这样以来,我们就可以定义相应的接口并自己继承和使用其他XPCOM里面的方法,接口的好处就在于制定了契约,大家都要这样去做。

不过,既然说到接口了,还是想要考考大家,interface和abstract倒底有什么区别呢:)希望大家自己好好考虑考虑。

424路过 1评论 Firefox Addon 阅读全文..

我们在了解了基本的文件结构之后,我们就可以考虑开始我们的命令行的编译系统的开发了,先别急,我们还得慢慢来,首先,我给大家介绍一个自动化编译工具,在自动化测试中,这个工具非常好用,这个工具就是Nant

Nant需要.NET Framework支持,当然,如果你是开源的拥护者,你可以使用Ant,基本上一样的逻辑,如果你要在MAC和Linux上使用Nant,可以装MONO,一样可以跑。由于Nant是我经常使用的,这里就介绍Nant,本身你不需要使用.NET或者C#的特性,大部分编译的软件基本上都是用XML去描述的。

我们可以下载了Nant,并且将相应的exe目录设置为环境变量,这样我们就可以在任何命令行下使用Nant了。我们可以看看官方给的介绍图片。

确实很简单,我们只需要在相应的目录下输入nant命令即可,在输入nant的时候,nant会自动去寻找default.build,如果没有default.build,你必须指定一个编译的文件,如nant abc.build。下面我们看看nant的build文件如何写。

nant的build文件也就是XML文件,不过其文件非常可读,如下。

<?xml version="1.0"?>
<!–项目名称–>
<!–default:默认运行的那个target–>
<!–basedir:命令行工作的基路径–>
<project name="Hello World" default="build" basedir=".">
    
<!–项目描述–>
    
<description>The Hello World of build files.</description>
    
<!–项目属性或参数–>
    
<property name="debug" value="true" overwrite="false" />
    
<!–Target:不知道如何翻译,大概可以看做是一个一个目标–>
    
<target name="clean" description="remove all generated files">
        
<delete file="HelloWorld.exe" failonerror="false" />
        
<delete file="HelloWorld.pdb" failonerror="false" />
    
</target>
    
<!–默认没标,应为项目名称里的default已经定义了–>
    
<target name="build" description="compiles the source code">
        
<csc target="exe" output="HelloWorld.exe" debug="${debug}">
            
<sources>
                
<includes name="HelloWorld.cs" />
            
</sources>
        
</csc>
    
</target>
</project>

上面的代码非常容易看懂,首先每个nant文件都需要有一个project属性,你可以自定义名称,描述等等,然后指定一个默认的目标。指定完默认目标之后,你就必须要写默认的目标,例如上面代码中默认目标为build。我们就需要写一个name属性为build的target,当执行这个文件时候,我们会执行相应的文件和内容,例如上面就是使用csc(C#编译器)去编译HelloWorld这个cs文件,并output为HelloWorld.exe。

我们可以看到其中有debug=“${debug}”,这里的这个${debug}写法是调用属性,我们前面有个属性名字叫debug,值为value,那么${debug}这个就是value,也就是true,相当于编程语言中的定义变量。

有时候我们还需要在执行build之前去执行clean,怎么办呢?我们可以用depends去做,我们修改代码如下。

<!–Nant会自动寻找clean这个依赖项,先执行这个–>
<target name="build" description="compiles the source code" depends="clean">
    
<csc target="exe" output="HelloWorld.exe" debug="${debug}">
        
<sources>
            
<includes name="HelloWorld.cs" />
        
</sources>
    
</csc>
</target>

这样,我们每次在执行build的时候,就会首先去找clean去执行,执行后才会回来执行build,这个是非常有用的功能。

nant还提供很多功能,如上面代码里的csc,就是C#编译器,同样也有zip打包和unzip解压缩,如我们可以写如下代码。

<target name="zip" depends="something">
    
<mkdir dir="build" />
    
<delete file="build\project.zip"/>
    
<zip zipfile="build\project.zip">
        
<fileset basedir="${temp.dir}" >
            
<include name="**/*" />
        
</fileset>
    
</zip>        
</target>

nant为我们提供了很多这样的工具,常用的工具nant都会为我们打包好,我可以直接使用相应的XML节点即可,可以看全部的Task列表,上面的baseddir是基于的路径,这里还有一个属性是workingdir,是工作的路径。在命令行中,你只能在当前路径工作,或者设置一些变量在某些路径里工作,而nant可以跨越路径工作,例如上面代码中的zip就是在temp.dir这个变量的路径里工作,而不是在当前的路径里工作,这是基路径,就是执行这个命令的时候的路径,同样workingdir就是工作路径,就是结果,产生的效果和影响所需要的路径。如我需要将temp路径下的所有文件拷贝到build下,我就可以说baseddir=temp,workingdir=build,他自己会在temp工作并将结果放到build里去。

可以看到,我们做nant的时候,会使用nant自带的命令或任务去完成一些操作,但是还是远远不能满足我们的要求,如果我们要调用一个自己的exe或批处理怎么办?我们可以这样写。

<target name="name" depends="soemthing">
    
<exec program="ABC.bat" workingdir="code\build" basedir="..\..\..\BatchFiles" >
        
<arg line="Para1 Para2 Para3" />
    
</exec>
</target>

上面,我们用exec去执行某个批处理或exe,并且设定了相应的工作路径和基路径,同样,我们也传递了3个参数给ABC.bat,这样我们就能够使用nant调用我们自己的exe或批处理了。

这里我们了解了nant的基本知识,这样为我们开发Firefox插件的开发环境提供了遍历,我们可以输入一个命令就可以打包我们的Firefox插件并安装到Firefox中运行,我们还可以开发公共的类库,从不同的地方拷贝类库组合成Firefox插件,以保证我们项目的代码的最新性和可维护性。你可以尝试做下面的事情。

  • 用nant打包一个文件夹,并命名为xpi。
  • 用nant编译一个简单的C++程序。
  • 用nant调用一个外部命令替换文件制定内容。

上面的内容都是我们做开发环境所必须的,所以掌握nant或ant,对自动化环境的开发和自动化测试的部署都是非常有用。而且你可以学到很多命令行的东西,比如学到如何使用msbuild,如何使用C#编译器,这些用IDE学不到的底层的命令。

231路过 0评论 Firefox Addon 阅读全文..

很早之前我就很想写类似的文章,不过考虑到不太现实,毕竟用命令行做开发的比较少,虽然我在JS,C#开发中经常会用到命令行,更不用说Linux开发了,所以命令行还是非常重要的,而我们自己的Firefox开发环境也从Netbeans到命令行,虽然是个复杂的转变,但是确实必要,因为Netbeans毕竟比较庞大,每次开发每个人都要搭建NB环境,是比较麻烦的。

所以我这里就写一个开发Firefox开发环境的文章,这样以便开发人员能够在任何环境下做Firefox开发,并且是轻量的,不过在了解这个之前,建议你还是了解基础中的基础

首先我们还是要说一下必备的基础知识,我在这篇文章中已经写到过插件的基本结构,当然,是在Netbeans里面。同样,所有的IDE都会为我们隐藏细节,当你需要自定义功能的时候,扩展起来是很麻烦的。所以我们先深入的了解一下基本结构。

FFTemplate.zip (6.70 kb)

首先先下载上面的压缩包,这个是我写的一个模板,一个非常基础的简单的Firefox扩展插件,当然,写他还是有点麻烦的,也需要花时间,但是有了一个版本后我们就不需要再写了,解压缩我们可以看到里面的文件。

  • content:代码文件夹,代码都会放到这个文件夹下。
  • defaults:一些pref的存放的地方,除非你是比较熟练的做FF开发,这里可以暂时先跳过,只要记得这个是存放默认pref的地方。
  • locals:一些翻译文件的地方,支持多语言。
  • skin:图片,CSS等文件存放的地方。
  • chrome.manifest:一些基本的配置信息。
  • default.build:编译文件,后面会提到,不是Firefox自带的,是我们使用其他工具编译插件。
  • ffbuild.build:同上,测试的编译文件。
  • install.js:安装文件,可不需要修改,可飘过。
  • install.rdf:插件的安装配置文件。

上面你会感到奇怪的地方可能是,manifest文件是配置文件,install.rdf也是配置文件,那么则这两个配置文件都有什么作用呢?我们看看代码就知道了。

content {appname}    content/
skin    {appname}    classic/1.0        skin/
locale  {appname}    en-US              locale/en-US/
style   chrome://global/content/customizeToolbar.xul chrome://{appname}/skin/overlay.css
overlay chrome://browser/content/browser.xul         chrome://{appname}/content/overlay.xul
overlay chrome://messenger/content/messenger.xul     chrome://{appname}/content/overlay.xul

上面的代码是manifest文件的配置信息,从上述代码我们可以看到,manifest文件的主要作用是告诉Firefox扩展的代码信息,你会告诉他这个插件的content是在content文件夹下,skin是在skin文件夹下。

所以install.rdf就是告诉Firefox我这个扩展的一些基本信息,我们同样可以看代码。

<?xml version="1.0"?>
<RDF:RDF xmlns:em="http://www.mozilla.org/2004/em-rdf#"
         xmlns:NC
="http://home.netscape.com/NC-rdf#"
         xmlns:RDF
="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    
<RDF:Description RDF:about="urn:mozilla:install-manifest">
        
<!–Firefox插件的名字,版本,id,描述等等–>
        
<em:name>{appname}</em:name>
        
<em:version>{appversion}</em:version>
        
<em:id>{appid}</em:id>
        
<em:description>{appdescription}</em:description>
        
<em:optionsURL>chrome://{appname}/content/options.xul</em:optionsURL>
        
<em:homepageURL>http://www.jguoer.com/</em:homepageURL>
        
<em:contributor>someone</em:contributor>
        
<em:translator>someone</em:translator>
        
<!–Firefox插件支持的平台,版本等–>
        
<em:targetApplication>
            
<RDF:Description>
                    
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
                    
<em:minVersion>3.0</em:minVersion>
                    
<em:maxVersion>3.6.*</em:maxVersion>
            
</RDF:Description>
        
</em:targetApplication>
        
<!–Firefox文件存放,默认locale等信息–>
        
<em:file>
        
<RDF:Description about="urn:mozilla:extension:file:{appname}.jar">
            
<em:package>content/</em:package>
            
<em:locale>locale/en-US/</em:locale>
            
<em:skin>skin/</em:skin>
        
</RDF:Description>
      
</em:file>
    
</RDF:Description>
</RDF:RDF>

这就是install.rdf和chrome.manifest的区别。在Netbeans里面开发的时候,我们会发现,Netbeans会将content里的内容全部打包成一个jar包,这是可以的,但是如果你要打成jar包,在相应的chrome.manifest里面就可以定义相应的路径,如jar:/content等等,具体路径如何去写我忘记了,可参考Netbeans。但是我推荐最好不要打成jar包,这样更具有扩展性。

既然我们了解了基本文件,我们就要编译Firefox插件,Firefox插件编译打包很简单,你直接打开FFTemplate文件夹里的东西,全选,发送到zip文件,然后把后缀变成XPI即可,你就可以直接拖动这个XPI包到Firefox里安装了,这个就非常简单。

当然你可以尝试将我们FFTemplate打包安装,你会发现是不正确的包,也是不完整的,因为Firefox会提示你说让你填写正确的appid,这个时候你就需要在install.rdf里面去填写相应的名称,id,而不能像上面的代码一样去写{appname},{appid}。

appid有两种形式可以写,第一种是email形式,如soundbbg@gmail.com,Firefox会自动翻译,去map匹配。第二种是使用GUID。当然你不能随便乱写{appid}了。

不过至于为什么我要填写{appname}和{appid},因为我们既然要开发命令行下的Firefox插件,我们肯定不能每次都自己去手动填写,只要一个命令就可以工作,而且还保留我们的源代码,这些都会在以后说到,这里,你只需要好好的吸收Firefox扩展的基本知识就行了。

259路过 0评论 Firefox Addon 阅读全文..

这篇文章针对JavaScript,正则表达式以及Firefox插件开发。

写这篇文章已经是预谋已久的事情了,但是好几天都没写,其主要原因还是太复杂了,但是其实说真正的复杂,也不算复杂,但是如果要真真正正的完全的实现这个需求还是有一定难度的,我基本上花了一个下午的时间去写一个正则表达式,写完之后才发现这个正则表达式如此之变态以至于回家之后我想改一下都读不懂自己的正则。看来就算对正则再熟悉的人,也会认为正则是火星文的。如果你不想看枯燥的过程,可以直接跳到下面很大一块的地方去拿结果

好,先来说说需求,首先,大家都会看到这个链接http://www.jguoer.com,或者soundbbg@gmail.com。恩,不错,不过会有很多人想要订阅我的网站或者给我发邮件,当你们看到这段话的时候,而且又想给我发邮件的时候,你可以这样做:复制超链接,粘贴到浏览器,按回车键。这样就能到我的网站或者给我发链接。恩,不错,如果我做一个Firefox插件把网页里面的所有这种文本类型的链接都变成超链接,那不是很方便用户,而且肯定有不错的市场,恩,有好的想法。

咱们来简单的抽象一下。

需求,浏览器中类似URL的文本链接(http://www.jguoer.com)变成超文本链接(http://www.jguoer.com)。其实不需要当成是浏览器插件,就单纯的更简单的抽象就可以了。

1.最简单的需求
输入:http://www.jguoer.com
输出:<a href="http://www.jguoer.com">http://www.jguoer.com</a>。

嗯,看上去不那么复杂,很多人都会说,用正则表达式来匹配去做嘛,很简单的,OK,好,匹配超链接的正则表达式不难,如下所示(随便写的,未验证,只是作为一步一步的深入讲解)。

(((http)|(ftp)|(https))://)([a-zA-Z0-9]*)[.]([a-zA-Z0-9]*)[.]([a-zA-Z0-9]*)

好吧,上面的正则表达式还是很简单的,第一,我们不仅仅匹配了http头,还匹配了ftp和https。OK,这个不难,但是这个肯定不能满足我们的需求的,为什么呢,看看下面的抽象。

2.适应复杂的超链接需求
输入:http://www.jguoer.com/default.aspx?id=100
输出:<a href="http://www.jguoer.com/default.aspx?id=100">http://www.jguoer.com/default.aspx?id=100</a>

好吧,现在你看到了情况(1)里面的需求很明显不能满足,因为那个正则表达式不能匹配有后缀的超链接,所以,我们还得加上一点东西。

((((http)|(ftp)|(https))://)([a-zA-Z0-9]*)[.]([a-zA-Z0-9]*)[.]([a-zA-Z0-9]*)((/[0-9a-z_!~*'().;?:@&=+$,%#/-]+)|(/)|()))

好吧,这已经有点复杂了,但是还是不能满足我们的需求,为什么呢,再看看下面的抽象。

3.适应不带http等协议开头的超链接的需求
输入:www.jguoer.com
输出:<a href="http://www.jguoer.com">www.jguoer.com</a>

对于情况(1),(2)呢,很幸运,有http开头,所以我们可以很简单的匹配到一个超链接,但是如果是情况(3)的话,明明是超链接,为什么就不显示超链接呢。所以情况(1),(2)的正则表达式是很明显不能满足需求的,那么我们只能先匹配出所有XXX.XXX.XXX这样的情况,然后再进行判断。判断的代码我就不写了,其基本逻辑就是匹配到www.jguoer.com,然后匹配完之后就搜索其中前7-8个字符串是否符合http://,ftp://等情况,如果符合的话,那么在添加超链接的时候我们就加上http://或者ftp://之类的。

好了好了,咱们休息一下,是不是看晕了还不能理解,那好,那咱慢慢理解,如果你理解了上面的思路的话,我们再来看看是否已经满足了需求。嗯。。很可惜,还是不能满足需求,为什么呢?大家看看这个http://www.jguoer.com,好吧,我在超链接那里加粗了,那么HTML代码就是http://www.<b>jguoer</b>.com,这我们的(1),(2),(3)的正则表达式就匹配不了了。看看这个还是有缺陷的啊。不过好在我们改进一下正则表达式,在XXX.ABC.XXX的中间的域名的里面加上匹配<,>就行了。就是要匹配http://www.abc.com,www.abc.com,http://www.a<tag>b</tag>c.com和www.a<tag>b</tag>c.com。好吧,有点复杂,我就直接上正则吧。

(?:(?:(?:[^@:(){}`\'\"\\[\]\s]+:)?[^@:(){}`\'\"\\[\]\s]+@)?(www|ftp|irc|jabber)\.(?:[^`~!@#$%^&*()_=+\[{\]}\\|;:\’\",.\?\s]+\.)+[a-z]{2,6}(?:[\#?](?:[^\^\[\]{}|\\\’\"`\s]*[^!@\^()\[\]{}|\\:;\’\",.?<>`\s])?)?)

上面的正则是不是已经有点看不懂了?别担心,至少上面的满足了我们的需求,匹配了上面加粗的内容。好像貌似我们已经基本上完成了,但是等等,好像有些情况还没考虑进去啊。还有哪些啊?

4.适应HTML代码里面加粗的超链接的需求
比如soundbbg@gmail.com(邮件形式),还有192.168.0.1(IP形式),当然还会包括上面所有的加粗之类的情况了,如soundbbg@gmail.com,192.168.0.1。

好了好了,情况够复杂了,还能更复杂吗?可以,因为还有很多情况你没考虑到。

5.适应多种协议以及以上所有需求的需求
这里只是将http://,https://,ftp://协议考虑进去了,如果是电驴呢?ed2k://file:xxxx,ok,还有邮箱的有mailto:soundbbg@gmail.com,还有,还有。。

那这样你可能会惊呼,哇,那这样的正则表达式要有多复杂啊。对,其实逻辑不难,但是写起来和调试起来还是很复杂的,咱们看看最终版本的正则吧,如果不懂而又有同样需求的话,可以免费COPY:)。

?:(news:\/\/|nntp:\/\/|telnet:\/\/|irc:\/\/|mms:\/\/|ed2k:\/\/|file:\/\/|about:|mailto:|xmpp:|h…s:\/\/|f.p:\/\/|h.?.?p:\/\/)[^\^\[\]{}|\\\’\"<>`\s]*[^!@\^()\[\]{}|\\:;\’\",.?<>`\s])|(?:(?:(?:(?:[^@:(){}`\'\"\/\[\]\s]+:)?[^@:(){}`\'\"\/\[\]\s]+@)?(www|ftp|irc|jabber)\.(?:[^`~!@#$%^&*()_=+\[{\]}\\|;:\’\",.\/?\s]+\.)+[a-z]{2,6}(?:[\/#?](?:[^\^\[\]{}|\\\’\"`\s]*[^!@\^()\[\]{}|\\:;\’\",.?`\s])?)?)|(?:(?:[^@:<>(){}`\'\"\/\[\]\s]+@)?((?:(?:(?:(?:[0-1]?[0-9]?[0-9])|(?:2[0-4][0-9])|(?:25[0-5]))(?:\.(?:(?:[0-1]?[0-9]?[0-9])|(?:2[0-4][0-9])|(?:25[0-5]))){3})|(?:[A-Fa-f0-9:]{16,39}))|(?:(?:[^`~!@#$%^&*()_=+\[{\]}\\|;:\’\",<.>\/?\s]+\.)+[a-z]{2,6}))\/(?:[^\^\[\]{}|\\\’\"<>`\s]*[^!@\^()\[\]{}|\\:;\’\",.?<>`\s](?:[#?](?:[^\^\[\]{}|\\\’\"<>`\s]*[^!@\^()\[\]{}|\\:;\’\",.?<>`\s])?)?)?)|(?:[^@:<>(){}`\'\"\/\[\]\s]+:[^@:<>(){}`\'\"\/\[\]\s]+@((?:(?:(?:(?:[0-1]?[0-9]?[0-9])|(?:2[0-4][0-9])|(?:25[0-5]))(?:\.(?:(?:[0-1]?[0-9]?[0-9])|(?:2[0-4][0-9])|(?:25[0-5]))){3})|(?:[A-Fa-f0-9:]{16,39}))|(?:(?:[^`~!@#$%^&*()_=+\[{\]}\\|;:\’\",<.>\/?\s]+\.)+[a-z]{2,6}))(?:\/(?:(?:[^\^\[\]{}|\\\’\"<>`\s]*[^!@\^()\[\]{}|\\:;\’\",.?<>`\s])?)?)?(?:[#?](?:[^\^\[\]{}|\\\’\"<>`\s]*[^!@\^()\[\]{}|\\:;\’\",.?<>`\s])?)?))|([^@:<>(){}`\'\"\/\[\]\s]+@(?:(?:(?:[^`~!@#$%^&*()_=+\[{\]}\\|;:\’\",<.>\/?\s]+\.)+[a-z]{2,6})|(?:(?:(?:(?:(?:[0-1]?[0-9]?[0-9])|(?:2[0-4][0-9])|(?:25[0-5]))(?:\.(?:(?:[0-1]?[0-9]?[0-9])|(?:2[0-4][0-9])|(?:25[0-5]))){3})|(?:[A-Fa-f0-9:]{16,39}))))(?:[^\^*\[\]{}|\\\"<>\/`\s]+[^!@\^()\[\]{}|\\:;\’\",.?<>`\s])?)|([0-9]{1,3})[.]([0-9]{1,3})[.]([0-9]{1,3})[.]([0-9]{1,3})

好吧,这个正则是有点复杂了,不过确实管用。我们能匹配所有的可以作为超链接的内容了。

但是,好吧,我也不想写但是,但是确实有但是。我们就算匹配了,也不能完全实现我们替换的链接,我们这里仅仅只走了第一步–即匹配了超链接

好,我们选择了http://www.abc.com,那么怎么变成<a href="http://www.abc.com">http://www.abc.com</a>呢?好吧,这是有点难,但是不是难点,所以这里就不过多讲解。这里只是提一下思路。

找到匹配的URL。

1.找到这个Match对象的前缀,如果是"或者包含http://,则说明这个是链接内(HTML标记内的链接),不能替换,否则替换。(看注释1)

2.然后找到Match对象的起始点,如果XXXXwww.abc.comXXX,这个时候不能直接用replace替换,而需要找到www的索引,然后用substring替换。(看注释2)

3.最后得到最终结果。

如果我每个都说的话,那就是2篇文章了。太麻烦了。好了,我们正确的找到了并替换了相应的超链接之后,就能够实现我们的需求了。这个变态的需求。下面我给大家一个文件,如果大家自己能写代码实现的话,那就真的理解了。大家可以锻炼锻炼。

输入的HTML效果图(超链接,IP和E-Mail)

输出的HTML(该变的都变成超链接了,注意,第四个本来是超链接的没有改动过)

下面是这个HTML的输入。

test.html (306.00 bytes)

注释1:使用正则表达式需要使用JS里面的正则表达式的Match对象并使用while循环匹配对象。

注释2:由于不能替换,所以我们必须要将替换的结果插入到替换的地方,所以必须使用注释1里面的Match对象的index属性进行调整。

好久没有写Firefox插件开发的相关文章了,自己总结了一下,主要有这几点,一,自己差不多把Firefox插件开发的过程掌握的一一二二了,所以很少再写文章了,二,没时间写,三,懒得写。其实我觉得最主要的原因还是一和三,毕竟我在写了这篇文章之后,大部分的问题都可以直接通过联系官方人员去了解了,所以我不会的问题,自然就直接和官方人联系,也很少自己弄很长时间弄出一个问题,然后感叹的洋洋洒洒的写一篇文章。其实我也说不上这是好是坏。

OK,今天要说的就是Firefox扩展区修改页面的内容。平常我们做Firefox扩展的时候,大都是通过XUL去写一个按钮或者窗口之类的,但是我们没有更改过网页里面的内容。因为如果直接使用document.getElementById的话,我们取到的是Firefox里面的一个元素(不了解的看这里),而如果我们要取到页面里面的HTML DOM的话,就要用另一种方式了。

//取到的是当前tab里面的HTML代码
var body = content.document

我们获取到页面的元素是干嘛的呢,当然是修改的了。比如我们像Firebug一样,可以修改节点,删除掉节点,增加一个节点,是不是很酷?所以我们只要获得到了DOM对象,就可以轻易的操作DOM了。

首先我们选中网页里面的一段话,这个时候我们想要对选中的地方进行操作,例如插入,删除等等,怎么办,我们首先可以获取Selection对象,代码如下。

var selection = window.content.getSelection();
if(selection!=null&&selection.toString()!="")
{
//Do something
}

我们找到选中的内容之后,判断选中的是否为空或者怎样,如果不为空就继续做我们想做的事情,如果你已经知道Firefox插件开发,可以看这个对象的作用,方法和一些属性。 

如果我有这样一个需求,我们选择了一段文字,然后想直接搜索文字。这个时候我们就像要是有一个小的弹出的泡泡就好了,如下图所示。

那这样我们怎样实现呢。首先我们可以分为下面几个步骤。

1.选中文字,判断是否选中。

2.在选中文字附近加入span标签,并设置为float,并绝对位置定位。

3.添加相应的内容到HTML文档中。

这三点中,第一点我们已经解决了,这个时候我们就需要做第二点,也就是当你选中一段代码后,我们弄一个小的Popup出来。这个地方我们要注意一下。我们在“abc”中选中b之后,我们就在“abc”的“b”后面添加一个span元素,就成为了“abdc”,这个时候我们只要将d的样式设置为float并绝对定位,就可以飘到上面去了,而实际上是一行HTML代码。这里首先要理解。所以我们就写一段这样的代码。

//创建一个DOM(document.createElement)
//
selection是选中的文字
var node = createNode(selection);
//get the index and range
var lastRange = selection.getRangeAt(selection.rangeCount - 1);
var newRange = document.createRange();
newRange.setStart(lastRange.endContainer, lastRange.endOffset);
newRange.insertNode(node);

createNode方法很好写,就是返回一个document.createElement创建的对象,我们这里可以创建span之类的,任何对象都可以。

创建完之后我们就讲我们的DOM元素插入到HTML文档中去了。不过值得注意的是,上面这段代码一般都是写在mouseup事件中的,当我们选中一段内容时,实际上就是鼠标按下=》移动=》鼠标释放着3个步骤,按下,移动我们都可以不用写,当鼠标释放的时候,我们就要获取选中的内容并创建一个DOM插入到选中的文本的最后面即可。

其实这里思路不是很难,不过有很多小细节要注意,如果你真的要实现这样的功能的话,建议自己亲手做一下,否则真的很难掌握。估计我后面写的文章大多数人也是看的云里雾里,如果有什么问题直接mail联系吧。:)

356路过 3评论 Firefox Addon 阅读全文..

我们经常在开发过程中遇到这样一个场景,例如不同的语言执行不同的方法,例如如果是中文,就说“你好”,如果是英文就是“Greeting” ,我们通常会写如下伪代码。

//伪代码
if(语言==中文)
{
    说“你好”
}
else if(语言==英语)
{
    说
"Greeting"
}

这样是可行的,但是如果语言多了,那是不是就要写很多代码呢,况且JS里面没有很好的委托的解决方案,不过JS倒也有另外一个特性,就是传递函数,然后用eval来执行,不过这个不太好看,也不方面维护,在做Firefox扩展开发的时候,我们可以利用Firefox的绑定的特性来实现这一效果。

首先我们还是写一个HelloWorld程序了,我们写XUL如下。

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://{appname}/skin/overlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://{appname}/locale/overlay.dtd">
<overlay id="{appname}-overlay"
         xmlns
="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <!– firefox –>
  
<menupopup id="menu_ToolsPopup">
    
<menuitem id="mymenuitem" label="Hello"
              oncommand
="this.BindingCSS()"/>
  
</menupopup>

</overlay>

这里我们创建了一个menuitem,并且说了当有命令的时候执行BindingCSS方法,可是在这里我们没有使用任何的JS文件啊,那这个方法写在哪里呢又为什么可以调用了呢。我们慢慢来。我们可以看到这个方法名字叫BindingCSS,这也就是我要告诉大家,Firefox可以绑定CSS特性来修改相应的事件。我们可以看相应的CSS代码。

#mymenuitem
{
    -moz-binding
: url(‘chrome://{appname}/content/test1.xml#mytest1′) !important;
    color:red;
}

这里我们使用了Firefox里面的属性【-moz-binding】,告诉我们这个控件应该绑定哪个xml,并且绑定到哪个id上,怎么?不太理解,我们慢慢来。

首先我们再content下建立一个test1.xml,然后我们写下如下代码。

<?xml version="1.0"?>
<bindings id="test"
          xmlns
="http://www.mozilla.org/xbl"
          xmlns:xul
="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl
="http://www.mozilla.org/xbl">
      
    
<binding id="mytest1" extends="chrome://global/content/bindings/menu.xml#menuitem">
        
<implementation>
        
<property name="para">
        
<getter>
                
<![CDATA[
                  return "Blinding1 Success!";
                
]]>
        
</getter>
        
</property>

        <method name="BindingCSS">
            
<body>
                
<![CDATA[
                    alert(this.para);
                
]]>
            
</body>
        
</method>
    
</binding>
</bindings>

现在到了重难点了,我们首先看一下这个XML,首先需要一个根节点bindings,id在这里可以随便取,没有问题,后面是一些命名空间了,直接照抄就可以了。

然后我们继续往下走,可以看到binding节点,嗯,我们可以看到这个id,是不是很熟悉,正确,这个地方就是我们要绑定的内容,前面我们写了test1.xml#mytest1就是说明当前控件绑定的是这个节点一下的东西。然后看属性,有一个extends属性,这个属性说明的是继承自Firefox的哪个控件,例如我这里就是menu里面的menuitem(这里也是#id的哦),如果你不知道如何看id,看这篇文章,用这个工具去找相应的控件。

然后我们继续看下面,下面就豁然开朗了,这里有属性(property),方法(method),代码就很简单了,我这里就不详细讲解了。于是,我们就给我们的一个XUL控件绑定了一个事件,是不是很神奇?当然,这里不只有属性,方法,还能写字段(field)和结构或者说类(constructor)。

OK,如果你上面的还没消化好,建议你好好消化,消化好了,我们就可以看如何修改CSS修改事件了。其实也不难,因为既然是通过CSS绑定的,我们只要修改一下控件的ID不就完了吗?对,聪明,我们可以绑定mytest2,我们可以添加代码如下。

<binding id="mytest2" extends="chrome://global/content/bindings/menu.xml#menuitem">
    
<implementation>
    
<property name="para">
    
<getter>
            
<![CDATA[
              return "Blinding2 Success!";
            
]]>
    
</getter>
    
</property>

    <method name="BindingCSS">
        
<body>
            
<![CDATA[
                alert(this.para);
            
]]>
        
</body>
    
</method>
</binding>

这个时候我们只要修改CSS里面相应的ID的属性就可以更改绑定的事件了。代码可以从下面下载。

test.rar (33.71 kb)

443路过 5评论 Firefox Addon 阅读全文..