Subversion Repositories wimsdev

Rev

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