2 * Description: CodeEditor plugin
3 * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA
8 * It's a best practice to pass jQuery to an IIFE (Immediately Invoked Function
9 * Expression) that maps it to the dollar sign so it can't be overwritten by
10 * another library in the scope of its execution.
15 var default_code_mirror_options = {
16 gutters: ["note-gutter", "CodeMirror-linenumbers"],
22 tabMode: 'spaces' // or 'shift'
27 jQuery.fn.CodeEditor = function( method ) {
28 if ( methods[method] ) {
29 return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
30 } else if ( typeof method === 'object' || ! method ) {
31 return methods.init.apply( this, arguments );
33 jQuery.error( 'Method ' + method + ' does not exist on jQuery.CodeEditor' );
37 /***************************************************************************
39 ***************************************************************************/
44 * @brief Plugin initialization
45 * @param options : an associative array of setting values
46 * @return : a jQuery collection of objects on which the plugin is
47 * applied, which allows to maintain chainability of calls
49 init : function ( options ) {
51 /* Default settings */
52 var options = $.extend( {
54 codeMirrorOptions: default_code_mirror_options,
55 syntaxHighlighting: []
58 return this.each(function() {
62 /* An object that will hold private variables and methods */
63 var plugin = new CodeEditor(options);
64 $this.data('Manifold', plugin);
67 $this.on('show.CodeEditor', methods.show);
73 * @brief Plugin destruction
74 * @return : a jQuery collection of objects on which the plugin is
75 * applied, which allows to maintain chainability of calls
77 destroy : function( ) {
79 return this.each(function() {
81 var hazelnut = $this.data('Manifold');
83 // Unbind all events using namespacing
84 $(window).unbind('Manifold');
86 // Remove associated data
88 $this.removeData('Manifold');
94 // xxx wtf. why [1] ? would expect 0...
96 messages.debug("Hitting suspicious line in hazelnut.show");
97 var oTable = $($('.dataTable', $this)[1]).dataTable();
98 oTable.fnAdjustColumnSizing()
100 /* Refresh dataTabeles if click on the menu to display it : fix dataTables 1.9.x Bug */
101 $(this).each(function(i,elt) {
102 if (jQuery(elt).hasClass('dataTables')) {
103 var myDiv=jQuery('#hazelnut-' + this.id).parent();
104 if(myDiv.height()==0) {
105 var oTable=$('#hazelnut-' + this.id).dataTable();
114 /***************************************************************************
116 ***************************************************************************/
118 function CodeEditor(options)
121 this.initialize = function() {
122 this.validationTooltip;
124 // switch off CodeMirror for IE
125 //if (Browser.Engine.trident) options.useCodeMirror = false;
126 this.element = $('#' + this.options.plugin_uuid + '-textarea')[0]; // jordan added [0] for jquery
127 if (!this.options.syntaxHighlighting.contains(this.options.language)) {
128 this.forceDefaultCodeMirrorOptions();
130 //this.setOptions(this.options);
131 var is_disallowed = (disallowedPlatforms.contains(Browser.Platform.name));
132 if (this.options.useCodeMirror && !is_disallowed) {
136 if (!this.options.codeMirrorOptions.stylesheet && this.options.stylesheet) {
137 this.options.codeMirrorOptions.stylesheet = this.options.stylesheet.map(function(path) {
138 return mediapath + path;
141 if (!this.options.codeMirrorOptions.path) {
142 var codemirrorpath = ''; // jordan
143 this.options.codeMirrorOptions.path = codemirrorpath + 'js/';
145 if (!this.options.codeMirrorOptions.content) {
146 this.options.codeMirrorOptions.content = this.element.get('value');
149 var parentNode = this.element.getParent();
150 var options = { value: this.element.value };
151 options = Object.append(options, this.options.codeMirrorOptions);
152 this.editor = CodeMirror(parentNode, options);
154 // run this after initialization
155 if (!this.options.codeMirrorOptions.initCallback){
156 if (this.options.codeMirrorOptions.autofocus) {
157 // set current editor
158 Layout.current_editor = this.options.name;
162 // set editor options that can only be set once the editor is initialized
163 var cur = this.editor.getLineHandle(this.editor.getCursor().line);
164 this.setEditorEvents({
166 Layout.current_editor = this.options.name;
167 // this.editor.removeLineClass(cur, 'background', 'activeline');
168 // this.editor.hlLine = this.editor.addLineClass(0, "background", "activeline");
170 cursorActivity: function(){
171 // var cur = this.editor.getLineHandle(this.editor.getCursor().line);
172 // if (cur != this.editor.hlLine) {
173 // this.editor.removeLineClass(this.editor.hlLine, 'background', 'activeline');
174 // this.editor.hlLine = this.editor.addLineClass(cur, 'background', 'activeline');
178 // this.editor.removeLineClass(this.editor.hlLine, 'background', 'activeline');
181 this.validateEditorInput.call(this, parentNode);
182 window.editorsModified = true;
186 // disable new line insertion when saving fiddle
187 CodeMirror.keyMap.default['Ctrl-Enter'] = function(){
191 // don't let Emmet capture this
192 delete CodeMirror.keyMap.default['Cmd-L'];
195 this.editorLabelFX = new Fx.Tween(this.getLabel(), {
200 this.getWindow().addEvents({
201 mouseenter: function() {
202 this.editorLabelFX.start(0);
204 mouseleave: function() {
205 this.editorLabelFX.start(0.8);
210 // mooshell.addEvents({
211 // 'run': this.b64decode.bind(this)
214 Layout.registerEditor(this);
215 this.setLabelName(this.options.language || this.options.name);
221 this.validateEditorInput = function(parentNode){
222 var currentValue = this.getCode();
225 // destroy tooltip if it already exists for this editor
226 if (this.validationTooltip){
227 this.validationTooltip.destroy();
230 // create the container
231 this.validationTooltip = Element('ul', {
232 'class': 'warningTooltip'
236 Object.each(this.options.disallowed, function(value, key){
237 if (currentValue.test(key, 'i')){
238 warnings.push('<li>' + value + '</li>');
243 this.validationTooltip = this.validationTooltip.inject(parentNode);
245 // squash and apply warnings
246 this.validationTooltip.set({
247 html: warnings.join('')
254 this.getEditor = function() {
255 return this.editor || this.element;
261 this.getWindow = function() {
263 this.window = this.element.getParent('.window');
271 this.getLabel = function() {
272 return this.getWindow().getElement('.window_label');
278 this.b64decode = function() {
279 this.element.set('value', this.before_decode);
285 this.getCode = function() {
286 return (this.editor) ? this.editor.getValue() : this.element.get('value');
292 this.updateFromMirror = function() {
293 this.before_decode = this.getCode();
294 this.element.set('value', Base64.encode(this.before_decode));
300 this.updateCode = function() {
301 this.element.set('value', this.getCode());
307 this.clean = function() {
308 this.element.set('value', '');
315 this.cleanEditor = function() {
316 if (this.editor) this.editor.setCode('');
322 this.hide = function() {
323 this.getWindow().hide();
326 this.show = function() {
327 this.getWindow().show();
333 this.setEditorOptions = function(options){
334 Object.each(options, function(fn, key){
335 this.editor.setOption(key, fn.bind(this));
342 this.setEditorEvents = function(e){
343 Object.each(e, function(fn, key){
344 this.editor.on(key, fn.bind(this));
351 this.setLanguage = function(language) {
352 // Todo: This is hacky
353 this.setLabelName(language);
359 this.setLabelName = function(language) {
360 this.getLabel().set('text', this.window_names[language] || language);
366 this.setStyle = function(key, value) {
367 if (this.editor) return $(this.editor.frame).setStyle(key, value);
368 return this.element.setStyle(key, value);
374 this.setStyles = function(options) {
375 if (this.editor) return $(this.editor.frame).setStyles(options);
376 return this.element.setStyles(options);
382 this.setWidth = function(width) {
383 this.getWindow().setStyle('width', width);
389 this.setHeight = function(height) {
390 this.getWindow().setStyle('height', height);
396 this.getPosition = function() {
397 if (this.editor) return $(this.editor.frame).getPosition();
398 return this.element.getPosition();
404 this.forceDefaultCodeMirrorOptions = function() {
405 this.options.codeMirrorOptions = default_code_mirror_options;
410 /* member variables */
411 this.options = options;
415 // adapted from EditorCM.js from jsfiddle.net
417 var disallowedPlatforms = ['ios', 'android', 'ipod'];
419 this.window_names = {
420 'javascript': 'JavaScript',
424 'coffeescript': 'CoffeeScript',
425 'javascript 1.7': 'JavaScript 1.7'
430 } // function CodeEditor