1 /*
  2  * CodePress - Real Time Syntax Highlighting Editor written in JavaScript - http://codepress.org/
  3  * 
  4  * Copyright (C) 2006 Fernando M.A.d.S. <fermads@gmail.com>
  5  *
  6  * This program is free software; you can redistribute it and/or modify it under the terms of the 
  7  * GNU Lesser General Public License as published by the Free Software Foundation.
  8  * 
  9  * Read the full licence: http://www.opensource.org/licenses/lgpl-license.php
 10  *
 11  * This file is not the original but has been modified to be more compliant with
 12  * ExtJs. Changes are made by S.J.Hoeksma
 13  */
 14 Ext.namespace('Ext.ux');
 15 
 16 /**
 17  * Component which wraps the <a href="http://codepress.org">CodePress</a> library to make
 18  * it available for ExtJs. CodePress gives syntax highlighting for different programming
 19  * languages.
 20  * @type component
 21  */
 22 Ext.ux.CodePress = Ext.extend(Ext.form.Field, {
 23   
 24     /**
 25      * The id of the element to pull code from
 26      * @type {String} 
 27      @cfg */
 28     sourceEl : false,
 29   
 30     /**
 31      * The code to use in the editor
 32      * @type {String} 
 33      @cfg */
 34      code : false,
 35   
 36     /**
 37      * The language to render the code with (defaults none)
 38      * @type {String} 
 39      @cfg */
 40     language : false,
 41   
 42     /**
 43      * The url used to read code which is display in editor
 44      * @type {String}
 45      @cfg  */
 46     url : false,
 47   
 48     /**
 49      * Height of the editor (defaults false)
 50      * @type {Int}
 51      @cfg  */
 52     height : false,
 53   
 54     /** 
 55      * Width of the editor (defaults false)
 56      * @type {Int}
 57      @cfg */
 58     width : false,
 59   
 60     /**
 61      * AutoResize window on change container (defaults false)
 62      * @type {Boolean}
 63      @cfg */
 64     autoResize : false,
 65   
 66     /**
 67      * Trim the code of trailing spaces and empty lines (defaults true)
 68      * @type {Boolean}
 69      @cfg */
 70     trim  : true,
 71   
 72     /**
 73      * Is autoComplete for keywords turned on or off (defaults true)
 74      * @type {Boolean}
 75      @cfg */
 76     autoComplete : true,
 77   
 78     /**
 79      * Is the editor readonly (defaults false)
 80      * @type {Boolean}
 81      @cfg */
 82     readOnly  : false,
 83   
 84     /**
 85      * Are lineNumbers visible (defaults true)
 86      * @type {Boolean}
 87      @cfg */
 88     lineNumbers : true,
 89 
 90     //@private Has the editor been initialized
 91     initialized : false,
 92   
 93     /**
 94      * @private Init the codepress component for ExtJs
 95      */
 96     initComponent : function(){
 97        if (!Ext.ux.CodePress.path) {
 98          s = document.getElementsByTagName('script');
 99          for(var i=0,n=s.length;i<n;i++) {
100            var name = s[i].src ? s[i].src : s[i].id;
101            if(name.match('Ext\.ux\.CodePress\.js')) {
102              Ext.ux.CodePress.path = name.replace("Ext.ux.CodePress.js",'');
103             break;
104            }
105          }
106       }
107       Ext.ux.CodePress.superclass.initComponent.call(this);
108 
109       // Hide the sourceEl if provided
110       if(this.sourceEl) Ext.get(this.sourceEl).hide();
111     
112       this.addEvents({
113           /**
114            * Fires when the editor is fully initialized (including the iframe)
115            * @event initialize 
116            * @param {Object} editor The editor
117            */
118           initialize: true,
119 
120           /**
121            * Fires when the editor is first receives the focus. Any insertion must wait
122            * until after this event.
123            * @event activate 
124            * @param {Object} editor The editor when activated
125            */
126           activate: true
127 
128       });
129     },
130   
131    /**
132     * @private (for BoxComponent)
133     */
134    adjustSize : Ext.BoxComponent.prototype.adjustSize,
135   
136     /**
137      * Resize the the editor depending, behavior depends on height,width and autoResize
138      */
139    resize : function(){
140       if (!this.editor) return;
141       var h,w;
142       if (this.autoResize) {
143         h =  this.ownerCt.body.dom.clientHeight +'px';
144         w =  this.ownerCt.body.dom.clientWidth +'px';
145       } else {
146        h = (this.height || this.ownerCt.body.dom.clientHeight) +'px';
147        w = (this.width || this.ownerCt.body.dom.clientWidth) +'px';
148       }
149       this.editor.body.style.width = w;
150       this.iframe.setStyle('height', h);
151       this.iframe.setStyle('width', w);
152     },
153 
154     /**
155      * @private During render we create textarea of code press
156      * @param {Component} ct The component to render
157      * @param {Object} position A object containing the position of the component
158      */
159     onRender : function(ct, position){
160         Ext.ux.CodePress.superclass.onRender.call(this, ct, position);
161     
162         //Taken from Ext.form.HtmlEditor
163         this.el.dom.style.border = '0 none';
164         this.el.dom.setAttribute('tabIndex', -1);
165         this.el.addClass('x-hidden');
166     
167     
168         if(Ext.isIE){ // fix IE 1px bogus margin
169             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
170         }
171         this.wrap = this.el.wrap({});
172     
173         // Create the iframe
174         this.iframe = Ext.get(document.createElement('iframe'));
175         this.iframe.src = (Ext.SSL_SECURE_URL || 'javascript:false');
176     
177         // Create the textarea element if not created
178         if(!this.sourceEl){
179             this.textarea = Ext.get(document.createElement('textarea'));
180           }else{
181             this.textarea = Ext.get(this.sourceEl);
182           }
183         this.textarea.dom.disabled = true;
184         this.textarea.dom.style.overflow = 'hidden';
185         this.textarea.dom.style.overflow = 'auto';
186         this.iframe.dom.frameBorder = 0; // remove IE internal iframe border
187         this.iframe.setStyle('visibility', 'hidden');
188         this.iframe.setStyle('position', 'absolute');
189         this.options = this.textarea.dom.className;    
190         this.wrap.dom.appendChild(this.textarea.dom);
191         this.textarea.dom.parentNode.insertBefore(this.iframe.dom, this.textarea.dom);    
192         this.edit();
193         this.height = (this.height || this.ownerCt.body.dom.clientHeight);
194         this.width= (this.width || this.ownerCt.body.dom.clientWidth);
195     },
196    
197    /**
198     * @private We don't support focus of editor
199     */
200     focus : function(){},
201   
202    /**
203     * @private Initialize the editor
204     */
205    initialize : function() {
206       if(Ext.isIE){
207         this.doc = this.iframe.dom.contentWindow.document;
208         this.win = this.iframe.dom.contentWindow;
209       } else {
210         this.doc = this.iframe.dom.contentDocument;
211         this.win = this.iframe.dom.contentWindow;
212       }
213       this.editor = this.win.CodePress;
214       this.editor.body = this.doc.getElementsByTagName('body')[0];
215       if(this.url){
216         Ext.Ajax.request({
217           url: this.url
218           , method:'get'
219           , success:function(response, options){
220             var code = response.responseText;
221             this.code = code;
222             this.editor.setCode(this.code);
223           }.createDelegate(this)
224         });
225       }else{
226         this.editor.setCode(this.code || this.textarea.dom.value);
227       }
228       this.resize();
229       this.setOptions();
230       this.editor.syntaxHighlight('init');
231       this.textarea.dom.style.display = 'none';
232       this.iframe.dom.style.position = 'static';
233       this.iframe.dom.style.visibility = 'visible';
234       this.iframe.dom.style.display = 'inline';
235 
236       this.initialized = true;
237       this.fireEvent('initialize', this);
238    },
239   
240    /**
241     * Initailize the editor with a element and set the langauge
242     * @param {Object} obj Can by a textarea id or a string
243     * @param {String} language The langauge to use
244     */
245    edit : function(obj,language) {
246     if(obj) this.textarea.dom.value = document.getElementById(obj) ? document.getElementById(obj).value : obj;
247     if(!this.textarea.dom.disabled) return;
248     this.language = language ? language : this.getLanguage();
249     this.iframe.dom.src = Ext.ux.CodePress.path+'codepress.html?language='+this.language+'&ts='+(new Date).getTime();
250     this.iframe.removeListener('load', this.initialize);
251     this.iframe.on('load', this.initialize, this);
252   },
253 
254   /**
255    * Get the current langauge used by the editor
256    * @return {String} The language used by editor
257    */
258   getLanguage : function() {
259     if(this.language) return this.language;
260     for (language in Ext.ux.CodePress.languages) 
261       if(this.options.match('\\b'+language+'\\b')) 
262         return Ext.ux.CodePress.languages[language] ? language : 'generic';
263   },
264   
265   /**
266    * Set the options of editor
267    * See config items autoComplete, readOnly, lineNumbers
268    */
269   setOptions : function() {
270     if(this.autoComplete===false || this.options.match('autocomplete-off')) this.toggleAutoComplete();
271     if(this.readOnly===true || this.options.match('readonly-on')) this.toggleReadOnly();
272     if(this.lineNumbers===false || this.options.match('linenumbers-off')) this.toggleLineNumbers();
273   },
274   
275   /**
276    * Original CodePress function to get the code from the editor. For compatibility reasons
277    * with ExtJs TextArea whe implemented getValue
278    * @return {String} The code from editor
279    */
280   getCode : function() {
281     var code;
282     if (this.textarea && this.editor) 
283      code = this.textarea.dom.disabled ? this.editor.getCode() : this.textarea.dom.value;
284     else
285      code = this.code || "";  
286     code =  this.trim ? code.replace(/^\s+|(\s+$|\n$|\r$)/g,"") : code;
287     return code;
288   },
289 
290   /**
291    * Original CodePress function to set the code of the editor.For compatibility reasons
292    * with ExtJs TextArea whe implemented setValue
293    * @param {String} code The code to be display in editor
294    */
295   setCode : function(code) {
296     if (this.textarea && this.editor) {
297       this.textarea.dom.disabled ? this.editor.setCode(code) : this.textarea.dom.value = code;
298       this.editor.syntaxHighlight('init');
299     } else {
300      this.code = code;
301    }
302   },
303   
304   /**
305    * Set the value to be used by the editor
306    * @param {String} text The code to be display in editor
307    */
308   setValue : function(text) {
309     this.setCode(text);
310   },
311   
312   /**
313    * Get the value of the code within the editor
314    * @return {String} The code within the editor
315    */
316   getValue : function() {
317     return this.getCode();
318   },
319 
320   /**
321    * Toggle autocomplreate on or off
322    */
323   toggleAutoComplete : function() {
324     if (this.editor)
325       this.editor.autocomplete = (this.editor.autocomplete) ? false : true;
326   },
327   
328   /**
329    * Toggle readonly on or off
330    */
331   toggleReadOnly : function() {
332     this.textarea.dom.readOnly = (this.textarea.dom.readOnly) ? false : true;
333     if(this.iframe.dom.style.display != 'none' && this.editor) // prevent exception on FF + iframe with display:none
334       this.editor.readOnly(this.textarea.dom.readOnly ? true : false);
335   },
336   
337   /**
338    * Toggle line numbers on or off
339    */
340   toggleLineNumbers : function() {
341     if (!this.editor) return;
342     var cn = this.editor.body.className;
343     this.editor.body.className = (cn==''||cn=='show-line-numbers') ? 'hide-line-numbers' : 'show-line-numbers';
344   },
345   
346   /**
347    * Toggle between codepress and textarea
348    */
349   toggleEditor : function() {
350     if(this.textarea.dom.disabled) {
351       this.textarea.dom.value = this.getCode();
352       this.textarea.dom.disabled = false;
353       this.iframe.dom.style.display = 'none';
354       this.textarea.dom.style.display = 'inline';
355     }
356     else {
357       this.textarea.dom.disabled = true;
358       this.setCode(this.textarea.dom.value);
359       if (this.editor) this.editor.syntaxHighlight('init');
360       this.iframe.domstyle.display = 'inline';
361       this.textarea.dom.style.display = 'none';
362     }
363   }
364 });
365 
366 Ext.reg('codepress', Ext.ux.CodePress);
367   
368 Ext.ux.CodePress.languages = {  
369   csharp : 'C#', 
370   css : 'CSS', 
371   generic : 'Generic',
372   html : 'HTML',
373   java : 'Java', 
374   javascript : 'JavaScript', 
375   perl : 'Perl', 
376   ruby : 'Ruby',  
377   php : 'PHP', 
378   text : 'Text', 
379   sql : 'SQL',
380   vbscript : 'VBScript'
381 }
382 
383