Subversion Repositories wimsdev

Rev

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

  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")) {
  454.              //s += indent + "<" + tag + this.getAttributes(oNode) + "/>\n";
  455.               s += indent + "<" + tag + this.getAttributes(oNode) + ">\n";
  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.  
  546.