From 81a256bd719f615bfb6c00a6e3e8c9c6053212e8 Mon Sep 17 00:00:00 2001 From: Doug Bell Date: Tue, 1 Sep 2009 21:16:30 +0000 Subject: [PATCH] fixed codearea to work better and more compatible --- docs/changelog/7.x.x.txt | 1 + lib/WebGUI/Form/Codearea.pm | 66 ++-- .../build/code-editor/code-editor.js | 294 ++++++++++++++++++ .../yui-webgui/build/code-editor/code.css | 9 + .../build/code-editor/line-numbers.png | Bin 0 -> 16556 bytes 5 files changed, 344 insertions(+), 26 deletions(-) create mode 100755 www/extras/yui-webgui/build/code-editor/code-editor.js create mode 100755 www/extras/yui-webgui/build/code-editor/code.css create mode 100755 www/extras/yui-webgui/build/code-editor/line-numbers.png 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 0000000000000000000000000000000000000000..ffea4e6aa163ba2cf184e2970ef11724bce4ef72 GIT binary patch literal 16556 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