vitro/webapp/web/src/widget/Editor2.js

396 lines
12 KiB
JavaScript
Raw Normal View History

/*
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:
* - font selector
* - test, bug fix, more features :)
*/
dojo.provide("dojo.widget.Editor2");
dojo.provide("dojo.widget.html.Editor2");
dojo.require("dojo.io.*");
dojo.require("dojo.widget.*");
dojo.require("dojo.widget.RichText");
dojo.require("dojo.widget.Editor2Toolbar");
// dojo.require("dojo.widget.ColorPalette");
// dojo.require("dojo.string.extras");
dojo.widget.defineWidget(
"dojo.widget.html.Editor2",
dojo.widget.html.RichText,
{
saveUrl: "",
saveMethod: "post",
saveArgName: "editorContent",
closeOnSave: false,
shareToolbar: false,
toolbarAlwaysVisible: false,
htmlEditing: false,
_inHtmlMode: false,
_htmlEditNode: null,
commandList: dojo.widget.html.Editor2Toolbar.prototype.commandList,
toolbarWidget: null,
scrollInterval: null,
editorOnLoad: function(){
var toolbars = dojo.widget.byType("Editor2Toolbar");
if((!toolbars.length)||(!this.shareToolbar)){
var tbOpts = {};
tbOpts.templatePath = dojo.uri.dojoUri("src/widget/templates/HtmlEditorToolbarOneline.html");
this.toolbarWidget = dojo.widget.createWidget("Editor2Toolbar",
tbOpts, this.domNode, "before");
dojo.event.connect(this, "destroy", this.toolbarWidget, "destroy");
this.toolbarWidget.hideUnusableButtons(this);
if(this.object){
this.tbBgIframe = new dojo.html.BackgroundIframe(this.toolbarWidget.domNode);
this.tbBgIframe.iframe.style.height = "30px";
}
// need to set position fixed to wherever this thing has landed
if(this.toolbarAlwaysVisible){
var src = document["documentElement"]||window;
this.scrollInterval = setInterval(dojo.lang.hitch(this, "globalOnScrollHandler"), 100);
// dojo.event.connect(src, "onscroll", this, "globalOnScrollHandler");
dojo.event.connect("before", this, "destroyRendering", this, "unhookScroller");
}
}else{
// FIXME: should we try harder to explicitly manage focus in
// order to prevent too many editors from all querying
// for button status concurrently?
// FIXME: selecting in one shared toolbar doesn't clobber
// selection in the others. This is problematic.
this.toolbarWidget = toolbars[0];
}
dojo.event.topic.registerPublisher("Editor2.clobberFocus", this.editNode, "onfocus");
// dojo.event.topic.registerPublisher("Editor2.clobberFocus", this.editNode, "onclick");
dojo.event.topic.subscribe("Editor2.clobberFocus", this, "setBlur");
dojo.event.connect(this.editNode, "onfocus", this, "setFocus");
dojo.event.connect(this.toolbarWidget.linkButton, "onclick",
dojo.lang.hitch(this, function(){
var range;
if(this.document.selection){
range = this.document.selection.createRange().text;
}else if(dojo.render.html.mozilla){
range = this.window.getSelection().toString();
}
if(range.length){
this.toolbarWidget.exec("createlink",
prompt("Please enter the URL of the link:", "http://"));
}else{
alert("Please select text to link");
}
})
);
var focusFunc = dojo.lang.hitch(this, function(){
if(dojo.render.html.ie){
this.editNode.focus();
}else{
this.window.focus();
}
});
dojo.event.connect(this.toolbarWidget, "formatSelectClick", focusFunc);
dojo.event.connect(this, "execCommand", focusFunc);
if(this.htmlEditing){
var tb = this.toolbarWidget.htmltoggleButton;
if(tb){
tb.style.display = "";
dojo.event.connect(this.toolbarWidget, "htmltoggleClick",
this, "toggleHtmlEditing");
}
}
},
toggleHtmlEditing: function(){
if(!this._inHtmlMode){
this._inHtmlMode = true;
this.toolbarWidget.highlightButton("htmltoggle");
if(!this._htmlEditNode){
this._htmlEditNode = document.createElement("textarea");
dojo.html.insertBefore(this._htmlEditNode, this.domNode);
}
this._htmlEditNode.style.display = "";
this._htmlEditNode.style.width = "100%";
this._htmlEditNode.style.height = dojo.style.getInnerHeight(this.editNode)+"px";
this._htmlEditNode.value = this.editNode.innerHTML;
this.domNode.style.display = "none";
}else{
this._inHtmlMode = false;
this.domNode.style.display = "";
this.toolbarWidget.unhighlightButton("htmltoggle");
dojo.lang.setTimeout(this, "replaceEditorContent", 1, this._htmlEditNode.value);
this._htmlEditNode.style.display = "none";
this.editNode.focus();
}
},
setFocus: function(){
// dojo.debug("setFocus:", this);
dojo.event.connect(this.toolbarWidget, "exec", this, "execCommand");
},
setBlur: function(){
// dojo.debug("setBlur:", this);
dojo.event.disconnect(this.toolbarWidget, "exec", this, "execCommand");
},
_scrollSetUp: false,
_fixEnabled: false,
_scrollThreshold: false,
_handleScroll: true,
globalOnScrollHandler: function(){
var isIE = dojo.render.html.ie;
if(!this._handleScroll){ return; }
var ds = dojo.style;
var tdn = this.toolbarWidget.domNode;
var db = document["body"];
var totalHeight = ds.getOuterHeight(tdn);
if(!this._scrollSetUp){
this._scrollSetUp = true;
var editorWidth = ds.getOuterWidth(this.domNode);
this._scrollThreshold = ds.abs(tdn, false).y;
// dojo.debug("threshold:", this._scrollThreshold);
if((isIE)&&(db)&&(ds.getStyle(db, "background-image")=="none")){
with(db.style){
backgroundImage = "url(" + dojo.uri.dojoUri("src/widget/templates/images/blank.gif") + ")";
backgroundAttachment = "fixed";
}
}
}
var scrollPos = (window["pageYOffset"]) ? window["pageYOffset"] : (document["documentElement"]||document["body"]).scrollTop;
// FIXME: need to have top and bottom thresholds so toolbar doesn't keep scrolling past the bottom
if(scrollPos > this._scrollThreshold){
// dojo.debug(scrollPos);
if(!this._fixEnabled){
this.domNode.style.marginTop = totalHeight+"px";
if(isIE){
// FIXME: should we just use setBehvior() here instead?
var cl = dojo.style.abs(tdn).x;
document.body.appendChild(tdn);
tdn.style.left = cl+dojo.style.getPixelValue(document.body, "margin-left")+"px";
dojo.html.addClass(tdn, "IEFixedToolbar");
if(this.object){
dojo.html.addClass(this.tbBgIframe, "IEFixedToolbar");
}
}else{
with(tdn.style){
position = "fixed";
top = "0px";
}
}
tdn.style.zIndex = 1000;
this._fixEnabled = true;
}
// if we're showing the floating toolbar, make sure that if
// we've scrolled past the bottom of the editor that we hide
// the toolbar for this instance of the editor.
// TODO: when we get multiple editor toolbar support working
// correctly, ensure that we check this against the scroll
// position of the bottom-most editor instance.
if(!dojo.render.html.safari){
// safari reports a bunch of things incorrectly here
var eHeight = (this.height) ? parseInt(this.height) : ((this.object) ? dojo.style.getInnerHeight(this.editNode) : this._lastHeight);
if(scrollPos > (this._scrollThreshold+eHeight)){
tdn.style.display = "none";
}else{
tdn.style.display = "";
}
}
}else if(this._fixEnabled){
this.domNode.style.marginTop = null;
with(tdn.style){
position = "";
top = "";
zIndex = "";
if(isIE){
marginTop = "";
}
}
if(isIE){
dojo.html.removeClass(tdn, "IEFixedToolbar");
dojo.html.insertBefore(tdn, this._htmlEditNode||this.domNode);
}
this._fixEnabled = false;
}
},
unhookScroller: function(){
this._handleScroll = false;
clearInterval(this.scrollInterval);
// var src = document["documentElement"]||window;
// dojo.event.disconnect(src, "onscroll", this, "globalOnScrollHandler");
if(dojo.render.html.ie){
dojo.html.removeClass(this.toolbarWidget.domNode, "IEFixedToolbar");
}
},
_updateToolbarLastRan: null,
_updateToolbarTimer: null,
_updateToolbarFrequency: 500,
updateToolbar: function(force){
if((!this.isLoaded)||(!this.toolbarWidget)){ return; }
// keeps the toolbar from updating too frequently
// TODO: generalize this functionality?
var diff = new Date() - this._updateToolbarLastRan;
if( (!force)&&(this._updateToolbarLastRan)&&
((diff < this._updateToolbarFrequency)) ){
clearTimeout(this._updateToolbarTimer);
var _this = this;
this._updateToolbarTimer = setTimeout(function() {
_this.updateToolbar();
}, this._updateToolbarFrequency/2);
return;
}else{
this._updateToolbarLastRan = new Date();
}
// end frequency checker
dojo.lang.forEach(this.commandList, function(cmd){
if(cmd == "inserthtml"){ return; }
try{
if(this.queryCommandEnabled(cmd)){
if(this.queryCommandState(cmd)){
this.toolbarWidget.highlightButton(cmd);
}else{
this.toolbarWidget.unhighlightButton(cmd);
}
}
}catch(e){
// alert(cmd+":"+e);
}
}, this);
var h = dojo.render.html;
// safari f's us for selection primitives
if(h.safari){ return; }
var selectedNode = (h.ie) ? this.document.selection.createRange().parentElement() : this.window.getSelection().anchorNode;
// make sure we actuall have an element
while((selectedNode)&&(selectedNode.nodeType != 1)){
selectedNode = selectedNode.parentNode;
}
if(!selectedNode){ return; }
var formats = ["p", "pre", "h1", "h2", "h3", "h4"];
// gotta run some specialized updates for the various
// formatting options
var type = formats[dojo.lang.find(formats, selectedNode.nodeName.toLowerCase())];
while((selectedNode)&&(selectedNode!=this.editNode)&&(!type)){
selectedNode = selectedNode.parentNode;
type = formats[dojo.lang.find(formats, selectedNode.nodeName.toLowerCase())];
}
if(!type){
type = "";
}else{
if(type.charAt(0)=="h"){
this.toolbarWidget.unhighlightButton("bold");
}
}
this.toolbarWidget.selectFormat(type);
},
updateItem: function(item) {
try {
var cmd = item._name;
var enabled = this._richText.queryCommandEnabled(cmd);
item.setEnabled(enabled, false, true);
var active = this._richText.queryCommandState(cmd);
if(active && cmd == "underline") {
// don't activate underlining if we are on a link
active = !this._richText.queryCommandEnabled("unlink");
}
item.setSelected(active, false, true);
return true;
} catch(err) {
return false;
}
},
_save: function(e){
// FIXME: how should this behave when there's a larger form in play?
if(!this.isClosed){
if(this.saveUrl.length){
var content = {};
content[this.saveArgName] = this.getHtml();
dojo.io.bind({
method: this.saveMethod,
url: this.saveUrl,
content: content
});
}else{
dojo.debug("please set a saveUrl for the editor");
}
if(this.closeOnSave){
this.close(e.getName().toLowerCase() == "save");
}
}
},
wireUpOnLoad: function(){
if(!dojo.render.html.ie){
/*
dojo.event.kwConnect({
srcObj: this.document,
srcFunc: "click",
targetObj: this.toolbarWidget,
targetFunc: "hideAllDropDowns",
once: true
});
*/
}
}
},
"html",
function(){
var cp = dojo.widget.html.Editor2.prototype;
if(!cp._wrappersSet){
cp._wrappersSet = true;
cp.fillInTemplate = (function(fit){
return function(){
fit.call(this);
this.editorOnLoad();
};
})(cp.fillInTemplate);
cp.onDisplayChanged = (function(odc){
return function(){
try{
odc.call(this);
this.updateToolbar();
}catch(e){}
};
})(cp.onDisplayChanged);
cp.onLoad = (function(ol){
return function(){
ol.call(this);
this.wireUpOnLoad();
};
})(cp.onLoad);
}
}
);