此內容未以你的語言提供。 以下為簡體中文。
4.4 版本插件开发API文档见:/m/plugin.html
为知笔记从4.5版本开始,升级Chrome内核版本为49,并采用多进程模式。带来的一个影响就是大部分插件需要升级才可以在4.5下面使用。 由于采用多进程模式,插件将无法直接访问笔记渲染浏览器的DOM对象,取而代之的,是通过WizChromeBrowser这样一个对象,和浏览器进行交互,例如执行脚本并获得返回值。
一个例子:在插件代码中访问当前笔记的文字:
4.5之前的版本代码:
var noteDoc = objApp.Window.CurrentDocumentHtmlDocument; //HTML Document
//4.5之前的版本,我们可以直接获得当前笔记所在浏览器的document对象,并获得body中的文字。
alert(noteDoc.body.innerText);
4.5以及之后的版本代码:
var objBrowser = objApp.Window.CurrentDocumentBrowserObject;
//4.5以及之后的版本,无法获得笔记所在浏览器的对象,而是获得了一个WizChromeBrowser的对象
//通过WizChromeBrowser提供的方法,我们可以要求笔记所在的浏览器,执行一段脚本,并将脚本的返回值返回给我们。
//WizChromeBrowser和浏览器交互的时候,都是异步进行的,结果通过回调函数返回。
objBrowser.ExecuteScript("document.body.innerText", function(text) {
alert(text);
});
从上面的例子可以看出插件如果要和笔记所在浏览器进行交互,则必须采用发送消息的形式来进行。 下面是WizChromeBrowser的定义:
interface IWizChromeBrowserObject : IDispatch{
// 执行一个脚本,异步执行
[id(9), helpstring("method ExecuteScript")] HRESULT ExecuteScript([in] BSTR bstrScript, [in] VARIANT callback);
// 执行一个.js文件里的所有脚本,异步执行
[id(10), helpstring("method ExecuteScriptFile")] HRESULT ExecuteScriptFile([in] BSTR scriptFileName, [in] VARIANT callback);
// 执行一个函数,无参数,异步执行
[id(11), helpstring("method ExecuteFunction0")] HRESULT ExecuteFunction0([in] BSTR functionName, [in] VARIANT callback);
// 执行一个函数,输入参数有一个,异步执行
[id(12), helpstring("method ExecuteFunction1")] HRESULT ExecuteFunction1([in] BSTR functionName, [in] VARIANT param1, [in] VARIANT callback);
// 执行一个函数,输入参数有两个,异步执行
[id(13), helpstring("method ExecuteFunction2")] HRESULT ExecuteFunction2([in] BSTR functionName, [in] VARIANT param1, [in] VARIANT param2, [in] VARIANT callback);
// 执行一个函数,输入参数有三个,异步执行
[id(14), helpstring("method ExecuteFunction3")] HRESULT ExecuteFunction3([in] BSTR functionName, [in] VARIANT param1, [in] VARIANT param2, [in] VARIANT param3, [in] VARIANT callback);
// 执行一个函数,输入参数有四个, 异步执行
[id(15), helpstring("method ExecuteFunction4")] HRESULT ExecuteFunction4([in] BSTR functionName, [in] VARIANT param1, [in] VARIANT param2, [in] VARIANT param3, [in] VARIANT param4, [in] VARIANT callback);
};
WizChromeBrowser是为知里面每一个Chrome浏览器所内置的一个对象,该对象可以在脚本中传递。对于笔记所在的浏览器,则可以通过为知的接口获得该对象(例如通过objApp.Window.CurrentDocumentBrowserObject获得当前笔记所在的浏览器对象)。 WizChromeBrowser的主要方法:
在插件中更改笔记内容(加粗当前选中的文字): 4.5之前的版本代码:
var doc = objApp.Window.CurrentDocumentHtmlDocument;
doc.execCommand("bold", false);
4.5以及之后的版本代码:
objApp.Window.CurrentDocumentBrowserObject.ExecuteScript("document.execCommand('bold', false);", function(ret){});
获取笔记中特定的元素(大纲插件) 4.5之前的版本代码:
function getBookmarks(doc){
if(!doc)
return ;
//
var arrLinks = doc.anchors;
for(var i = 0; i <arrLinks.length; i++){
var elem = arraLinks[i];
var elemName = elem.name;
…
}
}
4.5以及之后的版本代码:
/* getBookmarks.js*/
(function(){
var added = false;
var arrLinks = document.anchors;
var arrayBookmarkName = [];
var jsonelem = [];
//
for (var i = 0; i < arrLinks.length; i++) {
var elem = arrLinks[i];
// 获取DOM节点的属性值
var name = elem.name;
if (name == null || name == "")
continue;
//
…
//
arrayBookmarkName.push(name);
}
return arrayBookmarkName;
}());
/*Outline.html*/
var scriptFile = pluginPath + "getBookmarks.js";
objBrowser.ExecuteScriptFile(scriptFile, function(ret){
if(ret && ret.length > 0){
for(var i = 0; i < ret.length; i++){
var elemName = ret[i];
…
}
}
});
注意事项: 不管是执行脚本的返回值,还是执行函数的参数,只能是以下几种类型:
对于DOM对象,例如HTML Document,Window对象,都不能直接作为参数或者返回值传递。例如下面的代码无法执行:
objApp.Window.CurrentDocumentBrowserObject.ExecuteScript("document.body", function(body) {
alert(body.innerText);
});
因为document.body 不属于上面所允许的类型,因此无法作为返回值传递。 同样,js内部的JSON对象,也不能进行传递。下面的代码同样是非法的:
objApp.Window.CurrentDocumentBrowserObject.ExecuteScript("{'a':'a'}", function(obj) {
alert(obj.a);
});
如果要传递一个JSON对象,可以将JSON序列化成字符串。在使用的时候再重新解析成一个JSON对象。 示例代码:
function pluginXXX_GetElemAttributes(elemid){
var elem = document.getElementById(elemid);
if(elem){
var jsonelem = {
"tagName": elem.tagName.toLowerCase(),
"offsetTop": elem.offsetTop,
"innerText": elem.innerText
};
var json = JSON.stringify(jsonelem);
return json;
}
return null;
}
//将pluginXXX_GetElemAttributes函数注入到目标浏览器内
objBrowser.ExecuteScript(pluginXXX_GetElemAttributes.toString(), function(ret){
//调用目标浏览器内注入的函数
objBrowser.ExecuteFunction1("pluginXXX_GetElemAttributes", "test", function(ret){
if(ret && ret.length > 0){
var att = JSON.parse(ret);
var tagName = att.tagName;
var offsetTop = att.offsetTop;
var innerText = att.innerText;
}
});
});
其他注意事项: 声明插件支持的版本
部分接口改变,从传递document对象到传递WizChromeBrowser对象作为参数
定义的一些Wiz笔记响应事件
function WizOnHtmlDocumentCompleteEx(objBrowser, objWizDocument) { eventsHtmlDocumentCompleteEx.dispatch2(objBrowser, objWizDocument); } function WizOnDocumentBeforeChange(objBrowser, objWizDocumentOld, objWizDocumentNew) { return eventsDocumentBeforeChange.dispatch3(objBrowser, objWizDocumentOld, objWizDocumentNew); } function WizOnDocumentAfterChange(objBrowser, objWizDocumentOld, objWizDocumentNew) { return eventsDocumentAfterChange.dispatch3(objBrowser, objWizDocumentOld, objWizDocumentNew); } function WizOnDocumentBeforeEdit(objBrowser, objWizDocument) { return eventsDocumentBeforeEdit.dispatch2(objBrowser, objWizDocument); } function WizOnDocumentAfterEdit(objBrowser, objWizDocument) { return eventsDocumentAfterEdit.dispatch2(objBrowser, objWizDocument); }
eventsDocumentBeforeEdit 和 eventsDocumentAfterEdit 消息在Wiz 4.5.8以及之后版本中失效
IWizExplorerApp
HRESULT GetPluginAppPath([in] IDispatch* pBrowserObjDisp, [out, retval] BSTR* pVal); HRESULT GetPluginAppGUID ([in] IDispatch* pBrowserObjDisp, [out, retval] BSTR* pVal); HRESULT GetHtmlDocumentPath([in] IDispatch* pBrowserObjDisp, [out, retval] BSTR* pVal); // 以下三种方法均为异步执行 HRESULT PluginLocalizeHtmlDialog([in] IDispatch* pBrowserObjDisp); HRESULT LocalizeHtmlDocument([in] BSTR bstrLanguageFileName, [in] IDispatch* pBrowserObjDisp); //同LocalizeHtmlDocument方法,返回值通过callback返回 HRESULT LocalizeHtmlDocument2([in] BSTR bstrLanguageFileName, [in] IDispatch* pBrowserObjectDisp, [in] VARIANT callback); //添加方法 SetNoteModifiedByPlugin, 插件修改笔记后调用此方法,作为修改标志 HRESULT SetNoteModifiedByPlugin(); //添加方法 IsCurrentDocumentEditing, 获取当前显示的笔记是否处于编辑状态 HRESULT IsCurrrentDocumentEditing([out, retval] VARIANT_BOOL* pVb);
一个例子:对话框类型插件本地化 4.5之前版本代码:
var iniPath = pluginPath +"plugin.ini";
LocalizeHtmlDocument(iniPath, document); //同步执行
4.5以及之后的版本代码:
LocalizeHtmlDocument(iniPath, WizChromeBrowser); //异步执行
IWizExplorerWindow
// 属性 CurrentDocumentHtmlDocument不再使用, 改为使用 CurrentDocumentBrowserObject, 获取当前正在显示的笔记所在浏览器的 WizChromeBrowser 类型对象 HRESULT CurrentDocumentBrowserObject([out, retval] IDispatch** pVal); // 方法 ShowHtmlDialog 与 ShowHtmlDialog2 不再使用,改为使用 ShowHtmlDialogEx。 // vbModal 必为 false,vParam为传给对话框的参数,可通过方法 GetHtmlDialogParam 来获得。 此函数为异步调用,结果通过vCallBack返回。 HRESULT ShowHtmlDialogEx([in] VARIANT_BOOL vbModal, [in] BSTR bstrTitle, [in] BSTR bstrURL, [in] LONG nWidth, [in] LONG nHeight, [in] BSTR bstrExtOptions, [in] VARIANT vParam, [in] VARIANT vCallback); HRESULT CloseHtmlDialog([in] IDispatch* pBrowserObjDisp, [in] VARIANT vRet); HRESULT GetHtmlDialogParam([in] IDispatch* pBrowserObjDisp, [out, retval] VARIANT* pvParam); HRESULT GetHtmlDialogHWND([in] IDispatch* pBrowserObjDisp, [out, retval] OLE_HANDLE* phHWND); HRESULT SetDialogResult([in] IDispatch* pBrowserObjDisp, [in] VARIANT vRet); HRESULT CloseSelectorWindow([in] IDispatch* pdispSelectorBrowserDisp);
一个例子:在对话框插件中打开另一个html页面(自定义快速搜索) 4.5以前版本代码:
/*add_quick_search.htm*/
…
var retString;
//填充 retString
…
//
objWindow.CloseHtmlDialog(document, retString);
…
/*customize_quick_searches.htm*/
…
// ret即为add_quick_search.htm中retString变量
var ret = objApp.Window.ShowHtmlDialog("", objApp.AppPath + "files\\customize_quick_searches\\add_quick_search.htm", 500, 500, "");
//
…
4.5以及之后版本代码:
/*add_quick_search.htm*/
…
var retString;
//填充 retString
…
//
objWindow.CloseHtmlDialog(WizChromeBrowser, retString);
…
/*customize_quick_searches.htm*/
…
// ret即为add_quick_search.htm中retString变量, ShowHtmlDialogEx 第一个参数必为false, 弹出非模态对话框
objApp.Window.ShowHtmlDialogEx(false, "", objApp.AppPath + "files\\customize_quick_searches\\add_quick_search.htm", 500, 500, "", null, function(ret){
//
if(ret && ret.length > 0){
…
}
});
//
…
IWizHtmlEditorApp
// 属性 EditorDocument不再使用, 改为使用 EdtorBrowserObject, 获取当前正在显示的笔记所在浏览器的 WizChromeBrowser 类型对象 HRESULT EditorBrowserObject([out, retval] IDispatch** pVal); HRESULT LocalizeHtmlDocument([in] BSTR bstrLanguageFileName, [in] IDispatch* pBrowserObjectDisp); HRESULT GetHtmlDocumentPath([in] IDispatch* pBrowserObjectDisp, [out, retval] BSTR* pVal);
双浏览器变单浏览器
一个例子:阅读状态下,获取当前数据库的路径(为知助手) 4.5以前的版本代码:
var objDatabase = external.Database;
var databasePath = objDatabse.DatabasePath;
4.5以及之后的版本代码:
/* 注入脚本文件: KMContent.js*/
var objKMHelperApp;
var objKMHelperDatabse;
var objKMHelperPluginBrowser;
//
function KMInit(app, pluginBrowser) {
if (!app || !pluginBrowser)
return;
//
objKMHelperApp = app;
objKMHelperDatabase = app.Database;
var databasePath = objKMHelperDatabase.DatabasePath;
//
objKMHelperPluginBrowser = pluginBrowser;
…
}
/* 插件文件:KMHelper.js*/
function initEvents() {
/*
向Wiz注册一个事件,响应文档完成的消息。在Wiz内打开一个html文件的时候(例如阅读文档),如果Html文件打开完成,则调用这个方法。
*/
eventsHtmlDocumentComplete.add(KMOnHtmlDocumentComplete);
…
}
// 笔记加载完成, objBrowser 为当前显示的笔记所在浏览器的 WizChromeBrowser 对象
function KMOnHtmlDocumentComplete(objBrowser) {
if (!objBrowser)
return;
//
var pluginPath = objApp.GetPluginPathByScriptFileName("KMHelper.js");
var contentScriptPath = pluginPath + "KMContent.js";
//
objBrowser.ExecuteScriptFile(contentScriptPath, function (ret) {
// 执行注入脚本的 KMInit 方法, 将 WizExplorerApp, WizChromeBrowser 对象传到笔记所在浏览器中
objBrowser.ExecuteFunction2("KMInit", objApp, WizChromeBrowser, function (ret) {
…
});
});
}
笔记所在浏览器的 external 对象没有获取 IWizDatabase 对象的方法。从上例可看出,可调用注入脚本的函数,将 WizExplorerApp 对象传到笔记所在浏览器中,再获取数据库对象,由此也可以使用 WizExplorerApp 对象的其他方法。 上例中还将插件所在浏览器的 WizChromeBrowser 对象传到笔记所在浏览器中,这样可以通过此对象执行插件环境下的脚本。 一个例子: 鼠标移开后自动关闭目录设置界面(为知助手)
/*注入脚本文件 : KMContent.js*/
function KMOnSetAsContentClick() { //点击设置目录图标,弹出目录设置界面
// 设置目录逻辑
…
// 添加自动关闭窗口的计时器, objKMHelperPluginBrowser为插件所在浏览器的 WizChromeBrowser 对象
objKMHelperPluginBrowser.ExecuteFunction1("KMAutoCloseContentWindow", true, null);
}
function KMAutoCloseContentWindow() {
//关闭目录设置窗口逻辑
…
//去除计时器, objKMHelperPluginBrowser为插件所在浏览器的 WizChromeBrowser 对象
objKMHelperPluginBrowser.ExecuteFunction1("KMAutoCloseContentWindow", false, null);
}
/*插件文件: KMHelper.js*/
function KMAutoCloseContentWindow(isAddAfterRemove) {
//
objWindow.RemoveTimer("KMAutoCloseContentWindowTimer");
if (isAddAfterRemove) {
objWindow.AddTimer("KMAutoCloseContentWindowTimer", 1000); //KMAutoCloseContentWindowTimer, 计时器回调函数名
}
}
function KMAutoCloseContentWindowTimer() {
var objBrowser = objWindow.CurrentDocumentBrowserObject;
if (!objBrowser)
return;
//
objBrowser.ExecuteFunction0("KMAutoCloseContentWindow", null);
}
一个例子: 阅读状态下,插件对笔记的修改与保存(为知助手) 4.5以前版本代码:
this.colorword = function (doc, node, keyword, callback) {
if (node.childNodes == undefined)
return false;
//
if (node.id == "wizKMHighlighterSpan_t_t")
return false;
//
for (var i = 0; i < node.childNodes.length; i++) {
var childNode = node.childNodes[i];
if (childNode.nodeType == 3) {
//childNode is #text
…
//
re = new RegExp('(' + keyword.word + ')', 'i');
//
var forkNode = doc.createElement("span");
forkNode.id = "wizkm_highlight_tmp_span";
forkNode.innerHTML = childNode.data.replace(re, '<div id="wizKMHighlighterSpan_t_t" style="background-color:' + keyword.bgColor + ';color:' + keyword.foreColor + '; cursor:pointer; border-bottom: 1px #00c dashed;">$1</div>');
node.replaceChild(forkNode, childNode);
//
…
//
return true;
}
…
}
return false;
}
//
…
//
function KMSetDocumentModified(doc) {
if (!doc)
return;
var body = doc.body;
if (!body)
return;
//
body.setAttribute(g_KMDocumentModifiedAttributeName, "1", 0);
}
…
function KMIsDocumentModified(doc){
if(!doc)
return;
//
var body = doc.body;
if (!body)
return;
//
return body.getAttribute(g_KMDocumentModifiedAttributeName, 0) == "1";
}
function KMSaveDocument(objHtmlDocument, objWizDocument) {
if (!objWizDocument)
return;
if (!KMIsDocumentModified(objHtmlDocument))
return;
//
…
}
4.5以及之后版本代码:
/*KMContent.js*/
//注入脚本
this.colorword = function (node, keyword, callback) {
if (node.childNodes == undefined)
return false;
//
if (node.id == "wizKMHighlighterSpan_t_t")
return false;
//
for (var i = 0; i < node.childNodes.length; i++) {
var childNode = node.childNodes[i];
if (childNode.nodeType == 3) {
//childNode is #text
…
//
re = new RegExp('(' + keyword.word + ')', 'i');
//
var forkNode = document.createElement("span");
forkNode.id = "wizkm_highlight_tmp_span";
forkNode.innerHTML = childNode.data.replace(re, '<wiz_tmp_plugin_tag id="wizKMHighlighterSpan_t_t" style="background-color:' + keyword.bgColor + ';color:' + keyword.foreColor + '; cursor:pointer; border-bottom: 1px #00c dashed;">$1</wiz_tmp_plugin_tag>');
node.replaceChild(forkNode, childNode);
//
…
//
return true;
}
…
}
return false;
}
//
…
//
function KMSetDocumentModified(){
objKMHelperApp.SetNoteModifiedByPlugin();
}
…
注意: SetNoteModifiedByPlugin 方法应用于阅读状态下插件对笔记做出修改时进行标记。编辑状态下为知笔记编辑器会对DOM修改进行捕获,所以不需插件单独设置修改标记。
此上两种标签不可嵌套使用,否则会产生未知错误,只需用这两种标签创建相应的顶层节点即可。 笔记所在浏览器中, 避免全局脚本命名冲突