renamed hazelnut into querytable
[myslice.git] / to-be-integrated / plugins / code_editor / static / js / code_editor.js
1 /**
2  * Description: CodeEditor plugin
3  * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA
4  * License: GPLv3
5  */
6
7 /*
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.
11  */
12
13 (function($){
14
15      var default_code_mirror_options = {
16        gutters: ["note-gutter", "CodeMirror-linenumbers"],
17        tabSize: 4,
18        indentUnit: 4,
19        matchBrackets: true,
20        lineNumbers: true,
21        lineWrapping: true,
22        tabMode: 'spaces' // or 'shift'
23      };
24
25     
26     // routing calls
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 );
32                 } else {
33                         jQuery.error( 'Method ' +  method + ' does not exist on jQuery.CodeEditor' );
34                 }    
35     };
36
37     /***************************************************************************
38      * Public methods
39      ***************************************************************************/
40
41     var methods = {
42
43         /**
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
48          */
49         init : function ( options ) {
50
51             /* Default settings */
52             var options = $.extend( {
53                 useCodeMirror: true,
54                 codeMirrorOptions: default_code_mirror_options,
55                 syntaxHighlighting: []
56             }, options);
57
58             return this.each(function() {
59
60                 var $this = $(this);
61
62                 /* An object that will hold private variables and methods */
63                 var plugin = new CodeEditor(options);
64                 $this.data('Manifold', plugin);
65
66                 /* Events */
67                 $this.on('show.CodeEditor', methods.show);
68
69             }); // this.each
70         }, // init
71
72         /**
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
76          */
77         destroy : function( ) {
78
79             return this.each(function() {
80                 var $this = $(this);
81                 var querytable = $this.data('Manifold');
82
83                 // Unbind all events using namespacing
84                 $(window).unbind('Manifold');
85
86                 // Remove associated data
87                 querytable.remove();
88                 $this.removeData('Manifold');
89             });
90         }, // destroy
91
92         show : function( ) {
93             var $this=$(this);
94             // xxx wtf. why [1] ? would expect 0...
95             if (debug)
96                 messages.debug("Hitting suspicious line in querytable.show");
97             var oTable = $($('.dataTable', $this)[1]).dataTable();
98             oTable.fnAdjustColumnSizing()
99
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('#querytable-' + this.id).parent();
104                     if(myDiv.height()==0) {
105                         var oTable=$('#querytable-' + this.id).dataTable();
106                         oTable.fnDraw();
107                     }
108                 }
109             });
110         } // show
111
112     }; // var methods;
113
114     /***************************************************************************
115      * Plugin object
116      ***************************************************************************/
117
118     function CodeEditor(options)
119     {
120       
121         this.initialize = function() {
122             this.validationTooltip;
123         
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();
129             }
130             //this.setOptions(this.options);
131             var is_disallowed = (disallowedPlatforms.contains(Browser.Platform.name));
132             if (this.options.useCodeMirror && !is_disallowed) {
133                 // hide textarea
134                 this.element.hide();
135                 // prepare settings
136                 if (!this.options.codeMirrorOptions.stylesheet && this.options.stylesheet) {
137                     this.options.codeMirrorOptions.stylesheet = this.options.stylesheet.map(function(path) {
138                         return mediapath + path;
139                     });
140                 }
141                 if (!this.options.codeMirrorOptions.path) {
142                     var codemirrorpath = ''; // jordan
143                     this.options.codeMirrorOptions.path = codemirrorpath + 'js/';
144                 }
145                 if (!this.options.codeMirrorOptions.content) {
146                     this.options.codeMirrorOptions.content = this.element.get('value');
147                 }
148           
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);
153           
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;
159                     }
160                 }
161           
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({
165                     focus: function(){
166                         Layout.current_editor = this.options.name;
167                         // this.editor.removeLineClass(cur, 'background', 'activeline');
168                         // this.editor.hlLine = this.editor.addLineClass(0, "background", "activeline");
169                     },
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');
175                         // }
176                     },
177                     blur: function(){
178                         // this.editor.removeLineClass(this.editor.hlLine, 'background', 'activeline');
179                     },
180                     change: function(){
181                         this.validateEditorInput.call(this, parentNode);
182                         window.editorsModified = true;
183                     }.bind(this)
184                 });
185           
186                 // disable new line insertion when saving fiddle
187                 CodeMirror.keyMap.default['Ctrl-Enter'] = function(){
188                     return false;
189                 };
190         
191                 // don't let Emmet capture this
192                 delete CodeMirror.keyMap.default['Cmd-L'];
193             }
194         
195             this.editorLabelFX = new Fx.Tween(this.getLabel(), {
196                 property: 'opacity',
197                 link: 'cancel'
198             });
199         
200             this.getWindow().addEvents({
201                 mouseenter: function() {
202                     this.editorLabelFX.start(0);
203                 }.bind(this),
204                 mouseleave: function() {
205                     this.editorLabelFX.start(0.8);
206                 }.bind(this)
207             });
208         
209             // jordan commented
210             //    mooshell.addEvents({
211             //      'run': this.b64decode.bind(this)
212             //    });
213         
214             Layout.registerEditor(this);
215             this.setLabelName(this.options.language || this.options.name);
216         },
217       
218         /**
219          *
220          */
221         this.validateEditorInput = function(parentNode){
222             var currentValue = this.getCode();
223             var warnings = [];
224       
225             // destroy tooltip if it already exists for this editor
226             if (this.validationTooltip){
227                 this.validationTooltip.destroy();
228             }
229       
230             // create the container
231             this.validationTooltip = Element('ul', {
232                 'class': 'warningTooltip'
233             });
234       
235             // collect warnings
236             Object.each(this.options.disallowed, function(value, key){
237                 if (currentValue.test(key, 'i')){
238                     warnings.push('<li>' + value + '</li>');
239                 }
240             });
241       
242             // inject container
243             this.validationTooltip = this.validationTooltip.inject(parentNode);
244       
245             // squash and apply warnings
246             this.validationTooltip.set({
247                 html: warnings.join('')
248             });
249         },
250       
251         /**
252          *
253          */
254         this.getEditor = function() {
255             return this.editor || this.element;
256         },
257       
258         /**
259          *
260          */
261         this.getWindow = function() {
262             if (!this.window) {
263                 this.window = this.element.getParent('.window');
264             }
265             return this.window;
266         },
267       
268         /**
269          *
270          */
271         this.getLabel = function() {
272             return this.getWindow().getElement('.window_label');
273         },
274       
275         /**
276          *
277          */
278         this.b64decode = function() {
279             this.element.set('value', this.before_decode);
280         },
281       
282         /**
283          *
284          */
285         this.getCode = function() {
286             return (this.editor) ? this.editor.getValue() : this.element.get('value');
287         },
288       
289         /**
290          *
291          */
292         this.updateFromMirror = function() {
293             this.before_decode = this.getCode();
294             this.element.set('value', Base64.encode(this.before_decode));
295         },
296       
297         /**
298          *
299          */
300         this.updateCode = function() {
301             this.element.set('value', this.getCode());
302         },
303       
304         /**
305          *
306          */
307         this.clean = function() {
308             this.element.set('value', '');
309             this.cleanEditor();
310         },
311       
312         /**
313          *
314          */
315         this.cleanEditor = function() {
316             if (this.editor) this.editor.setCode('');
317         },
318       
319         /**
320          *
321          */
322         this.hide = function() {
323             this.getWindow().hide();
324         },
325       
326         this.show = function() {
327             this.getWindow().show();
328         },
329       
330         /**
331          *
332          */
333         this.setEditorOptions = function(options){
334             Object.each(options, function(fn, key){
335                 this.editor.setOption(key, fn.bind(this));
336             }, this);
337         },
338       
339         /**
340          *
341          */
342         this.setEditorEvents = function(e){
343             Object.each(e, function(fn, key){
344                 this.editor.on(key, fn.bind(this));
345             }, this);
346         },
347       
348         /**
349          *
350          */
351         this.setLanguage = function(language) {
352             // Todo: This is hacky
353             this.setLabelName(language);
354         },
355       
356         /**
357          *
358          */
359         this.setLabelName = function(language) {
360             this.getLabel().set('text', this.window_names[language] || language);
361         },
362       
363         /**
364          *
365          */
366         this.setStyle = function(key, value) {
367             if (this.editor) return $(this.editor.frame).setStyle(key, value);
368             return this.element.setStyle(key, value);
369         },
370       
371         /**
372          *
373          */
374         this.setStyles = function(options) {
375             if (this.editor) return $(this.editor.frame).setStyles(options);
376             return this.element.setStyles(options);
377         },
378       
379         /**
380          *
381          */
382         this.setWidth = function(width) {
383             this.getWindow().setStyle('width', width);
384         },
385       
386         /**
387          *
388          */
389         this.setHeight = function(height) {
390             this.getWindow().setStyle('height', height);
391         },
392       
393         /**
394          *
395          */
396         this.getPosition = function() {
397             if (this.editor) return $(this.editor.frame).getPosition();
398             return this.element.getPosition();
399         },
400       
401         /**
402          *
403          */
404         this.forceDefaultCodeMirrorOptions = function() {
405             this.options.codeMirrorOptions = default_code_mirror_options;
406         }
407
408         // BEGIN CONSTRUCTOR
409
410         /* member variables */
411         this.options = options;
412
413         var object = this;
414
415         // adapted from EditorCM.js from jsfiddle.net
416
417         var disallowedPlatforms = ['ios', 'android', 'ipod'];
418
419         this.window_names = {
420           'javascript': 'JavaScript',
421           'html': 'HTML',
422           'css': 'CSS',
423           'scss': 'SCSS',
424           'coffeescript': 'CoffeeScript',
425           'javascript 1.7': 'JavaScript 1.7'
426         };
427
428         this.initialize();
429         // END CONSTRUCTOR
430     } // function CodeEditor
431
432 })( jQuery );
433