Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSL 页面:兼容插件分发 #61

Open
northword opened this issue Jun 17, 2024 · 3 comments
Open

CSL 页面:兼容插件分发 #61

northword opened this issue Jun 17, 2024 · 3 comments
Assignees
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@northword
Copy link
Member

northword commented Jun 17, 2024

debug with:

Zotero.openInViewer("http://localhost:5173/styles/")

可能的方案:

以下方案大部分都需要一个组件用来与插件/ Zotero 交互,可能是打开链接,可能是剪贴板,可能是调用程序。

理想中,这个组件在 非 Zotero 打开时,应该隐藏,可以考虑通过 UA,使用 https://github.com/faisalman/ua-parser-js 解析 UA。

-> 不可行,Zotero 没有特殊UA。已证实不可行:Zotero 内置浏览器 UA 为

Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0

因此这个组件可以始终显示,但做好提示依赖 xx 插件。

方案1

网页提供不加 _target=black 的 csl 超链接,理想状态该链接被 Zotero 识别并安装。

-> 不可行,Zotero 会把所有链接都打开到浏览器,包括 zotero.org/styles 也是,可能是bug,也可能是新的安装途径。

方案2

组件提供一个复制链接的按钮,

插件打开页面时监听剪贴板,直到特殊格式的字符串出现(用户点击按钮),获取到URL,调用 Zotero.Styles.install。页面关闭时停止监听剪贴板。

方案3

组件提供一个按钮,用来拉起 protocol,例如 zotero://jasminum/install-style=url

插件方注册这个protocol,解析出 url,并调用相关方法从 url 安装样式。

安装 csl 方法:

Zotero.Styles.install({ url: downloadLinks.github }, downloadLinks.github, false)

注册 protocol 可以参考:https://github.com/windingwind/zotero-plugin-toolkit/blob/master/src/utils/pluginBridge.ts

@northword northword added enhancement New feature or request help wanted Extra attention is needed labels Jun 17, 2024
@northword
Copy link
Member Author

northword commented Jun 17, 2024

补充说明

Zotero.openInViewer 可以传入一个 onLoad 回调,可以在这个回调里注册监听页面关闭事件。似乎这个方法本身就提供了很多钩子?

zotero/zotero@0757109

可以通过 onLoad 回调修改 document,

