source: Dev/trunk/src/client/dojox/editor/plugins/Blockquote.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

  • Property svn:executable set to *
File size: 14.7 KB
Line 
1define([
2        "dojo",
3        "dijit",
4        "dojox",
5        "dijit/_editor/_Plugin",
6        "dijit/form/ToggleButton",
7        "dojo/_base/connect",
8        "dojo/_base/declare",
9        "dojo/i18n",
10        "dojo/i18n!dojox/editor/plugins/nls/Blockquote"
11], function(dojo, dijit, dojox, _Plugin) {
12
13var Blockquote = dojo.declare("dojox.editor.plugins.Blockquote", _Plugin, {
14        // summary:
15        //              This plugin provides Blockquote capability to the editor.
16        //              window/tab
17
18        // iconClassPrefix: [const] String
19        //              The CSS class name for the button node icon.
20        iconClassPrefix: "dijitAdditionalEditorIcon",
21
22        _initButton: function(){
23                // summary:
24                //              Over-ride for creation of the preview button.
25                this._nlsResources = dojo.i18n.getLocalization("dojox.editor.plugins", "Blockquote");
26                this.button = new dijit.form.ToggleButton({
27                        label: this._nlsResources["blockquote"],
28                        showLabel: false,
29                        iconClass: this.iconClassPrefix + " " + this.iconClassPrefix + "Blockquote",
30                        tabIndex: "-1",
31                        onClick: dojo.hitch(this, "_toggleQuote")
32                });
33        },
34
35        setEditor: function(editor){
36                // summary:
37                //              Over-ride for the setting of the editor.
38                // editor: Object
39                //              The editor to configure for this plugin to use.
40                this.editor = editor;
41                this._initButton();
42                this.connect(this.editor, "onNormalizedDisplayChanged", "updateState");
43                // We need the custom undo code since we manipulate the dom
44                // outside of the browser natives and only customUndo really handles
45                // that.  It will incur a performance hit, but should hopefully be
46                // relatively small.
47                editor.customUndo = true;
48        },
49       
50        _toggleQuote: function(arg){
51                // summary:
52                //              Function to trigger previewing of the editor document
53                // tags:
54                //              private
55                try{
56                        var ed = this.editor;
57                        ed.focus();
58
59                        var quoteIt = this.button.get("checked");
60                        var sel = dijit.range.getSelection(ed.window);
61                        var range, elem, start, end;
62                        if(sel && sel.rangeCount > 0){
63                                range = sel.getRangeAt(0);
64                        }
65                        if(range){
66                                ed.beginEditing();
67                                if(quoteIt){
68                                        // Lets see what we've got as a selection...
69                                        var bq, tag;
70                                        if(range.startContainer === range.endContainer){
71                                                // No selection, just cursor point, we need to see if we're
72                                                // in an indentable block, or similar.
73                                                if(this._isRootInline(range.startContainer)){
74                                                        // Text at the 'root' of the document, so we need to gather all of it.,
75               
76                                                        // First, we need to find the toplevel inline element that is rooted
77                                                        // to the document 'editNode'
78                                                        start = range.startContainer;
79                                                        while(start && start.parentNode !== ed.editNode){
80                                                                start = start.parentNode;
81                                                        }
82                                                        // Now we need to walk up its siblings and look for the first one in the rooting
83                                                        // that isn't inline or text, as we want to grab all of that for indent.
84                                                        while(start && start.previousSibling && (
85                                                                        this._isTextElement(start) ||
86                                                                        (start.nodeType === 1 &&
87                                                                         this._isInlineFormat(this._getTagName(start))
88                                                                ))){
89                                                                start = start.previousSibling;
90                                                        }
91                                                        if(start && start.nodeType === 1 &&
92                                                           !this._isInlineFormat(this._getTagName(start))){
93                                                                // Adjust slightly, we're one node too far back in this case.
94                                                                start = start.nextSibling;
95                                                        }
96               
97                                                        // Okay, we have a configured start, lets grab everything following it that's
98                                                        // inline and make it part of the blockquote!
99                                                        if(start){
100                                                                bq = ed.document.createElement("blockquote");
101                                                                dojo.place(bq, start, "after");
102                                                                bq.appendChild(start);
103                                                                end = bq.nextSibling;
104                                                                while(end && (
105                                                                        this._isTextElement(end) ||
106                                                                        (end.nodeType === 1 &&
107                                                                                this._isInlineFormat(this._getTagName(end)))
108                                                                        )){
109                                                                        // Add it.
110                                                                        bq.appendChild(end);
111                                                                        end = bq.nextSibling;
112                                                                }
113                                                        }
114                                                }else{
115                                                        // Figure out what to do when not root inline....
116                                                        var node = range.startContainer;
117                                                        while ((this._isTextElement(node) ||
118                                                                        this._isInlineFormat(this._getTagName(node))
119                                                                        || this._getTagName(node) === "li") &&
120                                                                node !== ed.editNode && node !== ed.document.body){
121                                                                node = node.parentNode;
122                                                        }
123                                                        if(node !== ed.editNode && node !==     node.ownerDocument.documentElement){
124                                                                bq = ed.document.createElement("blockquote");
125                                                                dojo.place(bq, node, "after");
126                                                                bq.appendChild(node);
127                                                        }
128                                                }
129                                                if(bq){
130                                                        ed._sCall("selectElementChildren", [bq]);
131                                                        ed._sCall("collapse", [true]);
132                                                }
133                                        }else{
134                                                var curNode;
135                                                // multi-node select.  We need to scan over them.
136                                                // Find the two containing nodes at start and end.
137                                                // then move the end one node past.  Then ... lets see
138                                                // what we can blockquote!
139                                                start = range.startContainer;
140                                                end = range.endContainer;
141                                                // Find the non-text nodes.
142
143                                                while(start && this._isTextElement(start) && start.parentNode !== ed.editNode){
144                                                        start = start.parentNode;
145                                                }
146
147                                                // Try to find the end node.  We have to check the selection junk
148                                                curNode = start;
149                                                while(curNode.nextSibling && ed._sCall("inSelection", [curNode])){
150                                                        curNode = curNode.nextSibling;
151                                                }
152                                                end = curNode;
153                                                if(end === ed.editNode || end === ed.document.body){
154                                                        // Unable to determine real selection end, so just make it
155                                                        // a single node indent of start + all following inline styles, if
156                                                        // present, then just exit.
157                                                        bq = ed.document.createElement("blockquote");
158                                                        dojo.place(bq, start, "after");
159                                                        tag = this._getTagName(start);
160                                                        if(this._isTextElement(start) || this._isInlineFormat(tag)){
161                                                                // inline element or textnode
162                                                                // Find and move all inline tags following the one we inserted also into the
163                                                                // blockquote so we don't split up content funny.
164                                                                var next = start;
165                                                                while(next && (
166                                                                        this._isTextElement(next) ||
167                                                                        (next.nodeType === 1 &&
168                                                                        this._isInlineFormat(this._getTagName(next))))){
169                                                                        bq.appendChild(next);
170                                                                        next = bq.nextSibling;
171                                                                }
172                                                        }else{
173                                                                bq.appendChild(start);
174                                                        }
175                                                        return;
176                                                }
177
178                                                // Has a definite end somewhere, so lets try to blockquote up to it.
179                                                // requires looking at the selections and in some cases, moving nodes
180                                                // into separate blockquotes.
181                                                end = end.nextSibling;
182                                                curNode = start;
183                                                while(curNode && curNode !== end){
184                                                        if(curNode.nodeType === 1){
185                                                                tag = this._getTagName(curNode);
186                                                                if(tag !== "br"){
187                                                                        if(!window.getSelection){
188                                                                                // IE sometimes inserts blank P tags, which we want to skip
189                                                                                // as they end up blockquoted, which messes up layout.
190                                                                                if(tag === "p" && this._isEmpty(curNode)){
191                                                                                        curNode = curNode.nextSibling;
192                                                                                        continue;
193                                                                                }
194                                                                        }
195                                                                        if(this._isInlineFormat(tag)){
196                                                                                // inline tag.
197                                                                                if(!bq){
198                                                                                        bq = ed.document.createElement("blockquote");
199                                                                                        dojo.place(bq, curNode, "after");
200                                                                                        bq.appendChild(curNode);
201                                                                                }else{
202                                                                                        bq.appendChild(curNode);
203                                                                                }
204                                                                                curNode = bq;
205                                                                        }else{
206                                                                                if(bq){
207                                                                                        if(this._isEmpty(bq)){
208                                                                                                bq.parentNode.removeChild(bq);
209                                                                                        }
210                                                                                }
211                                                                                bq = ed.document.createElement("blockquote");
212                                                                                dojo.place(bq, curNode, "after");
213                                                                                bq.appendChild(curNode);
214                                                                                curNode = bq;
215                                                                        }
216                                                                }
217                                                        }else if(this._isTextElement(curNode)){
218                                                                if(!bq){
219                                                                        bq = ed.document.createElement("blockquote");
220                                                                        dojo.place(bq, curNode, "after");
221                                                                        bq.appendChild(curNode);
222                                                                }else{
223                                                                        bq.appendChild(curNode);
224                                                                }
225                                                                curNode = bq;
226                                                        }
227                                                        curNode = curNode.nextSibling;
228                                                }
229                                                // Okay, check the last bq, remove it if no content.
230                                                if(bq){
231                                                        if(this._isEmpty(bq)){
232                                                                bq.parentNode.removeChild(bq);
233                                                        }else{
234                                                                ed._sCall("selectElementChildren", [bq]);
235                                                                ed._sCall("collapse", [true]);
236                                                        }
237                                                        bq = null;
238                                                }
239                                        }
240                                }else{
241                                        var found = false;
242                                        if(range.startContainer === range.endContainer){
243                                                elem = range.endContainer;
244                                                // Okay, now see if we can find one of the formatting types we're in.
245                                                while(elem && elem !== ed.editNode && elem !== ed.document.body){
246                                                        var tg = elem.tagName?elem.tagName.toLowerCase():"";
247                                                        if(tg === "blockquote"){
248                                                                found = true;
249                                                                break;
250                                                        }
251                                                        elem = elem.parentNode;
252                                                }
253                                                if(found){
254                                                        var lastChild;
255                                                        while(elem.firstChild){
256                                                                lastChild = elem.firstChild;
257                                                                dojo.place(lastChild, elem, "before");
258                                                        }
259                                                        elem.parentNode.removeChild(elem);
260                                                        if(lastChild){
261                                                                ed._sCall("selectElementChildren", [lastChild]);
262                                                                ed._sCall("collapse", [true]);
263                                                        }
264                                                }
265                                        }else{
266                                                // Multi-select!  Gotta find all the blockquotes contained within the selection area.
267                                                start = range.startContainer;
268                                                end = range.endContainer;
269                                                while(start && this._isTextElement(start) && start.parentNode !== ed.editNode){
270                                                        start = start.parentNode;
271                                                }
272                                                var selectedNodes = [];
273                                                var cNode = start;
274                                                while(cNode && cNode.nextSibling && ed._sCall("inSelection", [cNode])){
275                                                        if(cNode.parentNode && this._getTagName(cNode.parentNode) === "blockquote"){
276                                                                cNode = cNode.parentNode;
277                                                        }
278                                                        selectedNodes.push(cNode);
279                                                        cNode = cNode.nextSibling;
280                                                }
281                                               
282                                                // Find all the blocknodes now that we know the selection area.
283                                                var bnNodes = this._findBlockQuotes(selectedNodes);
284                                                while(bnNodes.length){
285                                                        var bn = bnNodes.pop();
286                                                        if(bn.parentNode){
287                                                                // Make sure we haven't seen this before and removed it.
288                                                                while(bn.firstChild){
289                                                                        dojo.place(bn.firstChild, bn, "before");
290                                                                }
291                                                                bn.parentNode.removeChild(bn);
292                                                        }
293                                                }
294                                        }
295                                }
296                                ed.endEditing();
297                        }
298                        ed.onNormalizedDisplayChanged();
299                }catch(e){ /* Squelch */ }
300        },
301
302        updateState: function(){
303                // summary:
304                //              Overrides _Plugin.updateState().  This controls whether or not the current
305                //              cursor position should toggle on the quote button or not.
306                // tags:
307                //              protected
308                var ed = this.editor;
309                var disabled = this.get("disabled");
310               
311                if(!ed || !ed.isLoaded){ return; }
312                if(this.button){
313                        this.button.set("disabled", disabled);
314                        if(disabled){
315                                return;
316                        }
317
318                        // Some browsers (WebKit) doesn't actually get the tag info right.
319                        // So ... lets check it manually.
320                        var elem;
321                        var found = false;
322                       
323                        // Try to find the ansestor element (and see if it is blockquote)
324                        var sel = dijit.range.getSelection(ed.window);
325                        if(sel && sel.rangeCount > 0){
326                                var range = sel.getRangeAt(0);
327                                if(range){
328                                        elem = range.endContainer;
329                                }
330                        }
331                        // Okay, now see if we can find one of the formatting types we're in.
332                        while(elem && elem !== ed.editNode && elem !== ed.document){
333                                var tg = elem.tagName?elem.tagName.toLowerCase():"";
334                                if(tg === "blockquote"){
335                                        found = true;
336                                        break;
337                                }
338                                elem = elem.parentNode;
339                        }
340                        // toggle whether or not the current selection is blockquoted.
341                        this.button.set("checked", found);
342                }
343        },
344
345        _findBlockQuotes: function(nodeList){
346                // summary:
347                //              function to find all the blocknode elements in a collection of
348                //              nodes
349                // nodeList:
350                //              The list of nodes.
351                // tags:
352                //              private
353                var bnList = [];
354                if(nodeList){
355                        var i;
356                        for(i = 0; i < nodeList.length; i++){
357                                var node = nodeList[i];
358                                if(node.nodeType === 1){
359                                        if(this._getTagName(node) === "blockquote"){
360                                                bnList.push(node);
361                                        }
362                                        if(node.childNodes && node.childNodes.length > 0){
363                                                bnList = bnList.concat(this._findBlockQuotes(node.childNodes));
364                                        }
365                                }
366                        }
367                }
368                return bnList;
369        },
370
371        /*****************************************************************/
372        /* Functions borrowed from NormalizeIndentOutdent                */
373        /*****************************************************************/
374
375        _getTagName: function(node){
376                // summary:
377                //              Internal function to get the tag name of an element
378                //              if any.
379                // node:
380                //              The node to look at.
381                // tags:
382                //              private
383                var tag = "";
384                if(node && node.nodeType === 1){
385                        tag = node.tagName?node.tagName.toLowerCase():"";
386                }
387                return tag;
388        },
389
390        _isRootInline: function(node){
391                // summary:
392                //              This functions tests whether an indicated node is in root as inline
393                //              or rooted inline elements in the page.
394                // node:
395                //              The node to start at.
396                // tags:
397                //              private
398                var ed = this.editor;
399                if(this._isTextElement(node) && node.parentNode === ed.editNode){
400                        return true;
401                }else if(node.nodeType === 1 && this._isInlineFormat(node) && node.parentNode === ed.editNode){
402                        return true;
403                }else if(this._isTextElement(node) && this._isInlineFormat(this._getTagName(node.parentNode))){
404                        node = node.parentNode;
405                        while(node && node !== ed.editNode && this._isInlineFormat(this._getTagName(node))){
406                                node = node.parentNode;
407                        }
408                        if(node === ed.editNode){
409                                return true;
410                        }
411                }
412                return false;
413        },
414
415        _isTextElement: function(node){
416                // summary:
417                //              Helper function to check for text nodes.
418                // node:
419                //              The node to check.
420                // tags:
421                //              private
422                if(node && node.nodeType === 3 || node.nodeType === 4){
423                        return true;
424                }
425                return false;
426        },
427
428        _isEmpty: function(node){
429                // summary:
430                //              Internal function to determine if a node is 'empty'
431                //              Eg, contains only blank text.  Used to determine if
432                //              an empty list element should be removed or not.
433                // node:
434                //              The node to check.
435                // tags:
436                //              private
437                if(node.childNodes){
438                        var empty = true;
439                        var i;
440                        for(i = 0; i < node.childNodes.length; i++){
441                                var n = node.childNodes[i];
442                                if(n.nodeType === 1){
443                                        if(this._getTagName(n) === "p"){
444                                                if(!dojo.trim(n.innerHTML)){
445                                                        continue;
446                                                }
447                                        }
448                                        empty = false;
449                                        break;
450                                }else if(this._isTextElement(n)){
451                                        // Check for empty text.
452                                        var nv = dojo.trim(n.nodeValue);
453                                        if(nv && nv !=="&nbsp;" && nv !== "\u00A0"){
454                                                empty = false;
455                                                break;
456                                        }
457                                }else{
458                                        empty = false;
459                                        break;
460                                }
461                        }
462                        return empty;
463                }else{
464                        return true;
465                }
466        },
467
468        _isInlineFormat: function(tag){
469                // summary:
470                //              Function to determine if the current tag is an inline
471                //              element that does formatting, as we don't want to
472                //              break/indent around it, as it can screw up text.
473                // tag:
474                //              The tag to examine
475                // tags:
476                //              private
477                switch(tag){
478                        case "a":
479                        case "b":
480                        case "strong":
481                        case "s":
482                        case "strike":
483                        case "i":
484                        case "u":
485                        case "em":
486                        case "sup":
487                        case "sub":
488                        case "span":
489                        case "font":
490                        case "big":
491                        case "cite":
492                        case "q":
493                        case "img":
494                        case "small":
495                                return true;
496                        default:
497                                return false;
498                }
499        }
500});
501
502// Register this plugin.
503dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
504        if(o.plugin){ return; }
505        var name = o.args.name.toLowerCase();
506        if(name === "blockquote"){
507                o.plugin = new Blockquote({});
508        }
509});
510
511return Blockquote;
512
513});
Note: See TracBrowser for help on using the repository browser.