source: DASISH/t5.6/client/branches/webannotator-basic/chrome/markingcollection/content/markingcollection/marker.js @ 5428

Last change on this file since 5428 was 5428, checked in by stephanie.roth@snd.gu.se, 10 years ago

Added new branch for work on schema change related client fixes.

File size: 21.5 KB
Line 
1var bitsMarker = {
2    markerWindow_count: 0,
3/////////////////////////////////////////////////////////////////////
4/////////////////////////////////////////////////////////////////////
5    get id_key() {
6        return "bits_marker_";
7    },
8    get XHTMLNS() {
9        return 'http://www.w3.org/1999/xhtml';
10    },
11    get nodePositionInRange() {
12        return {
13            SINGLE: 0,
14            START: 1,
15            MIDDLE: 2,
16            END: 3
17        };
18    },
19    get DEFAULT_ALIGMENT() {
20        return 3;
21    },
22    get alignment() {
23        return nsPreferences.getIntPref("wiredmarker.hyperanchor.alignment", this.DEFAULT_ALIGMENT);
24    },
25    set alignment(aVal) {
26        nsPreferences.setIntPref("wiredmarker.hyperanchor.alignment", aVal);
27    },
28    get STRING() {
29        return window.top.document.getElementById("MarkingCollectionOverlayString");
30    },
31    get PRESET_STYLES()
32    {
33        return [
34            // [0] no styles
35            "",
36            window.top.bitsMarkingCollection.STRING.getString("MARKER_CUSTOM_1"),
37            window.top.bitsMarkingCollection.STRING.getString("MARKER_CUSTOM_2"),
38            window.top.bitsMarkingCollection.STRING.getString("MARKER_CUSTOM_3"),
39            window.top.bitsMarkingCollection.STRING.getString("MARKER_CUSTOM_4"),
40            window.top.bitsMarkingCollection.STRING.getString("MARKER_CUSTOM_5"),
41            window.top.bitsMarkingCollection.STRING.getString("MARKER_CUSTOM_6"),
42            window.top.bitsMarkingCollection.STRING.getString("MARKER_CUSTOM_7"),
43            window.top.bitsMarkingCollection.STRING.getString("MARKER_CUSTOM_8"),
44        ];
45    },
46    get Common() {
47        return window.top.bitsObjectMng.Common;
48    },
49    get XPath() {
50        return window.top.bitsObjectMng.XPath;
51    },
52    get gBrowser() {
53        return window.top.bitsObjectMng.getBrowser();
54    },
55/////////////////////////////////////////////////////////////////////
56    init: function() {
57        var alignment = nsPreferences.getIntPref("wiredmarker.hyperanchor.alignment");
58        if (alignment == undefined)
59            nsPreferences.setIntPref("wiredmarker.hyperanchor.alignment", this.DEFAULT_ALIGMENT);
60    },
61/////////////////////////////////////////////////////////////////////
62    done: function() {
63    },
64/////////////////////////////////////////////////////////////////////
65    updateMarkerWindowCount: function(doc) {
66        var body = doc.body;
67        if (!body)
68            return;
69        var search_exp = new RegExp("^" + bitsMarker.id_key + "(\\d+)$", "mg");
70        var count = -1;
71        var elms = body.getElementsByTagName("span");
72        for (var i = 0; i < elms.length; i++) {
73            var elm = elms[i];
74            if (elm.id.search(search_exp) < 0)
75                continue;
76            var val = parseInt(RegExp.$1);
77            if (count < val)
78                count = val;
79        }
80        this.markerWindow_count = count;
81    },
82/////////////////////////////////////////////////////////////////////
83    unmarkerWindow: function(marker_id) {
84        var i;
85        for (i = 0; i < this.gBrowser.browsers.length; i++) {
86            bitsMarker.unmarkerWindow2(this.gBrowser.browsers[i].contentWindow, marker_id);
87        }
88    },
89    unmarkerWindow2: function(win, marker_id) {
90        var result = false;
91        if (win != null) {
92            var doc = win.document;
93            if (win.frames != null) {
94                var i;
95                for (i = 0; i < win.frames.length; i++) {
96                    bitsMarker.unmarkerWindow2(win.frames[i], marker_id);
97                }
98            }
99            if (!result) {
100                var marker = doc.getElementById(marker_id);
101                while (marker != null) {
102                    var parent = marker.parentNode;
103                    var children = marker.childNodes;
104                    var index;
105                    for (index = children.length - 1; index >= 0; index--) {
106                        parent.insertBefore(children[0], marker);
107                    }
108                    parent.removeChild(marker);
109                    parent.normalize();
110                    result = true;
111                    marker = doc.getElementById(marker_id)
112                }
113            }
114        }
115        return result;
116    },
117/////////////////////////////////////////////////////////////////////
118    markerRange: function(range, aAttributes) {
119        try {
120            var startContainer = range.startContainer;
121            var ownerDoc = startContainer.ownerDocument;
122            var spanNode = ownerDoc.createElement("span");
123            var attr;
124            for (attr in aAttributes) {
125                spanNode.setAttribute(attr, aAttributes[attr]);
126            }
127            var docfrag = range.extractContents();
128            spanNode.appendChild(docfrag);
129            range.insertNode(spanNode);
130            return spanNode;
131        } catch (ex) {
132            this._dump("bitsMarker.markerRange():" + ex);
133            return null;
134        }
135    },
136/////////////////////////////////////////////////////////////////////
137    getTextNode: function(aNode) {
138        var rtn_txt = "";
139        if (aNode.nodeName == "SELECT")
140            return rtn_txt;
141        if (aNode.style && aNode.style.display == 'none')
142            return rtn_txt;
143        if (aNode.childNodes.length) {
144            var elements = aNode.childNodes;
145            for (var j = 0; j < elements.length; j++) {
146                if (aNode.nodeName == "TR" && j > 0)
147                    rtn_txt += "\t";
148                rtn_txt += addSelectionNode(elements[j]);
149            }
150        }
151        if (aNode.nodeName.search(/^P|H1|H2|H3|H4|H5|H6|UL|OL|DIR|MENU|PRE|DL|DIV|CENTER|NOSCRIPT|NOFRAMES|BLOCKQUOTE|FORM|ISINDEX|HR|TABLE|TR|FIELDSET|ADDRESS|MULTICOL|BR$/img) == 0)
152            rtn_txt = "\n" + rtn_txt + "\n";
153        if (aNode.nodeValue && !(aNode.parentNode && aNode.parentNode.nodeName == "SCRIPT") && aNode.nodeName != "#comment") {
154            var txt = aNode.nodeValue;
155            txt = txt.replace(/[\r\n]+/img, "\n");
156            txt = txt.replace(/\n+/img, " ");
157            rtn_txt += txt;
158        }
159        if (aNode.nodeName == "IMG") {
160            var src = aNode.src;
161            aNode.setAttribute("src", src);
162        }
163        return rtn_txt;
164    },
165/////////////////////////////////////////////////////////////////////
166    decorateElement: function(aElement, aCssText) {
167        aElement.setAttribute("style", aCssText);
168        aElement.setAttribute("tooltiptext", aCssText);
169    },
170/////////////////////////////////////////////////////////////////////
171    _getFindRange: function() {
172        var findRange = Components.classes["@mozilla.org/embedcomp/rangefind;1"].createInstance();
173        if (!findRange || !(findRange instanceof Components.interfaces.nsIFind))
174            return undefined;
175        findRange.caseSensitive = true;
176        findRange.findBackwards = false;
177        return findRange;
178    },
179/////////////////////////////////////////////////////////////////////
180    xPathMarker: function(aDoc, aXPath, aAttributes) {
181        try {
182            var rtnNode = [];
183            var doc = aDoc;
184            if (!aDoc || !aXPath || !aXPath.start || !aXPath.end)
185                return null;
186            aXPath.start.match(/(.+)\(([0-9]+)\)\(([0-9]+)\)/);
187            aXPath.startPath = RegExp.$1;
188            aXPath.startOffset = RegExp.$2;
189            aXPath.startType = RegExp.$3;
190            aXPath.end.match(/(.+)\(([0-9]+)\)\(([0-9]+)\)/);
191            aXPath.endPath = RegExp.$1;
192            aXPath.endOffset = RegExp.$2;
193            aXPath.endType = RegExp.$3;
194            var startNode = null;
195            var endNode = null;
196            if (this.Common.getURLStringFromDocument(doc) != aXPath.con_url) {
197                var win = doc.defaultView;
198                doc = null;
199                if (win.frames != null) {
200                    var i;
201                    for (var i = 0; i < win.frames.length; i++) {
202                        if (this.Common.getURLStringFromDocument(win.frames[i].document) == aXPath.con_url) {
203                            doc = win.frames[i].document;
204                            break;
205                        }
206                    }
207                }
208            }
209            if (!doc)
210                return null;
211            startNode = this.XPath.getCurrentNodeFromXPath(doc, aXPath.startPath, aXPath.startOffset, aXPath.startType);
212            if (!startNode || !startNode.node)
213                return null;
214            endNode = this.XPath.getCurrentNodeFromXPath(doc, aXPath.endPath, aXPath.endOffset, aXPath.endType);
215            if (!endNode || !endNode.node)
216                return null;
217            //annotationProxy.log('aAttributes.id: ' + aAttributes.id);
218            if (!aAttributes || !aAttributes.id) {
219                this.updateMarkerWindowCount(doc);
220                this.markerWindow_count++;
221                if (!aAttributes)
222                    aAttributes = {};
223                aAttributes.id = bitsMarker.id_key + this.markerWindow_count;
224            } else if (aAttributes.id && aAttributes.dbtype) {
225                aAttributes.id = bitsMarker.id_key + aAttributes.dbtype + aAttributes.id;
226            } else if (aAttributes.id) {
227                aAttributes.id = bitsMarker.id_key + aAttributes.id;
228            }
229            var range = doc.createRange();
230            try {
231                //annotationProxy.log('aAttributes.id startNode.node: ' + startNode.node);
232                //annotationProxy.log('aAttributes.id endNode.node: ' + endNode.node);
233                //annotationProxy.log('aAttributes.id startNode.offset: ' + endNode.offset);
234                //annotationProxy.log('aAttributes.id endNode.offset: ' + endNode.offset);
235                range.setStart(startNode.node, startNode.offset);
236                range.setEnd(endNode.node, endNode.offset);
237            } catch (ex2) {
238                //annotationProxy.log('FAIL FOR: aAttributes.id: ' + aAttributes.id);
239                this._dump("bitsMarker.xPathMarker():" + aAttributes.id + "=[" + range.toString() + "]::" + ex2);
240                return null;
241            }
242            var xContext = aXPath.context.replace(/\s+$/mg, "").replace(/^\s+/mg, "");
243            var rContext = this.Common.exceptCode(range.toString().replace(/\s+$/mg, "").replace(/^\s+/mg, ""));
244            if (xContext != "" && xContext != rContext && (this._isTextNode(range.startContainer) || this._isTextNode(range.endContainer))) {
245                var alignment = this.alignment;
246                if (alignment == 0)
247                    return null;
248                var textPArray = [];
249                var textNArray = [];
250                var textContent;
251                var textCnt;
252                var nodeWalker = doc.createTreeWalker(
253                        doc.body,
254                        NodeFilter.SHOW_TEXT,
255                        {
256                            acceptNode: function(aNode) {
257                                if (aNode.nodeType == aNode.TEXT_NODE && !(/[^\t\n\r ]/.test(aNode.nodeValue)))
258                                    return NodeFilter.FILTER_REJECT;
259                                return NodeFilter.FILTER_ACCEPT;
260                            }
261                        },
262                false);
263                nodeWalker.currentNode = range.startContainer;
264                var textContentP = nodeWalker.currentNode.textContent;
265                for (textCnt = 0; textCnt < nodeWalker.currentNode.textContent.length; textCnt++) {
266                    textPArray.unshift({
267                        node: nodeWalker.currentNode,
268                        pos: textCnt
269                    });
270                }
271                if (textContentP.lastIndexOf(xContext) < 0 && textContentP.length < alignment) {
272                    while (nodeWalker.previousNode()) {
273                        textContentP = nodeWalker.currentNode.textContent + textContentP;
274                        for (textCnt = 0; textCnt < nodeWalker.currentNode.textContent.length; textCnt++) {
275                            textPArray.unshift({
276                                node: nodeWalker.currentNode,
277                                pos: textCnt
278                            });
279                        }
280                        if (textContentP.lastIndexOf(xContext) >= 0)
281                            break;
282                        if (textContentP.length > alignment)
283                            break;
284                    }
285                }
286                var indexP = textContentP.lastIndexOf(xContext);
287                var lengthP = 0;
288                if (indexP >= 0)
289                    lengthP = textContentP.length;
290                nodeWalker.currentNode = range.startContainer;
291                var textContentN = nodeWalker.currentNode.textContent;
292                for (textCnt = 0; textCnt < nodeWalker.currentNode.textContent.length; textCnt++) {
293                    textNArray.push({
294                        node: nodeWalker.currentNode,
295                        pos: textCnt
296                    });
297                }
298                if (textContentN.indexOf(xContext) < 0 && textContentN.length < alignment) {
299                    while (nodeWalker.nextNode()) {
300                        textContentN += nodeWalker.currentNode.textContent;
301                        for (textCnt = 0; textCnt < nodeWalker.currentNode.textContent.length; textCnt++) {
302                            textNArray.push({
303                                node: nodeWalker.currentNode,
304                                pos: textCnt
305                            });
306                        }
307                        if (textContentN.indexOf(xContext) >= 0)
308                            break;
309                        if (textContentN.length > alignment)
310                            break;
311                    }
312                }
313                var indexN = textContentN.indexOf(xContext);
314                var findRange = this._getFindRange();
315                var docRange = doc.createRange();
316                docRange.selectNode(doc.body);
317                var startRange = docRange.cloneRange();
318                var stopRange = docRange.cloneRange();
319                stopRange.collapse(false);
320                startRange.collapse(false);
321                if (indexP >= 0 && (lengthP - indexP) <= alignment && indexN >= 0 && indexN <= alignment) {
322                    if ((lengthP - indexP) <= indexN) {
323                        startRange.setStart(textPArray[indexP].node, indexP);
324                        startRange.setEnd(textPArray[indexP].node, indexP);
325                    } else {
326                        startRange.setStart(textNArray[indexN].node, textNArray[indexN].pos);
327                        startRange.setEnd(textNArray[indexN].node, textNArray[indexN].pos);
328                    }
329                } else if (indexP >= 0 && (lengthP - indexP) <= alignment) {
330                    startRange.setStart(textPArray[indexP].node, indexP);
331                    startRange.setEnd(textPArray[indexP].node, indexP);
332                } else if (indexN >= 0 && indexN <= alignment) {
333                    startRange.setStart(textNArray[indexN].node, textNArray[indexN].pos);
334                    startRange.setEnd(textNArray[indexN].node, textNArray[indexN].pos);
335                } else {
336                    return null;
337                }
338                var result = findRange.Find(xContext, docRange, startRange, stopRange);
339                if (!result)
340                    return null;
341                range = result.cloneRange();
342            }
343            rtnNode.push({id: aAttributes.id, text: range.toString()});
344            if (doc.getElementById(aAttributes.id))
345                return rtnNode;
346            var aWindow = doc.defaultView;
347            var startC = range.startContainer;
348            var endC = range.endContainer;
349            var sOffset = range.startOffset;
350            var eOffset = range.endOffset;
351            var sameNode = (startC == endC);
352            var sameOffset = (sOffset == eOffset);
353            if (sameNode && sameOffset) {
354                this._wrapTextNodeWithSpan(doc, startC, this._createSpanNode(aWindow, aAttributes, this.nodePositionInRange.SINGLE));
355                return rtnNode;
356            }
357            var createNode = false;
358            function _acceptNode(aNode) {
359                if (aNode.nodeType != aNode.TEXT_NODE && aNode.nodeType != aNode.ELEMENT_NODE)
360                    return NodeFilter.FILTER_REJECT;
361                return NodeFilter.FILTER_ACCEPT;
362            }
363            ;
364            if (!sameNode || !this._isTextNode(startC)) {
365                var endNode;
366                if (endC.nodeType == endC.ELEMENT_NODE && endC.childNodes.length > range.endOffset) {
367                    endNode = endC.childNodes[range.endOffset];
368                } else {
369                    endNode = endC;
370                }
371                var nodeWalker = doc.createTreeWalker(range.commonAncestorContainer, NodeFilter.SHOW_ALL, _acceptNode, false);
372                nodeWalker.currentNode = startC;
373                for (var txtNode = nodeWalker.nextNode(); txtNode && txtNode != endNode; txtNode = nodeWalker.nextNode()) {
374                    if (txtNode.nodeType == txtNode.ELEMENT_NODE)
375                        continue;
376                    var xContext = txtNode.nodeValue.replace(/[\r\n\t]+/mg, "");
377                    if (xContext.length == 0)
378                        continue;
379                    if (!createNode) {
380                        xContext = txtNode.nodeValue.replace(/\s+/mg, "");
381                        if (xContext.length == 0)
382                            continue;
383                    }
384                    if (
385                            txtNode.parentNode.nodeName == "SCRIPT" ||
386                            txtNode.parentNode.nodeName == "TABLE" ||
387                            txtNode.parentNode.nodeName == "THEAD" ||
388                            txtNode.parentNode.nodeName == "TBODY" ||
389                            txtNode.parentNode.nodeName == "TFOOT" ||
390                            txtNode.parentNode.nodeName == "TR"
391                            )
392                        continue;
393                    if (endC.compareDocumentPosition(txtNode) == endC.DOCUMENT_POSITION_FOLLOWING)
394                        continue;
395                    nodeWalker.currentNode = this._wrapTextNodeWithSpan(doc, txtNode, this._createSpanNode(aWindow, aAttributes, this.nodePositionInRange.MIDDLE));
396                    createNode = true;
397                }
398            }
399            if (endC.parentNode.nodeName != "SCRIPT") {
400                if (this._isTextNode(endC))
401                    endC.splitText(eOffset);
402                if (!sameNode && endC.nodeValue) {
403                    var xContext = endC.nodeValue.replace(/[\r\n\t\s]+/mg, "");
404                    if (xContext.length > 0) {
405                        this._wrapTextNodeWithSpan(doc, endC, this._createSpanNode(aWindow, aAttributes, this.nodePositionInRange.END));
406                        createNode = true;
407                    }
408                }
409            }
410            if (this._isTextNode(startC) && startC.parentNode.nodeName != "SCRIPT") {
411                var secondHalf = startC.splitText(sOffset);
412                var xContext = secondHalf.nodeValue.replace(/[\r\n\t\s]+/mg, "");
413                if (xContext.length > 0) {
414                    if (sameNode) {
415                        this._wrapTextNodeWithSpan(doc, secondHalf, this._createSpanNode(aWindow, aAttributes, this.nodePositionInRange.SINGLE));
416                        createNode = true;
417                    } else {
418                        this._wrapTextNodeWithSpan(doc, secondHalf, this._createSpanNode(aWindow, aAttributes, this.nodePositionInRange.START));
419                        createNode = true;
420                    }
421                }
422            }
423            if (!createNode && sameNode)
424                this.markerRange(range, aAttributes);
425            range.collapse(true);
426            if (aAttributes.id) {
427                var xPathResult = this.XPath.evaluate('//*[@id="' + aAttributes.id + '"]', doc);
428                for (var k = 0; k < xPathResult.snapshotLength; k++) {
429                    var node = xPathResult.snapshotItem(k);
430                    if (xPathResult.snapshotLength > 1) {
431                        if (k == 0) {
432                            node.style.borderRight = "";
433                        } else if (k == (xPathResult.snapshotLength - 1)) {
434                            node.style.borderLeft = "";
435                        } else {
436                            node.style.borderLeft = "";
437                            node.style.borderRight = "";
438                        }
439                    }
440                }
441            }
442            return rtnNode;
443        } catch (ex) {
444            this._dump("bitsMarker.xPathMarker():" + ex);
445            return null;
446        }
447    },
448/////////////////////////////////////////////////////////////////////
449    _isTextNode: function(aNode) {
450        return aNode.nodeType == aNode.TEXT_NODE;
451    },
452/////////////////////////////////////////////////////////////////////
453    _createSpanNode: function(aWindow, aAttributes, aNodePosInRange) {
454        var newNode = aWindow.document.createElement("span");
455        for (var attr in aAttributes) {
456            newNode.setAttribute(attr, aAttributes[attr]);
457        }
458        return newNode;
459    },
460/////////////////////////////////////////////////////////////////////
461    _wrapTextNodeWithSpan: function(aDoc, aTextNode, aSpanNode) {
462        var clonedTextNode = aTextNode.cloneNode(false);
463        var nodeParent = aTextNode.parentNode;
464        aSpanNode.appendChild(clonedTextNode);
465        nodeParent.replaceChild(aSpanNode, aTextNode);
466        return clonedTextNode;
467    },
468/////////////////////////////////////////////////////////////////////
469    _dump: function(aString) {
470        window.top.bitsMarkingCollection._dump(aString);
471    },
472}
Note: See TracBrowser for help on using the repository browser.