Separated logic & presentation, lowercased file names

I lowercased the file names. I've separated the code to generate an
overlay menu out into a module called overlay_page, it is the same code
as is in my other pull request for the keyboard shortcuts menu. It
should be easy enough to change the way it looks and behaves without
worrying about breaking anything. I've pulled code out into a module
called element_generator because it is pretty generic and isn't
concerned with gathering data for the settings menu. The element
generator can be reused and augmented but there is the possibility that
modules depending on it could break. show_settings_menu depends on the
element_generator. As it collects data it generates generic elements and
attaches event listeners to them.  The massive list of options has been
moved into its own module for two reasons (1) it makes maintaining
show_settings_menu much easier and (2) this is the only part of the menu
which has to be updated manually. We may be able to add something to the
build system to automatically generate this module based on which modes
and themes are found to alleviate us from having to remember to add said
options in when new themes and modes are added.
This commit is contained in:
Matthew Kastor 2013-03-31 00:18:09 -04:00 committed by nightwing
commit 1d631b5ca1
6 changed files with 943 additions and 644 deletions

View file

@ -45,7 +45,7 @@ exports.commands = [{
name: "showSettingsMenu",
bindKey: bindKey("Ctrl-q", "Command-q"),
exec: function (editor) {
config.loadModule("ace/ext/showSettingsMenu", function (e) {
config.loadModule("ace/ext/show_settings_menu", function (e) {
e(editor);
});
},

View file

@ -0,0 +1,360 @@
/*jslint
indent: 4,
maxerr: 50,
white: true,
browser: true,
vars: true
*/
/*global
define,
require
*/
/**
* Add Editor Menu Options
* @fileOverview Add Editor Menu Options <br />
* The menu options property needs to be added to the editor
* so that the settings menu can know about options for
* selection elements and track which option is selected.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
define(function(require, exports, module) {
'use strict';
/**
* The menu options property needs to be added to the editor
* so that the settings menu can know about options for
* selection elements and track which option is selected.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {ace.Editor} editor An instance of the ace editor.
*/
module.exports.addEditorMenuOptions = function addEditorMenuOptions (editor) {
editor.menuOptions = {
"setNewLineMode" : [{
"textContent" : "unix",
"value" : "unix"
}, {
"textContent" : "windows",
"value" : "windows"
}, {
"textContent" : "auto",
"value" : "auto"
}
],
"setTheme" : [{
"textContent" : "ambiance",
"value" : "ace/theme/ambiance"
}, {
"textContent" : "chaos",
"value" : "ace/theme/chaos"
}, {
"textContent" : "chrome",
"value" : "ace/theme/chrome"
}, {
"textContent" : "clouds",
"value" : "ace/theme/clouds"
}, {
"textContent" : "clouds_midnight",
"value" : "ace/theme/clouds_midnight"
}, {
"textContent" : "cobalt",
"value" : "ace/theme/cobalt"
}, {
"textContent" : "crimson_editor",
"value" : "ace/theme/crimson_editor"
}, {
"textContent" : "dawn",
"value" : "ace/theme/dawn"
}, {
"textContent" : "dreamweaver",
"value" : "ace/theme/dreamweaver"
}, {
"textContent" : "eclipse",
"value" : "ace/theme/eclipse"
}, {
"textContent" : "github",
"value" : "ace/theme/github"
}, {
"textContent" : "idle_fingers",
"value" : "ace/theme/idle_fingers"
}, {
"textContent" : "kr",
"value" : "ace/theme/kr"
}, {
"textContent" : "merbivore",
"value" : "ace/theme/merbivore"
}, {
"textContent" : "merbivore_soft",
"value" : "ace/theme/merbivore_soft"
}, {
"textContent" : "monokai",
"value" : "ace/theme/monokai"
}, {
"textContent" : "mono_industrial",
"value" : "ace/theme/mono_industrial"
}, {
"textContent" : "pastel_on_dark",
"value" : "ace/theme/pastel_on_dark"
}, {
"textContent" : "solarized_dark",
"value" : "ace/theme/solarized_dark"
}, {
"textContent" : "solarized_light",
"value" : "ace/theme/solarized_light"
}, {
"textContent" : "textmate",
"value" : "ace/theme/textmate"
}, {
"textContent" : "tomorrow",
"value" : "ace/theme/tomorrow"
}, {
"textContent" : "tomorrow_night",
"value" : "ace/theme/tomorrow_night"
}, {
"textContent" : "tomorrow_night_blue",
"value" : "ace/theme/tomorrow_night_blue"
}, {
"textContent" : "tomorrow_night_bright",
"value" : "ace/theme/tomorrow_night_bright"
}, {
"textContent" : "tomorrow_night_eighties",
"value" : "ace/theme/tomorrow_night_eighties"
}, {
"textContent" : "twilight",
"value" : "ace/theme/twilight"
}, {
"textContent" : "vibrant_ink",
"value" : "ace/theme/vibrant_ink"
}, {
"textContent" : "xcode",
"value" : "ace/theme/xcode"
}
],
"setMode" : [{
"textContent" : "abap",
"value" : "ace/mode/abap"
}, {
"textContent" : "asciidoc",
"value" : "ace/mode/asciidoc"
}, {
"textContent" : "c9search",
"value" : "ace/mode/c9search"
}, {
"textContent" : "clojure",
"value" : "ace/mode/clojure"
}, {
"textContent" : "coffee",
"value" : "ace/mode/coffee"
}, {
"textContent" : "coldfusion",
"value" : "ace/mode/coldfusion"
}, {
"textContent" : "csharp",
"value" : "ace/mode/csharp"
}, {
"textContent" : "css",
"value" : "ace/mode/css"
}, {
"textContent" : "curly",
"value" : "ace/mode/curly"
}, {
"textContent" : "c_cpp",
"value" : "ace/mode/c_cpp"
}, {
"textContent" : "dart",
"value" : "ace/mode/dart"
}, {
"textContent" : "diff",
"value" : "ace/mode/diff"
}, {
"textContent" : "django",
"value" : "ace/mode/django"
}, {
"textContent" : "dot",
"value" : "ace/mode/dot"
}, {
"textContent" : "ftl",
"value" : "ace/mode/ftl"
}, {
"textContent" : "glsl",
"value" : "ace/mode/glsl"
}, {
"textContent" : "golang",
"value" : "ace/mode/golang"
}, {
"textContent" : "groovy",
"value" : "ace/mode/groovy"
}, {
"textContent" : "haml",
"value" : "ace/mode/haml"
}, {
"textContent" : "haxe",
"value" : "ace/mode/haxe"
}, {
"textContent" : "html",
"value" : "ace/mode/html"
}, {
"textContent" : "jade",
"value" : "ace/mode/jade"
}, {
"textContent" : "java",
"value" : "ace/mode/java"
}, {
"textContent" : "javascript",
"value" : "ace/mode/javascript"
}, {
"textContent" : "json",
"value" : "ace/mode/json"
}, {
"textContent" : "jsp",
"value" : "ace/mode/jsp"
}, {
"textContent" : "jsx",
"value" : "ace/mode/jsx"
}, {
"textContent" : "latex",
"value" : "ace/mode/latex"
}, {
"textContent" : "less",
"value" : "ace/mode/less"
}, {
"textContent" : "liquid",
"value" : "ace/mode/liquid"
}, {
"textContent" : "lisp",
"value" : "ace/mode/lisp"
}, {
"textContent" : "livescript",
"value" : "ace/mode/livescript"
}, {
"textContent" : "logiql",
"value" : "ace/mode/logiql"
}, {
"textContent" : "lsl",
"value" : "ace/mode/lsl"
}, {
"textContent" : "lua",
"value" : "ace/mode/lua"
}, {
"textContent" : "luapage",
"value" : "ace/mode/luapage"
}, {
"textContent" : "lucene",
"value" : "ace/mode/lucene"
}, {
"textContent" : "makefile",
"value" : "ace/mode/makefile"
}, {
"textContent" : "markdown",
"value" : "ace/mode/markdown"
}, {
"textContent" : "objectivec",
"value" : "ace/mode/objectivec"
}, {
"textContent" : "ocaml",
"value" : "ace/mode/ocaml"
}, {
"textContent" : "pascal",
"value" : "ace/mode/pascal"
}, {
"textContent" : "perl",
"value" : "ace/mode/perl"
}, {
"textContent" : "pgsql",
"value" : "ace/mode/pgsql"
}, {
"textContent" : "php",
"value" : "ace/mode/php"
}, {
"textContent" : "powershell",
"value" : "ace/mode/powershell"
}, {
"textContent" : "python",
"value" : "ace/mode/python"
}, {
"textContent" : "r",
"value" : "ace/mode/r"
}, {
"textContent" : "rdoc",
"value" : "ace/mode/rdoc"
}, {
"textContent" : "rhtml",
"value" : "ace/mode/rhtml"
}, {
"textContent" : "ruby",
"value" : "ace/mode/ruby"
}, {
"textContent" : "sass",
"value" : "ace/mode/sass"
}, {
"textContent" : "scad",
"value" : "ace/mode/scad"
}, {
"textContent" : "scala",
"value" : "ace/mode/scala"
}, {
"textContent" : "scheme",
"value" : "ace/mode/scheme"
}, {
"textContent" : "scss",
"value" : "ace/mode/scss"
}, {
"textContent" : "sh",
"value" : "ace/mode/sh"
}, {
"textContent" : "sql",
"value" : "ace/mode/sql"
}, {
"textContent" : "stylus",
"value" : "ace/mode/stylus"
}, {
"textContent" : "svg",
"value" : "ace/mode/svg"
}, {
"textContent" : "tcl",
"value" : "ace/mode/tcl"
}, {
"textContent" : "tex",
"value" : "ace/mode/tex"
}, {
"textContent" : "text",
"value" : "ace/mode/text"
}, {
"textContent" : "textile",
"value" : "ace/mode/textile"
}, {
"textContent" : "tmsnippet",
"value" : "ace/mode/tmsnippet"
}, {
"textContent" : "tm_snippet",
"value" : "ace/mode/tm_snippet"
}, {
"textContent" : "toml",
"value" : "ace/mode/toml"
}, {
"textContent" : "typescript",
"value" : "ace/mode/typescript"
}, {
"textContent" : "vbscript",
"value" : "ace/mode/vbscript"
}, {
"textContent" : "xml",
"value" : "ace/mode/xml"
}, {
"textContent" : "xquery",
"value" : "ace/mode/xquery"
}, {
"textContent" : "yaml",
"value" : "ace/mode/yaml"
}
]
};
};
});

View file

@ -0,0 +1,125 @@
/*jslint
indent: 4,
maxerr: 50,
white: true,
browser: true,
vars: true
*/
/*global
define,
require
*/
/**
* Element Generator
* @fileOverview Element Generator <br />
* Contains methods for generating elements.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
define(function(require, exports, module) {
'use strict';
/**
* Creates a DOM option element
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {object} obj An object containing properties to add to the dom
* element. If one of those properties is named `selected` then it will be
* added as an attribute on the element instead.
*/
module.exports.createOption = function createOption (obj) {
var attribute;
var el = document.createElement('option');
for(attribute in obj) {
if(obj.hasOwnProperty(attribute)) {
if(attribute === 'selected') {
el.setAttribute(attribute, obj[attribute]);
} else {
el[attribute] = obj[attribute];
}
}
}
return el;
};
/**
* Creates a DOM checkbox element.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {string} id The id of the element.
* @param {boolean} checked Whether or not the element is checked.
* @param {string} clss The class of the element.
* @returns {DOMElement} Returns a checkbox element reference.
*/
module.exports.createCheckbox = function createCheckbox (id, checked, clss) {
var el = document.createElement('input');
el.setAttribute('type', 'checkbox');
el.setAttribute('id', id);
el.setAttribute('name', id);
el.setAttribute('value', checked);
el.setAttribute('class', clss);
if(checked) {
el.setAttribute('checked', 'checked');
}
return el;
};
/**
* Creates a DOM text input element.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {string} id The id of the element.
* @param {string} value The default value of the input element.
* @param {string} clss The class of the element.
* @returns {DOMElement} Returns an input element reference.
*/
module.exports.createInput = function createInput (id, value, clss) {
var el = document.createElement('input');
el.setAttribute('type', 'text');
el.setAttribute('id', id);
el.setAttribute('name', id);
el.setAttribute('value', value);
el.setAttribute('class', clss);
return el;
};
/**
* Creates a DOM label element.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {string} text The label text.
* @param {string} labelFor The id of the element being labeled.
* @returns {DOMElement} Returns a label element reference.
*/
module.exports.createLabel = function createLabel (text, labelFor) {
var el = document.createElement('label');
el.setAttribute('for', labelFor);
el.textContent = text;
return el;
};
/**
* Creates a DOM text input element.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {string} id The id of the element.
* @param {string} values An array of objects suitable for `createOption`
* @param {string} clss The class of the element.
* @returns {DOMElement} Returns a selection element reference.
* @see ace/ext/element_generator.createOption
*/
module.exports.createSelection = function createSelection (id, values, clss) {
var el = document.createElement('select');
el.setAttribute('id', id);
el.setAttribute('name', id);
el.setAttribute('class', clss);
values.forEach(function (item) {
el.appendChild(module.exports.createOption(item));
});
return el;
};
});

