source: branches/v.mische/test/002/wymeditor/jquery.wymeditor.js @ 189

Revision 189, 23.6 KB checked in by v.mische, 5 years ago (diff)

fixed html() for IE, WYMeditor now should with jQuery 1.1.2

Line 
1/*
2 * WYMeditor : what you see is What You Mean web-based editor
3 * Copyright (C) 2007 H.O.net - http://www.honet.be/
4 * Dual licensed under the MIT (MIT-license.txt)
5 * and GPL (GPL-license.txt) licenses.
6 *
7 * For further information visit:
8 *        http://www.wymeditor.org/
9 *
10 * File Name:
11 *        jquery.wymeditor.js
12 *        Main JS file with core class and functions.
13 *        See the documentation for more info.
14 *
15 * File Authors:
16 *        Jean-Francois Hovinne (jf.hovinne@wymeditor.org)
17 */
18
19var $j = jQuery;
20
21var aWYM_INSTANCES     = new Array();
22var sWYM_NAME          = "name";
23var sWYM_INDEX         = "{Wym_Index}";
24var sWYM_TOOLS         = "{wym_tools}";
25var sWYM_CLASSES       = "{Wym_Classes}";
26var sWYM_CONTAINERS    = "{Wym_Containers}";
27var sWYM_HTML          = "{Wym_Html}";
28var sWYM_IFRAME        = "{Wym_Iframe}";
29var sWYM_STATUS        = "{Wym_Status}";
30var sWYM_BODY          = "body";
31var sWYM_STRING        = "string";
32var sWYM_P             = "p";
33var sWYM_H1            = "h1";
34var sWYM_H2            = "h2";
35var sWYM_H3            = "h3";
36var sWYM_H4            = "h4";
37var sWYM_H5            = "h5";
38var sWYM_H6            = "h6";
39var sWYM_PRE           = "pre";
40var sWYM_BLOCKQUOTE    = "blockquote";
41var sWYM_TD            = "td";
42var sWYM_TH            = "th";
43var sWYM_LINK          = "link";
44var sWYM_IMAGE         = "image";
45var sWYM_TABLE         = "table";
46var sWYM_CREATE_LINK   = "CreateLink";
47var sWYM_INSERT_IMAGE  = "InsertImage";
48var sWYM_INSERT_TABLE  = "InsertTable";
49var sWYM_TOGGLE_HTML   = "ToggleHtml";
50
51
52var aWYM_CONTAINERS = new Array(sWYM_P,sWYM_H1,sWYM_H2,sWYM_H3,sWYM_H4,
53        sWYM_H5,sWYM_H6,sWYM_PRE,sWYM_BLOCKQUOTE);
54
55var aWYM_KEY = {
56    BACKSPACE: 8,
57    ENTER: 13,
58    END: 35,
59    HOME: 36,
60    LEFT: 37,
61    UP: 38,
62    RIGHT: 39,
63    DOWN: 40,
64    CURSOR: new Array(37, 38, 39, 40),
65    DELETE: 46
66};
67
68var aWYM_NODE = {
69    ELEMENT: 1,
70    ATTRIBUTE: 2,
71    TEXT: 3
72};
73
74
75/**
76 * Replace an HTML element by WYMeditor
77 *
78 * @example $(".wymeditor").wymeditor(
79 *                {
80 *
81 *                }
82 *            );
83 * @desc Example description here
84 *
85 * @name WYMeditor
86 * @description WYMeditor is a web-based WYSIWYM XHTML editor
87 * @param Hash hash A hash of parameters
88 * @option Integer iExample Description here
89 * @option String sExample Description here
90 *
91 * @type jQuery
92 * @cat Plugins/WYMeditor
93 * @author Jean-Francois Hovinne
94 */
95$j.fn.wymeditor = function(options, callback) {
96
97    options = $j.extend({
98
99        sHtml:              "",
100
101        sBoxHtml:           "<div class='wym_box'>"
102                            + "<div class='wym_area_top'>" + sWYM_TOOLS + "</div>"
103                            + "<div class='wym_area_left'></div>"
104                            + "<div class='wym_area_right'>" + sWYM_CONTAINERS + sWYM_CLASSES + "</div>"
105                            + "<div class='wym_area_main'>" + sWYM_HTML + sWYM_IFRAME + sWYM_STATUS + "</div>"
106                            + "<div class='wym_area_bottom'>" + "</div>"
107                            + "</div>",
108
109        sIframeHtml:        "<div class='wym_iframe wym_section'>"
110                            + "<iframe "
111                            + "src='wymeditor/wymiframe.html' "
112                            + "onload='window.parent.aWYM_INSTANCES[" + sWYM_INDEX + "].initIframe(this)' "
113                            + "></iframe>"
114                            + "</div>",
115
116        sToolsHtml:         "<div class='wym_tools wym_section'>"
117                            + "<h2>Tools</h2>"
118                            + "<ul>"
119                            + "<li class='wym_tools_strong'><a href='#' name='Bold'>{Strong}</a></li>"
120                            + "<li class='wym_tools_emphasis'><a href='#' name='Italic'>{Emphasis}</a></li>"
121                            + "<li class='wym_tools_superscript'><a href='#' name='Superscript'>{Superscript}</a></li>"
122                            + "<li class='wym_tools_subscript'><a href='#' name='Subscript'>{Subscript}</a></li>"
123                            + "<li class='wym_tools_ordered_list'><a href='#' name='InsertOrderedList'>{Ordered_List}</a></li>"
124                            + "<li class='wym_tools_unordered_list'><a href='#' name='InsertUnorderedList'>{Unordered_List}</a></li>"
125                            + "<li class='wym_tools_indent'><a href='#' name='Indent'>{Indent}</a></li>"
126                            + "<li class='wym_tools_outdent'><a href='#' name='Outdent'>{Outdent}</a></li>"
127                            + "<li class='wym_tools_undo'><a href='#' name='Undo'>{Undo}</a></li>"
128                            + "<li class='wym_tools_redo'><a href='#' name='Redo'>{Redo}</a></li>"
129                            + "<li class='wym_tools_link'><a href='#' name='CreateLink'>{Link}</a></li>"
130                            + "<li class='wym_tools_unlink'><a href='#' name='Unlink'>{Unlink}</a></li>"
131                            + "<li class='wym_tools_image'><a href='#' name='InsertImage'>{Image}</a></li>"
132                            + "<li class='wym_tools_table'><a href='#' name='InsertTable'>{Table}</a></li>"
133                            + "<li class='wym_tools_html'><a href='#' name='ToggleHtml'>{HTML}</a></li>"
134                            + "</ul>"
135                            + "</div>",
136
137        sContainersHtml:    "<div class='wym_containers wym_section'>"
138                            + "<h2>Containers</h2>"
139                            + "<ul>"
140                            + "<li class='wym_containers_p'><a href='#' name='P'>Paragraph</a></li>"
141                            + "<li class='wym_containers_h1'><a href='#' name='H1'>Heading 1</a></li>"
142                            + "<li class='wym_containers_h2'><a href='#' name='H2'>Heading 2</a></li>"
143                            + "<li class='wym_containers_h3'><a href='#' name='H3'>Heading 3</a></li>"
144                            + "<li class='wym_containers_h4'><a href='#' name='H4'>Heading 4</a></li>"
145                            + "<li class='wym_containers_h5'><a href='#' name='H5'>Heading 5</a></li>"
146                            + "<li class='wym_containers_h6'><a href='#' name='H6'>Heading 6</a></li>"
147                            + "<li class='wym_containers_pre'><a href='#' name='PRE'>Preformatted</a></li>"
148                            + "<li class='wym_containers_blockquote'><a href='#' name='BLOCKQUOTE'>Blockquote</a></li>"
149                            + "<li class='wym_containers_th'><a href='#' name='TH'>Table Header</a></li>"
150                            + "</ul>"
151                            + "</div>",
152
153        sClassesHtml:       "<div class='wym_classes wym_section'>"
154                            + "<h2>Classes</h2>"
155                            + "</div>",
156
157        sStatusHtml:        "<div class='wym_status wym_section'>"
158                            + "<h2>Status</h2>"
159                            + "</div>",
160
161        sHtmlHtml:          "<div class='wym_html wym_section'>"
162                            + "<h2>Source code</h2>"
163                            + "<textarea class='wym_html_val'></textarea>"
164                            + "</div>",
165
166        sBoxSelector:       ".wym_box",
167        sToolsSelector:     ".wym_tools",
168        sClassesSelector:   ".wym_classes",
169        sContainersSelector:".wym_containers",
170        sHtmlSelector:      ".wym_html",
171        sIframeSelector:    ".wym_iframe iframe",
172        sStatusSelector:    ".wym_status",
173        sToolsSelector:     ".wym_tools a",
174        sContainerSelector: ".wym_containers a",
175        sHtmlValSelector:   ".wym_html_val",
176       
177        sStringDelimiterLeft: "{",
178        sStringDelimiterRight:"}"
179
180    }, options);
181
182    return this.each(function(i) {
183
184        new Wymeditor($j(this),i,options,callback);
185    });
186};
187
188/* @name extend
189 * @description Returns the WYMeditor instance based on its index
190 */
191$j.extend({
192    wymeditors: function(i) {
193        return (aWYM_INSTANCES[i]);
194    },
195    wymstrings: function(sKey) {
196        return (aWYM_STRINGS[sKey]);
197    }
198});
199
200/* @name Wymeditor
201 * @description WYMeditor class
202 */
203function Wymeditor(elem,index,options,callback) {
204
205    aWYM_INSTANCES[index] = this;
206
207    this._element = elem;
208    this._index = index;
209    this._options = options;
210    this._html = $j(elem).val();
211    this._callback = callback;
212   
213    if(this._options.sHtml) this._html = this._options.sHtml;
214   
215    this.selection = new WymSelection();
216   
217    this.init();
218   
219};
220
221/* @name init
222 * @description Initializes a WYMeditor instance
223 */
224Wymeditor.prototype.init = function() {
225
226    //copy the instance
227    var wym = this;
228
229    //load subclass - browser specific
230    if ($j.browser.msie) {
231        var WymClass = new WymClassExplorer(this);
232        var WymSel = new WymSelExplorer(this);
233    }
234    else if ($j.browser.mozilla) {
235        var WymClass = new WymClassMozilla(this);
236        var WymSel = new WymSelMozilla(this);
237    }
238    else if ($j.browser.opera) {
239        var WymClass = new WymClassOpera(this);
240        var WymSel = new WymSelOpera(this);
241    }
242    else if ($j.browser.safari) {
243        var WymClass = new WymClassSafari(this);
244    }
245    else {
246        //TODO: handle unsupported browsers
247    }
248
249    //copy the subclass methods
250/*
251    for (prop in WymClass) {
252        this[prop] = WymClass[prop];
253    }
254   
255    for (prop in WymSel) {
256        this.selection[prop] = WymSel[prop];
257    }
258*/
259    $j.extend(this, WymClass);
260    $j.extend(this.selection, WymSel);
261
262
263    //load wymbox
264    this._box = $j(this._element).hide().after(this._options.sBoxHtml).next();
265   
266    //construct the iframe
267    var sIframeHtml = this._options.sIframeHtml;
268    sIframeHtml = sIframeHtml.replace(sWYM_INDEX,this._index);
269   
270    //construct wymbox
271    var sBoxHtml = $j(this._box).html();
272   
273    sBoxHtml = sBoxHtml.replace(sWYM_TOOLS, this._options.sToolsHtml);
274    sBoxHtml = sBoxHtml.replace(sWYM_CONTAINERS, this._options.sContainersHtml);
275    sBoxHtml = sBoxHtml.replace(sWYM_CLASSES, this._options.sClassesHtml);
276    sBoxHtml = sBoxHtml.replace(sWYM_HTML, this._options.sHtmlHtml);
277    sBoxHtml = sBoxHtml.replace(sWYM_IFRAME, sIframeHtml);
278    sBoxHtml = sBoxHtml.replace(sWYM_STATUS, this._options.sStatusHtml);
279   
280    //l10n
281    sBoxHtml = this.replaceStrings(sBoxHtml);
282   
283    //load html in wymbox
284    $j(this._box).html(sBoxHtml);
285   
286    //hide the html value
287    $j(this._box).find(this._options.sHtmlSelector).hide();
288
289    //handle click event on tools buttons
290    $j(this._box).find(this._options.sToolsSelector).click(function() {
291        wym.exec($(this).attr(sWYM_NAME));
292        return(false);
293    });
294   
295    //handle click event on containers buttons
296    $j(this._box).find(this._options.sContainerSelector).click(function() {
297        wym.container($(this).attr(sWYM_NAME));
298        return(false);
299    });
300   
301    //handle keyup event on html value: set the editor value
302    $j(this._box).find(this._options.sHtmlValSelector).keyup(function() {
303        $j(wym._doc.body).html($j(this).val());
304    });
305};
306
307Wymeditor.prototype.ready = function() {
308    return(this._doc != null);
309};
310
311
312/********** BASE METHODS **********/
313Wymeditor.prototype.handleEvents = function() {
314//    var _wym = this;
315    $j(this._doc).bind('keydown', {wym: this._wym}, this.handleKeydown);
316    $j(this._doc).bind('keypress', {wym: this._wym}, this.handleKeypress);
317    $j(this._doc).bind('keyup', {wym: this._wym}, this.handleKeyup);
318    // NOTE v.mische If you've multiple instances of WYMeditor, mousedown will
319    // be triggered in the instance you were and not in the one you will be
320    $j(this._doc).bind('mousedown', {wym: this._wym}, this.handleMousedown);
321    $j(this._doc).bind('mouseup', {wym: this._wym}, this.handleMouseup);
322};
323
324
325
326Wymeditor.prototype.handleKeydown = function(evt) {
327//    alert("keydown:"+evt.data._iframe.contentWindow);//._iframe.contentWindow
328//    var sBlockElements = sWYM_P+","+sWYM_H1+","+sWYM_H2+","+sWYM_H3+","+sWYM_H4
329//            +","+sWYM_H5+","+sWYM_H6+","+sWYM_PRE+","+sWYM_BLOCKQUOTE;
330
331    var sContainers = aWYM_CONTAINERS.join(",");
332
333    var _wym = evt.data.wym;
334    var _sel = _wym.selection.getSelection();
335
336/*
337    // some small tests
338    if (_sel.isAtStart(sContainers))
339        alert("isAtStart: "+_sel.startNode.parentNode.nodeName);
340    if (_sel.isAtEnd(sContainers))
341        alert("isAtEnd: "+_sel.endNode.parentNode.nodeName);
342    if (evt.keyCode==aWYM_KEY.DELETE)
343        if(_sel.deleteIfExpanded())
344            return false;
345    if (evt.keyCode==aWYM_KEY.HOME)
346    {
347        //_sel.cursorToStart(_sel.startNode);
348        _sel.cursorToStart(_sel.container);
349        return false;
350    }
351    if (evt.keyCode==aWYM_KEY.END)
352    {
353        //_sel.cursorToEnd(_sel.endNode);
354        _sel.cursorToEnd(_sel.container);
355        return false;
356    }
357*/
358};
359Wymeditor.prototype.handleKeypress = function(evt) {
360};
361Wymeditor.prototype.handleKeyup = function(evt) {
362};
363Wymeditor.prototype.handleMousedown = function(evt) {
364//alert("mouseDown:"+evt.data.wym._index);
365};
366Wymeditor.prototype.handleMouseup = function(evt) {
367//alert("mouseUp:"+evt.data.wym._index);
368};
369
370
371/* @name doc
372 * @description Get the edited document
373 */
374Wymeditor.prototype.doc = function() {
375    return(this._doc);
376}
377
378/* @name box
379 * @description Get the edited document
380 */
381Wymeditor.prototype.box = function() {
382    return(this._box);
383}
384
385/* @name html
386 * @description Get/Set the html value
387 */
388Wymeditor.prototype.html = function(sHtml) {
389
390    if(sHtml) $j(this._doc.body).html(sHtml);
391    else return($j(this._doc.body).html());
392};
393
394/* @name exec
395 * @description Executes a button command
396 */
397Wymeditor.prototype.exec = function(cmd) {
398   
399    //base function for execCommand
400    //open a dialog or exec
401    switch(cmd) {   
402        case sWYM_CREATE_LINK:
403            var container = this.container();
404            if(container) this.dialog(sWYM_LINK);
405        break;
406       
407        case sWYM_INSERT_IMAGE:
408            this.dialog(sWYM_IMAGE);
409        break;
410       
411        case sWYM_INSERT_TABLE:
412            this.dialog(sWYM_TABLE);
413        break;
414       
415        case sWYM_TOGGLE_HTML:
416            this.update();
417            this.toggleHtml();
418        break;
419       
420        default:
421            this._exec(cmd);
422        break;
423    }
424};
425
426/* @name container
427 * @description Get/Set the selected container
428 */
429Wymeditor.prototype.container = function(sType) {
430
431    if(sType) {
432   
433        var container = null;
434       
435        if(sType.toLowerCase() == sWYM_TH) {
436       
437            container = this.container();
438           
439            //find the TD or TH container
440            switch(container.tagName.toLowerCase()) {
441           
442                case sWYM_TD: case sWYM_TH:
443                    break;
444                default:
445                    var aTypes = new Array(sWYM_TD,sWYM_TH);
446                    container = this.findUp(aTypes);
447                    break;
448            }
449           
450            //if it exists, switch
451            if(container!=null) {
452           
453                sType = (container.tagName.toLowerCase() == sWYM_TD)? sWYM_TH: sWYM_TD;
454                this.switchTo(container,sType);
455                this.update();
456            }
457        } else {
458   
459            //set the container type
460            var aTypes=new Array(sWYM_P,sWYM_H1,sWYM_H2,sWYM_H3,sWYM_H4,sWYM_H5,sWYM_H6,sWYM_PRE,sWYM_BLOCKQUOTE);
461            container = this.findUp(aTypes);
462           
463            if(container) {
464   
465                var newNode = null;
466   
467                //blockquotes must contain a block level element
468                if(sType.toLowerCase() == sWYM_BLOCKQUOTE) {
469               
470                    var blockquote = this.findUp(sWYM_BLOCKQUOTE);
471                   
472                    if(blockquote == null) {
473                   
474                        newNode = this._doc.createElement(sType);
475                        container.parentNode.insertBefore(newNode,container);
476                        newNode.appendChild(container);
477                       
478                    } else {
479                   
480                        var nodes = blockquote.childNodes;
481                        var lgt = nodes.length;
482                        for(var x=0; x<lgt; x++) {
483                            blockquote.parentNode.insertBefore(nodes.item(0),blockquote);
484                        }
485                        blockquote.parentNode.removeChild(blockquote);
486                    }
487                }
488               
489                else this.switchTo(container,sType);
490           
491                this.update();
492            }
493        }
494    }
495    else return(this.selected());
496};
497
498/* @name finUp
499 * @description Returns the first parent or self container, based on its type
500 */
501Wymeditor.prototype.findUp = function(mFilter) {
502
503    //mFilter is a string or an array of strings
504
505    var container = this.container();
506    var tagname = container.tagName.toLowerCase();
507   
508    if(typeof(mFilter) == sWYM_STRING) {
509
510        while(tagname != mFilter && tagname != sWYM_BODY) {
511       
512            container = container.parentNode;
513            tagname = container.tagName.toLowerCase();
514        }
515   
516    } else {
517   
518        var bFound = false;
519       
520        while(!bFound && tagname != sWYM_BODY) {
521            for(var i = 0; i<mFilter.length; i++) {
522                if(tagname == mFilter[i]) {
523                    bFound = true;
524                    break;
525                }
526            }
527            if(!bFound) {
528                container = container.parentNode;
529                tagname = container.tagName.toLowerCase();
530            }
531        }
532    }
533   
534    if(tagname != sWYM_BODY) return(container);
535    else return(null);
536};
537
538/* @name switchTo
539 * @description Switch the node's type
540 */
541Wymeditor.prototype.switchTo = function(node,sType) {
542
543    var newNode = this._doc.createElement(sType);
544    var html = $j(node).html();
545    node.parentNode.replaceChild(newNode,node);
546    $j(newNode).html(html);
547};
548
549/********** UI RELATED **********/
550
551Wymeditor.prototype.replaceStrings = function(sVal) {
552
553    for (var key in aWYM_STRINGS) {
554        sVal = sVal.replace(this._options.sStringDelimiterLeft + key + this._options.sStringDelimiterRight, aWYM_STRINGS[key]);
555    }
556    return(sVal);
557};
558
559/* @name status
560 * @description Prints a status message
561 */
562Wymeditor.prototype.status = function(sMessage) {
563
564    //print status message
565    $j(this._box).find(this._options.sStatusSelector).html(sMessage);
566};
567
568/* @name update
569 * @description Updates the element and textarea values
570 */
571Wymeditor.prototype.update = function() {
572
573    var html = this.xhtml();
574    $j(this._element).val(html);
575    $j(this._box).find(this._options.sHtmlValSelector).val(html);
576};
577
578/* @name dialog
579 * @description Opens a dialog box
580 */
581Wymeditor.prototype.dialog = function(sType) {
582
583    console.info(this.selection);
584    console.info(this.selection.test);
585    console.info(this.selection.isAtStart());
586    console.info(this.selection.myTest());
587};
588
589/* @name toggleHtml
590 * @description Shows/Hides the HTML
591 */
592Wymeditor.prototype.toggleHtml = function() {
593
594    $j(this._box).find(this._options.sHtmlSelector).toggle();
595};
596
597/********** SELECTION API **********/
598
599function WymSelection() {
600    this.test = "test from WymSelection";
601};
602
603
604WymSelection.prototype = {
605    /* The following properties where set in the browser specific file (in
606     * getSelection()):
607     * this.original
608     * this.startNode
609     * this.endNode
610     * this.startOffset
611     * this.endOffset
612     * this.collapsed
613     * this.container
614     */
615
616    /* The following methods are implemented in browser specific file:
617     *  - deleteIfExpanded()
618     *  - cursorToStart()
619     *  - cursorToEnd()
620     */
621
622
623    isAtStart: function(jqexpr)
624    {
625        var parent = $j(this.startNode).parentsOrSelf(jqexpr);
626
627        // jqexpr isn't a parent of the current cursor position
628        if (parent.length==0)
629            return false;
630        else
631            parent = parent[0];
632
633
634        for (var n=this.startNode; n!=parent; n=n.parentNode)
635        {
636            //if (n.nodeType == nodeType.TEXT)
637            if (n.nodeType == aWYM_NODE.TEXT)
638            {
639                if (this.startOffset != 0)
640                    return false;
641            }
642            var firstChild = n.parentNode.firstChild;
643            // node isn't first child => cursor can't be at the beginning
644            // in gecko there the first child could be a phantom node
645
646            // sometimes also whitespacenodes which aren't phatom nodes
647            // get stripped, but this is ok, as this is a wysiwym editor
648            if ((firstChild != n
649                    || ($(firstChild).isPhantomNode()
650                        && firstChild.nextSibling != n)))
651            {
652                return false;
653            }
654        }
655
656        if (this.startOffset == 0)
657            return true;
658        else
659            return false;
660    },
661
662    isAtEnd: function(jqexpr)
663    {
664        var parent = $(this.endNode).parentsOrSelf(jqexpr);
665
666        // jqexpr isn't a parent of the current cursor position
667        if (parent.length==0)
668            return false;
669        else
670            parent = parent[0];
671
672
673        // This is the case if, e.g ("|" = cursor): <p>textnode|<br/></p>,
674        // there the offset of endNode (endOffset) is 1 (behind the first node
675        // of <p>)
676        if (this.endNode == parent)
677        {
678            // NOTE I don't know if it is a good idea to delete the <br>
679            // here, as "atEnd()" probably shouldn't change the dom tree,
680            // but only searching it
681            if (this.endNode.lastChild.nodeName == "BR")
682                this.endNode.removeChild(endNode.lastChild);
683
684            // if cursor is really at the end
685            if (this.endOffset > 0 &&
686                    this.endNode.childNodes[this.endOffset-1].nextSibling==null)
687            {
688                return true;
689            }
690        }
691        else
692        {
693            for (var n=this.endNode; n!=parent; n=n.parentNode)
694            {
695                if (n.nodeType == aWYM_NODE.TEXT)
696                {
697                    if (this.endOffset != this.endNode.data.length)
698                        return false;
699                }
700                else
701                {
702                    var lastChild = n.parentNode.lastChild;
703                    // node isn't last child => cursor can't be at the end
704                    // (is this true?) in gecko there the last child could be a
705                    //     phantom node
706
707                    // sometimes also whitespacenodes which aren't phatom nodes
708                    // get stripped, but this is ok, as this is a wysiwym editor
709                    if ((lastChild != n) ||
710                            ($(lastChild).isPhantomNode()
711                            && lastChild.previousSibling != n))
712                    {
713                        return false;
714                    }
715                }
716            }
717        }
718
719        if (this.endOffset == this.endNode.length)
720            return true;
721        else
722            return false;
723    }
724};
725
726WymSelection.prototype.myTest = function() {
727
728    return("myTest from WymSelection");
729};
730
731
732/********** HELPERS **********/
733
734// Returns true if it is a text node with whitespaces only
735$.fn.isPhantomNode = function()
736{
737    if (this[0].nodeType == 3)
738        return !(/[^\t\n\r ]/.test(this[0].data));
739
740    return false;
741}
742function isPhantomNode(n)
743{
744    if (n.nodeType == 3)
745        return !(/[^\t\n\r ]/.test(n.data));
746
747    return false;
748}
749
750
751
752// Returns the Parents or the node itself
753// jqexpr = a jQuery expression
754$.fn.parentsOrSelf = function(jqexpr)
755{
756    var n = this;
757
758    if (n[0].nodeType == 3)
759        n = n.parents().lt(1);
760
761//    if (n.is(jqexpr)) // XXX should work, but doesn't (probably a jQuery bug)
762    if (n.filter(jqexpr).size() == 1)
763        return n;
764    else
765        return n.parents(jqexpr).lt(1);
766}
767
768
769// from http://forum.de.selfhtml.org/archiv/2004/3/t76079/#m438193 (2007-02-06)
770Array.prototype.contains = function (elem) {
771//  var i;
772  for (var i = 0; i < this.length; i++) {
773    if (this[i] === elem) {
774      return true;
775    }
776  }
777  return false;
778};
779
Note: See TracBrowser for help on using the repository browser.