Index: openacs-4/packages/xowiki/www/resources/wymeditor/jquery.wymeditor.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/wymeditor/jquery.wymeditor.js,v diff -u -r1.4 -r1.5 --- openacs-4/packages/xowiki/www/resources/wymeditor/jquery.wymeditor.js 29 May 2008 19:13:44 -0000 1.4 +++ openacs-4/packages/xowiki/www/resources/wymeditor/jquery.wymeditor.js 12 Oct 2008 19:34:59 -0000 1.5 @@ -19,6 +19,7 @@ * Scott Lewis (lewiscot a-t gmail dotcom) * Bermi Ferrer (wymeditor a-t bermi dotorg) * Daniel Reszka (d.reszka a-t wymeditor dotorg) + * Jonatan Lundin (jonatan.lundin _at_ gmail.com) */ /* @@ -27,6 +28,19 @@ */ if(!WYMeditor) var WYMeditor = {}; +//Wrap the Firebug console in WYMeditor.console +(function() { + if ( !window.console || !console.firebug ) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", + "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + + WYMeditor.console = {}; + for (var i = 0; i < names.length; ++i) + WYMeditor.console[names[i]] = function() {} + + } else WYMeditor.console = window.console; +})(); + jQuery.extend(WYMeditor, { /* @@ -38,6 +52,7 @@ SKINS - An array of loaded WYMeditor skins. NAME - The "name" attribute. INDEX - A string replaced by the instance index. + WYM_INDEX - A string used to get/set the instance index. BASE_PATH - A string replaced by WYMeditor's base path. SKIN_PATH - A string replaced by WYMeditor's skin path. WYM_PATH - A string replaced by WYMeditor's main JS file path. @@ -47,6 +62,8 @@ IFRAME_BASE_PATH - A string replaced by the designmode iframe's base path. IFRAME_DEFAULT - The iframe's default base path. JQUERY_PATH - A string replaced by the computed jQuery path. + DIRECTION - A string replaced by the text direction (rtl or ltr). + LOGO - A string replaced by WYMeditor logo. TOOLS - A string replaced by the toolbar's HTML. TOOLS_ITEMS - A string replaced by the toolbar items. TOOL_NAME - A string replaced by a toolbar item's name. @@ -68,7 +85,7 @@ DIALOG_BODY - A string replaced by a dialog's HTML body. BODY - The BODY element. STRING - The "string" type. - BODY,P, + BODY,DIV,P, H1,H2,H3,H4,H5,H6, PRE,BLOCKQUOTE, A,BR,IMG, @@ -101,12 +118,13 @@ */ - VERSION : "0.5-a1", + VERSION : "0.5-a2", INSTANCES : [], STRINGS : [], SKINS : [], NAME : "name", INDEX : "{Wym_Index}", + WYM_INDEX : "wym_index", BASE_PATH : "{Wym_Base_Path}", CSS_PATH : "{Wym_Css_Path}", WYM_PATH : "{Wym_Wym_Path}", @@ -117,6 +135,8 @@ IFRAME_BASE_PATH : "{Wym_Iframe_Base_Path}", IFRAME_DEFAULT : "iframe/default/", JQUERY_PATH : "{Wym_Jquery_Path}", + DIRECTION : "{Wym_Direction}", + LOGO : "{Wym_Logo}", TOOLS : "{Wym_Tools}", TOOLS_ITEMS : "{Wym_Tools_Items}", TOOL_NAME : "{Wym_Tool_Name}", @@ -138,6 +158,7 @@ DIALOG_BODY : "{Wym_Dialog_Body}", STRING : "string", BODY : "body", + DIV : "div", P : "p", H1 : "h1", H2 : "h2", @@ -170,6 +191,7 @@ CREATE_LINK : "CreateLink", INSERT_IMAGE : "InsertImage", INSERT_TABLE : "InsertTable", + INSERT_HTML : "InsertHTML", PASTE : "Paste", INDENT : "Indent", OUTDENT : "Outdent", @@ -265,9 +287,6 @@ this._options.jQueryPath = this._options.jQueryPath || this.computeJqueryPath(); - //instanciate a WYMeditor Selection API class - this.selection = new WYMeditor.WymSelection(); - //initialize the editor instance this.init(); @@ -324,6 +343,8 @@ lang: "en", + direction: "ltr", + boxHtml: "
textnode|
)
- if (this.endNode == parent) {
- // NOTE I don't know if it is a good idea to delete the
- // here, as "atEnd()" probably shouldn't change the dom tree,
- // but only searching it
- if (this.endNode.lastChild.nodeName == "BR")
- this.endNode.removeChild(endNode.lastChild);
-
- // if cursor is really at the end
- if (this.endOffset == 0)
- return false;
- else {
- for (var nNext=this.endNode.childNodes[this.endOffset-1].nextSibling;
- nNext==null || nNext.nodeName == "BR";
- nNext=nNext.nextSibling)
-
- if (nNext==null)
- return true;
- }
-
- }
- else {
- var endNode = this.endNode;
- if (endNode.nodeType == WYMeditor.NODE.TEXT) {
- if ((endNode.nextSibling
- && !WYMeditor.isPhantomNode(endNode.nextSibling))
- || (this.endOffset != endNode.data.length))
- return false;
- else
- endNode = endNode.parentNode;
- }
-
- for (var n=endNode; n!=parent; n=n.parentNode) {
- var lastChild = n.parentNode.lastChild;
- // node isn't last child => cursor can't be at the end
- // (is this true?) in gecko there the last child could be a
- // phantom node
-
- // sometimes also whitespacenodes which aren't phatom nodes
- // get stripped, but this is ok, as this is a wysiwym editor
- if ((lastChild != n) ||
- (WYMeditor.isPhantomNode(lastChild)
- && lastChild.previousSibling != n)) {
- return false;
- }
- }
- }
-
- if (this.endOffset == this.endNode.length)
- return true;
- else
- return false;
- }
-};
-
/********** XHTML LEXER/PARSER **********/
/*
@@ -2417,8 +2334,9 @@
var possible_attributes = this.getPossibleTagAttributes(tag);
for(var attribute in attributes) {
var value = attributes[attribute];
- if(!this.skiped_attributes.contains(attribute) && !this.skiped_attribute_values.contains(value)){
- if (typeof value != 'function' && possible_attributes.contains(attribute)) {
+ var h = WYMeditor.Helper;
+ if(!h.contains(this.skiped_attributes, attribute) && !h.contains(this.skiped_attribute_values, value)){
+ if (typeof value != 'function' && h.contains(possible_attributes, attribute)) {
if (this.doesAttributeNeedsValidation(tag, attribute)) {
if(this.validateAttribute(tag, attribute, value)){
valid_attributes[attribute] = value;
@@ -2474,8 +2392,8 @@
for(var key in default_attributes_and_events) {
var defaults = default_attributes_and_events[key];
if(typeof defaults == 'object'){
-
- if ((defaults['except'] && defaults['except'].contains(tag)) || (defaults['only'] && !defaults['only'].contains(tag))) {
+ var h = WYMeditor.Helper;
+ if ((defaults['except'] && h.contains(defaults['except'], tag)) || (defaults['only'] && !h.contains(defaults['only'], tag))) {
continue;
}
@@ -2490,17 +2408,18 @@
},
doesAttributeNeedsValidation: function(tag, attribute)
{
- return this._tags[tag] && ((this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute]) || (this._tags[tag]['required'] && this._tags[tag]['required'].contains(attribute)));
+ return this._tags[tag] && ((this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute]) || (this._tags[tag]['required'] &&
+ WYMeditor.Helper.contains(this._tags[tag]['required'], attribute)));
},
validateAttribute : function(tag, attribute, value)
{
if ( this._tags[tag] &&
(this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute] && value.length > 0 && !value.match(this._tags[tag]['attributes'][attribute])) || // invalid format
- (this._tags[tag] && this._tags[tag]['required'] && this._tags[tag]['required'].contains(attribute) && value.length == 0) // required attribute
+ (this._tags[tag] && this._tags[tag]['required'] && WYMeditor.Helper.contains(this._tags[tag]['required'], attribute) && value.length == 0) // required attribute
) {
return false;
}
- return this._tags[tag] != undefined;
+ return typeof this._tags[tag] != 'undefined';
},
getPossibleTagAttributes : function(tag)
{
@@ -2748,11 +2667,11 @@
WYMeditor.Lexer.prototype.addPattern = function(pattern, mode)
{
var mode = mode || "accept";
- if (this._regexes[mode] == undefined) {
+ if (typeof this._regexes[mode] == 'undefined') {
this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
}
this._regexes[mode].addPattern(pattern);
- if (this._mode_handlers[mode] == undefined) {
+ if (typeof this._mode_handlers[mode] == 'undefined') {
this._mode_handlers[mode] = mode;
}
};
@@ -2772,11 +2691,11 @@
*/
WYMeditor.Lexer.prototype.addEntryPattern = function(pattern, mode, new_mode)
{
- if (this._regexes[mode] == undefined) {
+ if (typeof this._regexes[mode] == 'undefined') {
this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
}
this._regexes[mode].addPattern(pattern, new_mode);
- if (this._mode_handlers[new_mode] == undefined) {
+ if (typeof this._mode_handlers[new_mode] == 'undefined') {
this._mode_handlers[new_mode] = new_mode;
}
};
@@ -2791,11 +2710,11 @@
*/
WYMeditor.Lexer.prototype.addExitPattern = function(pattern, mode)
{
- if (this._regexes[mode] == undefined) {
+ if (typeof this._regexes[mode] == 'undefined') {
this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
}
this._regexes[mode].addPattern(pattern, "__exit");
- if (this._mode_handlers[mode] == undefined) {
+ if (typeof this._mode_handlers[mode] == 'undefined') {
this._mode_handlers[mode] = mode;
}
};
@@ -2814,11 +2733,11 @@
*/
WYMeditor.Lexer.prototype.addSpecialPattern = function(pattern, mode, special)
{
- if (this._regexes[mode] == undefined) {
+ if (typeof this._regexes[mode] == 'undefined') {
this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
}
this._regexes[mode].addPattern(pattern, '_'+special);
- if (this._mode_handlers[special] == undefined) {
+ if (typeof this._mode_handlers[special] == 'undefined') {
this._mode_handlers[special] = special;
}
};
@@ -2846,7 +2765,7 @@
*/
WYMeditor.Lexer.prototype.parse = function(raw)
{
- if (this._parser == undefined) {
+ if (typeof this._parser == 'undefined') {
return false;
}
@@ -3597,12 +3516,12 @@
WYMeditor.XhtmlSaxListener.prototype.isBlockTag = function(tag)
{
- return !this.avoided_tags.contains(tag) && this.block_tags.contains(tag);
+ return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.block_tags, tag);
};
WYMeditor.XhtmlSaxListener.prototype.isInlineTag = function(tag)
{
- return !this.avoided_tags.contains(tag) && this.inline_tags.contains(tag);
+ return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.inline_tags, tag);
};
WYMeditor.XhtmlSaxListener.prototype.insertContentAfterClosingTag = function(tag, content)
@@ -3650,7 +3569,7 @@
WYMeditor.WymCssLexer = function(parser, only_wym_blocks)
{
- var only_wym_blocks = (only_wym_blocks == undefined ? true : only_wym_blocks);
+ var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? true : only_wym_blocks);
jQuery.extend(this, new WYMeditor.Lexer(parser, (only_wym_blocks?'Ignore':'WymCss')));
@@ -3661,7 +3580,7 @@
this.addExitPattern("/\\\x2a[<\/\\s]*WYMeditor[>\\s]*\\\x2a/", 'WymCss');
}
- this.addSpecialPattern("\\\x2e[a-z-_0-9]+[\\sa-z]*", 'WymCss', 'WymCssStyleDeclaration');
+ this.addSpecialPattern("\\\x2e[a-z-_0-9]+[\\sa-z1-6]*", 'WymCss', 'WymCssStyleDeclaration');
this.addEntryPattern("/\\\x2a", 'WymCss', 'WymCssComment');
this.addExitPattern("\\\x2a/", 'WymCssComment');
@@ -3686,7 +3605,7 @@
WYMeditor.WymCssParser.prototype.parse = function(raw, only_wym_blocks)
{
- var only_wym_blocks = (only_wym_blocks == undefined ? this.only_wym_blocks : only_wym_blocks);
+ var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? this.only_wym_blocks : only_wym_blocks);
this._Lexer = new WYMeditor.WymCssLexer(this, only_wym_blocks);
this._Lexer.parse(raw);
};
@@ -3704,7 +3623,7 @@
if(status == WYMeditor.LEXER_UNMATCHED){
if(!this._in_style){
this._has_title = true;
- this._current_item = {'title':text.trim()};
+ this._current_item = {'title':WYMeditor.Helper.trim(text)};
}else{
if(this._current_item[this._current_element]){
if(!this._current_item[this._current_element].expressions){
@@ -3722,7 +3641,7 @@
WYMeditor.WymCssParser.prototype.WymCssStyle = function(match, status)
{
if(status == WYMeditor.LEXER_UNMATCHED){
- match = match.trim();
+ match = WYMeditor.Helper.trim(match);
if(match != ''){
this._current_item[this._current_element].style = match;
}
@@ -3780,19 +3699,19 @@
if(typeof details == 'object' && name != 'title'){
this.css_settings.classesItems.push({
- 'name': details.name.trim(),
+ 'name': WYMeditor.Helper.trim(details.name),
'title': style_details.title,
- 'expr' : (details.expressions||details.tags).join(', ').trim()
+ 'expr' : WYMeditor.Helper.trim((details.expressions||details.tags).join(', '))
});
if(details.feedback_style){
this.css_settings.editorStyles.push({
- 'name': '.'+details.name.trim(),
+ 'name': '.'+ WYMeditor.Helper.trim(details.name),
'css': details.feedback_style
});
}
if(details.style){
this.css_settings.dialogStyles.push({
- 'name': '.'+details.name.trim(),
+ 'name': '.'+ WYMeditor.Helper.trim(details.name),
'css': details.style
});
}
@@ -3836,60 +3755,57 @@
return n.parents(jqexpr).slice(0,1);
};
-// String helpers
-if(!String.prototype.insertAt) {
- String.prototype.insertAt = function(inserted, pos) {
- return(this.substr(0,pos) + inserted + this.substring(pos));
- };
-};
+// String & array helpers
-if(!String.prototype.replaceAll) {
- String.prototype.replaceAll = function(old, rep) {
+WYMeditor.Helper = {
+
+ //replace all instances of 'old' by 'rep' in 'str' string
+ replaceAll: function(str, old, rep) {
var rExp = new RegExp(old, "g");
- return(this.replace(rExp, rep));
- };
-};
+ return(str.replace(rExp, rep));
+ },
-if(!String.prototype.trim) {
- String.prototype.trim = function() {
- return this.replace(/^(\s*)|(\s*)$/gm,'');
- };
-};
+ //insert 'inserted' at position 'pos' in 'str' string
+ insertAt: function(str, inserted, pos) {
+ return(str.substr(0,pos) + inserted + str.substring(pos));
+ },
-// Array helpers
+ //trim 'str' string
+ trim: function(str) {
+ return str.replace(/^(\s*)|(\s*)$/gm,'');
+ },
-// from http://forum.de.selfhtml.org/archiv/2004/3/t76079/#m438193 (2007-02-06)
-if(!Array.prototype.contains) {
- Array.prototype.contains = function (elem) {
- for (var i = 0; i < this.length; i++) {
- if (this[i] === elem) return true;
+ //return true if 'arr' array contains 'elem', or false
+ contains: function(arr, elem) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] === elem) return true;
}
return false;
- };
-};
+ },
-if(!Array.prototype.indexof) {
- Array.prototype.indexOf = function (item) {
- var ret=-1;
- for(var i = 0; i < this.length; i++) {
- if (this[i] == item) {
- ret=i;
+ //return 'item' position in 'arr' array, or -1
+ indexOf: function(arr, item) {
+ var ret=-1;
+ for(var i = 0; i < arr.length; i++) {
+ if (arr[i] == item) {
+ ret = i;
break;
}
}
return(ret);
- };
-};
+ },
-if(!Array.prototype.findByName) {
- Array.prototype.findByName = function (name) {
- for(var i = 0; i < this.length; i++) {
- var item = this[i];
+ //return 'item' object in 'arr' array, checking its 'name' property, or null
+ findByName: function(arr, name) {
+ for(var i = 0; i < arr.length; i++) {
+ var item = arr[i];
if(item.name == name) return(item);
}
return(null);
- };
+ }
};
+
+
/*
* WYMeditor : what you see is What You Mean web-based editor
* Copyright (c) 2008 Jean-Francois Hovinne, http://www.wymeditor.org/
@@ -3907,7 +3823,8 @@
* File Authors:
* Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
* Bermi Ferrer (wymeditor a-t bermi dotorg)
- * Frédéric Palluel-Lafleur (fpalluel a-t gmail dotcom)
+ * Frédéric Palluel-Lafleur (fpalluel a-t gmail dotcom)
+ * Jonatan Lundin (jonatan.lundin _at_ gmail.com)
*/
WYMeditor.WymClassExplorer = function(wym) {
@@ -3932,7 +3849,10 @@
this.addCssRules(this._doc, aCss);
- this._doc.title = this._wym._index;
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
//init html value
jQuery(this._doc.body).html(this._wym._html);
@@ -3967,9 +3887,6 @@
//bind external events
this._wym.bindEvents();
- // NOTE v.mische this part will be changed on event-hacking
- jQuery(this._doc).bind("keydown", this.handleKeydown);
-
//post-init functions
if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
@@ -4024,14 +3941,60 @@
WYMeditor.WymClassExplorer.prototype.addCssRule = function(styles, oCss) {
styles.addRule(oCss.name, oCss.css);
+};
+
+WYMeditor.WymClassExplorer.prototype.insert = function(html) {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Overwrite selection with provided html
+ range.pasteHTML(html);
+ } catch (e) { }
+ } else {
+ // Fall back to the internal paste function if there's no selection
+ this.paste(html);
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.wrap = function(left, right) {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Overwrite selection with provided html
+ range.pasteHTML(left + range.text + right);
+ } catch (e) { }
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.unwrap = function() {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Unwrap selection
+ var text = range.text;
+ this._exec( 'Cut' );
+ range.pasteHTML( text );
+ } catch (e) { }
+ }
};
//keyup handler
WYMeditor.WymClassExplorer.prototype.keyup = function() {
this._selected_image = null;
};
-
WYMeditor.WymClassExplorer.prototype.setFocusToNode = function(node) {
var range = this._doc.selection.createRange();
range.moveToElementText(node);
@@ -4040,332 +4003,7 @@
range.select();
node.focus();
};
-
-WYMeditor.WymClassExplorer.prototype.handleKeydown = function(evt) {
-
- //'this' is the doc
- var wym = WYMeditor.INSTANCES[this.title];
-
- // "start" Selection API
- var sel = wym.selection.getSelection();
-
-/*
- // some small tests for the Selection API
- var containers = WYMeditor.MAIN_CONTAINERS.join(",");
- if (sel.isAtStart(containers))
- alert("isAtStart: "+sel.startNode.parentNode.nodeName);
- if (sel.isAtEnd(containers))
- alert("isAtEnd: "+sel.endNode.parentNode.nodeName);
- if (evt.keyCode==WYMeditor.KEY.DELETE) {
- // if deleteIfExpanded wouldn't work, no selected text would be
- // deleted if you press del-key
- if (sel.deleteIfExpanded())
- return false;
- }
- if (evt.keyCode==WYMeditor.KEY.HOME) {
- // if cursorToStart won't work, the cursor won't be set to start
- // if you press home-key
- sel.cursorToStart(sel.container);
- return false;
- }
- if (evt.keyCode==WYMeditor.KEY.END)
- {
- // if cursorToEnd won't work, the cursor won't be set to the end
- // if you press end-key
- sel.cursorToEnd(sel.container);
- return false;
- }
-*/
-};
-
-/********** SELECTION API **********/
-/*
-Class: WymSelExplorer
- Implementation of the Selection API for Microsoft Internet Explorer
-
-Properties:
- original - (object) Selection Object
- isCollapsed - (boolean) True if selection is collapsed to single position
- startNode - (DOM node) Node where the selection starts
- startOffset - (integer) Offset of the position in startNode
- endNode - (DOM node) Node where the selection ends
- endOffset - (integer) Offset of the position in endNode
- container - (DOM node) Node which includes the whole selection
-
-Example:
- "|" marks the start and the end of the selection.
- (start code)
-
A sm|all examp|le.
- (end code) - - isCollapsed - false - startNode - "small" - startOffset - 2 - endNode - " example" - endOffset - 6 - container -...
-*/ -WYMeditor.WymSelExplorer = function(wym) { - this._wym = wym; -}; - -WYMeditor.WymSelExplorer.prototype = { - /* - Property: getSelection - Return a Selection API object - - Example: - (start code) - var sel = wym.selection.getSelection(); - (end code) - - Returns: - (object) Selection API object - */ - getSelection: function() { - var sel = this._wym._iframe.contentWindow.document.selection; - var range = sel.createRange(); - - this.original = sel; - - if (sel.type == "None") - this.isCollapsed = true; - else - this.isCollapsed = false; - - // get start of the selection (resp. cursor position if collapsed) - var selStart = this._getNodeAndOffset(range.duplicate(), true); - this.startNode = selStart.node; - this.startOffset = selStart.offset; - - if (this.isCollapsed) { - this.endNode = this.startNode; - this.endOffset = this.startOffset; - } - else { - var selEnd = this._getNodeAndOffset(range.duplicate(), false); - this.endNode = selEnd.node; - this.endOffset = selEnd.offset; - } - - this.container = jQuery(range.parentElement()).parentsOrSelf( - WYMeditor.MAIN_CONTAINERS.join(","))[0]; - - return this; - }, - - - _getNodeAndOffset: function(range, collapseToStart) { - var parentElement = range.parentElement(); - - // range from the beginning of parentElement to cursor position - var parentRange = range.duplicate(); - - // length of parentElement's text - var parentLength; - - // offset from the beginning of parentElements to the cursor position - var parentOffset = 0; - - // offset of the position to its node - var offset = 0; - - // collapse to start or end - range.collapse(collapseToStart); - // select parent node and set range from the beginning of that node to - // the cursor position - parentRange.moveToElementText(parentElement); - parentLength = parentRange.text.length; - // XXX v.mische fix it - var backwardRange = parentRange.duplicate(); - var searchRange = parentRange.duplicate(); - - - parentRange.setEndPoint("EndToStart", range); - parentOffset = parentRange.text.length; - - // direction to search - // 1 = forward - // -1 = backward - var direction = 1; - - // where to start to find the position - var childPosition = 0; - - // ratio of the cursor position to the total parentElement text length - var offsetRatio = parentOffset/parentLength; - - // offset from start (if search direction==-1: end) to the child node - // we are currently at while searching the position - // finally: position of the cursor within its node - var offset = 0; - - var childNodes = parentElement.childNodes; - - // try to find a better start position for searching - //if (childNodes.length>20 && offsetRatio>0.1) { - if (childNodes.length>20 && offsetRatio>0.1) { - // range around the appropriate node - var childRange = searchRange.duplicate(); - // length from start to the beginning of a node near the node - // of the position we are looking for - //var childOffsetRange = parentRange.duplicate(); - - childPosition = Math.round(offsetRatio * childNodes.length-1); - - if (childPosition <= 0) - childPosition = 1; - - // moveToElementText doesn't work with text nodes - while ((childNodes[childPosition].nodeType == WYMeditor.NODE.TEXT - || childNodes[childPosition].nodeName == "BR") - && childPosition > 0) { - childPosition--; - } - - if (childPosition > 0) { - childRange.moveToElementText(childNodes[childPosition]); - // Range from start of parent node to the start of the proposed - // child node - childRange.setEndPoint("EndToStart", searchRange); - - // search forward - if (parentOffset > childRange.text.length) { - direction = 1; - searchRange.setEndPoint("StartToEnd", childRange); - searchRange.setEndPoint("EndToEnd", parentRange); - } - // search backwards - else { - direction = -1; - searchRange.setEndPoint("StartToEnd", parentRange); - searchRange.setEndPoint("EndToEnd", childRange); - - // caused by ranges - childPosition--; - } - - } - // start at the beginning - else { - direction = 1; - searchRange = parentRange.duplicate(); - } - } - else { - if (direction == 1) - searchRange = parentRange.duplicate(); - else { - searchRange = backwardRange.duplicate(); - searchRange.setEndPoint("StartToEnd", parentRange); - } - } - - var node=parentElement.childNodes[childPosition]; - - - var currentOffsetRange = searchRange.duplicate(); - if (direction==1) { - // text length of the current node - var nodeLength = 0; - var br = 0; - - while (node) { - if (node.nodeName == 'BR') - br++; - nodeLength = this._getTextLength(node); - if (currentOffsetRange.text.length > nodeLength) { - currentOffsetRange.moveStart('character', nodeLength + br); - br = 0; - } - else - break; - - node = node.nextSibling; - } - offset = currentOffsetRange.text.length; - } - // go backwards - else { - var nodeLength = 0; - var br = 0; - - while (node) { - if (node.nodeName == 'BR') - br++; - nodeLength = this._getTextLength(node); - - if (currentOffsetRange.text.length > nodeLength) { - var length = currentOffsetRange.text.length; - currentOffsetRange.moveEnd('character', -(nodeLength+br)); - // NOTE v.mische Sometimes the range isn't move as far as - // expected. I don't know whenwhy it happens, but it does - if (br == 0) { - var diff = (length-nodeLength) - currentOffsetRange.text.length; - currentOffsetRange.moveEnd('character', diff); - } - br = 0; - } - else - break; - - if (node.previousSibling) - node = node.previousSibling; - else - break; - } - - offset = nodeLength - currentOffsetRange.text.length; - } - - return {'node': node, 'offset': offset}; - }, - - /* - Property: _getTextLength - Get the length of the text of a DOM node. - - Arguments: - node - (DOM node) [required] The node you what to know the length of. - - Example: - (start code) - var node = document.createTextNode("foo"); - _getTextLength(node) // returns 3 - (end code) - - Returns: - (int) Length of a DOM node. - */ - _getTextLength: function(node) { - if (node.nodeType == WYMeditor.NODE.ELEMENT) - return node.innerText.length; - else if (node.nodeType == WYMeditor.NODE.TEXT) - return node.data.length; - }, - - /* - Property: deleteIfExpanded - Delete contents of a selection - - Example: - (start code) - var sel = wym.selection.getSelection(); - sel.deleteIfExpanded(); - (end code) - - Returns: - (boolean) True if it was expanded and thus deleted - */ - deleteIfExpanded: function() { - if(!this.isCollapsed) { - this.original.clear(); - return true; - } - return false; - } -}; - + /* * WYMeditor : what you see is What You Mean web-based editor * Copyright (c) 2008 Jean-Francois Hovinne, http://www.wymeditor.org/ @@ -4407,6 +4045,9 @@ this.addCssRules(this._doc, aCss); this._doc.title = this._wym._index; + + //set the text direction + jQuery('html', this._doc).attr('dir', this._options.direction); //init html value this.html(this._wym._html); @@ -4441,10 +4082,10 @@ */ WYMeditor.WymClassMozilla.prototype.html = function(html) { - if(html) { + if(typeof html === 'string') { //disable designMode - this._doc.designMode = "off"; + try { this._doc.designMode = "off"; } catch(e) { }; //replace em by i and strong by bold //(designMode issue) @@ -4533,37 +4174,6 @@ //'this' is the doc var wym = WYMeditor.INSTANCES[this.title]; - // "start" Selection API - var sel = wym.selection.getSelection(); - -/* - // some small tests for the Selection API - var containers = WYMeditor.MAIN_CONTAINERS.join(","); - if (sel.isAtStart(containers)) - alert("isAtStart: "+sel.startNode.parentNode.nodeName); - if (sel.isAtEnd(containers)) - alert("isAtEnd: "+sel.endNode.parentNode.nodeName); - if (evt.keyCode==WYMeditor.KEY.DELETE) { - // if deleteIfExpanded wouldn't work, no selected text would be - // deleted if you press del-key - if (sel.deleteIfExpanded()) - return false; - } - if (evt.keyCode==WYMeditor.KEY.HOME) { - // if cursorToStart won't work, the cursor won't be set to start - // if you press home-key - sel.cursorToStart(sel.container); - return false; - } - if (evt.keyCode==WYMeditor.KEY.END) - { - // if cursorToEnd won't work, the cursor won't be set to the end - // if you press end-key - sel.cursorToEnd(sel.container); - return false; - } -*/ - if(evt.ctrlKey){ if(evt.keyCode == 66){ //CTRL+b => STRONG @@ -4675,83 +4285,6 @@ return false; }; -/********** SELECTION API **********/ - -WYMeditor.WymSelMozilla = function(wym) { - this._wym = wym; -}; - -WYMeditor.WymSelMozilla.prototype = { - getSelection: function() { - var _sel = this._wym._iframe.contentWindow.getSelection(); - // NOTE v.mische can startNode/endNote be phantom nodes? - this.startNode = _sel.getRangeAt(0).startContainer; - this.endNode = _sel.getRangeAt(0).endContainer; - this.startOffset = _sel.getRangeAt(0).startOffset; - this.endOffset = _sel.getRangeAt(0).endOffset; - this.isCollapsed = _sel.isCollapsed; - this.original = _sel; - this.container = jQuery(this.startNode).parentsOrSelf( - WYMeditor.MAIN_CONTAINERS.join(","))[0]; - - return this; - }, - - cursorToStart: function(jqexpr) { - if (jqexpr.nodeType == WYMeditor.NODE.TEXT) - jqexpr = jqexpr.parentNode; - - var firstTextNode = jQuery(jqexpr)[0]; - - while (firstTextNode.nodeType!=WYMeditor.NODE.TEXT) { - if (!firstTextNode.hasChildNodes()) - break; - firstTextNode = firstTextNode.firstChild; - } - - if (WYMeditor.isPhantomNode(firstTextNode)) - firstTextNode = firstTextNode.nextSibling; - - // e.g. an - if (firstTextNode.nodeType == WYMeditor.NODE.ELEMENT) - this.original.collapse(firstTextNode.parentNode, 0); - else - this.original.collapse(firstTextNode, 0); - }, - - cursorToEnd: function(jqexpr) { - if (jqexpr.nodeType == WYMeditor.NODE.TEXT) - jqexpr = jqexpr.parentNode; - - var lastTextNode = jQuery(jqexpr)[0]; - - while (lastTextNode.nodeType!=WYMeditor.NODE.TEXT) { - if (!lastTextNode.hasChildNodes()) - break; - lastTextNode = lastTextNode.lastChild; - } - - if (WYMeditor.isPhantomNode(lastTextNode)) - lastTextNode = lastTextNode.previousSibling; - - // e.g. an - if (lastTextNode.nodeType == WYMeditor.NODE.ELEMENT) - this.original.collapse(lastTextNode.parentNode, - lastTextNode.parentNode.childNodes.length); - else - this.original.collapse(lastTextNode, lastTextNode.length); - }, - - deleteIfExpanded: function() { - if(!this.original.isCollapsed) { - this.original.deleteFromDocument(); - return true; - } - return false; - } -}; - - /* * WYMeditor : what you see is What You Mean web-based editor * Copyright (c) 2008 Jean-Francois Hovinne, http://www.wymeditor.org/ @@ -4789,6 +4322,9 @@ this.addCssRules(this._doc, aCss); this._doc.title = this._wym._index; + + //set the text direction + jQuery('html', this._doc).attr('dir', this._options.direction); //init designMode this._doc.designMode = "on"; @@ -4799,12 +4335,6 @@ //pre-bind functions if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this); - //hide indent and outdent until supported - jQuery(this._box).find(this._options.toolSelector - + '[@name=' + WYMeditor.INDENT +']').hide(); - jQuery(this._box).find(this._options.toolSelector - + '[@name=' + WYMeditor.OUTDENT +']').hide(); - //bind external events this._wym.bindEvents(); @@ -4823,19 +4353,9 @@ WYMeditor.WymClassOpera.prototype._exec = function(cmd,param) { - switch(cmd) { + if(param) this._doc.execCommand(cmd,false,param); + else this._doc.execCommand(cmd); - case WYMeditor.INDENT: case WYMeditor.OUTDENT: - //TODO: support nested lists - //Opera creates blockquotes - this.status("Unsupported feature."); - break; - default: - if(param) this._doc.execCommand(cmd,false,param); - else this._doc.execCommand(cmd); - break; - } - this.listen(); }; @@ -4860,41 +4380,13 @@ //'this' is the doc var wym = WYMeditor.INSTANCES[this.title]; - - // "start" Selection API - var sel = wym.selection.getSelection(); + var sel = wym._iframe.contentWindow.getSelection(); + startNode = sel.getRangeAt(0).startContainer; -/* - // some small tests for the Selection API - var containers = WYMeditor.MAIN_CONTAINERS.join(","); - if (sel.isAtStart(containers)) - alert("isAtStart: "+sel.startNode.parentNode.nodeName); - if (sel.isAtEnd(containers)) - alert("isAtEnd: "+sel.endNode.parentNode.nodeName); - if (evt.keyCode==WYMeditor.KEY.DELETE) { - // if deleteIfExpanded wouldn't work, no selected text would be - // deleted if you press del-key - if (sel.deleteIfExpanded()) - return false; - } - if (evt.keyCode==WYMeditor.KEY.HOME) { - // if cursorToStart won't work, the cursor won't be set to start - // if you press home-key - sel.cursorToStart(sel.container); - return false; - } - if (evt.keyCode==WYMeditor.KEY.END) - { - // if cursorToEnd won't work, the cursor won't be set to the end - // if you press end-key - sel.cursorToEnd(sel.container); - return false; - } -*/ - - //Get a P instead of no container - if(!sel.container + if(!jQuery(startNode).parentsOrSelf( + WYMeditor.MAIN_CONTAINERS.join(","))[0] + && !jQuery(startNode).parentsOrSelf('li') && evt.keyCode != WYMeditor.KEY.ENTER && evt.keyCode != WYMeditor.KEY.LEFT && evt.keyCode != WYMeditor.KEY.UP @@ -4919,83 +4411,6 @@ }; -/********** SELECTION API **********/ - -WYMeditor.WymSelOpera = function(wym) { - this._wym = wym; -}; - -WYMeditor.WymSelOpera.prototype = { - getSelection: function() { - var _sel = this._wym._iframe.contentWindow.getSelection(); - // NOTE v.mische can startNode/endNote be phantom nodes? - this.startNode = _sel.getRangeAt(0).startContainer; - this.endNode = _sel.getRangeAt(0).endContainer; - this.startOffset = _sel.getRangeAt(0).startOffset; - this.endOffset = _sel.getRangeAt(0).endOffset; - this.isCollapsed = _sel.isCollapsed; - this.original = _sel; - this.container = jQuery(this.startNode).parentsOrSelf( - WYMeditor.MAIN_CONTAINERS.join(","))[0]; - - return this; - }, - - cursorToStart: function(jqexpr) { - if (jqexpr.nodeType == WYMeditor.NODE.TEXT) - jqexpr = jqexpr.parentNode; - - var firstTextNode = jQuery(jqexpr)[0]; - - while (firstTextNode.nodeType!=WYMeditor.NODE.TEXT) { - if (!firstTextNode.hasChildNodes()) - break; - firstTextNode = firstTextNode.firstChild; - } - - if (WYMeditor.isPhantomNode(firstTextNode)) - firstTextNode = firstTextNode.nextSibling; - - // e.g. an - if (firstTextNode.nodeType == WYMeditor.NODE.ELEMENT) - this.original.collapse(firstTextNode.parentNode, 0); - else - this.original.collapse(firstTextNode, 0); - }, - - cursorToEnd: function(jqexpr) { - if (jqexpr.nodeType == WYMeditor.NODE.TEXT) - jqexpr = jqexpr.parentNode; - - var lastTextNode = jQuery(jqexpr)[0]; - - while (lastTextNode.nodeType!=WYMeditor.NODE.TEXT) { - if (!lastTextNode.hasChildNodes()) - break; - lastTextNode = lastTextNode.lastChild; - } - - if (WYMeditor.isPhantomNode(lastTextNode)) - lastTextNode = lastTextNode.previousSibling; - - // e.g. an - if (lastTextNode.nodeType == WYMeditor.NODE.ELEMENT) - this.original.collapse(lastTextNode.parentNode, - lastTextNode.parentNode.childNodes.length); - else - this.original.collapse(lastTextNode, lastTextNode.length); - }, - - deleteIfExpanded: function() { - if(!this.original.isCollapsed) { - this.original.deleteFromDocument(); - return true; - } - return false; - } -}; - - /* * WYMeditor : what you see is What You Mean web-based editor * Copyright (c) 2008 Jean-Francois Hovinne, http://www.wymeditor.org/ @@ -5036,6 +4451,9 @@ this._doc.title = this._wym._index; + //set the text direction + jQuery('html', this._doc).attr('dir', this._options.direction); + //init designMode this._doc.designMode = "on"; @@ -5143,37 +4561,6 @@ //'this' is the doc var wym = WYMeditor.INSTANCES[this.title]; - // "start" Selection API - //var sel = wym.selection.getSelection(); - -/* - // some small tests for the Selection API - var containers = WYMeditor.MAIN_CONTAINERS.join(","); - if (sel.isAtStart(containers)) - alert("isAtStart: "+sel.startNode.parentNode.nodeName); - if (sel.isAtEnd(containers)) - alert("isAtEnd: "+sel.endNode.parentNode.nodeName); - if (evt.keyCode==WYMeditor.KEY.DELETE) { - // if deleteIfExpanded wouldn't work, no selected text would be - // deleted if you press del-key - if (sel.deleteIfExpanded()) - return false; - } - if (evt.keyCode==WYMeditor.KEY.HOME) { - // if cursorToStart won't work, the cursor won't be set to start - // if you press home-key - sel.cursorToStart(sel.container); - return false; - } - if (evt.keyCode==WYMeditor.KEY.END) - { - // if cursorToEnd won't work, the cursor won't be set to the end - // if you press end-key - sel.cursorToEnd(sel.container); - return false; - } -*/ - if(evt.ctrlKey){ if(evt.keyCode == 66){ //CTRL+b => STRONG @@ -5208,8 +4595,13 @@ if(container && container.tagName.toLowerCase() == WYMeditor.PRE) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //create P after PRE } + + //fix #112 + if(evt.keyCode == 13 && evt.shiftKey) { + wym._exec('InsertLineBreak'); + } - else if(evt.keyCode != 8 + if(evt.keyCode != 8 && evt.keyCode != 17 && evt.keyCode != 46 && evt.keyCode != 224 @@ -5230,11 +4622,12 @@ name == "i" || name == "sub" || name == "sup" || - name == "a" + name == "a" || + name == "span" //fix #110 ) name = container.parentNode.tagName.toLowerCase(); - if(name == WYMeditor.BODY) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); + if(name == WYMeditor.BODY || name == WYMeditor.DIV) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //fix #110 for DIV } }; @@ -5260,6 +4653,11 @@ var tag = new_tag; this._tag_stack.push(new_tag); attributes.style = ''; + + //should fix #125 - also removed the xhtml() override + if(typeof attributes['class'] == 'string') + attributes['class'] = attributes['class'].replace(/apple-style-span/gi, ''); + } else { return; } @@ -5276,89 +4674,3 @@ if(/super/.test(style)) return 'sup'; return false; }; - -/* @name xhtml - * @description Cleans up the HTML - */ -WYMeditor.editor.prototype.xhtml = function() { - jQuery('.Apple-style-span', this._doc.body).removeClass('Apple-style-span'); - return this.parser.parse(this.html()); -}; - -/********** SELECTION API **********/ - -WYMeditor.WymSelSafari = function(wym) { - this._wym = wym; -}; - -WYMeditor.WymSelSafari.prototype = { - getSelection: function() { - var _sel = this._wym._iframe.contentWindow.getSelection(); - // NOTE v.mische can startNode/endNote be phantom nodes? - this.startNode = _sel.getRangeAt(0).startContainer; - this.endNode = _sel.getRangeAt(0).endContainer; - this.startOffset = _sel.getRangeAt(0).startOffset; - this.endOffset = _sel.getRangeAt(0).endOffset; - this.isCollapsed = _sel.isCollapsed; - this.original = _sel; - this.container = jQuery(this.startNode).parentsOrSelf( - WYMeditor.MAIN_CONTAINERS.join(","))[0]; - - return this; - }, - - cursorToStart: function(jqexpr) { - if (jqexpr.nodeType == WYMeditor.NODE.TEXT) - jqexpr = jqexpr.parentNode; - - var firstTextNode = jQuery(jqexpr)[0]; - - while (firstTextNode.nodeType!=WYMeditor.NODE.TEXT) { - if (!firstTextNode.hasChildNodes()) - break; - firstTextNode = firstTextNode.firstChild; - } - - if (WYMeditor.isPhantomNode(firstTextNode)) - firstTextNode = firstTextNode.nextSibling; - - // e.g. an - if (firstTextNode.nodeType == WYMeditor.NODE.ELEMENT) - this.original.collapse(firstTextNode.parentNode, 0); - else - this.original.collapse(firstTextNode, 0); - }, - - cursorToEnd: function(jqexpr) { - if (jqexpr.nodeType == WYMeditor.NODE.TEXT) - jqexpr = jqexpr.parentNode; - - var lastTextNode = jQuery(jqexpr)[0]; - - while (lastTextNode.nodeType!=WYMeditor.NODE.TEXT) { - if (!lastTextNode.hasChildNodes()) - break; - lastTextNode = lastTextNode.lastChild; - } - - if (WYMeditor.isPhantomNode(lastTextNode)) - lastTextNode = lastTextNode.previousSibling; - - // e.g. an - if (lastTextNode.nodeType == WYMeditor.NODE.ELEMENT) - this.original.collapse(lastTextNode.parentNode, - lastTextNode.parentNode.childNodes.length); - else - this.original.collapse(lastTextNode, lastTextNode.length); - }, - - deleteIfExpanded: function() { - if(!this.original.isCollapsed) { - this.original.deleteFromDocument(); - return true; - } - return false; - } -}; - -