Zotero.openInViewer("http://localhost:5173/styles/")
[object Window] {
    "close": function close() { [native code] }
    "stop": function stop() { [native code] }
    "focus": function focus() { [native code] }
    "blur": function blur() { [native code] }
    "open": function open() { [native code] }
    "alert": function alert() { [native code] }
    "confirm": function confirm() { [native code] }
    "prompt": function prompt() { [native code] }
    "print": function print() { [native code] }
    "printPreview": function printPreview() { [native code] }
    "postMessage": function postMessage() { [native code] }
    "captureEvents": function captureEvents() { [native code] }
    "releaseEvents": function releaseEvents() { [native code] }
    "getSelection": function getSelection() { [native code] }
    "getComputedStyle": function getComputedStyle() { [native code] }
    "matchMedia": function matchMedia() { [native code] }
    "moveTo": function moveTo() { [native code] }
    "moveBy": function moveBy() { [native code] }
    "resizeTo": function resizeTo() { [native code] }
    "resizeBy": function resizeBy() { [native code] }
    "scroll": function scroll() { [native code] }
    "scrollTo": function scrollTo() { [native code] }
    "scrollBy": function scrollBy() { [native code] }
    "getDefaultComputedStyle": function getDefaultComputedStyle() { [native code] }
    "scrollByLines": function scrollByLines() { [native code] }
    "scrollByPages": function scrollByPages() { [native code] }
    "sizeToContent": function sizeToContent() { [native code] }
    "updateCommands": function updateCommands() { [native code] }
    "find": function find() { [native code] }
    "dump": function dump() { [native code] }
    "setResizable": function setResizable() { [native code] }
    "getAttention": function getAttention() { [native code] }
    "getAttentionWithCycleCount": function getAttentionWithCycleCount() { [native code] }
    "setCursor": function setCursor() { [native code] }
    "maximize": function maximize() { [native code] }
    "minimize": function minimize() { [native code] }
    "restore": function restore() { [native code] }
    "getWorkspaceID": function getWorkspaceID() { [native code] }
    "moveToWorkspace": function moveToWorkspace() { [native code] }
    "notifyDefaultButtonLoaded": function notifyDefaultButtonLoaded() { [native code] }
    "getGroupMessageManager": function getGroupMessageManager() { [native code] }
    "promiseDocumentFlushed": function promiseDocumentFlushed() { [native code] }
    "requestIdleCallback": function requestIdleCallback() { [native code] }
    "cancelIdleCallback": function cancelIdleCallback() { [native code] }
    "getRegionalPrefsLocales": function getRegionalPrefsLocales() { [native code] }
    "getWebExposedLocales": function getWebExposedLocales() { [native code] }
    "requestAnimationFrame": function requestAnimationFrame() { [native code] }
    "cancelAnimationFrame": function cancelAnimationFrame() { [native code] }
    "reportError": function reportError() { [native code] }
    "btoa": function btoa() { [native code] }
    "atob": function atob() { [native code] }
    "setTimeout": function setTimeout() { [native code] }
    "clearTimeout": function clearTimeout() { [native code] }
    "setInterval": function setInterval() { [native code] }
    "clearInterval": function clearInterval() { [native code] }
    "queueMicrotask": function queueMicrotask() { [native code] }
    "createImageBitmap": function createImageBitmap() { [native code] }
    "structuredClone": function structuredClone() { [native code] }
    "fetch": function fetch() { [native code] }
    "self": <<Reference to parent object ROOT >>
    "name": ""
    "history": [object History] <<Maximum depth reached>>
    "customElements": [object CustomElementRegistry] <<Maximum depth reached>>
    "locationbar": [object BarProp] <<Maximum depth reached>>
    "menubar": [object BarProp] <<Maximum depth reached>>
    "personalbar": [object BarProp] <<Maximum depth reached>>
    "scrollbars": [object BarProp] <<Maximum depth reached>>
    "statusbar": [object BarProp] <<Maximum depth reached>>
    "toolbar": [object BarProp] <<Maximum depth reached>>
    "status": ""
    "closed": false
    "event": undefined
    "frames": <<Reference to parent object ROOT >>
    "length": 0
    "opener": null
    "parent": <<Reference to parent object ROOT >>
    "frameElement": null
    "navigator": [object Navigator] <<Maximum depth reached>>
    "clientInformation": [object Navigator] <<Maximum depth reached>>
    "external": [object External] <<Maximum depth reached>>
    "screen": [object Screen] <<Maximum depth reached>>
    "innerWidth": 118
    "innerHeight": 0
    "scrollX": 0
    "pageXOffset": 0
    "scrollY": 0
    "pageYOffset": 0
    "screenLeft": 0
    "screenTop": 0
    "screenX": 0
    "screenY": 0
    "outerWidth": 131
    "outerHeight": 36
    "performance": {
    "timeOrigin": 1718634156827.9246,
    "timing": {
        "navigationStart": 1718634156828,
        "unloadEventStart": 0,
        "unloadEventEnd": 0,
        "redirectStart": 0,
        "redirectEnd": 0,
        "fetchStart": 1718634156828,
        "domainLookupStart": 1718634156828,
        "domainLookupEnd": 1718634156828,
        "connectStart": 1718634156828,
        "connectEnd": 1718634156828,
        "secureConnectionStart": 1718634156828,
        "requestStart": 1718634156828,
        "responseStart": 1718634156828,
        "responseEnd": 1718634156828,
        "domLoading": 0,
        "domInteractive": 0,
        "domContentLoadedEventStart": 0,
        "domContentLoadedEventEnd": 0,
        "domComplete": 0,
        "loadEventStart": 0,
        "loadEventEnd": 0
    },
    "navigation": {
        "type": 0,
        "redirectCount": 0
    }
}
    "mozInnerScreenX": 0
    "mozInnerScreenY": 0
    "devicePixelRatio": 2
    "scrollMaxX": 0
    "scrollMaxY": 0
    "fullScreen": false
    "ondevicemotion": null
    "ondeviceorientation": null
    "ondeviceorientationabsolute": null
    "InstallTrigger": null
    "windowState": 3
    "isFullyOccluded": false
    "browserDOMWindow": null
    "messageManager": [object ChromeMessageBroadcaster] <<Maximum depth reached>>
    "isChromeWindow": true
    "intlUtils": [object IntlUtils] <<Maximum depth reached>>
    "visualViewport": [object VisualViewport] <<Maximum depth reached>>
    "crypto": [object Crypto] <<Maximum depth reached>>
    "onabort": null
    "onblur": null
    "onfocus": null
    "onauxclick": null
    "onbeforeinput": null
    "oncanplay": null
    "oncanplaythrough": null
    "onchange": null
    "onclick": null
    "onclose": null
    "oncontextmenu": null
    "oncopy": null
    "oncuechange": null
    "oncut": null
    "ondblclick": null
    "ondrag": null
    "ondragend": null
    "ondragenter": null
    "ondragexit": null
    "ondragleave": null
    "ondragover": null
    "ondragstart": null
    "ondrop": null
    "ondurationchange": null
    "onemptied": null
    "onended": null
    "onformdata": null
    "oninput": null
    "oninvalid": null
    "onkeydown": null
    "onkeypress": null
    "onkeyup": null
    "onload": null
    "onloadeddata": null
    "onloadedmetadata": null
    "onloadstart": null
    "onmousedown": null
    "onmouseenter": null
    "onmouseleave": null
    "onmousemove": null
    "onmouseout": null
    "onmouseover": null
    "onmouseup": null
    "onwheel": null
    "onpaste": null
    "onpause": null
    "onplay": null
    "onplaying": null
    "onprogress": null
    "onratechange": null
    "onreset": null
    "onresize": null
    "onscroll": null
    "onscrollend": null
    "onsecuritypolicyviolation": null
    "onseeked": null
    "onseeking": null
    "onselect": null
    "onslotchange": null
    "onstalled": null
    "onsubmit": null
    "onsuspend": null
    "ontimeupdate": null
    "onvolumechange": null
    "onwaiting": null
    "onselectstart": null
    "onselectionchange": null
    "ontoggle": null
    "onpointercancel": null
    "onpointerdown": null
    "onpointerup": null
    "onpointermove": null
    "onpointerout": null
    "onpointerover": null
    "onpointerenter": null
    "onpointerleave": null
    "ongotpointercapture": null
    "onlostpointercapture": null
    "onmozfullscreenchange": null
    "onmozfullscreenerror": null
    "onanimationcancel": null
    "onanimationend": null
    "onanimationiteration": null
    "onanimationstart": null
    "ontransitioncancel": null
    "ontransitionend": null
    "ontransitionrun": null
    "ontransitionstart": null
    "onwebkitanimationend": null
    "onwebkitanimationiteration": null
    "onwebkitanimationstart": null
    "onwebkittransitionend": null
    "onerror": null
    "speechSynthesis": [object SpeechSynthesis] <<Maximum depth reached>>
    "onafterprint": null
    "onbeforeprint": null
    "onbeforeunload": null
    "onhashchange": null
    "onlanguagechange": null
    "onmessage": null
    "onmessageerror": null
    "onoffline": null
    "ononline": null
    "onpagehide": null
    "onpageshow": null
    "onpopstate": null
    "onrejectionhandled": null
    "onstorage": null
    "onunhandledrejection": null
    "onunload": null
    "ongamepadconnected": null
    "ongamepaddisconnected": null
    "localStorage": <<Access Denied>>
    "origin": "null"
    "crossOriginIsolated": false
    "isSecureContext": true
    "indexedDB": [object IDBFactory] <<Maximum depth reached>>
    "caches": [object CacheStorage] <<Maximum depth reached>>
    "sessionStorage": <<Access Denied>>
    "STATE_MAXIMIZED": 1
    "STATE_MINIMIZED": 2
    "STATE_NORMAL": 3
    "STATE_FULLSCREEN": 4
    "mozScrollSnap": function mozScrollSnap() { [native code] }
    "sizeToContentConstrained": function sizeToContentConstrained() { [native code] }
    "openDialog": function openDialog() { [native code] }
    "getInterface": function getInterface() { [native code] }
    "shouldReportForServiceWorkerScope": function shouldReportForServiceWorkerScope() { [native code] }
    "setScrollMarks": function setScrollMarks() { [native code] }
    "controllers": [object Object] <<Maximum depth reached>>
    "realFrameElement": null
    "docShell": [object Object] <<Maximum depth reached>>
    "browsingContext": [object CanonicalBrowsingContext] <<Maximum depth reached>>
    "desktopToDeviceScale": 1
    "screenEdgeSlopX": 0
    "screenEdgeSlopY": 0
    "scrollMinX": 0
    "scrollMinY": 0
    "windowRoot": [object WindowRoot] <<Maximum depth reached>>
    "windowUtils": [object Object] <<Maximum depth reached>>
    "windowGlobalChild": [object WindowGlobalChild] <<Maximum depth reached>>
    "clientPrincipal": [object Object] <<Maximum depth reached>>
    "isInFullScreenTransition": false
    "Glean": [object GleanImpl] <<Maximum depth reached>>
    "GleanPings": [object GleanPingsImpl] <<Maximum depth reached>>
    "window": <<Reference to parent object ROOT >>
    "document": [object HTMLDocument] <<Maximum depth reached>>
    "location": [object Location] <<Maximum depth reached>>
    "top": <<Reference to parent object ROOT >>
    "addEventListener": function addEventListener() { [native code] }
    "removeEventListener": function removeEventListener() { [native code] }
    "dispatchEvent": function dispatchEvent() { [native code] }
    "setEventHandler": function setEventHandler() { [native code] }
    "getEventHandler": function getEventHandler() { [native code] }
    "ownerGlobal": <<Reference to parent object ROOT >>
}

@northword
Copy link
Member Author

CC @jiaojiaodubai 我觉得方案3的可行性比较高。

@jiaojiaodubai
Copy link
Contributor

我有空在 Jasminum 7 临时仓库 试验一下,待基本功能完善我会 transfer 给 @l0o0

@jiaojiaodubai jiaojiaodubai self-assigned this Aug 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants