~galaxyabstractor/lenasys/ace

« back to all changes in this revision

Viewing changes to js/ace/ext-textarea.js

  • Committer: galaxyAbstractor
  • Date: 2013-04-04 16:19:07 UTC
  • Revision ID: galaxyabstractor@gmail.com-20130404161907-eo1wnb7ac2rnizg8
Started implementation of a new codeviewer using Ace

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ***** BEGIN LICENSE BLOCK *****
 
2
 * Distributed under the BSD license:
 
3
 *
 
4
 * Copyright (c) 2010, Ajax.org B.V.
 
5
 * All rights reserved.
 
6
 * 
 
7
 * Redistribution and use in source and binary forms, with or without
 
8
 * modification, are permitted provided that the following conditions are met:
 
9
 *     * Redistributions of source code must retain the above copyright
 
10
 *       notice, this list of conditions and the following disclaimer.
 
11
 *     * Redistributions in binary form must reproduce the above copyright
 
12
 *       notice, this list of conditions and the following disclaimer in the
 
13
 *       documentation and/or other materials provided with the distribution.
 
14
 *     * Neither the name of Ajax.org B.V. nor the
 
15
 *       names of its contributors may be used to endorse or promote products
 
16
 *       derived from this software without specific prior written permission.
 
17
 * 
 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
21
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
28
 *
 
29
 * ***** END LICENSE BLOCK ***** */
 