View file

@ -0,0 +1,86 @@
/*jslint
indent: 4,
maxerr: 50,
white: true,
browser: true,
vars: true
*/
/*global
define,
require
*/
/**
* Overlay Page
* @fileOverview Overlay Page <br />
* Generates an overlay for displaying menus. The overlay is an absolutely
* positioned div.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
define(function(require, exports, module) {
'use strict';
/**
* Generates an overlay for displaying menus. The overlay is an absolutely
* positioned div.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {DOMElement} contentElement Any element which may be presented inside
* a div.
* @param {string|number} top absolute position value.
* @param {string|number} right absolute position value.
* @param {string|number} bottom absolute position value.
* @param {string|number} left absolute position value.
*/
module.exports.overlayPage = function overlayPage (contentElement, top, right, bottom, left) {
var div = document.createElement('div');
var contentContainer = document.createElement('div');
contentContainer.style.cssText = 'margin: 0px; padding: 0px; border: 0px;' +
'overflow: auto;';
contentElement.style.cssText = contentElement.style.cssText + 'overflow: auto;';
contentContainer.appendChild(contentElement);
var cl = document.createElement('img');
if (top) {
top = 'top: ' + top + ';';
} else {
top = '';
}
if (right) {
right = 'right: ' + right + ';';
} else {
right = '';
}
if (bottom) {
bottom = 'bottom: ' + bottom + ';';
} else {
bottom = '';
}
if (left) {
left = 'left: ' + left + ';';
} else {
left = '';
}
cl.src = '/BigRedX.png';
cl.style.cssText = 'margin: 5px 5px 0 0; padding: 0; ' +
'float: right; width: 25px; height: 25px; border: 1px solid black;';
div.style.cssText = 'margin:0; padding:0; position: absolute;' +
top + right + bottom + left +
'z-index:9999; background-color:white; color:black; overflow: auto;';
div.appendChild(cl);
div.appendChild(contentContainer);
document.body.appendChild(div);
cl.addEventListener('click', function () {
div.parentNode.removeChild(div);
div = null;
});
};
});

