UserJS 语法介绍

Opera 的脚本实现

Opera 浏览器提供了 6 个用户脚本级的调用方法(Method)和 13 个事件(Event)以及一个关键的对象(被称为 UserJSEvent 对象),来方便开发者编写用户脚本。除此之外,由于用户脚本被应用以后,其便成为所浏览网页的一部分,其同时也可以像正常的网页脚本一样,调用所有符合 W3C DOM 标准的方法和事件。

window.opera.addEventListener

在网页脚本中,通过 window.addEventListener 调用,我们可以设置常规的监听函数。同理,我们可以通过 window.opera.addEventListener 设置用户脚本级别的事件监听。同时,其回调函数的编写方法,与常规的网页脚本没有区别。要注意的是,用于监听的回调函数必须定义一个参数,用于事件对象的传递,这个对象被称为UserJSEvent 对象。通过此事件对象,用户脚本得以完成其特殊功能。

下面,向你介绍功能独特的用户脚本事件。

BeforeExternalScript

在 Opera 加载 HTML 页面时,当遇到带有 src 属性的 script 元素时触发。与此 script 元素对应的 DOM 对象,可以通过回调传参(UserJSEvent 对象)中的 element 属性获得。通过在事件处理中获得的 DOM 对象,你可以取消相应 js 文件的加载。而如果 js 被取消了加载,则不会载入外部资源且脚本元素不会被执行;同时,其相关的后续 BeforeScript 事件也不会被触发。

示例:

window.opera.addEventListener(
    "BeforeExternalScript",
    function (e) {
        if( e.element.getAttribute("src").match(/problem\/script\.js$/) ) {
            e.preventDefault();
        }
    }, false
);

BeforeScript

在 script 元素被解释执行前触发,这里的 script 包括网页中的 script 标记块,和外部引用的 script 文件。与此 script 元素对应的 DOM 对象,可以通过回调传参(UserJSEvent 对象)中的 element 属性获得。而 script 标记的内容,则可以通过 DOM 对象的 text 属性获得。并且,这个属性的值是可以修改的。你可以通过修改 text 属性的值,达到修改网页脚本的目的。或者,你还可以通过 UserJSEvent 对象的 preventDefault 方法,阻止此 script 被 Opera 解释执行。

另外,即使是 VBScript 脚本块,同样也会触发 BeforeScript 事件,只是 Opera 不会执行此类的脚本。

示例:

window.opera.addEventListener(
    "BeforeScript",
    function (e) {
        e.element.text = e.element.text.replace(/!=\s*null/, "");
    }, false
);

AfterScript

在 script 元素被解释执行后触发,其它细节同上。

BeforeEvent

在某个 DOM 事件被触发之前触发,而不管此事件是否已经被所在网页设置了处理函数。与此事件对应的 DOM 事件,可以通过回调传参(UserJSEvent 对象)中的 event 属性获得。你可以通过 UserJSEvent 对象的 preventDefault 方法,阻止此事件的触发。而如果此事件被阻止触发,则其默认动作将被执行,同时与其相关的所有 BeforeEventListener 事件都不会被触发。

BeforeEvent.type

和 BeforeEvent 一样,但只针对 type 所指定的事件。例如,我们可以监听 BeforeEvent.click 事件,那么只有 click 类型的事件,才会触发监听函数。同时,如果既监听了 BeforeEvent,又监听了 BeforeEvent.type,则二者都会被触发。

需要说明的是,在实际的使用过中,我们一般会有针对性地编写 BeforeEvent.type 事件监听,而不会简单地使用 BeforeEvent。因为 BeforeEvent 会触发大量的事件行为,极其影响性能,所以最好不要对这个事件编写处理代码。

示例:

window.opera.addEventListener(
    "BeforeEvent.click",
    function (e) {
        e.preventDefault();
    }, false
);

AfterEvent

在某个 DOM 事件触发并执行处理函数之后(不管所在网页是否定义了处理函数),但在此事件的默认动作被执行之前。与此事件对应的 DOM 事件,可以通过回调传参(UserJSEvent 对象)中的 event 属性获得。如果网页中的处理函数截获了这个事件,并试图阻止此事件的默认动作发生。那么,可以通过 UserJSEvent 对象的 eventCancelled 属性检测出来。同时,当调用 UserJSEvent 对象的 preventDefault 方法时,网页处理函数的任何阻止都将被忽略。

AfterEvent.type

和 AfterEvent 一样,但只针对 type 所指定的事件。例如,我们可以监听 AfterEvent.click 事件,那么只有 click 类型的事件,才会触发监听函数。同时,如果既监听了 AfterEvent,又监听了 AfterEvent.type,则二者都会被触发。

需要说明的是,在实际的使用过中,我们一般会有针对性地编写 AfterEvent.type 事件监听,而不会简单地使用 AfterEvent。因为 AfterEvent 会触发大量的事件行为,极其影响性能,所以最好不要对这个事件编写处理代码。

