| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Ajax.org B.V. nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
- define(function(require, exports, module) {
- "use strict";
- var event = require("../lib/event");
- var UA = require("../lib/useragent");
- var net = require("../lib/net");
- var ace = require("../ace");
- require("../theme/textmate");
- module.exports = exports = ace;
- /*
- * Returns the CSS property of element.
- * 1) If the CSS property is on the style object of the element, use it, OR
- * 2) Compute the CSS property
- *
- * If the property can't get computed, is 'auto' or 'intrinsic', the former
- * calculated property is used (this can happen in cases where the textarea
- * is hidden and has no dimension styles).
- */
- var getCSSProperty = function(element, container, property) {
- var ret = element.style[property];
- if (!ret) {
- if (window.getComputedStyle) {
- ret = window.getComputedStyle(element, '').getPropertyValue(property);
- } else {
- ret = element.currentStyle[property];
- }
- }
- if (!ret || ret == 'auto' || ret == 'intrinsic') {
- ret = container.style[property];
- }
- return ret;
- };
- function applyStyles(elm, styles) {
- for (var style in styles) {
- elm.style[style] = styles[style];
- }
- }
- function setupContainer(element, getValue) {
- if (element.type != 'textarea') {
- throw new Error("Textarea required!");
- }
- var parentNode = element.parentNode;
- // This will hold the editor.
- var container = document.createElement('div');
- // To put Ace in the place of the textarea, we have to copy a few of the
- // textarea's style attributes to the div container.
- //
- // The problem is that the properties have to get computed (they might be
- // defined by a CSS file on the page - you can't access such rules that
- // apply to an element via elm.style). Computed properties are converted to
- // pixels although the dimension might be given as percentage. When the
- // window resizes, the dimensions defined by percentages changes, so the
- // properties have to get recomputed to get the new/true pixels.
- var resizeEvent = function() {
- var style = 'position:relative;';
- [
- 'margin-top', 'margin-left', 'margin-right', 'margin-bottom'
- ].forEach(function(item) {
- style += item + ':' +
- getCSSProperty(element, container, item) + ';';
- });
- // Calculating the width/height of the textarea is somewhat tricky. To
- // do it right, you have to include the paddings to the sides as well
- // (eg. width = width + padding-left, -right). This works well, as
- // long as the width of the element is not set or given in pixels. In
- // this case and after the textarea is hidden, getCSSProperty(element,
- // container, 'width') will still return pixel value. If the element
- // has realtiv dimensions (e.g. width='95<percent>')
- // getCSSProperty(...) will return pixel values only as long as the
- // textarea is visible. After it is hidden getCSSProperty will return
- // the relative dimensions as they are set on the element (in the case
- // of width, 95<percent>).
- // Making the sum of pixel vaules (e.g. padding) and realtive values
- // (e.g. <percent>) is not possible. As such the padding styles are
- // ignored.
- // The complete width is the width of the textarea + the padding
- // to the left and right.
- var width = getCSSProperty(element, container, 'width') || (element.clientWidth + "px");
- var height = getCSSProperty(element, container, 'height') || (element.clientHeight + "px");
- style += 'height:' + height + ';width:' + width + ';';
- // Set the display property to 'inline-block'.
- style += 'display:inline-block;';
- container.setAttribute('style', style);
- };
- event.addListener(window, 'resize', resizeEvent);
- // Call the resizeEvent once, so that the size of the container is
- // calculated.
- resizeEvent();
- // Insert the div container after the element.
- parentNode.insertBefore(container, element.nextSibling);
- // Override the forms onsubmit function. Set the innerHTML and value
- // of the textarea before submitting.
- while (parentNode !== document) {
- if (parentNode.tagName.toUpperCase() === 'FORM') {
- var oldSumit = parentNode.onsubmit;
- // Override the onsubmit function of the form.
- parentNode.onsubmit = function(evt) {
- element.value = getValue();
- // If there is a onsubmit function already, then call
- // it with the current context and pass the event.
- if (oldSumit) {
- oldSumit.call(this, evt);
- }
- };
- break;
- }
- parentNode = parentNode.parentNode;
- }
- return container;
- }
- exports.transformTextarea = function(element, options) {
- var session;
- var container = setupContainer(element, function() {
- return session.getValue();
- });
- // Hide the element.
- element.style.display = 'none';
- container.style.background = 'white';
- //
- var editorDiv = document.createElement("div");
- applyStyles(editorDiv, {
- top: "0px",
- left: "0px",
- right: "0px",
- bottom: "0px",
- border: "1px solid gray",
- position: "absolute"
- });
- container.appendChild(editorDiv);
- var settingOpener = document.createElement("div");
- applyStyles(settingOpener, {
- position: "absolute",
- right: "0px",
- bottom: "0px",
- background: "red",
- cursor: "nw-resize",
- borderStyle: "solid",
- borderWidth: "9px 8px 10px 9px",
- width: "2px",
- borderColor: "lightblue gray gray lightblue",
- zIndex: 101
- });
- var settingDiv = document.createElement("div");
- var settingDivStyles = {
- top: "0px",
- left: "20%",
- right: "0px",
- bottom: "0px",
- position: "absolute",
- padding: "5px",
- zIndex: 100,
- color: "white",
- display: "none",
- overflow: "auto",
- fontSize: "14px",
- boxShadow: "-5px 2px 3px gray"
- };
- if (!UA.isOldIE) {
- settingDivStyles.backgroundColor = "rgba(0, 0, 0, 0.6)";
- } else {
- settingDivStyles.backgroundColor = "#333";
- }
- applyStyles(settingDiv, settingDivStyles);
- container.appendChild(settingDiv);
- options = options || exports.defaultOptions;
- // Power up ace on the textarea:
- var editor = ace.edit(editorDiv);
- session = editor.getSession();
- session.setValue(element.value || element.innerHTML);
- editor.focus();
- // Add the settingPanel opener to the editor's div.
- container.appendChild(settingOpener);
- // Create the API.
- setupApi(editor, editorDiv, settingDiv, ace, options, load);
- // Create the setting's panel.
- setupSettingPanel(settingDiv, settingOpener, editor);
- var state = "";
- event.addListener(settingOpener, "mousemove", function(e) {
- var rect = this.getBoundingClientRect();
- var x = e.clientX - rect.left, y = e.clientY - rect.top;
- if (x + y < (rect.width + rect.height)/2) {
- this.style.cursor = "pointer";
- state = "toggle";
- } else {
- state = "resize";
- this.style.cursor = "nw-resize";
- }
- });
- event.addListener(settingOpener, "mousedown", function(e) {
- if (state == "toggle") {
- editor.setDisplaySettings();
- return;
- }
- container.style.zIndex = 100000;
- var rect = container.getBoundingClientRect();
- var startX = rect.width + rect.left - e.clientX;
- var startY = rect.height + rect.top - e.clientY;
- event.capture(settingOpener, function(e) {
- container.style.width = e.clientX - rect.left + startX + "px";
- container.style.height = e.clientY - rect.top + startY + "px";
- editor.resize();
- }, function() {});
- });
- return editor;
- };
- function load(url, module, callback) {
- net.loadScript(url, function() {
- require([module], callback);
- });
- }
- function setupApi(editor, editorDiv, settingDiv, ace, options, loader) {
- var session = editor.getSession();
- var renderer = editor.renderer;
- loader = loader || load;
- function toBool(value) {
- return value === "true" || value == true;
- }
- editor.setDisplaySettings = function(display) {
- if (display == null)
- display = settingDiv.style.display == "none";
- if (display) {
- settingDiv.style.display = "block";
- settingDiv.hideButton.focus();
- editor.on("focus", function onFocus() {
- editor.removeListener("focus", onFocus);
- settingDiv.style.display = "none";
- });
- } else {
- editor.focus();
- }
- };
- editor.$setOption = editor.setOption;
- editor.$getOption = editor.getOption;
- editor.setOption = function(key, value) {
- switch (key) {
- case "mode":
- editor.$setOption("mode", "ace/mode/" + value)
- break;
- case "theme":
- editor.$setOption("theme", "ace/theme/" + value)
- break;
- case "keybindings":
- switch (value) {
- case "vim":
- editor.setKeyboardHandler("ace/keyboard/vim");
- break;
- case "emacs":
- editor.setKeyboardHandler("ace/keyboard/emacs");
- break;
- default:
- editor.setKeyboardHandler(null);
- }
- break;
- case "softWrap":
- case "fontSize":
- editor.$setOption(key, value);
- break;
-
- default:
- editor.$setOption(key, toBool(value));
- }
- };
- editor.getOption = function(key) {
- switch (key) {
- case "mode":
- return editor.$getOption("mode").substr("ace/mode/".length)
- break;
- case "theme":
- return editor.$getOption("theme").substr("ace/theme/".length)
- break;
- case "keybindings":
- var value = editor.getKeyboardHandler()
- switch (value && value.$id) {
- case "ace/keyboard/vim":
- return "vim";
- case "ace/keyboard/emacs":
- return "emacs";
- default:
- return "ace";
- }
- break;
- default:
- return editor.$getOption(key);
- }
- };
- editor.setOptions(options);
- return editor;
- }
- function setupSettingPanel(settingDiv, settingOpener, editor) {
- var BOOL = null;
- var desc = {
- mode: "Mode:",
- wrap: "Soft Wrap:",
- theme: "Theme:",
- fontSize: "Font Size:",
- showGutter: "Display Gutter:",
- keybindings: "Keyboard",
- showPrintMargin: "Show Print Margin:",
- useSoftTabs: "Use Soft Tabs:",
- showInvisibles: "Show Invisibles"
- };
- var optionValues = {
- mode: {
- text: "Plain",
- javascript: "JavaScript",
- xml: "XML",
- html: "HTML",
- css: "CSS",
- scss: "SCSS",
- python: "Python",
- php: "PHP",
- java: "Java",
- ruby: "Ruby",
- c_cpp: "C/C++",
- coffee: "CoffeeScript",
- json: "json",
- perl: "Perl",
- clojure: "Clojure",
- ocaml: "OCaml",
- csharp: "C#",
- haxe: "haXe",
- svg: "SVG",
- textile: "Textile",
- groovy: "Groovy",
- liquid: "Liquid",
- Scala: "Scala"
- },
- theme: {
- clouds: "Clouds",
- clouds_midnight: "Clouds Midnight",
- cobalt: "Cobalt",
- crimson_editor: "Crimson Editor",
- dawn: "Dawn",
- eclipse: "Eclipse",
- idle_fingers: "Idle Fingers",
- kr_theme: "Kr Theme",
- merbivore: "Merbivore",
- merbivore_soft: "Merbivore Soft",
- mono_industrial: "Mono Industrial",
- monokai: "Monokai",
- pastel_on_dark: "Pastel On Dark",
- solarized_dark: "Solarized Dark",
- solarized_light: "Solarized Light",
- textmate: "Textmate",
- twilight: "Twilight",
- vibrant_ink: "Vibrant Ink"
- },
- showGutter: BOOL,
- fontSize: {
- "10px": "10px",
- "11px": "11px",
- "12px": "12px",
- "14px": "14px",
- "16px": "16px"
- },
- wrap: {
- off: "Off",
- 40: "40",
- 80: "80",
- free: "Free"
- },
- keybindings: {
- ace: "ace",
- vim: "vim",
- emacs: "emacs"
- },
- showPrintMargin: BOOL,
- useSoftTabs: BOOL,
- showInvisibles: BOOL
- };
- var table = [];
- table.push("<table><tr><th>Setting</th><th>Value</th></tr>");
- function renderOption(builder, option, obj, cValue) {
- if (!obj) {
- builder.push(
- "<input type='checkbox' title='", option, "' ",
- cValue + "" == "true" ? "checked='true'" : "",
- "'></input>"
- );
- return;
- }
- builder.push("<select title='" + option + "'>");
- for (var value in obj) {
- builder.push("<option value='" + value + "' ");
- if (cValue == value) {
- builder.push(" selected ");
- }
- builder.push(">",
- obj[value],
- "</option>");
- }
- builder.push("</select>");
- }
- for (var option in exports.defaultOptions) {
- table.push("<tr><td>", desc[option], "</td>");
- table.push("<td>");
- renderOption(table, option, optionValues[option], editor.getOption(option));
- table.push("</td></tr>");
- }
- table.push("</table>");
- settingDiv.innerHTML = table.join("");
- var onChange = function(e) {
- var select = e.currentTarget;
- editor.setOption(select.title, select.value);
- };
- var onClick = function(e) {
- var cb = e.currentTarget;
- editor.setOption(cb.title, cb.checked);
- };
- var selects = settingDiv.getElementsByTagName("select");
- for (var i = 0; i < selects.length; i++)
- selects[i].onchange = onChange;
- var cbs = settingDiv.getElementsByTagName("input");
- for (var i = 0; i < cbs.length; i++)
- cbs[i].onclick = onClick;
- var button = document.createElement("input");
- button.type = "button";
- button.value = "Hide";
- event.addListener(button, "click", function() {
- editor.setDisplaySettings(false);
- });
- settingDiv.appendChild(button);
- settingDiv.hideButton = button;
- }
- // Default startup options.
- exports.defaultOptions = {
- mode: "javascript",
- theme: "textmate",
- wrap: "off",
- fontSize: "12px",
- showGutter: "false",
- keybindings: "ace",
- showPrintMargin: "false",
- useSoftTabs: "true",
- showInvisibles: "false"
- };
- });
|