View file

@ -1,643 +0,0 @@
/*jslint
indent: 4,
maxerr: 50,
white: true,
browser: true,
vars: true
*/
/*global
define,
getComputedStyle
*/
define(function(require, exports, module) {
"use strict";
/**
* These functions are necessary for the settings menu
* to provide a couple really useful settings.
*/
function addFunctionsForSettingsMenu (editor) {
// when building the settings menu matching get and set functions
// must be found or the function will be ignored
editor.getFontSize = function () {
return getComputedStyle(
editor.container).getPropertyValue('font-size');
};
// this allows the settings menu to supply a wrap limit
// using a text input field easily
editor.session.setWrapLimit = function (limit) {
editor.session.setWrapLimitRange(limit, limit);
};
}
/**
* Generates a list of set functions for the settings menu
* @param {object} editor The editor instance
* @return {array} Returns an array of objects. Each object contains the
* following properties: functionName, parentObj, and parentName.
*/
function getSetFunctions (editor) {
var out = [];
var my = {
'editor' : editor,
'session' : editor.session,
'renderer' : editor.renderer
};
var opts = [];
var skip = [
'setOption',
'setUndoManager',
'setDocument',
'setValue',
'setBreakpoints',
'setScrollTop',
'setScrollLeft',
'setSelectionStyle',
'setWrapLimitRange',
'setKeyboardHandler'
];
[
'renderer',
'session',
'editor'
].forEach(function (esra) {
var fn;
var esr = my[esra];
var clss = esra;
for(fn in esr) {
if(skip.indexOf(fn) === -1) {
if(/^set/.test(fn) && opts.indexOf(fn) === -1) {
// found set function
opts.push(fn);
out.push({
'functionName' : fn,
'parentObj' : esr,
'parentName' : clss
});
}
}
}
});
return out;
}
/**
* The menu options property needs to be added to the editor
* so that the settings menu can know about options for
* selection elements and track which option is selected.
*/
function addEditorMenuOptions (editor) {
editor.menuOptions = {
"setNewLineMode" : [{
"textContent" : "unix",
"value" : "unix"
}, {
"textContent" : "windows",
"value" : "windows"
}, {
"textContent" : "auto",
"value" : "auto"
}
],
"setTheme" : [{
"textContent" : "ambiance",
"value" : "ace/theme/ambiance"
}, {
"textContent" : "chaos",
"value" : "ace/theme/chaos"
}, {
"textContent" : "chrome",
"value" : "ace/theme/chrome"
}, {
"textContent" : "clouds",
"value" : "ace/theme/clouds"
}, {
"textContent" : "clouds_midnight",
"value" : "ace/theme/clouds_midnight"
}, {
"textContent" : "cobalt",
"value" : "ace/theme/cobalt"
}, {
"textContent" : "crimson_editor",
"value" : "ace/theme/crimson_editor"
}, {
"textContent" : "dawn",
"value" : "ace/theme/dawn"
}, {
"textContent" : "dreamweaver",
"value" : "ace/theme/dreamweaver"
}, {
"textContent" : "eclipse",
"value" : "ace/theme/eclipse"
}, {
"textContent" : "github",
"value" : "ace/theme/github"
}, {
"textContent" : "idle_fingers",
"value" : "ace/theme/idle_fingers"
}, {
"textContent" : "kr",
"value" : "ace/theme/kr"
}, {
"textContent" : "merbivore",
"value" : "ace/theme/merbivore"
}, {
"textContent" : "merbivore_soft",
"value" : "ace/theme/merbivore_soft"
}, {
"textContent" : "monokai",
"value" : "ace/theme/monokai"
}, {
"textContent" : "mono_industrial",
"value" : "ace/theme/mono_industrial"
}, {
"textContent" : "pastel_on_dark",
"value" : "ace/theme/pastel_on_dark"
}, {
"textContent" : "solarized_dark",
"value" : "ace/theme/solarized_dark"
}, {
"textContent" : "solarized_light",
"value" : "ace/theme/solarized_light"
}, {
"textContent" : "textmate",
"value" : "ace/theme/textmate"
}, {
"textContent" : "tomorrow",
"value" : "ace/theme/tomorrow"
}, {
"textContent" : "tomorrow_night",
"value" : "ace/theme/tomorrow_night"
}, {
"textContent" : "tomorrow_night_blue",
"value" : "ace/theme/tomorrow_night_blue"
}, {
"textContent" : "tomorrow_night_bright",
"value" : "ace/theme/tomorrow_night_bright"
}, {
"textContent" : "tomorrow_night_eighties",
"value" : "ace/theme/tomorrow_night_eighties"
}, {
"textContent" : "twilight",
"value" : "ace/theme/twilight"
}, {
"textContent" : "vibrant_ink",
"value" : "ace/theme/vibrant_ink"
}, {
"textContent" : "xcode",
"value" : "ace/theme/xcode"
}
],
"setMode" : [{
"textContent" : "abap",
"value" : "ace/mode/abap"
}, {
"textContent" : "asciidoc",
"value" : "ace/mode/asciidoc"
}, {
"textContent" : "c9search",
"value" : "ace/mode/c9search"
}, {
"textContent" : "clojure",
"value" : "ace/mode/clojure"
}, {
"textContent" : "coffee",
"value" : "ace/mode/coffee"
}, {
"textContent" : "coldfusion",
"value" : "ace/mode/coldfusion"
}, {
"textContent" : "csharp",
"value" : "ace/mode/csharp"
}, {
"textContent" : "css",
"value" : "ace/mode/css"
}, {
"textContent" : "curly",
"value" : "ace/mode/curly"
}, {
"textContent" : "c_cpp",
"value" : "ace/mode/c_cpp"
}, {
"textContent" : "dart",
"value" : "ace/mode/dart"
}, {
"textContent" : "diff",
"value" : "ace/mode/diff"
}, {
"textContent" : "django",
"value" : "ace/mode/django"
}, {
"textContent" : "dot",
"value" : "ace/mode/dot"
}, {
"textContent" : "ftl",
"value" : "ace/mode/ftl"
}, {
"textContent" : "glsl",
"value" : "ace/mode/glsl"
}, {
"textContent" : "golang",
"value" : "ace/mode/golang"
}, {
"textContent" : "groovy",
"value" : "ace/mode/groovy"
}, {
"textContent" : "haml",
"value" : "ace/mode/haml"
}, {
"textContent" : "haxe",
"value" : "ace/mode/haxe"
}, {
"textContent" : "html",
"value" : "ace/mode/html"
}, {
"textContent" : "jade",
"value" : "ace/mode/jade"
}, {
"textContent" : "java",
"value" : "ace/mode/java"
}, {
"textContent" : "javascript",
"value" : "ace/mode/javascript"
}, {
"textContent" : "json",
"value" : "ace/mode/json"
}, {
"textContent" : "jsp",
"value" : "ace/mode/jsp"
}, {
"textContent" : "jsx",
"value" : "ace/mode/jsx"
}, {
"textContent" : "latex",
"value" : "ace/mode/latex"
}, {
"textContent" : "less",
"value" : "ace/mode/less"
}, {
"textContent" : "liquid",
"value" : "ace/mode/liquid"
}, {
"textContent" : "lisp",
"value" : "ace/mode/lisp"
}, {
"textContent" : "livescript",
"value" : "ace/mode/livescript"
}, {
"textContent" : "logiql",
"value" : "ace/mode/logiql"
}, {
"textContent" : "lsl",
"value" : "ace/mode/lsl"
}, {
"textContent" : "lua",
"value" : "ace/mode/lua"
}, {
"textContent" : "luapage",
"value" : "ace/mode/luapage"
}, {
"textContent" : "lucene",
"value" : "ace/mode/lucene"
}, {
"textContent" : "makefile",
"value" : "ace/mode/makefile"
}, {
"textContent" : "markdown",
"value" : "ace/mode/markdown"
}, {
"textContent" : "objectivec",
"value" : "ace/mode/objectivec"
}, {
"textContent" : "ocaml",
"value" : "ace/mode/ocaml"
}, {
"textContent" : "pascal",
"value" : "ace/mode/pascal"
}, {
"textContent" : "perl",
"value" : "ace/mode/perl"
}, {
"textContent" : "pgsql",
"value" : "ace/mode/pgsql"
}, {
"textContent" : "php",
"value" : "ace/mode/php"
}, {
"textContent" : "powershell",
"value" : "ace/mode/powershell"
}, {
"textContent" : "python",
"value" : "ace/mode/python"
}, {
"textContent" : "r",
"value" : "ace/mode/r"
}, {
"textContent" : "rdoc",
"value" : "ace/mode/rdoc"
}, {
"textContent" : "rhtml",
"value" : "ace/mode/rhtml"
}, {
"textContent" : "ruby",
"value" : "ace/mode/ruby"
}, {
"textContent" : "sass",
"value" : "ace/mode/sass"
}, {
"textContent" : "scad",
"value" : "ace/mode/scad"
}, {
"textContent" : "scala",
"value" : "ace/mode/scala"
}, {
"textContent" : "scheme",
"value" : "ace/mode/scheme"
}, {
"textContent" : "scss",
"value" : "ace/mode/scss"
}, {
"textContent" : "sh",
"value" : "ace/mode/sh"
}, {
"textContent" : "sql",
"value" : "ace/mode/sql"
}, {
"textContent" : "stylus",
"value" : "ace/mode/stylus"
}, {
"textContent" : "svg",
"value" : "ace/mode/svg"
}, {
"textContent" : "tcl",
"value" : "ace/mode/tcl"
}, {
"textContent" : "tex",
"value" : "ace/mode/tex"
}, {
"textContent" : "text",
"value" : "ace/mode/text"
}, {
"textContent" : "textile",
"value" : "ace/mode/textile"
}, {
"textContent" : "tmsnippet",
"value" : "ace/mode/tmsnippet"
}, {
"textContent" : "tm_snippet",
"value" : "ace/mode/tm_snippet"
}, {
"textContent" : "toml",
"value" : "ace/mode/toml"
}, {
"textContent" : "typescript",
"value" : "ace/mode/typescript"
}, {
"textContent" : "vbscript",
"value" : "ace/mode/vbscript"
}, {
"textContent" : "xml",
"value" : "ace/mode/xml"
}, {
"textContent" : "xquery",
"value" : "ace/mode/xquery"
}, {
"textContent" : "yaml",
"value" : "ace/mode/yaml"
}
]
};
}
/**
* This massive thing is comprised mostly of element generation functions
* filtering out the
*/
function generateMenu (editor) {
var elements = [];
function createCheckbox (id, checked, clss) {
var el = document.createElement('input');
el.setAttribute('type', 'checkbox');
el.setAttribute('id', id);
el.setAttribute('name', id);
el.setAttribute('value', checked);
el.setAttribute('class', clss);
if(checked) {
el.setAttribute('checked', 'checked');
}
return el;
}
function createInput (id, value, clss) {
var el = document.createElement('input');
el.setAttribute('type', 'text');
el.setAttribute('id', id);
el.setAttribute('name', id);
el.setAttribute('value', value);
el.setAttribute('class', clss);
return el;
}
function createOption (obj) {
var attribute;
var el = document.createElement('option');
for(attribute in obj) {
if(el.hasOwnProperty(attribute)) {
if(attribute === 'selected') {
el.setAttribute(attribute, obj[attribute]);
}
el[attribute] = obj[attribute];
}
}
return el;
}
function createSelection (id, values, clss) {
var el = document.createElement('select');
el.setAttribute('id', id);
el.setAttribute('name', id);
el.setAttribute('class', clss);
values.forEach(function (item) {
el.appendChild(createOption(item));
});
return el;
}
function createLabel (text, labelFor) {
var el = document.createElement('label');
el.setAttribute('for', labelFor);
el.textContent = text;
return el;
}
// require editor
function createNewEntry(obj, clss, item, val) {
var el;
var div = document.createElement('div');
div.setAttribute('contains', item);
div.setAttribute('class', 'menuEntry');
div.appendChild(createLabel(item, item));
if(Array.isArray(val)) {
el = createSelection(item, val, clss);
el.addEventListener('change', function (e) {
try{
editor.menuOptions[e.target.id].forEach(function (x) {
if(x.textContent !== e.target.textContent) {
delete x.selected;
}
});
obj[e.target.id](e.target.value);
} catch (err) {
throw new Error(err);
}
});
} else if(typeof val === 'boolean') {
el = createCheckbox(item, val, clss);
el.addEventListener('change', function (e) {
try{
obj[e.target.id](!!e.target.checked);
} catch (err) {
throw new Error(err);
}
});
} else {
el = createInput(item, val, clss);
el.addEventListener('blur', function (e) {
try{
if(e.target.value === 'true') {
obj[e.target.id](true);
} else if(e.target.value === 'false') {
obj[e.target.id](false);
} else {
obj[e.target.id](e.target.value);
}
} catch (err) {
throw new Error(err);
}
});
}
div.appendChild(el);
return div;
}
function makeDropdown(item, esr, clss, fn) {
var val = editor.menuOptions[item];
val = val.map(function (valuex) {
if(valuex.value === esr[fn]()) {
valuex.selected = 'selected';
} else if(valuex.value === esr.$modeId) {
// is mode
valuex.selected = 'selected';
}
return valuex;
});
return createNewEntry(esr, clss, item, val);
}
// require editor, elements
function handleSet (setObj) {
var item = setObj.functionName;
var esr = setObj.parentObj;
var clss = setObj.parentName;
var val;
var fn = item.replace(/^set/, 'get');
if(editor.menuOptions[item] !== undefined) {
// has options for select element
elements.push(makeDropdown(item, esr, clss, fn));
} else if(typeof esr[fn] === 'function') {
// has get function
try {
val = esr[fn]();
if(typeof val === 'object') {
// setMode takes a string, getMode returns an object
// the $id property of that object is the string
// which may be given to setMode...
val = val.$id;
}
// the rest of the get functions return strings,
// booleans, or numbers.
elements.push(
createNewEntry(esr, clss, item, val)
);
} catch (e) {
// if there are errors it is because the element
// does not belong in the settings menu
}
}
}
function cleanupElementsList() {
elements.sort(function (a, b) {
var x = a.getAttribute('contains');
var y = b.getAttribute('contains');
return x.localeCompare(y);
});
}
function showMenu() {
var topmenu = document.createElement('div');
elements.forEach(function (element) {
topmenu.appendChild(element);
});
return topmenu;
}
getSetFunctions(editor).forEach(function (setObj) {
handleSet(setObj);
});
cleanupElementsList();
overlayPage(showMenu(), '0', '0', '0', null);
}
/**
* and here is the ugly overlay code again...
*/
function overlayPage (contentElement, top, right, bottom, left) {
var div = document.createElement('div');
var contentContainer = document.createElement('div');
contentContainer.style.cssText = 'margin: 0px; padding: 0px; border: 0px;' +
'overflow: auto;';
contentElement.style.cssText = contentElement.style.cssText + 'overflow: auto;';
contentContainer.appendChild(contentElement);
var cl = document.createElement('img');
if(top) {
top = 'top: ' + top + ';';
} else {
top = '';
}
if(right) {
right = 'right: ' + right + ';';
} else {
right = '';
}
if(bottom) {
bottom = 'bottom: ' + bottom + ';';
} else {
bottom = '';
}
if(left) {
left = 'left: ' + left + ';';
} else {
left = '';
}
cl.src = '/famfamfam_silk_icons_v013/icons/cross.png';
cl.style.cssText = 'margin: 5px 5px 0 0; padding: 0; ' +
'float: right; width: 25px;';
div.style.cssText = 'margin:0; padding:0; position: absolute;' +
top + right + bottom + left +
'z-index:9999; background-color:white; color:black; overflow: auto;';
div.appendChild(cl);
div.appendChild(contentContainer);
document.body.appendChild(div);
cl.addEventListener('click', function (e) {
div.parentNode.removeChild(div);
div = null;
});
}
/**
* This builds the settings menu and selects
* all the options currently in effect.
*/
module.exports = function (editor) {
addFunctionsForSettingsMenu(editor);
addEditorMenuOptions(editor);
generateMenu(editor);
};
});