示例:

window.opera.addEventListener(
    "AfterEvent.click",
    function (e) {
        if( e.eventCancelled ) {
            e.preventDefault();
        }
    }, false
);

BeforeEventListener

在某个 DOM 事件被网页中的 addEventListener 方法进行监听之前触发。与此事件对应的 DOM 事件,可以通过回调传参(UserJSEvent 对象)中的 event 属性获得。而将被调用的监听函数,则可以通过 UserJSEvent 对象的 listener 属性获得。你可以通过 UserJSEvent 对象的 preventDefault 方法,阻止此事件的触发。而如果此事件被阻止触发,则网页中的 addEventListener 调用将被忽略。

BeforeEventListener.type

和 BeforeEventListener 一样,但只针对 type 所指定的事件。例如,我们可以监听 BeforeEventListener.click 事件,那么只有 click 类型的事件,才会触发监听函数。同时,如果既监听了 BeforeEventListener,又监听了 BeforeEventListener.type,则二者都会被触发。

需要说明的是,在实际的使用过中,我们一般会有针对性地编写 BeforeEventListener.type 事件监听,而不会简单地使用 BeforeEventListener。因为 BeforeEventListener 会触发大量的事件行为,极其影响性能,所以最好不要对这个事件编写处理代码。

示例:

window.opera.addEventListener(
    "BeforeEventListener.click",
    function (e) {
        e.preventDefault();
    }, false
);

AfterEventListener

在某个 DOM 事件被网页中的 addEventListener 方法进行监听之后触发。与此事件对应的 DOM 事件,可以通过回调传参(UserJSEvent 对象)中的 event 属性获得。而被调用的监听函数,则可以通过 UserJSEvent 对象的 listener 属性获得。如果网页中的监听函数截获了这个事件,并试图阻止此事件的继续传播。那么,可以通过 UserJSEvent 对象的 propagationStopped 属性检测出来。同时,当调用 UserJSEvent 对象的 preventDefault 方法时,网页监听函数的传播阻止行为将被忽略。

AfterEventListener.type

和 AfterEventListener 一样,但只针对 type 所指定的事件。例如,我们可以监听 AfterEventListener.click 事件,那么只有 click 类型的事件,才会触发监听函数。同时,如果既监听了 AfterEventListener,又监听了 AfterEventListener.type,则二者都会被触发。

示例:

window.opera.addEventListener(
    "AfterEventListener.click",
    function (e) {
        if (e.propagationStopped) {
            e.preventDefault();
        }
    }, false
);

BeforeJavascriptURL

在 JavaScript 伪链接被执行之前触发。将被执行的 JavaScript 代码,可以通过回调传参(UserJSEvent 对象)中的 source 属性获得。并且,这个属性的值是可修改的。你可以通过修改 source 属性的值,达到修改网页脚本的目的。或者,你还可以通过 UserJSEvent 对象的 preventDefault 方法,阻止所在 JavaScript 代码的执行。

示例:

window.opera.addEventListener(
    "BeforeJavascriptURL",
    function (e) {
        // we can modify the JavaScript code by following
        e.source = ...
    }, false
);

AfterJavascriptURL

在 JavaScript 伪链接被执行之后触发。已经被执行的 JavaScript 代码,可以通过回调传参(UserJSEvent 对象)中的 source 属性获得。而所执行代码的返回值,可以通过 UserJSEvent 对象的 returnValue 属性获得。并且,这个属性的值是可修改的。或者,你还可以通过 UserJSEvent 对象的 preventDefault 方法,阻止 returnValue 的属性值被 Opera 当做新的 HTML 源代码进行显示。要注意的是,这个事件会被触发,当且仅当链接脚本执行成功。

示例:

window.opera.addEventListener(
    "AfterJavascriptURL",
    function (e) {
        // we can use e.returnValue to judge what to do.
        // and then, use e.preventDefault to prevent default behavior
        ...
    }, false
);

PluginInitialized

在插件(如 mediaplayer、flash、quicktime 等)初始化完成后触发。

window.opera.addEventListener(
    "PluginInitialized",
    function (e) {
        // we can do something after plugin initialized.
        ...
    }, false
);

