diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 05dc3b802..5693f4390 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -62,6 +62,7 @@ - fixed #10625: Map point no save button - fixed #10639: Map: Can't edit or delete points - fixed #10640: Map: points not working correctly + - fixed Codearea editor to work a lot better and be more compatible 7.7.17 - fixed #10697: Story: Image crowds text diff --git a/lib/WebGUI/Form/Codearea.pm b/lib/WebGUI/Form/Codearea.pm index 01fb4aeef..cff4b5674 100644 --- a/lib/WebGUI/Form/Codearea.pm +++ b/lib/WebGUI/Form/Codearea.pm @@ -16,6 +16,7 @@ package WebGUI::Form::Codearea; use strict; use base 'WebGUI::Form::Textarea'; +use HTML::Entities qw(encode_entities decode_entities); use WebGUI::International; =head1 NAME @@ -114,6 +115,21 @@ sub getName { #------------------------------------------------------------------- +=head2 getValue ( [value] ) + +Return the value, HTML decoded + +=cut + +sub getValue { + my ( $self, @args ) = @_; + my $value = $self->SUPER::getValue( @args ); + $self->session->log->warn( $value ); + return decode_entities( $value ); +} + +#------------------------------------------------------------------- + =head2 isDynamicCompatible ( ) A class method that returns a boolean indicating whether this control is compatible with the DynamicField control. @@ -136,7 +152,7 @@ sub toHtml { my $self = shift; my ($style, $url, $stow) = $self->session->quick(qw(style url stow)); - my $value = $self->fixMacros($self->fixTags($self->fixSpecialCharacters(scalar $self->getOriginalValue))); + my $value = encode_entities( $self->fixMacros($self->fixTags($self->fixSpecialCharacters(scalar $self->getOriginalValue))) ); my $width = $self->get('width') || 400; my $height = $self->get('height') || 150; my $id = $self->get('id'); @@ -146,33 +162,31 @@ sub toHtml { my $styleAttr = $self->get('style'); $style->setLink($url->extras("yui/build/resize/assets/skins/sam/resize.css"), {type=>"text/css", rel=>"stylesheet"}); - $style->setScript($url->extras("yui/build/utilities/utilities.js"), {type=>"text/javascript"}); - $style->setScript($url->extras("yui/build/resize/resize-min.js"), {type=>"text/javascript"}); - $style->setScript($url->extras('editarea/edit_area/edit_area_full.js'), {type=>"text/javascript"}); + $style->setLink($url->extras("yui/build/assets/skins/sam/skin.css"), {type=>"text/css", rel=>"stylesheet"}); + $style->setLink($url->extras('yui/build/logger/assets/skins/sam/logger.css'), {type=>"text/css", rel=>"stylesheet"}); + $style->setScript($url->extras("yui/build/yahoo-dom-event/yahoo-dom-event.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/utilities/utilities.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/container/container_core-min.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/menu/menu-min.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/button/button-min.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/element/element-min.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/dragdrop/dragdrop-min.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/resize/resize-min.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/editor/editor-debug.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui/build/logger/logger.js"),{type=>"text/javascript"}); + $style->setScript($url->extras("yui-webgui/build/code-editor/code-editor.js"),{type=>"text/javascript"}); + my $codeCss = $url->extras("yui-webgui/build/code-editor/code.css"); my $out = <<"END_HTML"; -
- -
+ END_HTML return $out; diff --git a/www/extras/yui-webgui/build/code-editor/code-editor.js b/www/extras/yui-webgui/build/code-editor/code-editor.js new file mode 100755 index 000000000..2d1a82a73 --- /dev/null +++ b/www/extras/yui-webgui/build/code-editor/code-editor.js @@ -0,0 +1,294 @@ +(function() { + var Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + Lang = YAHOO.lang + ; + + YAHOO.widget.CodeEditor = function (id, cfg) { + // Disable Editor configs that don't apply + cfg["animate"] = false; + cfg["dompath"] = false; + + // Default toolbar is different + cfg["toolbar"] = cfg["toolbar"] || { + titlebar : "Code Editor", + buttons : [] + }; + + YAHOO.widget.CodeEditor.superclass.constructor.call(this, id, cfg); + + // Allow us to have no buttons + // This will be fixed in a future version of YUI Editor + YAHOO.widget.Toolbar.prototype.disableAllButtons + = function () { + if (!this._buttonList) { + this._buttonList = []; + } + if (this.get('disabled')) { + return false; + } + var len = this._buttonList.length; + for (var i = 0; i < len; i++) { + this.disableButton(this._buttonList[i]); + } + }; + // End allow us to have no buttons + + this.on('editorContentLoaded', function() { + // Add the code stylesheet + var link = this._getDoc().createElement('link'); + link.rel = "stylesheet"; + link.type = "text/css"; + link.href = this.get('css_url'); + this._getDoc().getElementsByTagName('head')[0].appendChild(link); + // Highlight the initial value + if ( !this.browser.ie ) { // IE puts "!!CURSOR HERE!!" in the main doc, not the iframe... + this.highlight(false); + } + // Setup resize + if ( this.status ) { + this._writeStatus(); + this._setupResize(); + } + }, this, true); + this.on('editorKeyUp', function(ev) { + + // Don't highlight arrows or modifiers + if ( ( ev.ev.keyCode > 36 && ev.ev.keyCode < 41 ) + || ev.ev.keyCode == 16 || ev.ev.keyCode == 17 + || ev.ev.keyCode == 18 || ev.ev.keyCode == 91 // Safari "command" + || ev.ev.keyCode == 224 // Firefox "command" + ) { + return; + } + + // TODO: Don't re-highlight if there is a selection + // That is the problem we're trying to avoid with disabling + // highlighting for arrows and modifiers + + // Highlight every keypress + Lang.later(10, this, this.highlight); + Lang.later(100, this, this._writeStatus); + }, this, true); + + //Borrowed this from CodePress: http://codepress.sourceforge.net + this.cc = '\u2009'; // carret char + this.keywords = [ + { code: /(<DOCTYPE.*?-->.)/g, tag: '$1' }, // comments + { code: /(<[^!]*?>)/g, tag: '$1' }, // all tags + { code: /(<!--.*?-->.)/g, tag: '$1' }, // comments + { code: /\b(YAHOO|widget|util|Dom|Event|lang)\b/g, tag: '$1' }, // reserved words + { code: /\b(break|continue|do|for|new|this|void|case|default|else|function|return|typeof|while|if|label|switch|var|with|catch|boolean|int|try|false|throws|null|true|goto)\b/g, tag: '$1' }, // reserved words + { code: /\"(.*?)(\"|
|<\/P>)/gi, tag: '"$1$2' }, // strings double quote + { code: /\'(.*?)(\'|
|<\/P>)/gi, tag: '\'$1$2' }, // strings single quote + { code: /\b(alert|isNaN|parent|Array|parseFloat|parseInt|blur|clearTimeout|prompt|prototype|close|confirm|length|Date|location|Math|document|element|name|self|elements|setTimeout|navigator|status|String|escape|Number|submit|eval|Object|event|onblur|focus|onerror|onfocus|onclick|top|onload|toString|onunload|unescape|open|valueOf|window|onmouseover|innerHTML)\b/g, tag: '$1' }, // special words + { code: /([^:]|^)\/\/(.*?)(//$2$3' }, // comments // + { code: /\/\*(.*?)\*\//g, tag: '/*$1* /' } // comments / * */ + ]; + //End Borrowed Content + + }; + Lang.extend( YAHOO.widget.CodeEditor, YAHOO.widget.Editor, { + /** + * @property _defaultCSS + * @description The default CSS used in the config for 'css'. This way you can add to the config like this: { css: YAHOO.widget.SimpleEditor.prototype._defaultCSS + 'ADD MYY CSS HERE' } + * @type String + */ + _defaultCSS: 'html { height: 95%; } body { background-color: #fff; font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } .warning-localfile { border-bottom: 1px dashed red !important; } .yui-busy { cursor: wait !important; } img.selected { border: 2px dotted #808080; } img { cursor: pointer !important; border: none; } body.ptags.webkit div { margin: 11px 0; }' + }); + + + YAHOO.widget.CodeEditor.prototype._cleanIncomingHTML = function(str) { + //   before
for IE7 so lines show up correctly + str = str.replace(/\r?\n/g, " 
"); + return str; + }; + + /* Override to fix problem with the rest of what the normal _handleFormSubmit does + * ( it doesn't properly click the correct submit button ) + */ + YAHOO.widget.CodeEditor.prototype._handleFormSubmit = function () { + this.saveHTML(); + return; + }; + /* End override to fix problem */ + + YAHOO.widget.CodeEditor.prototype._writeStatus = function () { + if ( this.status ) { + var text = this.getEditorText(); + this.status.innerHTML + = 'C: ' + text.length + + ' L: ' + text.split(/\r?\n/).length + ; + } + }; + + /** + * @private + * @method _setupResize + * @description Creates the Resize instance and binds its events. + */ + YAHOO.widget.CodeEditor.prototype._setupResize + = function() { + if (!YAHOO.util.DD || !YAHOO.util.Resize) { return false; } + if (this.get('resize')) { + var config = {}; + Lang.augmentObject(config, this._resizeConfig); //Break the config reference + this.resize = new YAHOO.util.Resize(this.get('element_cont').get('element'), config); + this.resize.on('resize', function(args) { + var anim = this.get('animate'); + this.set('animate', false); + this.set('width', args.width + 'px'); + var h = args.height, + th = (this.toolbar.get('element').clientHeight + 2), + dh = 0; + if (this.status) { + dh = (this.status.clientHeight + 1); //It has a 1px top border.. + } + var newH = (h - th - dh); + this.set('height', newH + 'px'); + this.get('element_cont').setStyle('height', ''); + this.set('animate', anim); + }, this, true); + } + }; + + YAHOO.widget.CodeEditor.prototype.cleanHTML = function (html) { + if (!html) { + html = this.getEditorHTML(); + } + html = html.replace(/ /g," "); + html = html.replace(/ ?
/gi,'\n'); + html = html.replace(/<[^>]+>/g,''); + // Remove spaces at end of lines + html = html.replace(/ +\r?\n/g,""); + return html; + }; + + YAHOO.widget.CodeEditor.prototype.focusCaret = function() { + if (this.browser.gecko) { + if (this._getWindow().find(this.cc)) { + this._getSelection().getRangeAt(0).deleteContents(); + } + } else if (this.browser.webkit || this.browser.ie || this.browser.opera) { + var cur = this._getDoc().getElementById('cur'); + if ( cur ) { + cur.id = ''; + cur.innerHTML = ''; + this._selectNode(cur); + } + } + }; + + YAHOO.widget.CodeEditor.prototype.getEditorText + = function () { + return this.cleanHTML( this.getEditorHTML() ); + }; + + YAHOO.widget.CodeEditor.prototype.highlight = function(focus) { + + // Opera support is not working yet + if ( this.browser.opera ) { + return; + } + // Firefox < 3 support is not working yet + if ( this.browser.gecko && this.browser.gecko <= 1.8 ) { + return; + } + + if (!focus) { + if (this.browser.gecko) { + this._getSelection().getRangeAt(0).insertNode(this._getDoc().createTextNode(this.cc)); + } else if (this.browser.webkit || this.browser.ie || this.browser.opera) { + try { + this.execCommand('inserthtml', '!!CURSOR_HERE!!'); + } + catch (e) {} + } + } + var html = ''; + html = this._getDoc().body.innerHTML; + //if (this.browser.opera) { + // html = html.replace(/<(?!span|\/span|br).*?>/gi,''); + //} else + if (this.browser.webkit) { + //YAHOO.log('1: ' + html); + html = html.replace(/<\/div>/ig, ''); + html = html.replace(/
/ig, '
'); + html = html.replace(/
/ig, '
'); + html = html.replace(/
/ig,'\n'); + html = html.replace(/<.*?>/g,''); + html = html.replace(/\r?\n/g,'
'); + //YAHOO.log('2: ' + html); + } else { + if (this.browser.ie) { + html = html.replace(/<\/SPAN>/ig, ''); + } + YAHOO.log(html); + //   before
for IE7 + html = html.replace(/( |!!CURSOR_HERE!!)?]*>/gi,'$1\n'); + html = html.replace(/<[^>]*>/g,''); + html = html.replace(/\r?\n/g,'
'); + //   between
for IE6 + html = html.replace(/]*>(!!CURSOR_HERE!!)?]*>/gi, '
$1 
'); + YAHOO.log(html); + } + for (var i = 0; i < this.keywords.length; i++) { + html = html.replace(this.keywords[i].code, this.keywords[i].tag); + } + YAHOO.log("AFTER HIGHLIGHT:" + html); + html = html.replace('!!CURSOR_HERE!!', '|'); + + this._getDoc().body.innerHTML = html; + if (!focus) { + this.focusCaret(); + } + }; + + /** + * @method initAttributes + * @description Initializes all of the configuration attributes used to create + * the editor. + * @param {Object} attr Object literal specifying a set of + * configuration attributes used to create the editor. + */ + YAHOO.widget.CodeEditor.prototype.initAttributes + = function(attr) { + YAHOO.widget.CodeEditor.superclass.initAttributes.call(this, attr); + var self = this; + /** + * @attribute status + * @description Toggle the display of a status line below the editor + * @default false + * @type Boolean + */ + this.setAttributeConfig('status', { + value: attr.status || false, + method: function(status) { + if (status && !this.status) { + this.status = document.createElement('DIV'); + this.status.id = this.get('id') + '_status'; + Dom.addClass(this.status, 'dompath'); // Piggy-back on Editor's dompath + this.get('element_cont').get('firstChild').appendChild(this.status); + if (this.get('iframe')) { + this._writeStatus(); + } + } else if (!status && this.status) { + this.status.parentNode.removeChild(this.status); + this.status = null; + } + } + }); + /** + * @attribute css_url + * @description The URL to the CSS file for the inside of the code editor + * @default 'code.css' + * @type String + */ + this.setAttributeConfig('css_url', { + value: attr.css_url || 'code.css' + } ); + }; + +})(); + diff --git a/www/extras/yui-webgui/build/code-editor/code.css b/www/extras/yui-webgui/build/code-editor/code.css new file mode 100755 index 000000000..783326044 --- /dev/null +++ b/www/extras/yui-webgui/build/code-editor/code.css @@ -0,0 +1,9 @@ +body { background:white url(line-numbers.png) repeat-y scroll 0pt -4px; font-family:monospace; font-size:13px; height:100%; line-height:16px; margin-left:32px; margin-top:8px; white-space:pre; } +b, i, s, u, a, em, tt, ins, big, cite, strong, var, dfn {text-decoration:none;font-weight:normal;font-style:normal;font-size:13px;} +b, cite {color:#7F0055;font-weight:bold;} /* reserved words */ +u {color:darkblue;font-weight:bold;} /* special words */ +i, i b, i s, i u {color:green;font-weight:normal;} /* comments */ +s, s b, s u {color:#2A00FF;font-weight:normal;} /* strings */ +ins, ins b, ins s, ins em {color:green;} /* comments */ +a {color:blue; text-decoration: underline; } /* links */ +body.ie { padding-left: 32px; margin-left: 0px; background-position: 0 -10px; margin-top: 0px;} diff --git a/www/extras/yui-webgui/build/code-editor/line-numbers.png b/www/extras/yui-webgui/build/code-editor/line-numbers.png new file mode 100755 index 000000000..ffea4e6aa Binary files /dev/null and b/www/extras/yui-webgui/build/code-editor/line-numbers.png differ