30
 
 
31
define('ace/ext/textarea', ['require', 'exports', 'module' , 'ace/lib/event', 'ace/lib/useragent', 'ace/lib/net', 'ace/ace', 'ace/theme/textmate', 'ace/mode/text'], function(require, exports, module) {
 
32
 
 
33
 
 
34
var event = require("../lib/event");
 
35
var UA = require("../lib/useragent");
 
36
var net = require("../lib/net");
 
37
var ace = require("../ace");
 
38
 
 
39
require("../theme/textmate");
 
40
 
 
41
module.exports = exports = ace;
 
42
var getCSSProperty = function(element, container, property) {
 
43
    var ret = element.style[property];
 
44
 
 
45
    if (!ret) {
 
46
        if (window.getComputedStyle) {
 
47
            ret = window.getComputedStyle(element, '').getPropertyValue(property);
 
48
        } else {
 
49
            ret = element.currentStyle[property];
 
50
        }
 
51
    }
 
52
 
 
53
    if (!ret || ret == 'auto' || ret == 'intrinsic') {
 
54
        ret = container.style[property];
 
55
    }
 
56
    return ret;
 
57
};
 
58
 
 
59
function applyStyles(elm, styles) {
 
60
    for (var style in styles) {
 
61
        elm.style[style] = styles[style];
 
62
    }
 
63
}
 
64
 
 
65
function setupContainer(element, getValue) {
 
66
    if (element.type != 'textarea') {
 
67
        throw "Textarea required!";
 
68
    }
 
69
 
 
70
    var parentNode = element.parentNode;
 
71
    var container = document.createElement('div');
 
72
    var resizeEvent = function() {
 
73
        var style = 'position:relative;';
 
74
        [
 
75
            'margin-top', 'margin-left', 'margin-right', 'margin-bottom'
 
76
        ].forEach(function(item) {
 
77
            style += item + ':' +
 
78
                        getCSSProperty(element, container, item) + ';';
 
79
        });
 
80
        var width = getCSSProperty(element, container, 'width') || (element.clientWidth + "px");
 
81
        var height = getCSSProperty(element, container, 'height')  || (element.clientHeight + "px");
 
82
        style += 'height:' + height + ';width:' + width + ';';
 
83
        style += 'display:inline-block;';
 
84
        container.setAttribute('style', style);
 
85
    };
 
86
    event.addListener(window, 'resize', resizeEvent);
 
87
    resizeEvent();
 
88
    parentNode.insertBefore(container, element.nextSibling);
 
89
    while (parentNode !== document) {
 
90
        if (parentNode.tagName.toUpperCase() === 'FORM') {
 
91
            var oldSumit = parentNode.onsubmit;
 
92
            parentNode.onsubmit = function(evt) {
 
93
                element.value = getValue();
 
94
                if (oldSumit) {
 
95
                    oldSumit.call(this, evt);
 
96
                }
 
97
            };
 
98
            break;
 
99
        }
 
100
        parentNode = parentNode.parentNode;
 
101
    }
 
102
    return container;
 
103
}
 
104
 
 
105
exports.transformTextarea = function(element, loader) {
 
106
    var session;
 
107
    var container = setupContainer(element, function() {
 
108
        return session.getValue();
 
109
    });
 
110
    element.style.display = 'none';
 
111
    container.style.background = 'white';
 
112
    var editorDiv = document.createElement("div");
 
113
    applyStyles(editorDiv, {
 
114
        top: "0px",
 
115
        left: "0px",
 
116
        right: "0px",
 
117
        bottom: "0px",
 
118
        border: "1px solid gray",
 
119
        position: "absolute"
 
120
    });
 
121
    container.appendChild(editorDiv);
 
122
 
 
123
    var settingOpener = document.createElement("div");
 
124
    applyStyles(settingOpener, {
 
125
        position: "absolute",
 
126
        right: "0px",
 
127
        bottom: "0px",
 
128
        background: "red",
 
129
        cursor: "nw-resize",
 
130
        borderStyle: "solid",
 
131
        borderWidth: "9px 8px 10px 9px",
 
132
        width: "2px",
 
133
        borderColor: "lightblue gray gray lightblue",
 
134
        zIndex: 101
 
135
    });
 
136
 
 
137
    var settingDiv = document.createElement("div");
 
138
    var settingDivStyles = {
 
139
        top: "0px",
 
140
        left: "20%",
 
141
        right: "0px",
 
142
        bottom: "0px",
 
143
        position: "absolute",
 
144
        padding: "5px",
 
145
        zIndex: 100,
 
146
        color: "white",
 
147
        display: "none",
 
148
        overflow: "auto",
 
149
        fontSize: "14px",
 
150
        boxShadow: "-5px 2px 3px gray"
 
151
    };
 
152
    if (!UA.isOldIE) {
 
153
        settingDivStyles.backgroundColor = "rgba(0, 0, 0, 0.6)";
 
154
    } else {
 
155
        settingDivStyles.backgroundColor = "#333";
 
156
    }
 
157
 
 
158
    applyStyles(settingDiv, settingDivStyles);
 
159
    container.appendChild(settingDiv);
 
160
    var options = {};
 
161
 
 
162
    var editor = ace.edit(editorDiv);
 
163
    session = editor.getSession();
 
164
 
 
165
    session.setValue(element.value || element.innerHTML);
 
166
    editor.focus();
 
167
    container.appendChild(settingOpener);
 
168
    setupApi(editor, editorDiv, settingDiv, ace, options, loader);
 
169
    setupSettingPanel(settingDiv, settingOpener, editor, options);
 
170
    
 
171
    var state = "";
 
172
    event.addListener(settingOpener, "mousemove", function(e) {
 
173
        var rect = this.getBoundingClientRect();
 
174
        var x = e.clientX - rect.left, y = e.clientY - rect.top;
 
175
        if (x + y < (rect.width + rect.height)/2) {
 
176
            this.style.cursor = "pointer";
 
177
            state = "toggle";
 
178
        } else {
 
179
            state = "resize";
 
180
            this.style.cursor = "nw-resize";
 
181
        }
 
182
    });
 
183
    
 
184
    event.addListener(settingOpener, "mousedown", function(e) {
 
185
        if (state == "toggle") {
 
186
            editor.setDisplaySettings();
 
187
            return;
 
188
        }
 
189
        container.style.zIndex = 100000;
 
190
        var rect = container.getBoundingClientRect();
 
191
        var startX = rect.width  + rect.left - e.clientX;
 
192
        var startY = rect.height  + rect.top - e.clientY;
 
193
        event.capture(settingOpener, function(e) {
 
194
            container.style.width = e.clientX - rect.left + startX + "px";
 
195
            container.style.height = e.clientY - rect.top + startY + "px";
 
196
            editor.resize();
 
197
        }, function() {});
 
198
    });
 
199
 
 
200
    return editor;
 
201
};
 
202
 
 
203
function load(url, module, callback) {
 
204
    net.loadScript(url, function() {
 
205
        require([module], callback);
 
206
    });
 
207
}
 
208
 
 
209
function setupApi(editor, editorDiv, settingDiv, ace, options, loader) {
 
210
    var session = editor.getSession();
 
211
    var renderer = editor.renderer;
 
212
    loader = loader || load;
 
213
 
 
214
    function toBool(value) {
 
215
        return value === "true" || value == true;
 
216
    }
 
217
 
 
218
    editor.setDisplaySettings = function(display) {
 
219
        if (display == null)
 
220
            display = settingDiv.style.display == "none";
 
221
        if (display) {
 
222
            settingDiv.style.display = "block";
 
223
            settingDiv.hideButton.focus();
 
224
            editor.on("focus", function onFocus() {
 
225
                editor.removeListener("focus", onFocus);
 
226
                settingDiv.style.display = "none"
 
227
            });
 
228
        } else {
 
229
            editor.focus();
 
230
        };
 
231
    };
 
232
    
 
233
    editor.setOption = function(key, value) {
 
234
        if (options[key] == value) return;
 
235
 
 
236
        switch (key) {
 
237
            case "gutter":
 
238
                renderer.setShowGutter(toBool(value));
 
239
            break;
 
240
 
 
241
            case "mode":
 
242
                if (value != "text") {
 
243
                    loader("mode-" + value + ".js", "ace/mode/" + value, function() {
 
244
                        var aceMode = require("../mode/" + value).Mode;
 
245
                        session.setMode(new aceMode());
 
246
                    });
 
247
                } else {
 
248
                    session.setMode(new (require("../mode/text").Mode));
 
249
                }
 
250
            break;
 
251
 
 
252
            case "theme":
 
253
                if (value != "textmate") {
 
254
                    loader("theme-" + value + ".js", "ace/theme/" + value, function() {
 
255
                        editor.setTheme("ace/theme/" + value);
 
256
                    });
 
257
                } else {
 
258
                    editor.setTheme("ace/theme/textmate");
 
259
                }
 
260
            break;
 
261
 
 
262
            case "fontSize":
 
263
                editorDiv.style.fontSize = value;
 
264
            break;
 
265
 
 
266
            case "keybindings":
 
267
                switch (value) {
 
268
                    case "vim":
 
269
                        editor.setKeyboardHandler("ace/keyboard/vim");
 
270
                        break;
 
271
                    case "emacs": 
 
272
                        editor.setKeyboardHandler("ace/keyboard/emacs");
 
273
                        break;
 
274
                    default:
 
275
                        editor.setKeyboardHandler(null);
 
276
                }
 
277
            break;
 
278
 
 
279
            case "softWrap":
 
280
                switch (value) {
 
281
                    case "off":
 
282
                        session.setUseWrapMode(false);
 
283
                        renderer.setPrintMarginColumn(80);
 
284
                    break;
 
285
                    case "40":
 
286
                        session.setUseWrapMode(true);
 
287
                        session.setWrapLimitRange(40, 40);
 
288
                        renderer.setPrintMarginColumn(40);
 
289
                    break;
 
290
                    case "80":
 
291
                        session.setUseWrapMode(true);
 
292
                        session.setWrapLimitRange(80, 80);
 
293
                        renderer.setPrintMarginColumn(80);
 
294
                    break;
 
295
                    case "free":
 
296
                        session.setUseWrapMode(true);
 
297
                        session.setWrapLimitRange(null, null);
 
298
                        renderer.setPrintMarginColumn(80);
 
299
                    break;
 
300
                }
 
301
            break;
 
302
 
 
303
            case "useSoftTabs":
 
304
                session.setUseSoftTabs(toBool(value));
 
305
            break;
 
306
 
 
307
            case "showPrintMargin":
 
308
                renderer.setShowPrintMargin(toBool(value));
 
309
            break;
 
310
 
 
311
            case "showInvisibles":
 
312
                editor.setShowInvisibles(toBool(value));
 
313
            break;
 
314
        }
 
315
 
 
316
        options[key] = value;
 
317
    };
 
318
 
 
319
    editor.getOption = function(key) {
 
320
        return options[key];
 
321
    };
 
322
 
 
323
    editor.getOptions = function() {
 
324
        return options;
 
325
    };
 
326
 
 
327
    for (var option in exports.options) {
 
328
        editor.setOption(option, exports.options[option]);
 
329
    }
 
330
 
 
331
    return editor;
 
332
}
 
333
 
 
334
function setupSettingPanel(settingDiv, settingOpener, editor, options) {
 
335
    var BOOL = null;
 
336
 
 
337
    var desc = {
 
338
        mode:            "Mode:",
 
339
        gutter:          "Display Gutter:",
 
340
        theme:           "Theme:",
 
341
        fontSize:        "Font Size:",
 
342
        softWrap:        "Soft Wrap:",
 
343
        keybindings:     "Keyboard",
 
344
        showPrintMargin: "Show Print Margin:",
 
345
        useSoftTabs:     "Use Soft Tabs:",
 
346
        showInvisibles:  "Show Invisibles"
 
347
    };
 
348
 
 
349
    var optionValues = {
 
350
        mode: {
 
351
            text:       "Plain",
 
352
            javascript: "JavaScript",
 
353
            xml:        "XML",
 
354
            html:       "HTML",
 
355
            css:        "CSS",
 
356
            scss:       "SCSS",
 
357
            python:     "Python",
 
358
            php:        "PHP",
 
359
            java:       "Java",
 
360
            ruby:       "Ruby",
 
361
            c_cpp:      "C/C++",
 
362
            coffee:     "CoffeeScript",
 
363
            json:       "json",
 
364
            perl:       "Perl",
 
365
            clojure:    "Clojure",
 
366
            ocaml:      "OCaml",
 
367
            csharp:     "C#",
 
368
            haxe:       "haXe",
 
369
            svg:        "SVG",
 
370
            textile:    "Textile",
 
371
            groovy:     "Groovy",
 
372
            liquid:     "Liquid",
 
373
            Scala:      "Scala"
 
374
        },
 
375
        theme: {
 
376
            clouds:           "Clouds",
 
377
            clouds_midnight:  "Clouds Midnight",
 
378
            cobalt:           "Cobalt",
 
379
            crimson_editor:   "Crimson Editor",
 
380
            dawn:             "Dawn",
 
381
            eclipse:          "Eclipse",
 
382
            idle_fingers:     "Idle Fingers",
 
383
            kr_theme:         "Kr Theme",
 
384
            merbivore:        "Merbivore",
 
385
            merbivore_soft:   "Merbivore Soft",
 
386
            mono_industrial:  "Mono Industrial",
 
387
            monokai:          "Monokai",
 
388
            pastel_on_dark:   "Pastel On Dark",
 
389
            solarized_dark:   "Solarized Dark",
 
390
            solarized_light:  "Solarized Light",
 
391
            textmate:         "Textmate",
 
392
            twilight:         "Twilight",
 
393
            vibrant_ink:      "Vibrant Ink"
 
394
        },
 
395
        gutter: BOOL,
 
396
        fontSize: {
 
397
            "10px": "10px",
 
398
            "11px": "11px",
 
399
            "12px": "12px",
 
400
            "14px": "14px",
 
401
            "16px": "16px"
 
402
        },
 
403
        softWrap: {
 
404
            off:    "Off",
 
405
            40:     "40",
 
406
            80:     "80",
 
407
            free:   "Free"
 
408
        },
 
409
        keybindings: {
 
410
            ace: "ace",
 
411
            vim: "vim",
 
412
            emacs: "emacs"
 
413
        },
 
414
        showPrintMargin:    BOOL,
 
415
        useSoftTabs:        BOOL,
 
416
        showInvisibles:     BOOL
 
417
    };
 
418
 
 
419
    var table = [];
 
420
    table.push("<table><tr><th>Setting</th><th>Value</th></tr>");
 
421
 
 
422
    function renderOption(builder, option, obj, cValue) {
 
423
        if (!obj) {
 
424
            builder.push(
 
425
                "<input type='checkbox' title='", option, "' ",
 
426
                    cValue == "true" ? "checked='true'" : "",
 
427
               "'></input>"
 
428
            );
 
429
            return
 
430
        }
 
431
        builder.push("<select title='" + option + "'>");
 
432
        for (var value in obj) {
 
433
            builder.push("<option value='" + value + "' ");
 
434
 
 
435
            if (cValue == value) {
 
436
                builder.push(" selected ");
 
437
            }
 
438
 
 
439
            builder.push(">",
 
440
                obj[value],
 
441
                "</option>");
 
442
        }
 
443
        builder.push("</select>");
 
444
    }
 
445
 
 
446
    for (var option in options) {
 
447
        table.push("<tr><td>", desc[option], "</td>");
 
448
        table.push("<td>");
 
449
        renderOption(table, option, optionValues[option], options[option]);
 
450
        table.push("</td></tr>");
 
451
    }
 
452
    table.push("</table>");
 
453
    settingDiv.innerHTML = table.join("");
 
454
 
 
455
    var onChange = function(e) {
 
456
        var select = e.currentTarget;
 
457
        editor.setOption(select.title, select.value);
 
458
    };
 
459
    var onClick = function(e) {
 
460
        var cb = e.currentTarget;
 
461
        editor.setOption(cb.title, cb.checked);
 
462
    };
 
463
    var selects = settingDiv.getElementsByTagName("select");
 
464
    for (var i = 0; i < selects.length; i++)
 
465
        selects[i].onchange = onChange;
 
466
    var cbs = settingDiv.getElementsByTagName("input");
 
467
    for (var i = 0; i < cbs.length; i++)
 
468
        cbs[i].onclick = onClick;
 
469
 
 
470
 
 
471
    var button = document.createElement("input");
 
472
    button.type = "button";
 
473
    button.value = "Hide";
 
474
    event.addListener(button, "click", function() {
 
475
        editor.setDisplaySettings(false);
 
476
    });
 
477
    settingDiv.appendChild(button);
 
478
    settingDiv.hideButton = button;
 
479
}
 
480
exports.options = {
 
481
    mode:               "text",
 
482
    theme:              "textmate",
 
483
    gutter:             "false",
 
484
    fontSize:           "12px",
 
485
    softWrap:           "off",
 
486
    keybindings:        "ace",
 
487
    showPrintMargin:    "false",
 
488
    useSoftTabs:        "true",
 
489
    showInvisibles:     "false"
 
490
};
 
491
 
 
492
});