You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by jk...@apache.org on 2006/06/10 15:59:38 UTC
svn commit: r413300 [11/16] - in /tapestry/tapestry4/trunk/framework/src/js:
dojo/ dojo/src/ dojo/src/animation/ dojo/src/collections/ dojo/src/compat/
dojo/src/crypto/ dojo/src/data/ dojo/src/data/format/
dojo/src/data/provider/ dojo/src/debug/ dojo/s...
Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/RichText.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/RichText.js?rev=413300&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/RichText.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/RichText.js Sat Jun 10 06:59:28 2006
@@ -0,0 +1,1451 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+ /* -*- tab-width: 4 -*- */
+dojo.provide("dojo.widget.RichText");
+dojo.provide("dojo.widget.html.RichText");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html");
+dojo.require("dojo.event.*");
+dojo.require("dojo.style");
+dojo.require("dojo.string");
+
+// used to save content
+try {
+ document.write('<textarea id="dojo.widget.RichText.savedContent" ' +
+ 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>');
+}catch(e){ }
+
+dojo.widget.defineWidget(
+ "dojo.widget.html.RichText",
+ dojo.widget.HtmlWidget,
+ {
+ /** whether to inherit the parent's width or simply use 100% */
+ inheritWidth: false,
+ focusOnLoad: true,
+
+ /**
+ * If a save name is specified the content is saved and restored if the
+ * editor is not properly closed after editing has started.
+ */
+ saveName: "",
+ _content: "",
+
+ /* set height to fix the editor at a specific height, with scrolling */
+ height: null,
+
+ /** The minimum height that the editor should have */
+ minHeight: "1em",
+
+ isClosed: true,
+ isLoaded: false,
+
+ /** whether to use the active-x object in IE */
+ useActiveX: false,
+
+ /* whether to use relative URLs for images - if this is enabled
+ images will be given absolute URLs when inside the editor but
+ will be changed to use relative URLs (to the current page) on save
+ */
+ relativeImageUrls: false,
+
+ _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
+
+ // contentFilters: [],
+
+ /*
+ defaultContentCleaner: function(content){
+ if(!dojo.render.html.ie){
+ return content;
+ }
+
+ content = content.replace(/\x20/g, " ");
+ // alert(content);
+ return content;
+ },
+ */
+
+ /* Init
+ *******/
+
+ fillInTemplate: function(){
+ this.open();
+
+ // add the formatting functions
+ var funcs = ["queryCommandEnabled", "queryCommandState",
+ "queryCommandValue", "execCommand"];
+ for(var i = 0; i < funcs.length; i++){
+ dojo.event.connect("around", this, funcs[i], this, "_normalizeCommand");
+ }
+
+ // backwards compatibility, needs to be removed
+ dojo.event.connect(this, "onKeyPressed", this, "afterKeyPress");
+ dojo.event.connect(this, "onKeyPress", this, "keyPress");
+ dojo.event.connect(this, "onKeyDown", this, "keyDown");
+ dojo.event.connect(this, "onKeyUp", this, "keyUp");
+
+ // add default some key handlers
+ var ctrl = this.KEY_CTRL;
+ var exec = function (cmd, arg) {
+ return arguments.length == 1 ? function () { this.execCommand(cmd); } :
+ function () { this.execCommand(cmd, arg); }
+ }
+
+ this.addKeyHandler("b", ctrl, exec("bold"));
+ this.addKeyHandler("i", ctrl, exec("italic"));
+ this.addKeyHandler("u", ctrl, exec("underline"));
+ this.addKeyHandler("a", ctrl, exec("selectall"));
+ //this.addKeyHandler("k", ctrl, exec("createlink", ""));
+ //this.addKeyHandler("K", ctrl, exec("unlink"));
+ this.addKeyHandler("s", ctrl, function () { this.save(true); });
+
+ this.addKeyHandler("1", ctrl, exec("formatblock", "h1"));
+ this.addKeyHandler("2", ctrl, exec("formatblock", "h2"));
+ this.addKeyHandler("3", ctrl, exec("formatblock", "h3"));
+ this.addKeyHandler("4", ctrl, exec("formatblock", "h4"));
+
+ this.addKeyHandler("\\", ctrl, exec("insertunorderedlist"));
+ if(!dojo.render.html.ie){
+ this.addKeyHandler("Z", ctrl, exec("redo"));
+ }
+ },
+
+
+ events: ["onBlur", "onFocus", "onKeyPress", "onKeyDown", "onKeyUp", "onClick"],
+
+ /**
+ * Transforms the node referenced in this.domNode into a rich text editing
+ * node. This can result in the creation and replacement with an <iframe> if
+ * designMode is used, an <object> and active-x component if inside of IE or
+ * a reguler element if contentEditable is available.
+ */
+ open: function (element) {
+ dojo.event.topic.publish("dojo.widget.RichText::open", this);
+
+ if (!this.isClosed) { this.close(); }
+ this._content = "";
+ if((arguments.length == 1)&&(element["nodeName"])){ this.domNode = element; } // else unchanged
+
+ if( (this.domNode["nodeName"])&&
+ (this.domNode.nodeName.toLowerCase() == "textarea")){
+ this.textarea = this.domNode;
+ var html = dojo.string.trim(this.textarea.value);
+ if(html == ""){ html = " "; }
+ this.domNode = document.createElement("div");
+ with(this.textarea.style){
+ display = "block";
+ position = "absolute";
+ width = "1px";
+ height = "1px";
+ border = margin = padding = "0px";
+ visiblity = "hidden";
+ if(dojo.render.html.ie){
+ overflow = "hidden";
+ }
+ }
+ dojo.dom.insertBefore(this.domNode, this.textarea);
+ this.domNode.innerHTML = html;
+
+ if(this.textarea.form){
+ dojo.event.connect(this.textarea.form, "onsubmit",
+ // FIXME: should we be calling close() here instead?
+ dojo.lang.hitch(this, function(){
+ this.textarea.value = this.getEditorContent();
+ })
+ );
+ }
+
+ // dojo plucks our original domNode from the document so we need
+ // to go back and put ourselves back in
+ var editor = this;
+ dojo.event.connect(this, "postCreate", function (){
+ dojo.dom.insertAfter(editor.textarea, editor.domNode);
+ });
+ }else{
+ var html = dojo.string.trim(this.domNode.innerHTML);
+ if(html == ""){ html = " "; }
+ }
+
+ this._oldHeight = dojo.style.getContentHeight(this.domNode);
+ this._oldWidth = dojo.style.getContentWidth(this.domNode);
+
+ this._firstChildContributingMargin = this._getContributingMargin(this.domNode, "top");
+ this._lastChildContributingMargin = this._getContributingMargin(this.domNode, "bottom");
+
+ this.savedContent = document.createElement("div");
+ while (this.domNode.hasChildNodes()) {
+ this.savedContent.appendChild(this.domNode.firstChild);
+ }
+
+ // If we're a list item we have to put in a blank line to force the
+ // bullet to nicely align at the top of text
+ if( (this.domNode["nodeName"])&&
+ (this.domNode.nodeName == "LI")){
+ this.domNode.innerHTML = " <br>";
+ }
+
+ if(this.saveName != ""){
+ var saveTextarea = document.getElementById("dojo.widget.RichText.savedContent");
+ if (saveTextarea.value != "") {
+ var datas = saveTextarea.value.split(this._SEPARATOR);
+ for (var i = 0; i < datas.length; i++) {
+ var data = datas[i].split(":");
+ if (data[0] == this.saveName) {
+ html = data[1];
+ datas.splice(i, 1);
+ break;
+ }
+ }
+ }
+ dojo.event.connect("before", window, "onunload", this, "_saveContent");
+ // dojo.event.connect(window, "onunload", this, "_saveContent");
+ }
+
+ // Safari's selections go all out of whack if we do it inline,
+ // so for now IE is our only hero
+ //if (typeof document.body.contentEditable != "undefined") {
+ if (this.useActiveX && dojo.render.html.ie) { // active-x
+ this._drawObject(html);
+ // dojo.debug(this.object.document);
+ } else if (dojo.render.html.ie) { // contentEditable, easy
+ this.editNode = document.createElement("div");
+ with (this.editNode) {
+ innerHTML = html;
+ contentEditable = true;
+ style.height = this.height ? this.height : this.minHeight;
+ }
+
+ if(this.height){ this.editNode.style.overflowY="scroll"; }
+ // FIXME: setting contentEditable on switches this element to
+ // IE's hasLayout mode, triggering weird margin collapsing
+ // behavior. It's particularly bad if the element you're editing
+ // contains childnodes that don't have margin: defined in local
+ // css rules. It would be nice if it was possible to hack around
+ // this. Sadly _firstChildContributingMargin and
+ // _lastChildContributingMargin don't work on IE unless all
+ // elements have margins set in CSS :-(
+
+ this.domNode.appendChild(this.editNode);
+
+ dojo.lang.forEach(this.events, function(e){
+ dojo.event.connect(this.editNode, e.toLowerCase(), this, e);
+ }, this);
+
+ this.window = window;
+ this.document = document;
+
+ this.onLoad();
+ } else { // designMode in iframe
+ this._drawIframe(html);
+ }
+
+ // TODO: this is a guess at the default line-height, kinda works
+ if (this.domNode.nodeName == "LI") { this.domNode.lastChild.style.marginTop = "-1.2em"; }
+ dojo.html.addClass(this.domNode, "RichTextEditable");
+
+ this.isClosed = false;
+ },
+
+ _hasCollapseableMargin: function(element, side) {
+ // check if an element has padding or borders on the given side
+ // which would prevent it from collapsing margins
+ if (dojo.style.getPixelValue(element,
+ 'border-'+side+'-width',
+ false)) {
+ return false;
+ } else if (dojo.style.getPixelValue(element,
+ 'padding-'+side,
+ false)) {
+ return false;
+ } else {
+ return true;
+ }
+ },
+
+ _getContributingMargin: function(element, topOrBottom) {
+ // calculate how much margin this element and its first or last
+ // child are contributing to the total margin between this element
+ // and the adjacent node. CSS border collapsing makes this
+ // necessary.
+
+ if (topOrBottom == "top") {
+ var siblingAttr = "previousSibling";
+ var childSiblingAttr = "nextSibling";
+ var childAttr = "firstChild";
+ var marginProp = "margin-top";
+ var siblingMarginProp = "margin-bottom";
+ } else {
+ var siblingAttr = "nextSibling";
+ var childSiblingAttr = "previousSibling";
+ var childAttr = "lastChild";
+ var marginProp = "margin-bottom";
+ var siblingMarginProp = "margin-top";
+ }
+
+ var elementMargin = dojo.style.getPixelValue(element, marginProp, false);
+
+ function isSignificantNode(element) {
+ // see if an node is significant in the current context
+ // for calulating margins
+ return !(element.nodeType==3 && dojo.string.isBlank(element.data))
+ && dojo.style.getStyle(element, "display") != "none"
+ && !dojo.style.isPositionAbsolute(element);
+ }
+
+ // walk throuh first/last children to find total collapsed margin size
+ var childMargin = 0;
+ var child = element[childAttr];
+ while (child) {
+ // skip over insignificant elements (whitespace, etc)
+ while ((!isSignificantNode(child)) && child[childSiblingAttr]) {
+ child = child[childSiblingAttr];
+ }
+
+ childMargin = Math.max(childMargin, dojo.style.getPixelValue(child, marginProp, false));
+ // stop if we hit a bordered/padded element
+ if (!this._hasCollapseableMargin(child, topOrBottom)) break;
+ child = child[childAttr];
+ }
+
+ // if this element has a border, return full child margin immediately
+ // as there won't be any margin collapsing
+ if (!this._hasCollapseableMargin(element, topOrBottom)){ return parseInt(childMargin); }
+
+ // find margin supplied by nearest sibling
+ var contextMargin = 0;
+ var sibling = element[siblingAttr];
+ while (sibling) {
+ if (isSignificantNode(sibling)) {
+ contextMargin = dojo.style.getPixelValue(sibling,
+ siblingMarginProp,
+ false);
+ break;
+ }
+ sibling = sibling[siblingAttr];
+ }
+ if (!sibling) { // no sibling, look at parent's margin instead
+ contextMargin = dojo.style.getPixelValue(element.parentNode,
+ marginProp, false);
+ }
+
+ if (childMargin > elementMargin) {
+ return parseInt(Math.max((childMargin-elementMargin)-contextMargin, 0));
+ } else {
+ return 0;
+ }
+
+ },
+
+ /** Draws an iFrame using the existing one if one exists.
+ Used by Mozilla, Safari, and Opera */
+ _drawIframe: function (html) {
+
+ // detect firefox < 1.5, which has some iframe loading issues
+ var oldMoz = Boolean(dojo.render.html.moz && (
+ typeof window.XML == 'undefined'))
+
+ if (!this.iframe) {
+ var currentDomain = (new dojo.uri.Uri(document.location)).host;
+ this.iframe = document.createElement("iframe");
+ with (this.iframe) {
+ scrolling = this.height ? "auto" : "no";
+ style.border = "none";
+ style.lineHeight = "0"; // squash line height
+ style.verticalAlign = "bottom";
+ }
+ }
+ // opera likes this to be outside the with block
+ this.iframe.src = dojo.uri.dojoUri("src/widget/templates/richtextframe.html") + "#" + ((document.domain != currentDomain) ? document.domain : "");
+ this.iframe.width = this.inheritWidth ? this._oldWidth : "100%";
+ if (this.height) {
+ this.iframe.style.height = this.height;
+ } else {
+ var height = this._oldHeight;
+ if (this._hasCollapseableMargin(this.domNode, 'top')) {
+ height += this._firstChildContributingMargin;
+ }
+ if (this._hasCollapseableMargin(this.domNode, 'bottom')) {
+ height += this._lastChildContributingMargin;
+ }
+ this.iframe.height = height;
+ }
+
+ var tmpContent = document.createElement('div');
+ tmpContent.innerHTML = html;
+
+ // make relative image urls absolute
+ if (this.relativeImageUrls) {
+ var imgs = tmpContent.getElementsByTagName('img');
+ for (var i=0; i<imgs.length; i++) {
+ imgs[i].src = (new dojo.uri.Uri(window.location, imgs[i].src)).toString();
+ }
+ html = tmpContent.innerHTML;
+ }
+
+ // fix margins on tmpContent
+ var firstChild = dojo.dom.firstElement(tmpContent);
+ var lastChild = dojo.dom.lastElement(tmpContent);
+ if(firstChild){
+ firstChild.style.marginTop = this._firstChildContributingMargin+"px";
+ }
+ if(lastChild){
+ lastChild.style.marginBottom = this._lastChildContributingMargin+"px";
+ }
+
+ // show existing content behind iframe for now
+ tmpContent.style.position = "absolute";
+ this.domNode.appendChild(tmpContent);
+ this.domNode.appendChild(this.iframe);
+
+ var _iframeInitialized = false;
+
+ // now we wait for onload. Janky hack!
+ var ifrFunc = dojo.lang.hitch(this, function(){
+ if(!_iframeInitialized){
+ _iframeInitialized = true;
+ }else{ return; }
+ if(!this.editNode){
+ if(this.iframe.contentWindow){
+ this.window = this.iframe.contentWindow;
+ }else{
+ // for opera
+ this.window = this.iframe.contentDocument.window;
+ }
+ if(dojo.render.html.moz){
+ this.document = this.iframe.contentWindow.document
+ }else{
+ this.document = this.iframe.contentDocument;
+ }
+
+ // curry the getStyle function
+ var getStyle = (function (domNode) { return function (style) {
+ return dojo.style.getStyle(domNode, style);
+ }; })(this.domNode);
+
+ var font =
+ getStyle('font-weight') + " " +
+ getStyle('font-size') + " " +
+ getStyle('font-family');
+
+ // line height is tricky - applying a units value will mess things up.
+ // if we can't get a non-units value, bail out.
+ var lineHeight = "1.0";
+ var lineHeightStyle = dojo.style.getUnitValue(this.domNode, 'line-height');
+ if (lineHeightStyle.value && lineHeightStyle.units=="") {
+ lineHeight = lineHeightStyle.value;
+ }
+
+ dojo.style.insertCssText(
+ ' body,html { background: transparent; padding: 0; margin: 0; }\n' +
+ // TODO: left positioning will case contents to disappear out of view
+ // if it gets too wide for the visible area
+ ' body { top: 0; left: 0; right: 0;' +
+ (this.height ? '' : ' position: fixed; ') +
+ ' font: ' + font + ';\n' +
+ ' min-height: ' + this.minHeight + '; \n' +
+ ' line-height: ' + lineHeight + '} \n' +
+ ' p { margin: 1em 0 !important; }\n' +
+ ' body > *:first-child { padding-top: 0 !important; margin-top: ' + this._firstChildContributingMargin + 'px !important; }\n' + // FIXME: test firstChild nodeType
+ ' body > *:last-child { padding-bottom: 0 !important; margin-bottom: ' + this._lastChildContributingMargin + 'px !important; }\n' +
+ ' li > ul:-moz-first-node, li > ol:-moz-first-node { padding-top: 1.2em; }\n' +
+ ' li { min-height: 1.2em; }\n' +
+ //' p,ul,li { padding-top: 0; padding-bottom: 0; margin-top:0; margin-bottom: 0; }\n' +
+ '', this.document);
+
+ tmpContent.parentNode.removeChild(tmpContent);
+ this.document.body.innerHTML = html;
+ if(oldMoz){
+ this.document.designMode = "on";
+ }
+ this.onLoad();
+ }else{
+ tmpContent.parentNode.removeChild(tmpContent);
+ this.editNode.innerHTML = html;
+ this.onDisplayChanged();
+ }
+ });
+
+ if(this.editNode){
+ ifrFunc(); // iframe already exists, just set content
+ }else if(dojo.render.html.moz){
+ // FIXME: if we put this on a delay, we get a height of 20px.
+ // Otherwise we get the correctly specified minHeight value.
+ this.iframe.onload = function(){
+ setTimeout(ifrFunc, 250);
+ }
+ }else{ // new mozillas, opera, safari
+ this.iframe.onload = ifrFunc;
+ }
+ },
+
+ /** Draws an active x object, used by IE */
+ _drawObject: function (html) {
+ this.object = document.createElement("object");
+
+ with (this.object) {
+ classid = "clsid:2D360201-FFF5-11D1-8D03-00A0C959BC0A";
+ width = this.inheritWidth ? this._oldWidth : "100%";
+ style.height = this.height ? this.height : (this._oldHeight+"px");
+ Scrollbars = this.height ? true : false;
+ Appearance = this._activeX.appearance.flat;
+ }
+ this.domNode.appendChild(this.object);
+
+ this.object.attachEvent("DocumentComplete", dojo.lang.hitch(this, "onLoad"));
+ this.object.attachEvent("DisplayChanged", dojo.lang.hitch(this, "_updateHeight"));
+ this.object.attachEvent("DisplayChanged", dojo.lang.hitch(this, "onDisplayChanged"));
+
+ dojo.lang.forEach(this.events, function(e){
+ this.object.attachEvent(e.toLowerCase(), dojo.lang.hitch(this, e));
+ }, this);
+
+ this.object.DocumentHTML = '<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' +
+ '<title></title>' +
+ '<style type="text/css">' +
+ ' body,html { padding: 0; margin: 0; }' + //font: ' + font + '; }' +
+ (this.height ? '' : ' body { overflow: hidden; }') +
+ //' #bodywrapper { }' +
+ '</style>' +
+ //'<base href="' + window.location + '">' +
+ '<body><div id="bodywrapper">' + html + '</div></body>';
+ },
+
+ /* Event handlers
+ *****************/
+
+ _isResized: function(){ return false; },
+
+ onLoad: function(e){
+ this.isLoaded = true;
+ if (this.object){
+ this.document = this.object.DOM;
+ this.window = this.document.parentWindow;
+ this.editNode = this.document.body.firstChild;
+ this.domNode.style.height = this.height ? this.height : this.minHeight;
+ this.connect(this, "onDisplayChanged", "_updateHeight");
+ }else if (this.iframe){
+ this.editNode = this.document.body;
+ this.connect(this, "onDisplayChanged", "_updateHeight");
+
+ try { // sanity check for Mozilla
+ this.document.execCommand("useCSS", false, true); // old moz call
+ this.document.execCommand("styleWithCSS", false, false); // new moz call
+ //this.document.execCommand("insertBrOnReturn", false, false); // new moz call
+ }catch(e2){ }
+
+ if (dojo.render.html.safari) {
+ /*
+ this.iframe.style.visiblity = "visible";
+ this.iframe.style.border = "1px solid black";
+ this.editNode.style.visiblity = "visible";
+ this.editNode.style.border = "1px solid black";
+ */
+ // this.onDisplayChanged();
+ this.connect(this.editNode, "onblur", "onBlur");
+ this.connect(this.editNode, "onfocus", "onFocus");
+
+ this.interval = setInterval(dojo.lang.hitch(this, "onDisplayChanged"), 750);
+ // dojo.raise("onload");
+ // dojo.debug(this.editNode.parentNode.parentNode.parentNode.nodeName);
+ } else if (dojo.render.html.mozilla || dojo.render.html.opera) {
+
+ // We need to unhook the blur event listener on close as we
+ // can encounter a garunteed crash in FF if another event is
+ // also fired
+ var doc = this.document;
+ var blurfp = dojo.event.browser.addListener(this.document, "blur", dojo.lang.hitch(this, "onBlur"));
+ var unBlur = { unBlur: function(e){
+ dojo.event.browser.removeListener(doc, "blur", blurfp);
+ } };
+ dojo.event.connect("before", this, "close", unBlur, "unBlur");
+ dojo.event.browser.addListener(this.document, "focus", dojo.lang.hitch(this, "onFocus"));
+
+ // safari can't handle key listeners, it kills the speed
+ var addListener = dojo.event.browser.addListener;
+ addListener(this.document, "keypress", dojo.lang.hitch(this, "onKeyPress"));
+ addListener(this.document, "keydown", dojo.lang.hitch(this, "onKeyDown"));
+ addListener(this.document, "keyup", dojo.lang.hitch(this, "onKeyUp"));
+ addListener(this.document, "click", dojo.lang.hitch(this, "onClick"));
+ }
+
+ // FIXME: when scrollbars appear/disappear this needs to be fired
+ }else if(dojo.render.html.ie){
+ // IE contentEditable
+ this.editNode.style.zoom = 1.0;
+ }
+
+ if(this.focusOnLoad){
+ this.focus();
+ }
+ this.onDisplayChanged(e);
+ },
+
+ /** Fired on keydown */
+ onKeyDown: function(e){
+ if((!e)&&(this.object)){
+ e = dojo.event.browser.fixEvent(this.window.event);
+ }
+ dojo.debug("onkeydown:", e.keyCode);
+ // we need this event at the moment to get the events from control keys
+ // such as the backspace. It might be possible to add this to Dojo, so that
+ // keyPress events can be emulated by the keyDown and keyUp detection.
+ if((dojo.render.html.ie)&&(e.keyCode == e.KEY_TAB)){
+ e.preventDefault();
+ e.stopPropagation();
+ // FIXME: this is a poor-man's indent/outdent. It would be
+ // better if it added 4 " " chars in an undoable way.
+ // Unfortuantly pasteHTML does not prove to be undoable
+ this.execCommand((e.shiftKey ? "outdent" : "indent"));
+ }else if(dojo.render.html.ie){
+ if((65 <= e.keyCode)&&(e.keyCode <= 90)){
+ e.charCode = e.keyCode;
+ this.onKeyPress(e);
+ }
+ // dojo.debug(e.ctrlKey);
+ // dojo.debug(e.keyCode);
+ // dojo.debug(e.charCode);
+ // this.onKeyPress(e);
+ }
+ },
+
+ /** Fired on keyup */
+ onKeyUp: function(e){
+ return;
+ },
+
+ KEY_CTRL: 1,
+
+ /** Fired on keypress. */
+ onKeyPress: function(e){
+ if((!e)&&(this.object)){
+ e = dojo.event.browser.fixEvent(this.window.event);
+ }
+ // handle the various key events
+
+ var character = e.charCode > 0 ? String.fromCharCode(e.charCode) : null;
+ var code = e.keyCode;
+
+ var modifiers = e.ctrlKey ? this.KEY_CTRL : 0;
+
+ if (this._keyHandlers[character]) {
+ dojo.debug("char:", character);
+ var handlers = this._keyHandlers[character], i = 0, handler;
+ while (handler = handlers[i++]) {
+ if (modifiers == handler.modifiers) {
+ handler.handler.call(this);
+ e.preventDefault();
+ break;
+ }
+ }
+ }
+
+ /*
+ // define some key combos
+ if (e.ctrlKey || e.metaKey) { // modifier pressed
+ switch (character) {
+ case "b": this.execCommand("bold"); break;
+ case "i": this.execCommand("italic"); break;
+ case "u": this.execCommand("underline"); break;
+ //case "a": this.execCommand("selectall"); break;
+ //case "k": this.execCommand("createlink", ""); break;
+ //case "K": this.execCommand("unlink"); break;
+ case "Z": this.execCommand("redo"); break;
+ case "s": this.close(true); break; // saves
+
+ case "1": this.execCommand("formatblock", "h1"); break;
+ case "2": this.execCommand("formatblock", "h2"); break;
+ case "3": this.execCommand("formatblock", "h3"); break;
+ case "4": this.execCommand("formatblock", "h4"); break;
+
+ case "\\": this.execCommand("insertunorderedlist"); break;
+
+ default: switch (code) {
+ case e.KEY_LEFT_ARROW:
+ case e.KEY_RIGHT_ARROW:
+ //break; // preventDefault stops the browser
+ // going through its history
+ default:
+ preventDefault = false; break; // didn't handle here
+ }
+ }
+ } else {
+ switch (code) {
+ case e.KEY_TAB:
+ // commenting out bcs it's crashing FF
+ // this.execCommand(e.shiftKey ? "unindent" : "indent");
+ // break;
+ default:
+ preventDefault = false; break; // didn't handle here
+ }
+ }
+
+ if (preventDefault) { e.preventDefault(); }
+ */
+
+ // function call after the character has been inserted
+ dojo.lang.setTimeout(this, this.onKeyPressed, 1, e);
+ },
+
+ addKeyHandler: function (key, modifiers, handler) {
+ if (!(this._keyHandlers[key] instanceof Array)) { this._keyHandlers[key] = []; }
+ this._keyHandlers[key].push({
+ modifiers: modifiers || 0,
+ handler: handler
+ });
+ },
+
+
+
+ /**
+ * Fired after a keypress event has occured and it's action taken. This
+ * is useful if action needs to be taken after text operations have
+ * finished
+ */
+ onKeyPressed: function (e) {
+ // Mozilla adds a single <p> with an embedded <br> when you hit enter once:
+ // <p><br>\n</p>
+ // when you hit enter again it adds another <br> inside your enter
+ // <p><br>\n<br>\n</p>
+ // and if you hit enter again it splits the <br>s over 2 <p>s
+ // <p><br>\n</p>\n<p><br>\n</p>
+ // now this assumes that <p>s have double the line-height of <br>s to work
+ // and so we need to remove the <p>s to ensure the position of the cursor
+ // changes from the users perspective when they hit enter, as the second two
+ // html snippets render the same when margins are set to 0.
+
+ // TODO: doesn't really work; is this really needed?
+ //if (dojo.render.html.moz) {
+ // for (var i = 0; i < this.document.getElementsByTagName("p").length; i++) {
+ // var p = this.document.getElementsByTagName("p")[i];
+ // if (p.innerHTML.match(/^<br>\s$/m)) {
+ // while (p.hasChildNodes()) { p.parentNode.insertBefore(p.firstChild, p); }
+ // p.parentNode.removeChild(p);
+ // }
+ // }
+ //}
+ this.onDisplayChanged(/*e*/); // can't pass in e
+ },
+
+ onClick: function(e){ this.onDisplayChanged(e); },
+ onBlur: function(e){ },
+ _initialFocus: true,
+ onFocus: function(e){
+ if( (dojo.render.html.mozilla)&&(this._initialFocus) ){
+ this._initialFocus = false;
+ if(dojo.string.trim(this.editNode.innerHTML) == " "){
+ this.execCommand("selectall");
+ this.window.getSelection().collapseToStart();
+ }
+ }
+ },
+
+ blur: function () {
+ if (this.iframe) { this.window.blur(); }
+ else if (this.editNode) { this.editNode.blur(); }
+ },
+
+ focus: function () {
+ if(this.iframe){
+ this.window.focus();
+ }else if(this.editNode){
+ this.editNode.focus();
+ }
+ },
+
+ /** this event will be fired everytime the display context changes and the
+ result needs to be reflected in the UI */
+ onDisplayChanged: function (e){ },
+
+
+ /* Formatting commands
+ **********************/
+
+ /** IE's Active X codes */
+ _activeX: {
+ command: {
+ bold: 5000,
+ italic: 5023,
+ underline: 5048,
+
+ justifycenter: 5024,
+ justifyleft: 5025,
+ justifyright: 5026,
+
+ cut: 5003,
+ copy: 5002,
+ paste: 5032,
+ "delete": 5004,
+
+ undo: 5049,
+ redo: 5033,
+
+ removeformat: 5034,
+ selectall: 5035,
+ unlink: 5050,
+
+ indent: 5018,
+ outdent: 5031,
+
+ insertorderedlist: 5030,
+ insertunorderedlist: 5051,
+
+ // table commands
+ inserttable: 5022,
+ insertcell: 5019,
+ insertcol: 5020,
+ insertrow: 5021,
+ deletecells: 5005,
+ deletecols: 5006,
+ deleterows: 5007,
+ mergecells: 5029,
+ splitcell: 5047,
+
+ // the command need mapping, they don't translate directly
+ // to the contentEditable commands
+ setblockformat: 5043,
+ getblockformat: 5011,
+ getblockformatnames: 5012,
+ setfontname: 5044,
+ getfontname: 5013,
+ setfontsize: 5045,
+ getfontsize: 5014,
+ setbackcolor: 5042,
+ getbackcolor: 5010,
+ setforecolor: 5046,
+ getforecolor: 5015,
+
+ findtext: 5008,
+ font: 5009,
+ hyperlink: 5016,
+ image: 5017,
+
+ lockelement: 5027,
+ makeabsolute: 5028,
+ sendbackward: 5036,
+ bringforward: 5037,
+ sendbelowtext: 5038,
+ bringabovetext: 5039,
+ sendtoback: 5040,
+ bringtofront: 5041,
+
+ properties: 5052
+ },
+
+ ui: {
+ "default": 0,
+ prompt: 1,
+ noprompt: 2
+ },
+
+ status: {
+ notsupported: 0,
+ disabled: 1,
+ enabled: 3,
+ latched: 7,
+ ninched: 11
+ },
+
+ appearance: {
+ flat: 0,
+ inset: 1
+ },
+
+ state: {
+ unchecked: 0,
+ checked: 1,
+ gray: 2
+ }
+ },
+
+ /**
+ * Used as the advice function by dojo.event.connect to map our
+ * normalized set of commands to those supported by the target
+ * browser
+ *
+ * @param arugments The arguments Array, containing at least one
+ * item, the command and an optional second item,
+ * an argument.
+ */
+ _normalizeCommand: function (joinObject){
+ var drh = dojo.render.html;
+
+ var command = joinObject.args[0].toLowerCase();
+ if(command == "formatblock"){
+ if(drh.safari){ command = "heading"; }
+ if(drh.ie){ joinObject.args[1] = "<"+joinObject.args[1]+">"; }
+ }
+ if (command == "hilitecolor" && !drh.mozilla) { command = "backcolor"; }
+ joinObject.args[0] = command;
+
+ if (joinObject.args.length > 1) { // a command was specified
+ var argument = joinObject.args[1];
+ if (command == "heading") { throw new Error("unimplemented"); }
+ joinObject.args[1] = argument;
+ }
+
+ return joinObject.proceed();
+ },
+
+ /**
+ * Tests whether a command is supported by the host. Clients SHOULD check
+ * whether a command is supported before attempting to use it, behaviour
+ * for unsupported commands is undefined.
+ *
+ * @param command The command to test for
+ * @return true if the command is supported, false otherwise
+ */
+ queryCommandAvailable: function (command) {
+ var ie = 1;
+ var mozilla = 1 << 1;
+ var safari = 1 << 2;
+ var opera = 1 << 3;
+ function isSupportedBy (browsers) {
+ return {
+ ie: Boolean(browsers & ie),
+ mozilla: Boolean(browsers & mozilla),
+ safari: Boolean(browsers & safari),
+ opera: Boolean(browsers & opera)
+ }
+ }
+
+ var supportedBy = null;
+
+ switch (command.toLowerCase()) {
+ case "bold": case "italic": case "underline":
+ case "subscript": case "superscript":
+ case "fontname": case "fontsize":
+ case "forecolor": case "hilitecolor":
+ case "justifycenter": case "justifyfull": case "justifyleft":
+ case "justifyright": case "delete": case "undo": case "redo":
+ supportedBy = isSupportedBy(mozilla | ie | safari | opera);
+ break;
+
+ case "createlink": case "unlink": case "removeformat":
+ case "inserthorizontalrule": case "insertimage":
+ case "insertorderedlist": case "insertunorderedlist":
+ case "indent": case "outdent": case "formatblock":
+ case "inserthtml":
+ supportedBy = isSupportedBy(mozilla | ie | opera);
+ break;
+
+ case "strikethrough":
+ supportedBy = isSupportedBy(mozilla | opera | (this.object ? 0 : ie));
+ break;
+
+ case "blockdirltr": case "blockdirrtl":
+ case "dirltr": case "dirrtl":
+ case "inlinedirltr": case "inlinedirrtl":
+ case "cut": case "copy": case "paste":
+ supportedBy = isSupportedBy(ie);
+ break;
+
+ case "inserttable":
+ supportedBy = isSupportedBy(mozilla | (this.object ? ie : 0));
+ break;
+
+ case "insertcell": case "insertcol": case "insertrow":
+ case "deletecells": case "deletecols": case "deleterows":
+ case "mergecells": case "splitcell":
+ supportedBy = isSupportedBy(this.object ? ie : 0);
+ break;
+
+ default: return false;
+ }
+
+ return (dojo.render.html.ie && supportedBy.ie) ||
+ (dojo.render.html.mozilla && supportedBy.mozilla) ||
+ (dojo.render.html.safari && supportedBy.safari) ||
+ (dojo.render.html.opera && supportedBy.opera);
+ },
+
+ /**
+ * Executes a command in the Rich Text area
+ *
+ * @param command The command to execute
+ * @param argument An optional argument to the command
+ */
+ execCommand: function (command, argument){
+ var returnValue;
+ if(this.object){
+ if(command == "forecolor"){
+ command = "setforecolor";
+ }else if(command == "backcolor"){
+ command = "setbackcolor";
+ }
+
+ //if (typeof this._activeX.command[command] == "undefined") { return null; }
+
+ if(command == "inserttable"){
+ var tableInfo = this.constructor._tableInfo;
+ if(!tableInfo){
+ tableInfo = document.createElement("object");
+ tableInfo.classid = "clsid:47B0DFC7-B7A3-11D1-ADC5-006008A5848C";
+ document.body.appendChild(tableInfo);
+ this.constructor._table = tableInfo;
+ }
+
+ tableInfo.NumRows = argument["rows"];
+ tableInfo.NumCols = argument["cols"];
+ tableInfo.TableAttrs = argument["TableAttrs"];
+ tableInfo.CellAttrs = argument["CellAttrs"];
+ tableInfo.Caption = argument["Caption"];
+ }
+
+ if(command == "inserthtml"){
+ var insertRange = this.document.selection.createRange();
+ insertRange.select();
+ insertRange.pasteHTML(argument);
+ insertRange.collapse(true);
+ return true;
+ }else if(arguments.length == 1){
+ return this.object.ExecCommand(this._activeX.command[command],
+ this._activeX.ui.noprompt);
+ }else{
+ return this.object.ExecCommand(this._activeX.command[command],
+ this._activeX.ui.noprompt, argument);
+ }
+
+ /* */
+ }else if(command == "inserthtml"){
+ // on IE, we can use the pasteHTML method of the textRange object
+ // to get an undo-able innerHTML modification
+ if(dojo.render.html.ie){
+ dojo.debug("inserthtml breaks the undo stack when not using the ActiveX version of the control!");
+ var insertRange = this.document.selection.createRange();
+ insertRange.select();
+ insertRange.pasteHTML(argument);
+ insertRange.collapse(true);
+ return true;
+ }else{
+ return this.document.execCommand(command, false, argument);
+ }
+ /* */
+ // fix up unlink in Mozilla to unlink the link and not just the selection
+ }else if((command == "unlink")&&
+ (this.queryCommandEnabled("unlink"))&&
+ (dojo.render.html.mozilla)){
+ // grab selection
+ // Mozilla gets upset if we just store the range so we have to
+ // get the basic properties and recreate to save the selection
+ var selection = this.window.getSelection();
+ var selectionRange = selection.getRangeAt(0);
+ var selectionStartContainer = selectionRange.startContainer;
+ var selectionStartOffset = selectionRange.startOffset;
+ var selectionEndContainer = selectionRange.endContainer;
+ var selectionEndOffset = selectionRange.endOffset;
+
+ // select our link and unlink
+ var range = document.createRange();
+ var a = this.getSelectedNode();
+ while(a.nodeName != "A"){ a = a.parentNode; }
+ range.selectNode(a);
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ returnValue = this.document.execCommand("unlink", false, null);
+
+ // restore original selection
+ var selectionRange = document.createRange();
+ selectionRange.setStart(selectionStartContainer, selectionStartOffset);
+ selectionRange.setEnd(selectionEndContainer, selectionEndOffset);
+ selection.removeAllRanges();
+ selection.addRange(selectionRange);
+
+ return returnValue;
+ }else if((command == "inserttable")&&(dojo.render.html.mozilla)){
+
+ var cols = "<tr>";
+ for (var i = 0; i < argument.cols; i++) { cols += "<td></td>"; }
+ cols += "</tr>";
+
+ var table = "<table><tbody>";
+ for (var i = 0; i < argument.rows; i++) { table += cols; }
+ table += "</tbody></table>";
+ returnValue = this.document.execCommand("inserthtml", false, table);
+
+ }else if((command == "hilitecolor")&&(dojo.render.html.mozilla)){
+ // mozilla doesn't support hilitecolor properly when useCSS is
+ // set to false (bugzilla #279330)
+
+ this.document.execCommand("useCSS", false, false);
+ returnValue = this.document.execCommand(command, false, argument);
+ this.document.execCommand("useCSS", false, true);
+
+ }else if((dojo.render.html.ie)&&( (command == "backcolor")||(command == "forecolor") )){
+ // IE weirdly collapses ranges when we exec these commands, so prevent it
+ var tr = this.document.selection.createRange();
+ argument = arguments.length > 1 ? argument : null;
+ returnValue = this.document.execCommand(command, false, argument);
+ // timeout is workaround for weird IE behavior were the text
+ // selection gets correctly re-created, but subsequent input
+ // apparently isn't bound to it
+ setTimeout(function(){tr.select();}, 1);
+ }else{
+ // dojo.debug("command:", command, "arg:", argument);
+
+ argument = arguments.length > 1 ? argument : null;
+ if(dojo.render.html.moz){
+ this.document = this.iframe.contentWindow.document
+ }
+ returnValue = this.document.execCommand(command, false, argument);
+
+ // try{
+ // }catch(e){
+ // dojo.debug(e);
+ // }
+ }
+
+ this.onDisplayChanged();
+ return returnValue;
+ },
+
+ queryCommandEnabled: function(command, argument){
+ if(this.object){
+ if(command == "forecolor"){
+ command = "setforecolor";
+ }else if(command == "backcolor"){
+ command = "setbackcolor";
+ }
+
+ if(typeof this._activeX.command[command] == "undefined"){ return false; }
+ var status = this.object.QueryStatus(this._activeX.command[command]);
+ return ((status != this.activeX.status.notsupported)&&
+ (status != this.activeX.status.diabled));
+ }else{
+ // mozilla returns true always
+ if(command == "unlink" && dojo.render.html.mozilla){
+ var node = this.getSelectedNode();
+ while (node.parentNode && node.nodeName != "A") { node = node.parentNode; }
+ return node.nodeName == "A";
+ } else if (command == "inserttable" && dojo.render.html.mozilla) {
+ return true;
+ }
+
+ // return this.document.queryCommandEnabled(command);
+ var elem = (dojo.render.html.ie) ? this.document.selection.createRange() : this.document;
+ return elem.queryCommandEnabled(command);
+ }
+ },
+
+ queryCommandState: function(command, argument){
+ if(this.object){
+ if(command == "forecolor"){
+ command = "setforecolor";
+ }else if(command == "backcolor"){
+ command = "setbackcolor";
+ }
+
+ if(typeof this._activeX.command[command] == "undefined"){ return null; }
+ var status = this.object.QueryStatus(this._activeX.command[command]);
+ return ((status == this._activeX.status.enabled)||
+ (status == this._activeX.status.ninched));
+ }else{
+ return this.document.queryCommandState(command);
+ }
+ },
+
+ queryCommandValue: function (command, argument) {
+ if (this.object) {
+ switch (command) {
+ case "forecolor":
+ case "backcolor":
+ case "fontsize":
+ case "fontname":
+ case "blockformat":
+ command = "get" + command;
+ return this.object.execCommand(
+ this._activeX.command[command],
+ this._activeX.ui.noprompt);
+ }
+
+ //var status = this.object.QueryStatus(this._activeX.command[command]);
+ } else {
+ return this.document.queryCommandValue(command);
+ }
+ },
+
+
+ /* Misc.
+ ********/
+
+ getSelectedNode: function(){
+ if(!this.isLoaded){ return; }
+ if(this.document.selection){
+ return this.document.selection.createRange().parentElement();
+ }else if(dojo.render.html.mozilla){
+ return this.window.getSelection().getRangeAt(0).commonAncestorContainer;
+ }
+ return this.editNode;
+ },
+
+ placeCursorAtStart: function(){
+ if(!this.isLoaded){
+ dojo.event.connect(this, "onLoad", this, "placeCursorAtEnd");
+ return;
+ }
+ dojo.event.disconnect(this, "onLoad", this, "placeCursorAtEnd");
+ if(this.window.getSelection){
+ var selection = this.window.getSelection;
+ if(selection.removeAllRanges){ // Mozilla
+ var range = this.document.createRange();
+ range.selectNode(this.editNode.firstChild);
+ range.collapse(true);
+ var selection = this.window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }else{ // Safari
+ // not a great deal we can do
+ }
+ }else if(this.document.selection){ // IE
+ var range = this.document.body.createTextRange();
+ range.moveToElementText(this.editNode);
+ range.collapse(true);
+ range.select();
+ }
+ },
+
+ replaceEditorContent: function(html){
+ if(this.window.getSelection){
+ var selection = this.window.getSelection;
+ // if(selection.removeAllRanges){ // Mozilla
+ if(dojo.render.html.moz){ // Mozilla
+ var range = this.document.createRange();
+ range.selectNodeContents(this.editNode);
+ var selection = this.window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ this.execCommand("inserthtml", html);
+ }else{ // Safari
+ // look ma! it's a totally f'd browser!
+ this.editNode.innerHTML = html;
+ }
+ }else if(this.document.selection){ // IE
+ var range = this.document.body.createTextRange();
+ range.moveToElementText(this.editNode);
+ range.select();
+ this.execCommand("inserthtml", html);
+ }
+ },
+
+ placeCursorAtEnd: function(){
+ if(!this.isLoaded){
+ dojo.event.connect(this, "onLoad", this, "placeCursorAtEnd");
+ return;
+ }
+ dojo.event.disconnect(this, "onLoad", this, "placeCursorAtEnd");
+ if(this.window.getSelection){
+ var selection = this.window.getSelection;
+ if(selection.removeAllRanges){ // Mozilla
+ var range = this.document.createRange();
+ range.selectNode(this.editNode.lastChild);
+ range.collapse(false);
+ var selection = this.window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }else{ // Safari
+ // not a great deal we can do
+ }
+ }else if(this.document.selection){ // IE
+ var range = this.document.body.createTextRange();
+ range.moveToElementText(this.editNode);
+ range.collapse(true);
+ range.select();
+ }
+ },
+
+ _lastHeight: 0,
+
+ /** Updates the height of the iframe to fit the contents. */
+ _updateHeight: function(){
+ if(!this.isLoaded){ return; }
+ if(this.height){ return; }
+ if(this.iframe){
+ /*
+ if(!this.document.body["offsetHeight"]){
+ return;
+ }
+ */
+ // The height includes the padding, borders and margins so these
+ // need to be added on
+ var heights = ["margin-top", "margin-bottom",
+ "padding-bottom", "padding-top",
+ "border-width-bottom", "border-width-top"];
+ for(var i = 0, chromeheight = 0; i < heights.length; i++){
+ var height = dojo.style.getStyle(this.iframe, heights[i]);
+ // Safari doesn't have all the heights so we have to test
+ if(height){
+ chromeheight += Number(height.replace(/[^0-9]/g, ""));
+ }
+ }
+
+ if(this.document.body["offsetHeight"]){
+ this._lastHeight = Math.max(this.document.body.scrollHeight, this.document.body.offsetHeight) + chromeheight;
+ this.iframe.height = this._lastHeight + "px";
+ this.window.scrollTo(0, 0);
+ }
+ // dojo.debug(this.iframe.height);
+ }else if(this.object){
+ this.object.style.height = dojo.style.getInnerHeight(this.editNode)+"px";
+ }
+ },
+
+ /**
+ * Saves the content in an onunload event if the editor has not been closed
+ */
+ _saveContent: function(e){
+ var saveTextarea = document.getElementById("dojo.widget.RichText.savedContent");
+ saveTextarea.value += this._SEPARATOR + this.saveName + ":" + this.getEditorContent();
+ },
+
+ getEditorContent: function(){
+ var ec = "";
+ try{
+ ec = (this._content.length > 0) ? this._content : this.editNode.innerHTML;
+ if(dojo.string.trim(ec) == " "){ ec = ""; }
+ }catch(e){ /* squelch */ }
+
+ dojo.lang.forEach(this.contentFilters, function(ef){
+ ec = ef(ec);
+ });
+
+ if (this.relativeImageUrls) {
+ // why use a regexp instead of dom? because IE is stupid
+ // and won't let us set img.src to a relative URL
+ // this comes after contentFilters because once content
+ // gets innerHTML'd img urls will be fully qualified
+ var siteBase = window.location.protocol + "//" + window.location.host;
+ var pathBase = window.location.pathname;
+ if (pathBase.match(/\/$/)) {
+ // ends with slash, match full path
+ } else {
+ // match parent path to find siblings
+ var pathParts = pathBase.split("/");
+ if (pathParts.length) {
+ pathParts.pop();
+ }
+ pathBase = pathParts.join("/") + "/";
+
+ }
+
+ var sameSite = new RegExp("(<img[^>]*\ src=[\"'])("+siteBase+"("+pathBase+")?)", "ig");
+ ec = ec.replace(sameSite, "$1");
+ }
+ return ec;
+ },
+
+ /**
+ * Kills the editor and optionally writes back the modified contents to the
+ * element from which it originated.
+ *
+ * @param save Whether or not to save the changes. If false, the changes are
+ * discarded.
+ * @return true if the contents has been modified, false otherwise
+ */
+ close: function(save, force){
+ if(this.isClosed){return false; }
+
+ if (arguments.length == 0) { save = true; }
+ this._content = this.editNode.innerHTML;
+ var changed = (this.savedContent.innerHTML != this._content);
+
+ // line height is squashed for iframes
+ // FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; }
+
+ if(this.interval){ clearInterval(this.interval); }
+
+ if(dojo.render.html.ie && !this.object){
+ dojo.event.browser.clean(this.editNode);
+ }
+
+ if (this.iframe) {
+ // FIXME: should keep iframe around for later re-use
+ delete this.iframe;
+ }
+ this.domNode.innerHTML = "";
+
+ if(save){
+ // kill listeners on the saved content
+ dojo.event.browser.clean(this.savedContent);
+ if(dojo.render.html.moz){
+ var nc = document.createElement("span");
+ this.domNode.appendChild(nc);
+ nc.innerHTML = this.editNode.innerHTML;
+ }else{
+ this.domNode.innerHTML = this._content;
+ }
+ } else {
+ while (this.savedContent.hasChildNodes()) {
+ this.domNode.appendChild(this.savedContent.firstChild);
+ }
+ }
+ delete this.savedContent;
+
+ dojo.html.removeClass(this.domNode, "RichTextEditable");
+ this.isClosed = true;
+ this.isLoaded = false;
+ // FIXME: is this always the right thing to do?
+ delete this.editNode;
+
+ return changed;
+ },
+
+ destroyRendering: function(){}, // stub!
+
+ destroy: function (){
+ this.destroyRendering();
+ if(!this.isClosed){ this.close(false); }
+
+ // disconnect those listeners.
+ while(this._connected.length){
+ this.disconnect(this._connected[0],
+ this._connected[1], this._connected[2]);
+ }
+ },
+
+ _connected: [],
+ connect: function (targetObj, targetFunc, thisFunc) {
+ dojo.event.connect(targetObj, targetFunc, this, thisFunc);
+ // this._connected.push([targetObj, targetFunc, thisFunc]);
+ },
+
+ // FIXME: below two functions do not work with the above line commented out
+ disconnect: function (targetObj, targetFunc, thisFunc) {
+ for (var i = 0; i < this._connected.length; i++) {
+ if (this._connected[0] == targetObj &&
+ this._connected[1] == targetFunc &&
+ this._connected[2] == thisFunc) {
+ dojo.event.disconnect(targetObj, targetFunc, this, thisFunc);
+ this._connected.splice(i, 1);
+ break;
+ }
+ }
+ },
+
+ disconnectAllWithRoot: function (targetObj) {
+ for (var i = 0; i < this._connected.length; i++) {
+ if (this._connected[0] == targetObj) {
+ dojo.event.disconnect(targetObj,
+ this._connected[1], this, this._connected[2]);
+ this._connected.splice(i, 1);
+ }
+ }
+ }
+
+ },
+ "html",
+ function(){
+ this.contentFilters = [];
+ // this.contentFilters.push(this.defaultContentCleaner);
+
+ this._keyHandlers = {};
+ }
+);
Propchange: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/RichText.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Rounded.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Rounded.js?rev=413300&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Rounded.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Rounded.js Sat Jun 10 06:59:28 2006
@@ -0,0 +1,713 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.Rounded");
+dojo.widget.tags.addParseTreeHandler("dojo:rounded");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.html.ContentPane");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+
+/*
+ * The following script is derived (with permission) from curvyCorners,
+ * written by Cameron Cooke (CLA on file) and was adapted to Dojo by Brian
+ * Lucas (CLA on file)
+ */
+
+dojo.widget.Rounded = function() {
+ dojo.widget.html.ContentPane.call(this);
+}
+
+dojo.inherits(dojo.widget.Rounded, dojo.widget.html.ContentPane);
+
+dojo.lang.extend(dojo.widget.Rounded, {
+ isSafari: dojo.render.html.safari,
+ widgetType: "Rounded",
+ boxMargin: "50px", // margin outside rounded corner box
+ radius: 14, // radius of corners
+ domNode: "",
+ corners: "TR,TL,BR,BL", // corner string to render
+ antiAlias: true, // false to disable anti-aliasing
+
+ fillInTemplate: function(args, frag) {
+ dojo.widget.Rounded.superclass.fillInTemplate.call(this, args, frag);
+
+ dojo.style.insertCssFile(this.templateCssPath);
+
+ // Magic to automatically calculate the box height/width if not supplied
+ if (this.domNode.style.height<=0) {
+ var minHeight = (this.radius*1)+this.domNode.clientHeight;
+ this.domNode.style.height = minHeight+"px";
+ }
+
+ if (this.domNode.style.width<=0) {
+ var minWidth = (this.radius*1)+this.domNode.clientWidth;
+ this.domNode.style.width = minWidth+"px";
+ }
+
+ var cornersAvailable = ["TR", "TL", "BR", "BL"];
+ var cornersPassed = this.corners.split(",");
+
+ this.settings = {
+ antiAlias: this.antiAlias
+ };
+
+ var setCorner = function(currentCorner) {
+ var val = currentCorner.toLowerCase();
+ if(dojo.lang.inArray(cornersPassed, currentCorner)) {
+ this.settings[val] = { radius: this.radius, enabled: true };
+ } else {
+ this.settings[val] = { radius: 0 }
+ }
+ }
+ dojo.lang.forEach(cornersAvailable, setCorner, this);
+
+ this.domNode.style.margin = this.boxMargin;
+ this.curvyCorners(this.settings);
+ this.applyCorners();
+ },
+
+ // ------------- curvyCorners OBJECT
+
+ curvyCorners: function(settings){
+
+ // Setup Globals
+ this.box = this.domNode;
+ this.topContainer = null;
+ this.bottomContainer = null;
+ this.masterCorners = [];
+
+ // Get box formatting details
+ var boxHeight = dojo.style.getStyle(this.box, "height");
+ if(boxHeight=="") boxHeight="0px";
+ var boxWidth = dojo.style.getStyle(this.box, "width");
+ var borderWidth = dojo.style.getStyle(this.box, "borderTopWidth");
+ if(borderWidth=="") borderWidth="0px";
+ //alert(borderWidth);
+
+ var borderColour = dojo.style.getStyle(this.box, "borderTopColor");
+ // Set to true if we have a border
+ if(borderWidth>0) this.antiAlias=true;
+
+ var boxColour = dojo.style.getStyle(this.box, "backgroundColor");
+ var backgroundImage = dojo.style.getStyle(this.box, "backgroundImage");
+ var boxPosition = dojo.style.getStyle(this.box, "position");
+
+ // Set formatting propertes
+ this.boxHeight = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight));
+ this.boxWidth = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth));
+ this.borderWidth = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0));
+
+ // DEBUG ME?
+
+ //dojo.debug(this.rgb2Hex(boxColour));
+ var test = new dojo.graphics.color.Color(boxColour);
+ //dojo.debug(test.toHex());
+
+ this.boxColour = ((boxColour != "" && boxColour != "transparent")? ((boxColour.substr(0, 3) == "rgb")? this.rgb2Hex(boxColour) : boxColour) : "#ffffff");
+ this.borderColour = ((borderColour != "" && borderColour != "transparent" && this.borderWidth > 0)? ((borderColour.substr(0, 3) == "rgb")? this.rgb2Hex(borderColour) : borderColour) : this.boxColour);
+ this.borderString = this.borderWidth + "px" + " solid " + this.borderColour;
+ this.backgroundImage = ((backgroundImage != "none")? backgroundImage : "");
+
+ // Make box relative if not already absolute
+ if(boxPosition != "absolute") this.box.style.position = "relative";
+
+ //This method creates the corners and
+ //applies them to the div element.
+
+ this.applyCorners = function() {
+ // Create top and bottom containers.
+ // These will be used as a parent for the corners and bars.
+ for(var t = 0; t < 2; t++) {
+ switch(t) {
+ // Top
+ case 0:
+ // Only build top bar if a top corner is to be draw
+ if(this.settings.tl.enabled || this.settings.tr.enabled ) {
+ var newMainContainer = document.createElement("DIV");
+
+ with(newMainContainer.style){
+ width = "100%";
+ fontSize = "1px";
+ overflow = "hidden";
+ position = "absolute";
+ //backgroundColor = "#FFFFC4";
+ paddingLeft = this.borderWidth + "px";
+ paddingRight = this.borderWidth + "px";
+ var topMaxRadius = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0);
+ height = topMaxRadius + "px";
+ top = 0 - topMaxRadius + "px";
+ left = 0 - this.borderWidth + "px";
+ }
+
+ this.topContainer = this.box.appendChild(newMainContainer);
+ }
+ break;
+
+ // Bottom
+ case 1:
+ // Only build bottom bar if a top corner is to be draw
+ if(this.settings.bl.enabled || this.settings.br.enabled) {
+ var newMainContainer = document.createElement("DIV");
+ with(newMainContainer.style){
+ width = "100%";
+ fontSize = "1px";
+ overflow = "hidden";
+ position = "absolute";
+ //backgroundColor = "#FFFFC4";
+ paddingLeft = this.borderWidth + "px";
+ paddingRight = this.borderWidth + "px";
+ var botMaxRadius = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0);
+ height = botMaxRadius + "px";
+ bottom = 0 - botMaxRadius + "px";
+ left = 0 - this.borderWidth + "px";
+ }
+ this.bottomContainer = this.box.appendChild(newMainContainer);
+ }
+ break;
+ }
+ }
+
+ // Turn off current borders
+ if(this.topContainer) this.box.style.borderTopWidth = "0px";
+ if(this.bottomContainer) this.box.style.borderBottomWidth = "0px";
+
+ // Create array of available corners
+ var corners = ["tr", "tl", "br", "bl"];
+
+ //Loop for each corner
+
+ for(var i in corners) {
+ // Get current corner type from array
+ var cc = corners[i];
+
+ // Has the user requested the currentCorner be round?
+ if(!this.settings[cc]) {
+ // No
+ if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null)) {
+ // We need to create a filler div to fill the space upto the next horzontal corner.
+ var newCorner = document.createElement("DIV");
+
+ // Setup corners properties
+ newCorner.style.position = "relative";
+ newCorner.style.fontSize = "1px";
+ newCorner.style.overflow = "hidden";
+
+ // Add background image?
+ if(this.backgroundImage == "") {
+ newCorner.style.backgroundColor = this.boxColour;
+ } else {
+ newCorner.style.backgroundImage = this.backgroundImage;
+ }
+
+ switch(cc) {
+ case "tl":
+ with(newCorner.style){
+ height = topMaxRadius - this.borderWidth + "px";
+ marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px";
+ borderLeft = this.borderString;
+ borderTop = this.borderString;
+ left = -this.borderWidth + "px";
+ }
+ break;
+
+ case "tr":
+ with(newCorner.style){
+ height = topMaxRadius - this.borderWidth + "px";
+ marginLeft = this.settings.tl.radius - (this.borderWidth*2) + "px";
+ borderRight = this.borderString;
+ borderTop = this.borderString;
+ backgroundPosition = "-" + this.boxWidth + "px 0px";
+ left = this.borderWidth + "px";
+ }
+ break;
+
+ case "bl":
+ with(newCorner.style){
+ height = botMaxRadius - this.borderWidth + "px";
+ marginRight = this.settings.br.radius - (this.borderWidth*2) + "px";
+ borderLeft = this.borderString;
+ borderBottom = this.borderString;
+ left = -this.borderWidth + "px";
+ }
+ break;
+
+ case "br":
+ with(newCorner.style){
+ height = botMaxRadius - this.borderWidth + "px";
+ marginLeft = this.settings.bl.radius - (this.borderWidth*2) + "px";
+ borderRight = this.borderString;
+ borderBottom = this.borderString;
+ left = this.borderWidth + "px"
+ }
+ break;
+ }
+ }
+ } else {
+ /*
+ PERFORMANCE NOTE:
+
+ If more than one corner is requested and a corner has been already
+ created for the same radius then that corner will be used as a master and cloned.
+ The pixel bars will then be repositioned to form the new corner type.
+ All new corners start as a bottom right corner.
+ */
+ if(this.masterCorners[this.settings[cc].radius]) {
+ // Create clone of the master corner
+ var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true);
+ } else {
+ // Yes, we need to create a new corner
+ var newCorner = document.createElement("DIV");
+ with(newCorner.style){
+ height = this.settings[cc].radius + "px";
+ width = this.settings[cc].radius + "px";
+ position = "absolute";
+ fontSize = "1px";
+ overflow = "hidden";
+ }
+ // THE FOLLOWING BLOCK OF CODE CREATES A ROUNDED CORNER
+ // ---------------------------------------------------- TOP
+
+ // Get border radius
+ var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth);
+
+ // Cycle the x-axis
+ for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++) {
+ // Calculate the value of y1 which identifies the pixels inside the border
+ if((intx +1) >= borderRadius) {
+ var y1 = -1;
+ } else {
+ var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1);
+ }
+
+ // Only calculate y2 and y3 if there is a border defined
+ if(borderRadius != j) {
+ if((intx) >= borderRadius) {
+ var y2 = -1;
+ } else {
+ var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2)));
+ }
+
+ if((intx+1) >= j) {
+ var y3 = -1;
+ } else {
+ var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);
+ }
+ }
+
+ // Calculate y4
+ if((intx) >= j) {
+ var y4 = -1;
+ } else {
+ var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2)));
+ }
+
+ // Draw bar on inside of the border with foreground colour
+ if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius);
+
+ // Only draw border/foreground antialiased pixels and border if there is a border defined
+ if(borderRadius != j) {
+ // Draw aa pixels?
+ if(this.antiAlias) {
+ // Cycle the y-axis
+ for(var inty = (y1 + 1); inty < y2; inty++) {
+ // For each of the pixels that need anti aliasing between the foreground and border colour draw single pixel divs
+ if(this.backgroundImage != "") {
+ var borderFract = (this.pixelFraction(intx, inty, borderRadius) * 100);
+
+ if (borderFract < 30) {
+ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, 0, this.settings[cc].radius);
+ } else {
+ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, -1, this.settings[cc].radius);
+ }
+ } else {
+ var pixelcolour = dojo.graphics.color.blend(this.boxColour, this.borderColour, this.pixelFraction(intx, inty, borderRadius));
+ this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius);
+ }
+ }
+ }
+
+ // Draw bar for the border
+ if(y3 >= y2) {
+ if (y1 == -1) {
+ y1 = 0;
+ }
+ this.drawPixel(intx, y2, this.borderColour, 100, (y3 - y2 + 1), newCorner, 0, this.settings[cc].radius);
+ }
+ // Set the colour for the outside curve
+ var outsideColour = this.borderColour;
+ } else {
+ // Set the coour for the outside curve
+ var outsideColour = this.boxColour;
+ var y3 = y1;
+ }
+
+ // Draw aa pixels?
+ if(this.antiAlias) {
+ // Cycle the y-axis and draw the anti aliased pixels on the
+ // outside of the curve
+ for(var inty = (y3 + 1); inty < y4; inty++) {
+ // For each of the pixels that need anti aliasing between
+ //the foreground/border colour & background draw single pixel divs
+ this.drawPixel(intx, inty, outsideColour, (this.pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius);
+ }
+ }
+ }
+
+ // END OF CORNER CREATION
+ // ---------------------------------------------------- END
+
+ // We now need to store the current corner in the masterConers array
+ this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true);
+ }
+
+ //Now we have a new corner we need to reposition all the pixels unless
+ //the current corner is the bottom right.
+ if(cc != "br") {
+ // Loop through all children (pixel bars)
+ for(var t = 0, k = newCorner.childNodes.length; t < k; t++) {
+ // Get current pixel bar
+ var pixelBar = newCorner.childNodes[t];
+
+ // Get current top and left properties
+ var pixelBarTop = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px")));
+ var pixelBarLeft = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px")));
+ var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px")));
+
+ // Reposition pixels
+ if(cc == "tl" || cc == "bl") {
+ pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px"; // Left
+ }
+ if(cc == "tr" || cc == "tl") {
+ pixelBar.style.top = this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px"; // Top
+ }
+ var value;
+
+ switch(cc) {
+ case "tr":
+ value = (-1 *( Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) - (Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth))));
+ pixelBar.style.backgroundPosition = value + "px";
+
+ break;
+
+ case "tl":
+ value = (-1 *( Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) - (Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth))));
+ pixelBar.style.backgroundPosition = value + "px";
+
+ break;
+
+ case "bl":
+ value = (-1 *( Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) - (Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth))));
+ pixelBar.style.backgroundPosition = value + "px";
+
+ break;
+ }
+ }
+ }
+ }
+ if(newCorner) {
+ // Position the container
+ switch(cc) {
+ case "tl":
+ if(newCorner.style.position == "absolute") newCorner.style.top = "0px";
+ if(newCorner.style.position == "absolute") newCorner.style.left = "0px";
+ if(this.topContainer) this.topContainer.appendChild(newCorner);
+ break;
+
+ case "tr":
+ if(newCorner.style.position == "absolute") newCorner.style.top = "0px";
+ if(newCorner.style.position == "absolute") newCorner.style.right = "0px";
+ if(this.topContainer) this.topContainer.appendChild(newCorner);
+ break;
+
+ case "bl":
+ if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px";
+ if(newCorner.style.position == "absolute") newCorner.style.left = "0px";
+ if(this.bottomContainer) this.bottomContainer.appendChild(newCorner);
+ break;
+
+ case "br":
+ if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px";
+ if(newCorner.style.position == "absolute") newCorner.style.right = "0px";
+ if(this.bottomContainer) this.bottomContainer.appendChild(newCorner);
+ break;
+ }
+ }
+ }
+ //The last thing to do is draw the rest of the filler DIVs.
+ //We only need to create a filler DIVs when two corners have
+ //diffrent radiuses in either the top or bottom container.
+
+ // Find out which corner has the biiger radius and get the difference amount
+ var radiusDiff = [];
+ radiusDiff["t"] = this.settings.tl.enabled && this.settings.tr.enabled ? Math.abs(this.settings.tl.radius - this.settings.tr.radius) : 0;
+ radiusDiff["b"] = this.settings.bl.enabled && this.settings.br.enabled ? Math.abs(this.settings.bl.radius - this.settings.br.radius) : 0;
+
+ for(var z in radiusDiff) {
+ if(radiusDiff[z]) {
+ // Get the type of corner that is the smaller one
+ var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r");
+
+ // First we need to create a DIV for the space under the smaller corner
+ var newFiller = document.createElement("DIV");
+ with(newFiller.style) {
+ height = radiusDiff[z] + "px";
+ width = this.settings[smallerCornerType].radius+ "px"
+ position = "absolute";
+ fontSize = "1px";
+ overflow = "hidden";
+ backgroundColor = this.boxColour;
+ }
+
+ // Position filler
+ switch(smallerCornerType) {
+ case "tl":
+ with(newFiller.style) {
+ bottom = "0px";
+ left = "0px";
+ borderLeft = this.borderString;
+ }
+ this.topContainer.appendChild(newFiller);
+ break;
+
+ case "tr":
+ with(newFiller.style) {
+ bottom = "0px";
+ right = "0px";
+ borderRight = this.borderString;
+ }
+ this.topContainer.appendChild(newFiller);
+ break;
+
+ case "bl":
+ with(newFiller.style) {
+ top = "0px";
+ left = "0px";
+ borderLeft = this.borderString;
+ }
+ this.bottomContainer.appendChild(newFiller);
+ break;
+
+ case "br":
+ with(newFiller.style) {
+ top = "0px";
+ right = "0px";
+ borderRight = this.borderString;
+ }
+ this.bottomContainer.appendChild(newFiller);
+ break;
+ }
+ }
+
+ // Create the bar to fill the gap between each corner horizontally
+ var newFillerBar = document.createElement("DIV");
+ with(newFillerBar.style) {
+ position = "relative";
+ fontSize = "1px";
+ overflow = "hidden";
+ backgroundColor = this.boxColour;
+ }
+
+ switch(z) {
+ case "t":
+ // Top Bar
+ if(this.topContainer) {
+ with(newFillerBar.style) {
+ height = topMaxRadius - this.borderWidth + "px";
+ marginLeft = this.settings.tl.radius - this.borderWidth + "px";
+ marginRight = this.settings.tr.radius - this.borderWidth + "px";
+ borderTop = this.borderString;
+ }
+ this.topContainer.appendChild(newFillerBar);
+ }
+ break;
+
+ case "b":
+ if(this.bottomContainer) {
+ // Bottom Bar
+ with(newFillerBar.style) {
+ height = botMaxRadius - this.borderWidth + "px";
+ marginLeft = this.settings.bl.radius - this.borderWidth + "px";
+ marginRight = this.settings.br.radius - this.borderWidth + "px";
+ borderBottom = this.borderString;
+ }
+ this.bottomContainer.appendChild(newFillerBar);
+ }
+ break;
+ }
+ }
+ }
+
+ // This function draws the pixles
+ this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius) {
+ // Create pixel
+ var pixel = document.createElement("DIV");
+
+
+ // Section doesn't like with (pixel.style) { DEBUG?
+ pixel.style.height = height + "px";
+ pixel.style.width = "1px";
+ pixel.style.position = "absolute";
+ pixel.style.fontSize = "1px";
+ pixel.style.overflow = "hidden";
+
+ // Dont apply background image to border pixels
+ if(image == -1 && this.backgroundImage != "") {
+ pixel.style.backgroundImage = this.backgroundImage;
+ pixel.style.backgroundPosition = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + cornerRadius + inty) -this.borderWidth) + "px";
+ } else {
+ pixel.style.backgroundColor = colour;
+ }
+
+ // Set opacity if the transparency is anything other than 100
+ if (transAmount != 100) {
+ dojo.style.setOpacity(pixel, transAmount);
+ }
+ // Set the pixels position
+ pixel.style.top = inty + "px";
+ pixel.style.left = intx + "px";
+
+ newCorner.appendChild(pixel);
+ }
+ },
+
+ //For a pixel cut by the line determines the fraction of the pixel on the 'inside' of the
+ //line. Returns a number between 0 and 1
+ pixelFraction: function(x, y, r) {
+ var pixelfraction = 0;
+
+ //determine the co-ordinates of the two points on the perimeter of the pixel that the
+ //circle crosses
+
+ var xvalues = [];
+ var yvalues = [];
+ var point = 0;
+ var whatsides = "";
+
+ // x + 0 = Left
+ var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2)));
+
+ if ((intersect >= y) && (intersect < (y+1))) {
+ whatsides = "Left";
+ xvalues[point] = 0;
+ yvalues[point] = intersect - y;
+ point = point + 1;
+ }
+
+ // y + 1 = Top
+ var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2)));
+
+ if ((intersect >= x) && (intersect < (x+1))) {
+ whatsides = whatsides + "Top";
+ xvalues[point] = intersect - x;
+ yvalues[point] = 1;
+ point = point + 1;
+ }
+ // x + 1 = Right
+ var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2)));
+
+ if ((intersect >= y) && (intersect < (y+1))) {
+ whatsides = whatsides + "Right";
+ xvalues[point] = 1;
+ yvalues[point] = intersect - y;
+ point = point + 1;
+ }
+ // y + 0 = Bottom
+ var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2)));
+
+ if ((intersect >= x) && (intersect < (x+1))) {
+ whatsides = whatsides + "Bottom";
+ xvalues[point] = intersect - x;
+ yvalues[point] = 0;
+ }
+
+ //depending on which sides of the perimeter of the pixel the circle crosses calculate the
+ //fraction of the pixel inside the circle
+
+ switch (whatsides) {
+ case "LeftRight":
+ pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2);
+ break;
+
+ case "TopRight":
+ pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2);
+ break;
+
+ case "TopBottom":
+ pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2);
+ break;
+
+ case "LeftBottom":
+ pixelfraction = (yvalues[0]*xvalues[1])/2;
+ break;
+
+ default:
+ pixelfraction = 1;
+ }
+ return pixelfraction;
+ },
+
+ // This function converts CSS rgb(x, x, x) to hexadecimal
+ rgb2Hex: function (rgbColour) {
+ try{
+ // Get array of RGB values
+ var rgbArray = this.rgb2Array(rgbColour);
+
+ // Get RGB values
+ var red = parseInt(rgbArray[0]);
+ var green = parseInt(rgbArray[1]);
+ var blue = parseInt(rgbArray[2]);
+
+ // Build hex colour code
+ var hexColour = "#" + this.intToHex(red) + this.intToHex(green) + this.intToHex(blue);
+ }
+ catch(e){ alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");
+ }
+ return hexColour;
+ },
+
+ //Converts a number to hexadecimal format
+
+ intToHex: function (strNum) {
+ var base = strNum / 16;
+ var rem = strNum % 16;
+ var base = base - (rem / 16);
+ var baseS = this.makeHex(base);
+ var remS = this.makeHex(rem);
+ return baseS + '' + remS;
+ },
+ //gets the hex bits of a number
+
+ makeHex: function(x) {
+ if((x >= 0) && (x <= 9)) {
+ return x;
+ } else {
+ switch(x) {
+ case 10: return "A";
+ case 11: return "B";
+ case 12: return "C";
+ case 13: return "D";
+ case 14: return "E";
+ case 15: return "F";
+ }
+ }
+ },
+
+ // Returns an array of rbg values
+ rgb2Array: function(rgbColour) {
+ // Remove rgb()
+ var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")"));
+
+ // Split RGB into array
+ var rgbArray = rgbValues.split(", ");
+ return rgbArray;
+ }
+}); // end function
Propchange: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Rounded.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Select.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Select.js?rev=413300&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Select.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Select.js Sat Jun 10 06:59:28 2006
@@ -0,0 +1,76 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.Select");
+dojo.provide("dojo.widget.html.Select");
+
+dojo.require("dojo.widget.html.ComboBox");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.html.stabile");
+
+/*
+ * The Select widget is an enhanced version of HTML's <select> tag.
+ *
+ * Similar features:
+ * - There is a drop down list of possible values.
+ * - You can only enter a value from the drop down list. (You can't enter an arbitrary value.)
+ * - The value submitted with the form is the hidden value (ex: CA),
+ not the displayed value a.k.a. label (ex: California)
+ *
+ * Enhancements over plain HTML version:
+ * - If you type in some text then it will filter down the list of possible values in the drop down list.
+ * - List can be specified either as a static list or via a javascript function (that can get the list from a server)
+ */
+
+dojo.widget.defineWidget(
+ "dojo.widget.html.Select",
+ dojo.widget.html.ComboBox,
+ {
+ widgetType: "Select",
+ forceValidOption: true,
+
+ setValue: function(value) {
+ this.comboBoxValue.value = value;
+ dojo.widget.html.stabile.setState(this.widgetId, this.getState(), true);
+ },
+
+ setLabel: function(value){
+ // FIXME, not sure what to do here!
+ this.comboBoxSelectionValue.value = value;
+ if (this.textInputNode.value != value) { // prevent mucking up of selection
+ this.textInputNode.value = value;
+ }
+ },
+
+ getLabel: function(){
+ return this.comboBoxSelectionValue.value;
+ },
+
+ getState: function() {
+ return {
+ value: this.getValue(),
+ label: this.getLabel()
+ };
+ },
+
+ onKeyUp: function(evt){
+ this.setLabel(this.textInputNode.value);
+ },
+
+ setState: function(state) {
+ this.setValue(state.value);
+ this.setLabel(state.label);
+ },
+
+ setAllValues: function(value1, value2){
+ this.setValue(value2);
+ this.setLabel(value1);
+ }
+ });
Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Show.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Show.js?rev=413300&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Show.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Show.js Sat Jun 10 06:59:28 2006
@@ -0,0 +1,45 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.Show");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lang.common");
+
+dojo.widget.Show = function(){}
+dojo.lang.extend(dojo.widget.Show, {
+ isContainer: true,
+ _slide: -1,
+ _slides: [],
+ gotoSlide: function(/*int*/ slide){
+ this._slide = slide;
+ // summary: Placeholder
+ },
+ nextSlide: function(/*Event?*/ event){
+ if(!this._slides[this._slide].nextAction(event)){
+ if((this._slide + 1) != this._slides.length){
+ this.gotoSlide(this._slide + 1);
+ return true; // boolean
+ }
+ return false; // boolean
+ }
+ },
+ previousSlide: function(/*Event?*/ event){
+ if(!this._slides[this._slide].previousAction(event)){
+ if((this._slide - 1) != -1){
+ this.gotoSlide(this._slide - 1);
+ return true; // boolean
+ }
+ return false; // boolean
+ }
+ }
+});
+
+dojo.requireAfterIf("html", "dojo.widget.html.Show");
\ No newline at end of file
Propchange: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Show.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/ShowSlide.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/ShowSlide.js?rev=413300&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/ShowSlide.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/ShowSlide.js Sat Jun 10 06:59:28 2006
@@ -0,0 +1,43 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.ShowSlide");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lang.common");
+
+dojo.widget.ShowSlide = function(){
+}
+dojo.lang.extend(dojo.widget.ShowSlide, {
+ title: "",
+ _action: -1,
+ isContainer: true,
+ _components: {},
+ _actions: [],
+ gotoAction: function(/*int*/ action){
+ this._action = action;
+ },
+ nextAction: function(/*Event?*/ event){
+ if((this._action + 1) != this._actions.length){
+ ++this._action;
+ return true; // boolean
+ }
+ return false; // boolean
+ },
+ previousAction: function(/*Event?*/ event){
+ if((this._action - 1) != -1){
+ --this._action;
+ return true; // boolean
+ }
+ return false; // boolean
+ }
+});
+
+dojo.requireAfterIf("html", "dojo.widget.html.ShowSlide");
\ No newline at end of file
Propchange: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/ShowSlide.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/SimpleDropdownButtons.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/SimpleDropdownButtons.js?rev=413300&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/SimpleDropdownButtons.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/SimpleDropdownButtons.js Sat Jun 10 06:59:28 2006
@@ -0,0 +1,158 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/* TODO:
+ * - make the dropdowns "smart" so they can't get cutoff on bottom of page, sides of page, etc.
+ * - unify menus with the MenuItem and Menu classes so we can add stuff to all menus at once
+ * - allow buttons to be enabled/disabled at runtime
+ * - this probably means creating all menus upfront and then triggering a disable action
+ * for disabled buttons in the constructor loop. we'll need a disable and enable action anyway
+ * - should each button with menu be a widget object of it's own?
+ */
+dojo.provide("dojo.widget.SimpleDropdownButtons");
+dojo.provide("dojo.widget.HtmlSimpleDropdownButtons");
+
+dojo.deprecated("dojo.widget.SimpleDropdownButtons", "use dojo.widget.DropDownButton", "0.4");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.dom");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+dojo.widget.tags.addParseTreeHandler("dojo:simpledropdownbuttons");
+
+dojo.widget.HtmlSimpleDropdownButtons = function() {
+ dojo.widget.HtmlWidget.call(this);
+
+ this.widgetType = "SimpleDropdownButtons";
+ this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlSimpleDropdownButtons.css");
+
+ this.menuTriggerClass = "dojoSimpleDropdownButtons";
+ this.menuClass = "dojoSimpleDropdownButtonsMenu";
+
+ // overwrite buildRendering so we don't clobber our list
+ this.buildRendering = function(args, frag) {
+ if(this.templateCssPath) {
+ dojo.style.insertCssFile(this.templateCssPath, null, true);
+ }
+ this.domNode = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+
+ var menu = this.domNode;
+ if( !dojo.html.hasClass(menu, this.menuTriggerClass) ) {
+ dojo.html.addClass(menu, this.menuTriggerClass);
+ }
+ var li = dojo.dom.getFirstChildElement(menu);
+ var menuIDs = [];
+ var arrowIDs = [];
+
+ while(li) {
+ if(li.getElementsByTagName("ul").length > 0) {
+ var a = dojo.dom.getFirstChildElement(li);
+ var arrow = document.createElement("a");
+ arrow.href = "javascript:;";
+ arrow.innerHTML = " ";
+ dojo.html.setClass(arrow, "downArrow");
+ if(!arrow.id) {
+ arrow.id = dojo.dom.getUniqueId();
+ }
+ arrowIDs.push(arrow.id);
+ var submenu = dojo.dom.getNextSiblingElement(a);
+ if(!submenu.id) {
+ submenu.id = dojo.dom.getUniqueId();
+ }
+ menuIDs.push(submenu.id);
+
+ if( dojo.html.hasClass(a, "disabled") ) {
+ dojo.html.addClass(arrow, "disabled");
+ dojo.html.disableSelection(li);
+ arrow.onfocus = function(){ this.blur(); }
+ } else {
+ dojo.html.addClass(submenu, this.menuClass);
+ document.body.appendChild(submenu);
+ dojo.event.connect(arrow, "onmousedown", (function() {
+ var ar = arrow;
+ return function(e) {
+ dojo.html.addClass(ar, "pressed");
+ }
+ })());
+ dojo.event.connect(arrow, "onclick", (function() {
+ var aa = a;
+ var ar = arrow;
+ var sm = submenu;
+ var setWidth = false;
+
+ return function(e) {
+ hideAll(sm, ar);
+ sm.style.left = (dojo.html.getScrollLeft()
+ + e.clientX - e.layerX + aa.offsetLeft) + "px";
+ sm.style.top = (dojo.html.getScrollTop() + e.clientY
+ - e.layerY + aa.offsetTop + aa.offsetHeight) + "px";
+ sm.style.display = sm.style.display == "block" ? "none" : "block";
+ if(sm.style.display == "none") {
+ dojo.html.removeClass(ar, "pressed");
+ e.target.blur()
+ }
+ if(!setWidth && sm.style.display == "block"
+ && sm.offsetWidth < aa.offsetWidth + ar.offsetWidth) {
+ sm.style.width = aa.offsetWidth + ar.offsetWidth + "px";
+ setWidth = true;
+ }
+ e.preventDefault();
+ }
+ })());
+ }
+
+ dojo.event.connect(a, "onclick", function(e) {
+ if(e && e.target && e.target.blur) {
+ e.target.blur();
+ }
+ });
+
+ if(a.nextSibling) {
+ li.insertBefore(arrow, a.nextSibling);
+ } else {
+ li.appendChild(arrow);
+ }
+
+ }
+ li = dojo.dom.getNextSiblingElement(li);
+ }
+
+ function hideAll(excludeMenu, excludeArrow) {
+ // hide menus
+ for(var i = 0; i < menuIDs.length; i++) {
+ var m = document.getElementById(menuIDs[i]);
+ if(!excludeMenu || m != excludeMenu) {
+ document.getElementById(menuIDs[i]).style.display = "none";
+ }
+ }
+ // restore arrows to non-pressed state
+ for(var i = 0; i < arrowIDs.length; i++) {
+ var m = document.getElementById(arrowIDs[i]);
+ if(!excludeArrow || m != excludeArrow) {
+ dojo.html.removeClass(m, "pressed");
+ }
+ }
+ }
+
+ dojo.event.connect(document.documentElement, "onmousedown", function(e) {
+ if( dojo.html.hasClass(e.target, "downArrow") ) { return };
+ for(var i = 0; i < menuIDs.length; i++) {
+ if( dojo.dom.isDescendantOf(e.target, document.getElementById(menuIDs[i])) ) {
+ return;
+ }
+ }
+ hideAll();
+ });
+ }
+}
+dojo.inherits(dojo.widget.HtmlSimpleDropdownButtons, dojo.widget.HtmlWidget);
Propchange: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/SimpleDropdownButtons.js
------------------------------------------------------------------------------
svn:eol-style = native