BeforeCSS

  • 在 CSS 样式表被解析之前触发。
  • 触发该用户脚本事件的元素,可以是 HTML 文档中的 style 元素或引用外部样式表的 link 元素。这些元素用多种方式导入样式规则,如用 <style> 标签内嵌样式文本或导入外部样式文本,也可以用带有外部样式表 url 的 link 占位元素引入样式表(同时该元素还可以带有 media 及其他属性)。
  • 该事件有一个可读写的 cssText 属性,重写这个属性,可以更改样式表内容:
    opera.addEventListener('BeforeCSS', function(userJSEvent){
      userJSEvent.cssText = userJSEvent.cssText
        .replace(/-(moz|ms|webkit|o)-(border|text-overflow)/g,'$2')
        .replace(/-(moz|ms|webkit)-(gradient|transform|transition)/g,'-o-$2');
    }, false);
  • 该事件可取消浏览器对样式表的解析,从而使得该样式表不起作用:
    opera.addEventListener('BeforeCSS', function(userJSEvent){
    	userJSEvent.preventDefault();
    }, false);
  • 如果没有取消样式表的解析,那么对样式表内容的修改,也将应用到页面上。

AfterCSS

  • 如果各自对应的 BeforeCSS 事件没有取消样式表解析,那么 AfterCSS 事件在样式表解析后立刻触发。
  • 触发该用户脚本事件的元素,可以是 HTML 文档中的 style 元素或引用外部样式表的 link 元素。这些元素用多种方式导入样式规则,如用 <style> 标签内嵌样式文本或导入外部样式文本,也可以用带有外部样式表 url 的 link 占位元素引入样式表(同时该元素还可以带有 media 及其他属性)。
  • 该事件拥有一个可读写的 cssText 属性,用于访问样式表内容。
  • 修改 cssText 属性,可将更改后的样式重新应用到页面,并触发一个新的 BeforeCSS 事件。

UserJSEvent 对象

在上面的解释过程中,我们经常可以看到 UserJSEvent 对象,这个对象是通过 window.opera.addEventListener 设置监听函数时,监听函数必须定义的传入参数。当上面列举的任意事件被触发时,用户脚本事件的上下文对象 UserJSEvent,便会被传入相应的监听函数。而监听函数就可以利用这个特殊对象,做进一步的处理。

下面,将列举出 UserJSEvent 的成员。而其下各种属性的值,在不同的情况下,表现不同。

属性 类型 其他
bubbles boolean
cancelBubble boolean
cancelable boolean
currentTarget object [object Opera]
element object 只读:支持大部分常用的 DOM 方法,例如 getAttribute 和 setAttribute。可用于 BeforeExternalScript、BeforeScript 和 AfterScript 事件中。
element.text string 读写:script 标记中将被执行的 JS 代码。可用于 BeforeScript 和 AfterScript 事件中。在 User JS 中,element.text 不受同源策略的限制,无论 script 引用的 JS 来自哪里,都可以读取。
event object 只读:标准的 event 对象。可用于 BeforeScript 和 AfterScript 事件中。
eventCancelled boolean 只读:如果事件处理函数将事件取消了则为 true,否则为 false。可用于 AfterScript 事件中。
eventPhase number
initEvent function function initEvent() { [native code] }
listener function 只读:事件处理函数的引用。可用于 BeforeEventListener 和 AfterEventListener 事件中。
returnValue undefined 只读:JavascriptURL 的返回值。可用于 AfterJavascriptURL 事件中。
source undefined 只读:JavascriptURL 的源码。可用于 BeforeJavascriptURL 和 AfterJavascriptURL 事件中。
srcElement object [object Opera]
stopPropagation function function stopPropagation() { [native code] }
target object [object Opera]
timeStamp number
type string 只读:当前事件的类型,如:BeforeScript、BeforeJavascriptURL。可用于所有的事件中。
preventDefault function 只读:阻止默认行为,如:阻止脚本运行或者阻止事件处理函数的执行。可用于所有事件中,但在 AfterScript 事件中使用是没有效果的,因为脚本已经被执行了。
propagationStopped boolean 只读:如果事件处理函数使事件停止传播则为 true,否则为 false。可用于 AfterScript 事件中。

window.opera.scriptStorage

该属性会返回一个 Storage 对象(该对象由 W3C Web Storage API 定义),并且此返回对象在每个用户脚本中相互独立。在用户脚本中可使用此对象来实现跨域间的存储共享。此对象现阶段还不能响应 storage 的各事件。window.opera.scriptStorage 属性现在只能在 Opera Presto 2.6 渲染引擎上可用。关于更多的信息,请查阅此文章:eWeb specifications support in Opera Presto 2.6: Web Storage

scriptStorage 只能由用户脚本的主体代码直接调用,其他地方,如以各种方式调用的以 “javascript:” 开头的 js 代码、由事件驱动的 js 代码、外部引用的以及自行创建的 <script> 标签内的代码等,都不能直接访问 opera.scriptStorage。如果要将 scriptStorage 用于事件驱动的 js 代码中,请在主体代码中传递一个引用过去,如下面代码:

function(opera, storage)
	{
		window.addEventListener(
			'DOMContentLoaded',
			function (e){
				alert(storage);
			},false
		);
	}
)(opera, opera.scriptStorage);

