Remove styled-textbox XBL binding

This commit is contained in:
Dan Stillman 2023-07-06 22:48:12 -04:00
parent af892e5cbd
commit 071162f914
2 changed files with 0 additions and 827 deletions

View file

@ -1,813 +0,0 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright © 2009 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
-->
<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd">
<bindings xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="styled-textbox">
<implementation>
<field name="_editable"/>
<field name="_mode"/>
<field name="_format"/>
<field name="_changed"/>
<field name="_loadHandler"/>
<field name="_commandString"/>
<field name="_eventHandler"/>
<field name="_editor"/>
<field name="_value"/>
<field name="_timer"/>
<field name="_focus"/>
<field name="_constructed"/>
<field name="_loadOnConstruct"/>
<constructor><![CDATA[
this.mode = this.getAttribute('mode');
this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view");
// Atomic units, HTML -> RTF (cleanup)
//[/<\/p>(?!\s*$)/g, "\\par{}"],
//[/ /g, "&nbsp;"],
//[/\u00A0/g, " "],
this._htmlRTFmap = [
[/<br \/>/g, "\x0B"],
[/<span class=\"tab\">&nbsp;<\/span>/g, "\\tab{}"],
[/&lsquo;/g, ""],
[/&rsquo;/g, ""],
[/&ldquo;/g, "“"],
[/&rdquo;/g, "”"],
[/&nbsp;/g, "\u00A0"],
[/"(\w)/g, "“$1"],
[/([\w,.?!])"/g, "$1”"],
[/<p>/g, ""],
[/<\/?div[^>]*>/g, ""]
];
// Atomic units, RTF -> HTML (cleanup)
this._rtfHTMLmap = [
[/\\uc0\{?\\u([0-9]+)\}?(?:{}| )?/g, function(wholeStr, aCode) { return String.fromCharCode(aCode) }],
[/\\tab(?:\{\}| )/g, '<span class="tab">&nbsp;</span>'],
[/(?:\\par{}|\\\r?\n)/g, "</p><p>"]
];
this.prepare = function() {
// DEBUG: Does this actually happen?
if (this.prepared) return;
// Tag data
var _rexData = [
[
[
["<span +style=\"font-variant: *small-caps;\">"],
["{\\scaps ", "{\\scaps{}"]
],
[
["<\/span>"],
["}"]
]
],
[
[
["<span +style=\"text-decoration: *underline;\">"],
["{\\ul{}", "{\\ul "]
],
[
["<\/span>"],
["}"]
]
],
[
[
["<sup>"],
["\\super ", "\\super{}"]
],
[
["</sup>"],
["\\nosupersub{}", "\\nosupersub "]
]
],
[
[
["<sub>"],
["\\sub ", "\\sub{}"]
],
[
["</sub>"],
["\\nosupersub{}", "\\nosupersub "]
]
],
[
[
["<em>"],
["{\\i{}", "{\\i "]
],
[
["</em>"],
["}"]
]
],
[
[
["<i>"],
["{\\i{}", "{\\i "]
],
[
["</i>"],
["}"]
]
],
[
[
["<b>"],
["{\\b{}", "{\\b "]
],
[
["</b>"],
["}"]
]
],
[
[
["<strong>"],
["{\\b{}", "{\\b "]
],
[
["</strong>"],
["}"]
]
],
[
[
["<span +style=\"font-variant: *normal;\">"],
["{\\scaps0{}", "{\\scaps0 "]
],
[
["</span>"],
["}"]
]
],
[
[
["<span +style=\"font-style: *normal;\">"],
["{\\i0{}", "{\\i0 "]
],
[
["</span>"],
["}"]
]
],
[
[
["<span +style=\"font-weight: *normal;\">"],
["{\\b0{}", "{\\b0 "]
],
[
["</span>"],
["}"]
]
]
];
function longestFirst(a, b) {
if (a.length < b.length) {
return 1;
} else if (a.length > b.length) {
return -1;
} else {
return 0;
}
}
function normalizeRegExpString(str) {
if (!str) return str;
return str.replace(/\s+/g, " ")
.replace(/(?:[\+]|\s[\*])/g, "")
.replace(/[\']/g, '\"')
.replace(/:\s/g, ":");
}
this.normalizeRegExpString = normalizeRegExpString;
function composeRex(rexes, noGlobal) {
var lst = [];
for (var rex in rexes) {
lst.push(rex);
}
lst.sort(longestFirst);
var rexStr = "(?:" + lst.join("|") + ")";
return new RegExp(rexStr, "g");
}
// Create splitting regexps
function splitRexMaker(segment) {
var rexes = {};
for (var i=0,ilen=_rexData.length; i < ilen; i++) {
for (var j=0,jlen=_rexData[i].length; j < jlen; j++) {
for (var k=0,klen=_rexData[i][j][segment].length; k < klen; k++) {
rexes[_rexData[i][j][segment][k].replace("\\", "\\\\")] = true;
}
}
}
var ret = composeRex(rexes, true);
return ret;
}
this.rtfHTMLsplitRex = splitRexMaker(1);
this.htmlRTFsplitRex = splitRexMaker(0);
// Create open-tag sniffing regexp
function openSniffRexMaker(segment) {
var rexes = {};
for (var i=0,ilen=_rexData.length; i < ilen; i++) {
for (var j=0,jlen=_rexData[i][0][segment].length; j < jlen; j++) {
rexes[_rexData[i][0][segment][j].replace("\\", "\\\\")] = true;
}
}
return composeRex(rexes);
}
this.rtfHTMLopenSniffRex = openSniffRexMaker(1);
this.htmlRTFopenSniffRex = openSniffRexMaker(0);
// Create open-tag remapper
function openTagRemapMaker(segment) {
var ret = {};
for (var i=0,ilen=_rexData.length; i < ilen; i++) {
var primaryVal = normalizeRegExpString(_rexData[i][0][segment][0]);
for (var j=0,jlen=_rexData[i][0][segment].length; j < jlen; j++) {
var key = normalizeRegExpString(_rexData[i][0][segment][j]);
ret[key] = primaryVal;
}
}
return ret;
}
this.rtfHTMLopenTagRemap = openTagRemapMaker(1);
this.htmlRTFopenTagRemap = openTagRemapMaker(0);
// Create open-tag-keyed close-tag sniffing regexps
function closeTagRexMaker(segment) {
var ret = {};
var rexes = {};
for (var i=0,ilen=_rexData.length; i < ilen; i++) {
var primaryVal = _rexData[i][0][segment][0];
for (var j=0,jlen=_rexData[i][1][segment].length; j < jlen; j++) {
rexes[_rexData[i][1][segment][j]] = true;
}
ret[primaryVal] = composeRex(rexes);
}
return ret;
}
this.rtfHTMLcloseTagRex = closeTagRexMaker(1);
this.htmlRTFcloseTagRex = closeTagRexMaker(0);
// Create open-tag-keyed open/close tag registry
function tagRegistryMaker(segment) {
var antisegment = 1;
if (segment == 1) {
antisegment = 0;
}
var ret = {};
for (var i=0,ilen=_rexData.length; i < ilen; i++) {
var primaryVal = normalizeRegExpString(_rexData[i][0][segment][0]);
ret[primaryVal] = {
open: normalizeRegExpString(_rexData[i][0][antisegment][0]),
close: _rexData[i][1][antisegment][0]
}
}
return ret;
}
this.rtfHTMLtagRegistry = tagRegistryMaker(1);
this.htmlRTFtagRegistry = tagRegistryMaker(0);
this.prepared = true;
}
this.prepare();
this.getSplit = function(mode, txt) {
if (!txt) return [];
var splt = txt.split(this[mode + "splitRex"]);
var mtch = txt.match(this[mode + "splitRex"]);
var lst = [splt[0]];
for (var i=1,ilen=splt.length; i < ilen; i++) {
lst.push(mtch[i-1]);
lst.push(splt[i]);
}
return lst;
}
this.getOpenTag = function(mode, str) {
var m = str.match(this[mode + "openSniffRex"]);
if (m) {
m = this[mode + "openTagRemap"][this.normalizeRegExpString(m[0])];
}
return m;
}
this.convert = function(mode, txt) {
var lst = this.getSplit(mode, txt);
var sdepth = 0;
var depth = 0;
for (var i=1,ilen=lst.length; i < ilen; i += 2) {
var openTag = this.getOpenTag(mode, lst[i]);
if (openTag) {
sdepth++;
depth = sdepth;
for (var j=(i+2),jlen=lst.length; j < jlen; j += 2) {
var closeTag = !this.getOpenTag(mode, lst[j]);
if (closeTag) {
if (depth === sdepth && lst[j].match(this[mode + "closeTagRex"][openTag])) {
lst[i] = this[mode + "tagRegistry"][openTag].open;
lst[j] = this[mode + "tagRegistry"][openTag].close;
break;
}
depth--;
} else {
depth++;
}
}
} else {
sdepth--;
}
}
return lst.join("");
}
this.htmlToRTF = function(txt) {
txt = this.convert("htmlRTF", txt);
for (var i=0,ilen=this._htmlRTFmap.length; i < ilen; i++) {
var entry = this._htmlRTFmap[i];
txt = txt.replace(entry[0], entry[1]);
}
txt = Zotero.Utilities.unescapeHTML(txt);
txt = txt.replace(/[\x7F-\uFFFF]/g, function(aChar) { return "\\uc0\\u"+aChar.charCodeAt(0).toString()+"{}"});
return txt.trim();
}
this.rtfToHTML = function(txt) {
for (var i=0,ilen=this._rtfHTMLmap.length; i < ilen; i++) {
var entry = this._rtfHTMLmap[i];
txt = txt.replace(entry[0], entry[1]);
}
txt = this.convert("rtfHTML", txt);
return txt.trim();
}
this._constructed = true;
// Don't load if a value hasn't yet been set
if (this._loadOnConstruct) {
this._load();
}
]]></constructor>
<property name="mode">
<getter><![CDATA[
if (!this._mode) {
throw ("mode is not defined in styled-textbox.xml");
}
return this._mode;
]]></getter>
<setter><![CDATA[
Zotero.debug("Setting mode to " + val);
switch (val) {
case 'note':
this._eventHandler = function (event) {
// Necessary in Fx32+
if (event.wrappedJSObject) {
event = event.wrappedJSObject;
}
var commandEvent = false;
if (Zotero.Prefs.get('debugNoteEvents')) {
Zotero.debug(event.type);
Zotero.debug(event.which);
}
switch (event.type) {
case 'keydown':
// Handle forward-delete, which doesn't register as a keypress
// when a selection is cleared
if (event.which == event.DOM_VK_DELETE) {
this._changed = true;
commandEvent = true;
}
break;
case 'keypress':
// Ignore keypresses that don't change
// any text
//Zotero.debug(event.which);
if (!event.which &&
event.keyCode != event.DOM_VK_DELETE &&
event.keyCode != event.DOM_VK_BACK_SPACE) {
//Zotero.debug("Not a char");
return;
}
this._changed = true;
commandEvent = true;
break;
// 'change' includes text added via drag-and-drop
case 'change':
case 'undo':
case 'redo':
this._changed = true;
commandEvent = true;
break;
case 'ZoteroLinkClick':
var zp = typeof ZoteroPane != 'undefined'
? ZoteroPane
: window.opener.ZoteroPane;
zp.loadURI(event.value);
break;
default:
return;
}
// Trigger command on change
if (commandEvent && this.timeout) {
if (this._timer) {
clearTimeout(this._timer);
}
this._timer = setTimeout(function () {
var attr = this.getAttribute('oncommand');
attr = attr.replace('this', 'thisObj');
var func = new Function('thisObj', 'event', attr);
func(this, event);
}.bind(this), this.timeout);
}
return true;
}.bind(this);
break;
case 'integration':
break;
default:
throw ("Invalid mode '" + val + "' in styled-textbox.xml");
}
return this._mode = val;
]]></setter>
</property>
<!-- Sets or returns formatting (currently, HTML or Integration) of rich text box -->
<property name="initialized">
<getter><![CDATA[
return !!this._editor;
]]></getter>
</property>
<!-- Sets or returns formatting (currently, HTML or Integration) of rich text box -->
<property name="format">
<getter><![CDATA[
return this._format;
]]></getter>
<setter><![CDATA[
return this._format = val;
]]></setter>
</property>
<!-- Sets or returns contents of rich text box -->
<property name="value">
<getter><![CDATA[
if (!this._editor) {
return null;
}
var output = this._editor.getContent().trim();
if(this._format == "RTF") {
// strip divs
if(output.substr(0, 5) == "<div>" && output.substr(-6) == "</div>") {
output = output.slice(0, output.length-6).slice(5).trim();
}
output = this.htmlToRTF(output)
}
return output;
]]></getter>
<setter><![CDATA[
if (self._timer) {
clearTimeout(self._timer);
}
if(!this._editor) {
Zotero.debug('No editor yet');
this._value = val;
if (!this._constructed) {
Zotero.debug('Styled textbox not yet constructed', 2);
this._loadOnConstruct = true;
}
else if (!this._loaded) {
this._load();
}
return ;
}
// Hack to ignore incomplete editors due to rapid note creation in tests
if (Zotero.test && !this._editor.getContent) {
Zotero.logError("editor.getContent doesn't exist");
return;
}
if (this.value == val) {
Zotero.debug("Textbox value hasn't changed");
this._changed = false;
return;
}
var html = val;
if(this._format == "RTF") {
var bodyStyle = "";
if(html.substr(0, 3) == "\\li") {
// try to show paragraph formatting
var returnIndex = html.indexOf("\r\n");
var tags = html.substr(1, returnIndex).split("\\");
html = html.substr(returnIndex+2);
for(var i=0; i<tags.length; i++) {
var tagName = tags[i].substr(0, 2);
var tagValue = tags[i].substring(2, tags[i].length-1);
if(tagName == "li") {
var li = parseInt(tagValue, 10);
} else if(tagName == "fi") {
var fi = parseInt(tagValue, 10);
}
}
// don't negatively indent
if(fi < 0 && li == 0) li = -fi;
bodyStyle = "margin-left:"+(li/20+6)+"pt;text-indent:"+(fi/20)+"pt;";
}
html = this.rtfToHTML(html);
html = '<div style="'+bodyStyle+'"><p>'+html+"</p></div>";
}
Zotero.debug("Setting content to " + html);
this._editor.setContent(html);
this._changed = false;
return val;
]]></setter>
</property>
<property name="timeout"
onset="this.setAttribute('timeout', val); return val;"
onget="return parseInt(this.getAttribute('timeout')) || 0;"/>
<property name="changed" onget="return this._changed;" onset="this._changed = !!val;"/>
<method name="focus">
<body>
<![CDATA[
if (this._editor) {
this._iframe.focus();
this._editor.focus();
this._focus = false;
}
else {
this._focus = true;
}
]]>
</body>
</method>
<method name="hasFocus">
<body>
<![CDATA[
return this._editor ? this._editor.hasFocus() : false;
]]>
</body>
</method>
<method name="clearUndo">
<body>
<![CDATA[
if (this._editor) {
this._editor.undoManager.clear();
this._editor.undoManager.add();
}
]]>
</body>
</method>
<method name="onInit">
<parameter name="callback"/>
<body><![CDATA[
if (this.initialized) {
// Hack to ignore incomplete editors due to rapid note creation in tests
if (Zotero.test && !this._editor.setMode) {
Zotero.logError("editor.setMode doesn't exist after initialization");
return;
}
callback(this._editor);
}
else {
if (!this._onInitCallbacks) {
this._onInitCallbacks = [];
}
this._onInitCallbacks.push(callback);
}
]]></body>
</method>
<field name="_loaded"/>
<method name="_load">
<body>
<![CDATA[
this._loaded = true;
// Unless we find a better way, use a separate HTML file
// for read-only mode
var htmlFile = this.mode + (this.getAttribute('readonly') != 'true' ? "" : "view");
var url = `resource://zotero/tinymce/${htmlFile}.html`
// Pass directionality (LTR/RTL) and locale in URL
+ "?locale=" + encodeURIComponent(Zotero.locale)
+ "&dir=" + Zotero.dir;
Components.utils.import("resource://gre/modules/Services.jsm");
var uri = Services.io.newURI(url, null, null);
Zotero.debug("Loading " + uri.spec);
// Register handler for deferred setting of content
var self = this;
var matchTo = null;
var listener = function(e) {
var win = self._iframe.contentWindow;
var SJOW = win.wrappedJSObject;
// only fire if the target matches, or _zoteroMatchTo, which we set last
// time the target matched, matches
if(e.target !== self._iframe.contentDocument
&& (!SJOW._zoteroMatchTo || SJOW._zoteroMatchTo !== matchTo)) return;
if (!SJOW.tinyMCE) {
Zotero.getInstalledExtensions().then(function(exts) {
for (let ext of exts) {
if (ext.indexOf('NoScript') != -1 && ext.indexOf('disabled') == -1) {
var doc = win.document;
var div = doc.getElementById('tinymce');
var warning = doc.createElement('div');
warning.id = 'noScriptWarning';
var str = "The NoScript extension is preventing Zotero "
+ "from displaying notes. To use NoScript and Zotero together, "
+ "whitelist the 'file:' scheme in the NoScript preferences "
+ "and restart " + Zotero.appName + ".";
warning.appendChild(document.createTextNode(str));
div.appendChild(warning);
break;
}
}
});
return;
}
if (!SJOW.zoteroInit) {
SJOW.zoteroInit = function(editor) {
// Necessary in Fx32+
if (editor.wrappedJSObject) {
self._editor = editor.wrappedJSObject;
}
else {
self._editor = editor;
}
if (self._value) {
self.value = self._value;
// Prevent undoing to empty note after initialization
self._editor.undoManager.clear();
self._editor.undoManager.add();
}
if (self._focus) {
setTimeout(function () {
self._iframe.focus();
self._editor.focus();
});
self._focus = false;
}
// Add CSS rules to notes
if (self.mode == 'note') {
let fontSize = Zotero.Prefs.get('note.fontSize');
// Fix empty old font prefs before a value was enforced
if (fontSize < 6) {
fontSize = 11;
}
var css = "body#zotero-tinymce-note, "
+ "body#zotero-tinymce-note p, "
+ "body#zotero-tinymce-note th, "
+ "body#zotero-tinymce-note td, "
+ "body#zotero-tinymce-note pre { "
+ "font-size: " + fontSize + "px; "
+ "} "
+ "body#zotero-tinymce-note, "
+ "body#zotero-tinymce-note p { "
+ "font-family: "
+ Zotero.Prefs.get('note.fontFamily') + "; "
+ "}"
+ Zotero.Prefs.get('note.css');
var doc = editor.contentDocument;
var head = doc.getElementsByTagName("head")[0];
var style = doc.createElement("style");
style.innerHTML = css;
head.appendChild(style);
}
let cb;
if (this._onInitCallbacks) {
while (cb = this._onInitCallbacks.shift()) {
// Hack to ignore incomplete editors due to rapid note creation in tests
if (Zotero.test && !this._editor.setMode) {
Zotero.logError("editor.setMode doesn't exist");
return;
}
cb(this._editor);
}
}
}.bind(this);
}
var editor = SJOW.tinyMCE.get("tinymce");
if (!editor) {
Zotero.debug("editor not ready");
// this is a hack; I'm not sure why we can't identify the event target
// next time without it, but apparently we can't
matchTo = Zotero.randomString();
SJOW._zoteroMatchTo = matchTo;
// Not ready yet
return;
}
self._iframe.removeEventListener("DOMContentLoaded", listener, false);
if (self._eventHandler) {
win.wrappedJSObject.zoteroHandleEvent = self._eventHandler;
}
// Run Cut/Copy/Paste with chrome privileges
win.wrappedJSObject.zoteroExecCommand = function (doc, command, ui, value) {
return doc.execCommand(command, ui, value);
}
}.bind(this);
this._iframe.addEventListener("DOMContentLoaded", listener, false);
let loadURIOptions = {
triggeringPrincipal: null,
csp: null,
loadFlags: Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
referrerInfo: null,
postData: null,
};
this._iframe.webNavigation.loadURI(uri.spec, loadURIOptions);
]]>
</body>
</method>
</implementation>
<content>
<xul:iframe flex="1" anonid="rt-view" class="rt-view" type="content"
xbl:inherits="onfocus,onblur,flex,width,height,hidden"
style="overflow: hidden"/>
</content>
</binding>
</bindings>

View file

@ -15,20 +15,6 @@
padding:0;
}
/* Bindings */
textbox[type="styled"]
{
-moz-binding: url('chrome://zotero/content/bindings/styled-textbox.xml#styled-textbox');
}
zoteromergegroup {
-moz-binding: url('chrome://zotero/content/bindings/merge.xml#merge-group');
}
zoteromergepane {
-moz-binding: url('chrome://zotero/content/bindings/merge.xml#merge-pane');
}
richlistitem {
padding: 0.2em 0.4em;
}