From f883d8b35f412558271650ed559f1399c35fac82 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Tue, 5 Oct 2010 14:44:53 -0700 Subject: [PATCH] Remove the YUI based code editor from WebGUI, and replace it with a textarea. Fixes bug #1618, and others. --- docs/changelog/7.x.x.txt | 1 + lib/WebGUI/Form/Codearea.pm | 104 --- .../build/code-editor/code-editor.js | 747 ------------------ .../yui-webgui/build/code-editor/code.css | 9 - .../build/code-editor/line-numbers.png | Bin 16556 -> 0 bytes 5 files changed, 1 insertion(+), 860 deletions(-) delete mode 100755 www/extras/yui-webgui/build/code-editor/code-editor.js delete mode 100755 www/extras/yui-webgui/build/code-editor/code.css delete 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 8a0eee4c6..6d707c912 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -10,6 +10,7 @@ - fixed #11871: Metadata display and criteria builder problems - fixed #10189: pbworkflow000000000007 Hanging - fixed #11897: Continue to the site link loses current page + - fixed #11618: Code Editor: Content loses it's whitespace formatting 7.10.1 - fixed #11851: Story Topic: top story variables should be available all the time diff --git a/lib/WebGUI/Form/Codearea.pm b/lib/WebGUI/Form/Codearea.pm index e262fec87..bfdeab57f 100644 --- a/lib/WebGUI/Form/Codearea.pm +++ b/lib/WebGUI/Form/Codearea.pm @@ -16,7 +16,6 @@ package WebGUI::Form::Codearea; use strict; use base 'WebGUI::Form::Textarea'; -use HTML::Entities qw(encode_entities decode_entities); use WebGUI::International; =head1 NAME @@ -90,18 +89,6 @@ sub definition { #------------------------------------------------------------------- -=head2 getDatabaseFieldType ( ) - -Returns "MEDIUMTEXT". - -=cut - -sub getDatabaseFieldType { - return "MEDIUMTEXT"; -} - -#------------------------------------------------------------------- - =head2 getName ( session ) Returns the human readable name of this control. @@ -113,95 +100,4 @@ sub getName { return WebGUI::International->new($session, 'WebGUI')->get('codearea'); } -#------------------------------------------------------------------- - -=head2 getValue ( [value] ) - -Return the value, HTML decoded - -=cut - -sub getValue { - my ( $self, @args ) = @_; - my $value = $self->SUPER::getValue( @args ); - return decode_entities( $value ); -} - -#------------------------------------------------------------------- - -=head2 headTags ( ) - -Set the head tags for this form plugin - -=cut - -sub headTags { - my $self = shift; - my ($style, $url) = $self->session->quick(qw(style url)); - $style->setLink($url->extras("yui/build/resize/assets/skins/sam/resize.css"), {type=>"text/css", rel=>"stylesheet"}); - $style->setLink($url->extras("yui/build/assets/skins/sam/skin.css"), {type=>"text/css", rel=>"stylesheet"}); - $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/resize/resize-min.js"),{type=>"text/javascript"}); - $style->setScript($url->extras("yui/build/editor/editor-min.js"),{type=>"text/javascript"}); - $style->setScript($url->extras("yui-webgui/build/code-editor/code-editor.js"),{type=>"text/javascript"}); - #$style->setLink($url->extras("yui/build/logger/assets/logger.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/logger/logger.js"),{type=>"text/javascript"}); - $self->SUPER::headTags(); -} - -#------------------------------------------------------------------- - -=head2 isDynamicCompatible ( ) - -A class method that returns a boolean indicating whether this control is compatible with the DynamicField control. - -=cut - -sub isDynamicCompatible { - return 1; -} - -#------------------------------------------------------------------- - -=head2 toHtml ( ) - -Renders a code area field. - -=cut - -sub toHtml { - my $self = shift; - - $self->headTags; - 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'); - my $name = $self->get('name'); - my $extras = $self->get('extras'); - my $syntax = $self->get('syntax'); - my $styleAttr = $self->get('style'); - - my $codeCss = $self->session->url->extras("yui-webgui/build/code-editor/code.css"); - my $out = <<"END_HTML"; - - -END_HTML - return $out; -} - 1; - diff --git a/www/extras/yui-webgui/build/code-editor/code-editor.js b/www/extras/yui-webgui/build/code-editor/code-editor.js deleted file mode 100755 index 7908dff6d..000000000 --- a/www/extras/yui-webgui/build/code-editor/code-editor.js +++ /dev/null @@ -1,747 +0,0 @@ -/* - * HTML Parser By John Resig (ejohn.org) - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - * - * // Use like so: - * HTMLParser(htmlString, { - * start: function(tag, attrs, unary) {}, - * end: function(tag) {}, - * chars: function(text) {}, - * comment: function(text) {} - * }); - * - * // or to get an XML string: - * HTMLtoXML(htmlString); - * - * // or to get an XML DOM Document - * HTMLtoDOM(htmlString); - * - * // or to inject into an existing document/DOM node - * HTMLtoDOM(htmlString, document); - * HTMLtoDOM(htmlString, document.body); - * - */ - -(function(){ - - // Regular Expressions for parsing tags and attributes - var startTag = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, - endTag = /^<\/(\w+)[^>]*>/, - attr = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; - - // Empty Elements - HTML 4.01 - var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed"); - - // Block Elements - HTML 4.01 - var block = makeMap("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul"); - - // Inline Elements - HTML 4.01 - var inline = makeMap("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"); - - // Elements that you can, intentionally, leave open - // (and which close themselves) - var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"); - - // Attributes that have their values filled in disabled="disabled" - var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"); - - // Special Elements (can contain anything) - var special = makeMap("script,style"); - - var HTMLParser = this.HTMLParser = function( html, handler ) { - var index, chars, match, stack = [], last = html; - stack.last = function(){ - return this[ this.length - 1 ]; - }; - - while ( html ) { - chars = true; - - // Make sure we're not in a script or style element - if ( !stack.last() || !special[ stack.last() ] ) { - - // Comment - if ( html.indexOf(""); - - if ( index >= 0 ) { - if ( handler.comment ) - handler.comment( html.substring( 4, index ) ); - html = html.substring( index + 3 ); - chars = false; - } - - // end tag - } else if ( html.indexOf("]*>"), function(all, text){ - text = text.replace(//g, "$1") - .replace(//g, "$1"); - - if ( handler.chars ) - handler.chars( text ); - - return ""; - }); - - parseEndTag( "", stack.last() ); - } - - if ( html == last ) - throw "Parse Error: " + html; - last = html; - } - - // Clean up any remaining tags - parseEndTag(); - - function parseStartTag( tag, tagName, rest, unary ) { - if ( block[ tagName ] ) { - while ( stack.last() && inline[ stack.last() ] ) { - parseEndTag( "", stack.last() ); - } - } - - if ( closeSelf[ tagName ] && stack.last() == tagName ) { - parseEndTag( "", tagName ); - } - - unary = empty[ tagName ] || !!unary; - - if ( !unary ) - stack.push( tagName ); - - if ( handler.start ) { - var attrs = []; - - rest.replace(attr, function(match, name) { - var value = arguments[2] ? arguments[2] : - arguments[3] ? arguments[3] : - arguments[4] ? arguments[4] : - fillAttrs[name] ? name : ""; - - attrs.push({ - name: name, - value: value, - escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //" - }); - }); - - if ( handler.start ) - handler.start( tagName, attrs, unary ); - } - } - - function parseEndTag( tag, tagName ) { - // If no tag name is provided, clean shop - if ( !tagName ) - var pos = 0; - - // Find the closest opened tag of the same type - else - for ( var pos = stack.length - 1; pos >= 0; pos-- ) - if ( stack[ pos ] == tagName ) - break; - - if ( pos >= 0 ) { - // Close all the open elements, up the stack - for ( var i = stack.length - 1; i >= pos; i-- ) - if ( handler.end ) - handler.end( stack[ i ] ); - - // Remove the open elements from the stack - stack.length = pos; - } - } - }; - - this.HTMLtoXML = function( html ) { - var results = ""; - - HTMLParser(html, { - start: function( tag, attrs, unary ) { - results += "<" + tag; - - for ( var i = 0; i < attrs.length; i++ ) - results += " " + attrs[i].name + '="' + attrs[i].escaped + '"'; - - results += (unary ? "/" : "") + ">"; - }, - end: function( tag ) { - results += ""; - }, - chars: function( text ) { - results += text; - }, - comment: function( text ) { - results += ""; - } - }); - - return results; - }; - - this.HTMLtoDOM = function( html, doc ) { - // There can be only one of these elements - var one = makeMap("html,head,body,title"); - - // Enforce a structure for the document - var structure = { - link: "head", - base: "head" - }; - - if ( !doc ) { - if ( typeof DOMDocument != "undefined" ) - doc = new DOMDocument(); - else if ( typeof document != "undefined" && document.implementation && document.implementation.createDocument ) - doc = document.implementation.createDocument("", "", null); - else if ( typeof ActiveX != "undefined" ) - doc = new ActiveXObject("Msxml.DOMDocument"); - - } else - doc = doc.ownerDocument || - doc.getOwnerDocument && doc.getOwnerDocument() || - doc; - - var elems = [], - documentElement = doc.documentElement || - doc.getDocumentElement && doc.getDocumentElement(); - - // If we're dealing with an empty document then we - // need to pre-populate it with the HTML document structure - if ( !documentElement && doc.createElement ) (function(){ - var html = doc.createElement("html"); - var head = doc.createElement("head"); - head.appendChild( doc.createElement("title") ); - html.appendChild( head ); - html.appendChild( doc.createElement("body") ); - doc.appendChild( html ); - })(); - - // Find all the unique elements - if ( doc.getElementsByTagName ) - for ( var i in one ) - one[ i ] = doc.getElementsByTagName( i )[0]; - - // If we're working with a document, inject contents into - // the body element - var curParentNode = one.body; - - HTMLParser( html, { - start: function( tagName, attrs, unary ) { - // If it's a pre-built element, then we can ignore - // its construction - if ( one[ tagName ] ) { - curParentNode = one[ tagName ]; - return; - } - - var elem = doc.createElement( tagName ); - - for ( var attr in attrs ) - elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value ); - - if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] != "boolean" ) - one[ structure[ tagName ] ].appendChild( elem ); - - else if ( curParentNode && curParentNode.appendChild ) - curParentNode.appendChild( elem ); - - if ( !unary ) { - elems.push( elem ); - curParentNode = elem; - } - }, - end: function( tag ) { - elems.length -= 1; - - // Init the new parentNode - curParentNode = elems[ elems.length - 1 ]; - }, - chars: function( text ) { - curParentNode.appendChild( doc.createTextNode( text ) ); - }, - comment: function( text ) { - // create comment node - } - }); - - return doc; - }; - - function makeMap(str){ - var obj = {}, items = str.split(","); - for ( var i = 0; i < items.length; i++ ) - obj[ items[i] ] = true; - return obj; - } -})(); - - -(function() { - var Dom = YAHOO.util.Dom, - Event = YAHOO.util.Event, - Lang = YAHOO.lang - ; - - YAHOO.widget.CodeEditor = function (id, cfg) { - // TODO: Make a cfg for off by default - this.editorState = "on"; - - // Disable Editor configs that don't apply - cfg["animate"] = false; - cfg["dompath"] = false; - cfg["focusAtStart"] = 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.getEditorText() != this.old_text ) { - Lang.later(10, this, function () { this.highlight(true) } ); - if ( this.status ) { - Lang.later(100, this, this._writeStatus); - } - this.old_text = this.getEditorText(); - } - // Setup resize - if ( this.status ) { - this._setupResize(); - } - }, this, true); - - this.on('editorKeyUp', function(ev) { - // Highlight only if content has changed - if ( this.getEditorText() != this.old_text ) { - Lang.later(10, this, this.highlight); - if ( this.status ) { - Lang.later(100, this, this._writeStatus); - } - this.old_text = this.getEditorText(); - } - }, this, true); - - - //Borrowed this from CodePress: http://codepress.sourceforge.net - this.cc = '\u2009'; // carret char - // TODO: Make this configurable based on a syntax definition - 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 - - - if ( cfg["toggleButton"] ) { - var editor = this; - - // Add toggle button - var button = document.createElement("input"); - - button.type = "button"; - button.value = "Toggle Editor"; - - Event.addListener( button, "click", function () { - if ( editor.editorState == "off" ) { - editor.editorState = "on"; - var fc = editor.get('element').previousSibling, - el = editor.get('element'); - - Dom.setStyle(fc, 'position', 'static'); - Dom.setStyle(fc, 'top', '0'); - Dom.setStyle(fc, 'left', '0'); - Dom.setStyle(el, 'visibility', 'hidden'); - Dom.setStyle(el, 'top', '-9999px'); - Dom.setStyle(el, 'left', '-9999px'); - Dom.setStyle(el, 'position', 'absolute'); - editor.get('element_cont').addClass('yui-editor-container'); - YAHOO.log('Reset designMode on the Editor', 'info', 'example'); - editor._setDesignMode('on'); - YAHOO.log('Inject the HTML from the textarea into the editor', 'info', 'example'); - - // Escape HTML - var div = document.createElement("div"); - var text = editor.get('textarea').value; - // IE truncates whitespace internally, so go line by line - var lines = text.split(/\n/); - for ( var i = 0; i < lines.length; i++ ) { - var line = lines[i]; - YAHOO.log( i + ": " + line, "info", "CodeEditor" ); - div.appendChild( document.createTextNode( line ) ); - div.appendChild( document.createElement( "br" ) ); - } - var html = div.innerHTML; - // We have
, not \n - html = html.replace(/\n/g,""); - - YAHOO.log( html, "info", "CodeEditor" ); - editor.setEditorHTML(html); - editor.highlight(); - } - else { - editor.editorState = "off"; - editor.saveHTML(); - var fc = editor.get('element').previousSibling, - el = editor.get('element'); - - Dom.setStyle(fc, 'position', 'absolute'); - Dom.setStyle(fc, 'top', '-9999px'); - Dom.setStyle(fc, 'left', '-9999px'); - editor.get('element_cont').removeClass('yui-editor-container'); - Dom.setStyle(el, 'visibility', 'visible'); - Dom.setStyle(el, 'top', ''); - Dom.setStyle(el, 'left', ''); - Dom.setStyle(el, 'position', 'static'); - - // Unescape HTML - var div = document.createElement("div"); - var text = editor.getEditorText(); - // IE truncates all whitespace internally, so add HTML for it - if ( editor.browser.ie && editor.browser.ie <= 8 ) { - text = text.replace(/\n/g, " 
"); - text = text.replace(/\t/g, "    "); - } - div.innerHTML = text; - editor.get('element').value = ""; - for ( var i = 0; i < div.childNodes.length; i++ ) { - if ( div.childNodes[i].nodeName == "#text" ) { - editor.get('element').value = editor.get('element').value - + div.childNodes[i].nodeValue - + "\n" - ; - } - } - YAHOO.log( editor.getEditorText(), "info", "CodeEditor" ); - YAHOO.log( div.childNodes[0].nodeValue, "info", "CodeEditor" ); - YAHOO.log( editor.get('element').value, "info", "CodeEditor" ); - } - } ); - - // Put it right after the text area - var ta = document.getElementById( id ); - if ( ta.nextSibling ) { - ta.parentNode.insertBefore( button, ta.nextSibling ); - } - else { - ta.parentNode.appendChild( button ); - } - } - }; - Lang.extend( YAHOO.widget.CodeEditor, YAHOO.widget.SimpleEditor, { - /** - * @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; }' - }); - - /** - * @private - * @method _cleanIncomingHTML - * @description Clean up the HTML that the textarea starts with - */ - YAHOO.widget.CodeEditor.prototype._cleanIncomingHTML = function(str) { - // Workaround for bug in Lang.substitute - str = str.replace(/{/gi, 'RIGHT_BRACKET'); - str = str.replace(/}/gi, 'LEFT_BRACKET'); - - //   before
for IE8- so lines show up correctly - if ( this.browser.ie && this.browser.ie <= 8 ) { - str = str.replace(/\r?\n/g, " 
"); - } - - // Fix tabs into softtabs - str = str.replace(/\t/g, '    '); // TODO: Make softtabs configurable - - 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 () { - if ( this.editorState == "on" ) { - this.saveHTML(); - } - return; - }; - /* End override to fix problem */ - - /** - * @private - * @method _writeStatus - * @description Write the number of Characters and Lines to the status line - */ - 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); - } - }; - - /* - * @method cleanHTML - * @description Reduce the HTML in the editor to plain text to be put back in the - * textarea. Called by saveHTML() - */ - YAHOO.widget.CodeEditor.prototype.cleanHTML = function (html) { - if (!html) { - html = this.getEditorHTML(); - } - - // Handle special-case HTML - html = html.replace(/( ){4}/g,"\t"); // TODO: make softtabs configurable - html = html.replace(/ /g," "); - // Remove spaces at end of lines - html = html.replace(/ ?
/gi,'\n'); - - // Parse the text out of the remaining HTML - text = ""; - HTMLParser( html, { - chars : function (t) { text += t } - } ); - - // If, after all this, we are left with only a \n, user didn't add anything - // (editor adds a
if it starts blank) - if ( text == "\n" ) { - text = ""; - } - - return text; - }; - - /* - * @method focusCaret - * @description I don't actually know what this does, it was like this when I got here - */ - 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); - } - } - }; - - /** - * @method getEditorText - * @description Get the text inside the editor, removing any HTML used for highlighting - */ - YAHOO.widget.CodeEditor.prototype.getEditorText - = function () { - var html = this.getEditorHTML(); - var text = this.cleanHTML( html ); - return text; - }; - - /** - * @method highlight - * @description Apply the syntax highlighting to the content of the editor - * @param {Boolean} focus If true, editor currently has focus - */ - 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; - } - - // Keep track of where the cursor is right now - 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', this.cc); - } - catch (e) {} - } - } - - // Remove existing highlighting - var html = this.getEditorText(); - - // Fix line breaks - html = html.replace( /\t/g, "    " ); - if ( this.browser.ie ) { - html = html.replace( /\n/g, " 
" ); - } - else { - html = html.replace( /\n/g, "
"); - } - - // Apply new highlighting - for (var i = 0; i < this.keywords.length; i++) { - html = html.replace(this.keywords[i].code, this.keywords[i].tag); - } - - // Replace cursor - if ( !this.browser.gecko ) { - html = html.replace(this.cc, '|'); - } - - 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 deleted file mode 100755 index 783326044..000000000 --- a/www/extras/yui-webgui/build/code-editor/code.css +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100755 index ffea4e6aa163ba2cf184e2970ef11724bce4ef72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16556 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