要使用 scriptStorage 对象,必须打开对其所作的限制。在 opera:config#PersistentStorage|UserJSStorageQuota 中,如果设置为 0 ,那么 scriptStorage 将不可用,而大于 0 的其他数,则表示可使用的存储大小,单位为 kB。要查看由 scriptStorage 创建的信息,请打开 opera:webstorage 页面。

window.opera.removeEventListener

用于取消 window.opera.addEventListener 设置的事件监听。

window.opera.defineMagicVariable

此方法用于覆盖网页中定义的全局变量。它的工作原理是,在网页代码试图读取或设置指定全局变量值的时候,加入了中间处理层。通过加入的中间处理层,开发人员得以完成一些特殊的逻辑处理。 调用格式及参数

window.opera.defineMagicVariable(
    targetVariableName,
    function (currentVal) {
        // do what you want to when getting
        ...
    },
    function (newVal) {
        // do what you want to when setting
        ...
    }
);
属性 类型 其他
targetVariableName string 指定被覆盖的变量名称;
function (currentVal) { } function 回调函数。当有代码试图访问被覆盖的变量时,其传入参数(currentVal)的值,为所覆盖变量的当前值;而此回调函数的返回值将被当做变量的当前值处理。
function (newVal) { } function 回调函数,可选。设置为 null 时,表示不使用。当有代码试图向此变量赋值时,此函数将被执行,传入参数(newVal)的值为所赋的值,而返回值为实际赋到变量上的值。

window.opera.defineMagicFunction

当网页中的某个函数,不符合某种功能要求时,我们可以通过此方法,将其替换成我们想要完成的功能。 调用格式及参数

window.opera.defineMagicFunction(
    targetFunctionName,
    function(originalFunctin, oThis, oParam1, oParam2) {
 
    }
);
属性 类型 其他
targetFunctionName string 指定被覆盖的函数名称;
function(originalFunctin, oThis, oParam1, oParam2) { } function 自定义函数,用来覆盖原来的函数;
originalFunctin function 被覆盖函数原来的对象;
oThis object 前函数的上下文对象;
oParam1, oParam2 various 被覆盖函数原来的传入参数表;

window.opera.setOverrideHistoryNavigationMode

解释同下

window.opera.getOverrideHistoryNavigationMode

Opera 默认支持从浏览器的缓存中读取前进及后退的网页请求。但这样做,带来了一个不好的后果,因为有一些网页会在 load 和 unload 时,做一些脚本操作。比如,在 load 时初始化网页的界面,或是在 unload 时,提示用户“是否离开当前页面”。但有时 Opera 检测不到这种脚本动作,因而会造成前进及后退时,网页状态不正确。为解决这一问题,Opera 提供了“历史导航模式”的特性,即通过这一设置,可以改善网页加载及卸载时的问题。

这个“历史导航模式”可以被设置成 3 种值,分别是:automatic, compatible, 或 fast。我们可以通过 opera:config 来完成设置,也可以通过网页脚本自身的 history.navigationMode 属性来完成,还可以通过当前段落所解释的这两个方法来完成。

编写综述

编写用户脚本,实际上是一个 Hack 网页代码及脚本的过程。通过在网页已经提供代码及功能的基础上,修修补补,完成自己想要做的事情。那么,你得有一个相应的工具,来满足你查看网页“微观世界”的想法。Opera 提供了 DragonFly,一个可以满足你调试网页结构,查看网页元素样式,以及实时调试 JavaScript 代码的工具。通过这个工具,再加上 Opera 用户脚本这一特性,你可以对你浏览到的网页,做任何你想做的“手术”。

许多情况下,我们都是通过改写网页的 JavaScript 代码来 Hack 网页功能的。那么,从上至下,你可以看到如下的一个可操作顺序。

  1. 通过 defineMagicFunction 方法,覆盖网页函数为自定义函数;
  2. ?通过监听 BeforeScript 事件,直接改写函数中的 JavaScript 代码;
  3. ?通过 defineMagicVariable 方法,覆盖网页全局变量,间接地影响代码的处理;
  4. ?通过监听 BeforeEvent.type 事件,在某个事件触发前,加入一些逻辑处理;
  5. ?通过监听 BeforeJavascriptURL 事件,更改脚本链接的运行逻辑;
  6. ?另外,如果我们想加入一些功能,我们还可以像网页脚本一样,对网页事件进行监听并处理。比如,下面的代码即为监听 DOM 的内容被加载后触发。
indow.addEventListener(
    "DOMContentLoaded",
    function(e) {
        // we can add our logic following
        ...
    }, false
);

编写用户脚本是一件很有意思的事情,因为挑战自己,在针尖上跳舞,未尝不是计算机科学经常面对的。也许你想变得更 Geek,那么就从 Hack 并“篡改”别人的网页代码开始吧! :)

4条评论

  1. Pingback: 帮助 « OperaIM

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注