View file

@ -0,0 +1,371 @@
/*jslint
indent: 4,
maxerr: 50,
white: true,
browser: true,
vars: true
*/
/*global
define,
require,
getComputedStyle
*/
/**
* Show Settings Menu
* @fileOverview Show Settings Menu <br />
* Displays an interactive settings menu mostly generated on the fly based on
* the current state of the editor.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
define(function(require, exports, module) {
"use strict";
var overlayPage = require('./overlay_page').overlayPage;
var addEditorMenuOptions = require('./add_editor_menu_options').addEditorMenuOptions;
/**
* These functions are necessary for the settings menu
* to provide a couple really useful settings.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {ace.Editor} editor An instance of the ace editor.
*/
function addFunctionsForSettingsMenu (editor) {
// when building the settings menu matching get and set functions
// must be found or the function will be ignored
editor.getFontSize = function () {
return getComputedStyle(
editor.container).getPropertyValue('font-size');
};
// this allows the settings menu to supply a wrap limit
// using a text input field easily
editor.session.setWrapLimit = function (limit) {
editor.session.setWrapLimitRange(limit, limit);
};
}
/**
* Generates a list of set functions for the settings menu.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {object} editor The editor instance
* @return {array} Returns an array of objects. Each object contains the
* following properties: functionName, parentObj, and parentName. The
* function name will be the name of a method beginning with the string
* `set` which was found. The parent object will be a reference to the
* object having the method matching the function name. The parent name
* will be a string representing the identifier of the parent object e.g.
* `editor`, `session`, or `renderer`.
*/
function getSetFunctions (editor) {
/**
* Output array. Will hold the objects described above.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
var out = [];
/**
* This object provides a map between the objects which will be
* traversed and the parent name which will appear in the output.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
var my = {
'editor' : editor,
'session' : editor.session,
'renderer' : editor.renderer
};
/**
* This array will hold the set function names which have already been
* found so that they are not added to the output multiple times.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
var opts = [];
/**
* This is a list of set functions which will not appear in the settings
* menu. I don't know what to do with setKeyboardHandler. When I tried
* to use it, it didn't appear to be working. Someone who knows better
* could remove it from this list and add it's options to
* add_editor_menu_options.js
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
var skip = [
'setOption',
'setUndoManager',
'setDocument',
'setValue',
'setBreakpoints',
'setScrollTop',
'setScrollLeft',
'setSelectionStyle',
'setWrapLimitRange',
'setKeyboardHandler'
];
/**
* This will search the objects mapped to the `my` variable above. When
* it finds a set function in the object that is not listed in the
* `skip` list or the `opts` list it will push a new object to the
* output array.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
[
'renderer',
'session',
'editor'
].forEach(function (esra) {
var fn;
var esr = my[esra];
var clss = esra;
for(fn in esr) {
if(skip.indexOf(fn) === -1) {
if(/^set/.test(fn) && opts.indexOf(fn) === -1) {
// found set function
opts.push(fn);
out.push({
'functionName' : fn,
'parentObj' : esr,
'parentName' : clss
});
}
}
}
});
return out;
}
/**
* Generates an interactive menu with settings useful to end users.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {ace.Editor} editor An instance of the ace editor.
*/
function generateMenu (editor) {
/**
* container for dom elements that will go in the menu.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
var elements = [];
/**
* Sorts the menu entries (elements var) so they'll appear in alphabetical order
* the sort is performed based on the value of the contains property
* of each element. Since this is an `array.sort` the array is sorted
* in place.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
function cleanupElementsList() {
elements.sort(function (a, b) {
var x = a.getAttribute('contains');
var y = b.getAttribute('contains');
return x.localeCompare(y);
});
}
/**
* Wraps all dom elements contained in the elements var with a single
* div.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
*/
function wrapElements() {
var topmenu = document.createElement('div');
elements.forEach(function (element) {
topmenu.appendChild(element);
});
return topmenu;
}
/**
* Creates a new menu entry.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {object} obj This is a reference to the object containing the
* set function. It is used to set up event listeners for when the
* menu options change.
* @param {string} clss Maps to the class of the dom element. This is
* the name of the object containing the set function e.g. `editor`,
* `session`, `renderer`.
* @param {string} item Maps to the id of the dom element. This is the
* set function name.
* @param {mixed} val This is the value of the setting. It is mapped to
* the dom element's value, checked, or selected option accordingly.
*/
function createNewEntry(obj, clss, item, val) {
var egen = require('./element_generator');
var el;
var div = document.createElement('div');
div.setAttribute('contains', item);
div.setAttribute('class', 'menuEntry');
div.appendChild(egen.createLabel(item, item));
if(Array.isArray(val)) {
el = egen.createSelection(item, val, clss);
el.addEventListener('change', function (e) {
try{
editor.menuOptions[e.target.id].forEach(function (x) {
if(x.textContent !== e.target.textContent) {
delete x.selected;
}
});
// editor.session['setMode']('ace/mode/javascript')
obj[e.target.id](e.target.value);
} catch (err) {
throw new Error(err);
}
});
} else if(typeof val === 'boolean') {
el = egen.createCheckbox(item, val, clss);
el.addEventListener('change', function (e) {
try{
// renderer['setHighlightGutterLine'](true);
obj[e.target.id](!!e.target.checked);
} catch (err) {
throw new Error(err);
}
});
} else {
// this aids in giving the ability to specify settings through
// post and get requests.
// /ace_editor.html?setMode=ace/mode/html&setOverwrite=true
el = egen.createInput(item, val, clss);
el.addEventListener('blur', function (e) {
try{
if(e.target.value === 'true') {
obj[e.target.id](true);
} else if(e.target.value === 'false') {
obj[e.target.id](false);
} else {
obj[e.target.id](e.target.value);
}
} catch (err) {
throw new Error(err);
}
});
}
div.appendChild(el);
return div;
}
/**
* Generates selection fields for the menu and populates their options
* using information from `editor.menuOptions`
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {string} item The set function name.
* @param {object} esr A reference to the object having the set function.
* @param {string} clss The name of the object containing the set function.
* @param {string} fn The matching get function's function name.
* @returns {DOMElement} Returns a dom element containing a selection
* element populated with options. The option whose value matches that
* returned from `esr[fn]()` will be selected.
*/
function makeDropdown(item, esr, clss, fn) {
var val = editor.menuOptions[item];
val = val.map(function (valuex) {
if(valuex.value === esr[fn]()) {
valuex.selected = 'selected';
} else if(valuex.value === esr.$modeId) {
// is mode
valuex.selected = 'selected';
}
return valuex;
});
return createNewEntry(esr, clss, item, val);
}
/**
* Processes the set functions returned from `getSetFunctions`. First it
* checks for menu options defined in `editor.menuOptons`. If no
* options are specified then it checks whether there is a get function
* (replace set with get) for the setting. When either of those
* conditions are met it will attempt to create a new entry for the
* settings menu and push it into the elements array defined above.
* It can only do so for get functions which return
* strings, numbers, and booleans. A special case is written in for
* `getMode` where it looks at the returned objects `$id` property and
* forwards that through instead. Other special cases could be written
* in but that would get a bit ridiculous.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {object} setObj An item from the array returned by
* `getSetFunctions`.
*/
function handleSet (setObj) {
var item = setObj.functionName;
var esr = setObj.parentObj;
var clss = setObj.parentName;
var val;
var fn = item.replace(/^set/, 'get');
if(editor.menuOptions[item] !== undefined) {
// has options for select element
elements.push(makeDropdown(item, esr, clss, fn));
} else if(typeof esr[fn] === 'function') {
// has get function
try {
val = esr[fn]();
if(typeof val === 'object') {
// setMode takes a string, getMode returns an object
// the $id property of that object is the string
// which may be given to setMode...
val = val.$id;
}
// the rest of the get functions return strings,
// booleans, or numbers.
elements.push(
createNewEntry(esr, clss, item, val)
);
} catch (e) {
// if there are errors it is because the element
// does not belong in the settings menu
}
}
}
// gather the set functions
getSetFunctions(editor).forEach(function (setObj) {
// populate the elements array with good stuff.
handleSet(setObj);
});
// sort the menu entries in the elements list so people can find
// the settings in alphabetical order.
cleanupElementsList();
// dump the entries from the elements list and wrap them up in a div
// then put the div into the generic menu and show it.
overlayPage(wrapElements(), '0', '0', '0', null);
}
/**
* This builds the settings menu and selects
* all the options currently in effect.
* @author <a href="mailto:matthewkastor@gmail.com">
* Matthew Christopher Kastor-Inare III </a><br />
* Hial Atropa!!
* @param {ace.Editor} editor An instance of the ace editor.
*/
module.exports = function (editor) {
addFunctionsForSettingsMenu(editor);
addEditorMenuOptions(editor);
generateMenu(editor);
};
});