Subversion Repositories wimsdev

Rev

Rev 1442 | Rev 8231 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1442 bpr 1
 
2
//Call speck Init to convert all text areas with "iEdit" to speck edit windows.
3
//Alternatively:
4
//var editor = new speckEditor();
5
//editor.init(textareaelement, optional callback on loaded)
6
function speckInit(callback) {    
7
    var tas = document.getElementsByTagName("textarea");
8
    var found = false;
9
    for(i=0;i<tas.length;i++) {
10
                var ta = tas[i];
11
                if (ta.className == "iEdit") {
12
                    var editor = new speckEditor();
13
                    editor.init(ta, callback);
14
                    found = true;
15
                }
16
    }  
17
    if (!found) {
18
        if (callback)
19
            callback.call();
20
    }
21
        return true;
22
};
23
 
24
//Be sure to call speckClose if you are hiding or removing a speck edit 
25
//from the screen and plan to open another before refreshing the page. 
26
function speckClose() {
27
    for(var i in iEdit)
28
    {
29
        iEdit[i].iframe.contentWindow.document.designMode = "off";
30
    }
31
};
32
 
33
//Collection of all text windows: accessible by:
34
//iEdit[TextAreaId].html();
35
var iEdit = [];
36
 
37
//One instance of a speck edit window
38
function speckEditor() {};
39
speckEditor.prototype = {
40
    //Edit this path to point to the location of the stylesheet you would like applied to the edit window.
41
    editStylePath: window.location.protocol + "//" + window.location.host + "/speck/editstyle.css",
42
 
43
    //Remove or add features from this list. a=action, t=hover information text
44
    features: [
45
        "html", "Toggle HTML View",
46
        "bold", "Bold",
47
        "italic", "Italic",
48
        "underline", "Underline",
49
        "justifyleft", "Align Left",
50
        "justifycenter", "Align Center",
51
        "justifyright", "Align Right",
52
        "insertorderedlist", "Insert Ordered List",
53
        "insertunorderedlist", "Insert Unordered List",
54
        "link", "Create Link",
55
        "unlink", "Remove Link",
56
        "formatblock", "Choose Format",
57
        "fontname", "Choose Font Style",
58
        "fontsize", "Choose Font Size",
59
        "forecolor", "Choose Font Color",
60
        "removeformat", "Remove Formatting"
61
    ],
62
 
63
    //Colors available in the color bar
64
    colors: [ "006600", "666600", "ccff66", "669933", "ffffff", "eeeeee", "999999", "333333", "000000", "666699", "000033", "000066", "ccffff", "ffff33", "ffcc00", "ffff99", "990000", "330033", "cc3399", "ffcccc", "ffffcc", "996633", "663300", "330000"],    
65
 
66
    //Block Options for formatblock
67
    blockOptions: [
68
        "<h1>", "Heading 1",
69
        "<h2>", "Heading 2",
70
        "<h3>", "Heading 3",
71
        "<h4>", "Heading 4",
72
        "<h5>", "Heading 5",
73
        "<h6>", "Heading 6",
74
        "<p>", "Normal",
75
        "<blockquote>", "Block Quote"
76
    ],
77
 
78
    //Font options for fontname
79
    fontOptions: [
80
        "Verdana", "Verdana",
81
        "Arial", "Arial",
82
        "Georgia", "Georgia",
83
        "Trebuchet", "Trebuchet",
84
        "Courier New", "Courier",
85
        "Times New Roman", "Times"
86
    ],
87
 
88
    //Font size options for fontsize
89
    fontSizes: [
90
        "1", "Small",
91
        "3", "Medium",
92
        "5", "Large",
93
        "7", "Largest"
94
    ],
95
 
96
    //Initialize the edit window
97
    init: function(ta, callback) {
98
 
99
            this.ta = ta;
100
        this.id = ta.id;        
101
        iEdit[this.id] = this;
102
 
103
            if (callback)
104
                this.loaded = callback;
105
 
106
        //Get the current width
107
        this.width = this.ta.offsetWidth;
108
        if (this.width == 0)
109
                this.width = parseInt(this.ta.style.width);
110
 
111
        //Get the current height
112
        this.height = this.ta.offsetHeight;
113
        if (this.height == 0)
114
                this.height = parseInt(this.ta.style.height);
115
 
116
        this.ta.style.display = "none";
117
 
118
            this.container = this.$new("div");
119
 
120
            this.tb = this.$new("div");
121
            this.tb.className = "speckToolbar";
122
        this.tb.style.width = this.width + "px";
123
        this.container.appendChild(this.tb);
124
 
125
        //Add the features 
126
            for(var i=0;i<this.features.length;i+=2) {
127
                this.addFeature(this.features[i], this.features[i+1]);
128
            }  
129
 
130
        this.ta.style.height = (this.height-32) + "px";    
131
        this.ta.parentNode.replaceChild(this.container, this.ta);
132
            this.container.appendChild(this.ta);    
133
 
134
        this.initEdit();
135
 
136
    },
137
    initEdit: function(content) {    
138
        if (this.iframe)
139
            this.container.removeChild(this.iframe);
140
 
141
        //Create the iframe
142
        this.iframe = this.$new("iframe");
143
        this.iframe.className = "speckFrame";
144
        this.iframe.frameBorder = "0";
145
        this.iframe.style.width = (this.width-2) + "px";
146
        this.iframe.style.height = (this.height-32) + "px";
147
        this.container.appendChild(this.iframe);
148
 
149
        //Save style properties with property called savestyle
150
        content = this.ta.value.replace(/(style|STYLE|Style)=('|").+?('|")/g, function(match){ return match + " save" + match; });                          
151
 
152
        //Write out current content to the iframe window, include edit mode stylesheet
153
            this.iframe.contentWindow.document.open();
154
            //this.iframe.contentWindow.document.write("<html><head><link id='ThemeStyle' href='" + this.editStylePath + "' type='text/css' rel='stylesheet' /></head><body style='background:#fff url();color:#000;'>" + content + "</body></html>");
155
            this.iframe.contentWindow.document.write("<html><head></head><body style='background:#fff url();color:#000;'>" + content + "</body></html>");
156
            this.iframe.contentWindow.document.close();
157
 
158
        this.enterDesignMode();
159
 
160
            return true;
161
    },
162
    enterDesignMode: function() {
163
 
164
        //Firefox needs a little time for this.
165
        if (!this.iframe.contentWindow.document.body) {
166
            var self = this;
167
                setTimeout(function() { self.enterDesignMode(); }, 1);
168
                return;
169
            }
170
 
171
            //Turn on design mode
172
            this.iframe.contentWindow.document.designMode = "on";
173
 
174
            this.wysiwyg = true;
175
 
176
        //call loaded event
177
        if (this.loaded)
178
            this.loaded.call();
179
    },
180
    addFeature: function(action, text) {
181
        switch (action) {
182
            case "formatblock":
183
                    this.hBar = this.$new("div");
184
                    this.hBar.id = this.id + "Hbar";      
185
                this.addBar(this.hBar, "formatblock", this.blockOptions, text);
186
                this.container.appendChild(this.hBar);
187
                break;
188
            case "fontname":
189
                    this.fBar = this.$new("div");
190
                    this.fBar.id = this.id + "Fbar";        
191
                this.addBar(this.fBar, "fontname", this.fontOptions, text);
192
                this.container.appendChild(this.fBar);
193
                break;
194
            case "fontsize":
195
                    this.sBar = this.$new("div");
196
                    this.sBar.id = this.id + "Sbar";        
197
                this.addBar(this.sBar, "fontsize", this.fontSizes, text);
198
                this.container.appendChild(this.sBar);
199
                break;
200
            case "forecolor":
201
                this.addColor("forecolor", this.colors, text);
202
                this.container.appendChild(this.cBar);                    
203
                break;            
204
            case "link":
205
                this.addLinkBar("link", text);  
206
                this.container.appendChild(this.lBar);                    
207
                break;
208
            default:
209
                this.tb.appendChild(this.getButton(action, text));      
210
                break;
211
        }
212
    },
213
    getButton: function(action, text) {
214
        var self = this;
215
 
216
        var button = this.$new("input");
217
        button.type = "button";
218
        button.id = action + "Button";
219
        button.title = text;
220
        button.className = "speckButton";
221
        button.action = action;
222
        button.onclick = function() { self.execCommand(this); };
223
        return button;
224
    },
225
    addColor: function(action, options, text) {
226
 
227
            var button = this.getButton(action, text); // this.$new("input");
228
        var self = this;
229
        button.onclick = function() { self.showSelect(this); };
230
 
231
            var bar = this.$new("div");
232
            bar.id = action + "Select";
233
            bar.className = "speckColorBar";
234
        bar.style.width = (this.width - 2) + "px"; //2 is border width
235
        bar.style.display = "none";    
236
            for (var i=0;i<options.length;i++)
237
            {
238
                    var option = this.$new("input");
239
                    option.val = options[i];
240
                    option.type = "button";
241
                    option.style.backgroundColor = "#" + option.val;
242
                    option.action = action;
243
                    option.className = "speckColor";
244
            option.onclick = function() { self.execCommand(this); };
245
 
246
                    bar.appendChild(option);
247
            }
248
            this.cBar = bar;
249
        button.selectMenuId = bar.id;
250
        this.tb.appendChild(button);
251
            return true;
252
    },
253
    addLinkBar: function(action, text) {
254
            var button = this.getButton(action, text); //this.$new("input");
255
 
256
        var self = this;
257
        button.onclick = function() { self.showSelect(this); };
258
 
259
            var bar = this.$new("div");
260
            bar.id = action + "linkbar";
261
            bar.className = "speckLinkbar";
262
        bar.style.width = (this.width-2) + "px"; //2 is border width
263
        bar.style.display = "none";    
264
 
265
            this.linkUrl = this.$new("input");
266
            this.linkUrl.id = this.id + "link";
267
        this.linkUrl.style.width = (this.width - 60) + "px";
268
        this.linkUrl.value = "http://";
269
 
270
        var link = this.$new("input");
271
        link.type = "button";
272
        link.id = "linkingButton";
273
        link.value = "link";    
274
        link.className = "linkbarButton";
275
        link.action = "createlink";
276
        link.onclick = function() { self.execCommand(this) };
277
        bar.appendChild(this.linkUrl);
278
        bar.appendChild(link);
279
 
280
            this.lBar = bar;
281
        button.selectMenuId = bar.id;
282
        this.tb.appendChild(button);
283
            return true;
284
    },
285
    addBar: function(bar, action, options, text) {
286
            var button = this.getButton(action, text); //this.$new("input");
287
        var self = this;
288
        button.onclick = function() { self.showSelect(this); };
289
 
290
            bar.className = "speckBar";
291
        bar.style.width = (this.width-2) + "px";
292
        bar.style.display = "none";
293
 
294
            for (var i=0;i<options.length;i+=2)
295
            {
296
                    var option = this.$new("input");
297
                    option.val = options[i];
298
                    option.value = options[i + 1];
299
                    option.type = "button";
300
                    option.action = action;
301
                    option.editor = this.id;
302
                    option.className = "speckOption";
303
            option.onclick = function() { self.execCommand(this) };
304
 
305
                    bar.appendChild(option);
306
            }
307
        button.selectMenuId = bar.id;
308
        this.tb.appendChild(button);
309
            return true;
310
    },
311
    hideSelects: function() {
312
        var buttons = this.tb.childNodes;
313
        for(var i=0;i<buttons.length;i++) {
314
            var button = buttons[i];
315
            if (button.selectMenuId) {
316
                var selectMenu = document.getElementById(button.selectMenuId);
317
                selectMenu.style.display = "none";
318
            }
319
        }
320
        this.lBar.style.display = "none";
321
        this.iframe.style.height = (this.height-32) + "px";
322
        this.ta.style.height = (this.height-32) + "px";
323
    },
324
    addLinkbar: function() {
325
            this.lBar = this.$new("div");
326
            this.lBar.id = this.id + "linkbar";
327
            this.lBar.className = "speckLinkbar";
328
        this.lBar.style.width = this.width-10 + "px";
329
        this.lBar.style.display = "none";
330
 
331
            this.linkUrl = this.$new("input");
332
            this.linkUrl.id = this.id + "link";
333
        this.linkUrl.style.width = (this.width - 60) + "px";
334
        this.linkUrl.value = "http://";
335
 
336
        var link = this.$new("input");
337
        link.type = "button";
338
        link.value = "link";    
339
        link.className = "linkbarButton";
340
        link.action = "createlink";
341
        var self = this;
342
        link.onclick = function() { self.execCommand(); };
343
        this.lBar.appendChild(this.linkUrl);
344
        this.lBar.appendChild(link);
345
    },
346
    toggleLinkbar: function() {
347
        if (this.lBar.style.display == "none") {
348
            this.lBar.style.display = "block";
349
            this.iframe.style.height = (this.height-(64)) + "px";
350
        } else {
351
            this.lBar.style.display = "none";
352
            this.iframe.style.height = (this.height-32) + "px";        
353
        }
354
    },
355
    setFocus: function() {
356
            if (this.wysiwyg == true)
357
                    this.iframe.contentWindow.focus();
358
            else
359
                    this.ta.focus();
360
    },
361
    toggleMode: function() {
362
        this.hideSelects();
363
        if (this.wysiwyg) {
364
            this.html(); //update html
365
            this.ta.style.display = "block";
366
            this.iframe.style.display = "none";
367
            this.wysiwyg = false;
368
        }
369
        else {
370
            this.ta.style.display = "none";
371
            this.initEdit(this.ta.value);
372
            this.wysiwyg = true;
373
        }
374
    },
375
    html: function() {
376
        this.ta.value = this.innerXML(this.iframe.contentWindow.document.body.cloneNode(true));
377
        return this.ta.value;
378
    },
379
    showSelect: function(selector) {
380
 
381
        var selectMenu = document.getElementById(selector.selectMenuId);
382
 
383
        //if select bar is already open, close it.       
384
        if (selectMenu.style.display == "block") {
385
            this.hideSelects();
386
            return;
387
        }
388
 
389
        this.hideSelects();    
390
        this.iframe.style.height = (this.height-64) + "px";
391
        this.ta.style.height = (this.height-64) + "px";
392
        selectMenu.style.display = "block";
393
    },
394
    execCommand: function(button) {
395
 
396
        var doc = this.iframe.contentWindow.document;
397
 
398
            switch (button.action) {
399
                    case "createlink":
400
                        if (this.linkUrl.value != "") {
401
                    doc.execCommand(button.action, false, this.linkUrl.value);
402
                    this.toggleLinkbar();                
403
                }
404
                break;
405
                    case "formatblock":
406
                    case "fontsize":
407
                    case "forecolor":
408
                    case "fontname":
409
                        doc.execCommand(button.action, false, button.val);     
410
                break;
411
            case "link":
412
                this.toggleLinkbar();
413
                break;
414
            case "html":
415
                this.toggleMode();
416
                break;            
417
                    default:
418
                        doc.execCommand(button.action, false, null);
419
                break;
420
        }
421
 
422
        if (button.parentNode.className == "speckSelect") {
423
            button.parentNode.style.display = "none";
424
            return false;
425
        }    
426
        this.setFocus();    
427
    },
428
    innerXML: function(oNode) {
429
        //Returns the innerXML of an HTML DOM node
430
        var s = "";
431
 
432
        var nodes = oNode.childNodes;
433
        for(var i=0; i < nodes.length; i++)
434
        {
435
            var node = nodes[i];
436
            var nodeType = node.nodeType;
437
            if (nodeType == 1 || nodeType == 9 || nodeType == 11) //Element Node, Document Node, Document Fragment Node
438
                s += this.xml(node, "");
439
            else
440
                s += node.data;
441
        }
442
        return s;
443
    },
444
    xml: function(oNode, indent) {
445
        //Returns outerXML of the node.
446
 
447
        var s = "";
448
        var nodes = oNode.childNodes;
449
        var tag = oNode.nodeName.toLowerCase();
450
 
451
        if (nodes.length == 0 && (tag == "input" || tag == "img" || tag == "hr" || tag == "br" || tag == "feature" ))
452
        {
453
            if (!oNode.getAttribute("_moz_editor_bogus_node")) {
7298 bpr 454
              s += indent + "<" + tag + this.getAttributes(oNode) + "/>\n";
455
              //s += indent + "<" + tag + this.getAttributes(oNode) + ">\n";
1442 bpr 456
            }
457
        }
458
        else
459
        {  
460
            if (this.isEmptyNode(oNode))
461
                return s;
462
 
463
            s += indent + "<" + tag + this.getAttributes(oNode) + ">";
464
            for(var i=0; i < nodes.length; i++)
465
            {
466
                var node = nodes[i];
467
                var nodeType = node.nodeType;
468
 
469
                if (nodeType == 1 || nodeType == 9 || nodeType == 11) //Element Node, Document Node, Document Fragment Node
470
                    s += this.xml(node, indent + "");
471
                else
472
                    s += indent + node.data;
473
            }
474
            s += indent + "</" + tag + ">";
475
        }
476
        return s;
477
    },    
478
    getAttributes: function(oNode) {
479
        var s = "";
480
        var atts = oNode.attributes;
481
        var style = "";
482
        for(var i=0; i < atts.length; i++) {
483
            var att = atts[i];
484
 
485
            var name = att.nodeName.toLowerCase();
486
            var val = att.nodeValue;
487
 
488
            if (this.validAttribute(att, oNode.nodeName)) {
489
                if (name == "savestyle" || name == "style") {
490
                    if (style.indexOf(val)<0) //not already there
491
                        style += val;
492
                } else {
493
                    s += " " + name + "=\"" + val.replace("about:/", "/") + "\" ";                
494
                }
495
            }
496
        }
497
        if (style.length>0)
498
            s += " style=\"" + style + "\" ";
499
 
500
        return s;
501
    },
502
    validAttribute: function(att, tag) {
503
        //eliminate unwanted or unsupported attributes
504
        var name = att.nodeName.toLowerCase();
505
        var val = att.nodeValue;
506
 
507
        if (name == "start")
508
            return false;    
509
 
510
        if (val == null || val == "" || val == "inherit" || val == "_moz")
511
            return false;
512
 
513
        if (name == "colspan" || name == "rowspan")
514
            if (val == "1") { return false; }
515
 
516
        if (tag == "INPUT")
517
            if (name == "height" || name == "maxlength" || name == "loop")
518
                return false;
519
 
520
        if (tag == "IMG")
521
            if (name == "start" || name == "loop")
522
                return false;
523
 
524
        return true;
525
    },
526
    isEmptyNode: function(oNode) {
527
        var nodes = oNode.childNodes;
528
        for(var i=0; i < nodes.length; i++) {
529
            var node = nodes[i];
530
            var nodeType = node.nodeType;
531
 
532
            if (nodeType == 1 || nodeType == 9 || nodeType == 11) {
533
                return false;
534
            } else {      
535
                if  (node.data.replace(/^\s+|\s+$/g,"").replace(/^\n+|\n+$/g,"") != "")
536
                    return false;
537
            }
538
        }
539
        return true;
540
    },
541
    $new: function(tag) {
542
        return document.createElement(tag);
543
    }          
544
}
545