ares-ide
Version:
A browser-based code editor and UI designer for Enyo 2 projects
995 lines (934 loc) • 32.1 kB
JavaScript
/*global analyzer, ares, enyo, $L, ProjectCtrl, setTimeout */
enyo.kind({
name: "Phobos",
classes: "enyo-unselectable",
components: [
{kind: "FittableRows", classes: "enyo-fit", components: [
{name: "body", fit: true, kind: "FittableColumns", components: [
{name: "middle", fit: true, classes: "panel", components: [
{classes: "enyo-fit ares_phobos_panel border ", components: [
{
kind: "AceWrapper",
name: "aceWrapper",
classes: "enyo-fit ace-code-editor",
onAutoCompletion: "startAutoCompletion",
onChange: "docChanged",
onCursorChange: "cursorChanged",
onFind: "findpop",
onFkey: "fkeypressed",
onScroll: "handleScroll",
onWordwrap: "toggleww"
},
{name: "imageViewer", kind: "enyo.Image"}
]}
]},
{name: "right", kind: "rightPanels", showing: false, classes: "ares_phobos_right", arrangerKind: "CardArranger"}
]}
]},
{name: "autocomplete", kind: "Phobos.AutoComplete"},
{name: "findpop", kind: "FindPopup", centered: true, modal: true, floating: true, onFindNext: "findNext", onFindPrevious: "findPrevious", onReplace: "replace", onReplaceAll:"replaceAll", onHide:"doAceFocus", onClose: "findClose", onReplaceFind: "replacefind"},
{
name: "editorSettingsPopup",
kind: "onyx.Popup",
classes:"enyo-unselectable ares-classic-popup",
centered: true,
modal: true,
floating: true,
autoDismiss: false,
onCloseSettings: "closeEditorPopup",
onChangeSettings:"applyPreviewSettings",
onChangeRightPane: "changeRightPane",
components: [
{name:"title", classes: "title draggable", kind: "Ares.PopupTitle", content: "EDITOR GLOBAL SETTINGS"},
{kind: "editorSettings", classes:"ace-settings-popup" }
],
}
],
events: {
onHideWaitPopup: "",
onError: "",
onRegisterMe: "",
onFileEdited: " ",
onChildRequest: "",
onAceFocus: ""
},
handlers: {
onCss: "newcssAction",
onReparseAsked: "reparseAction",
onInitNavigation: "initNavigation",
onNavigateInCodeEditor: "navigateInCodeEditor"
},
published: {
projectData: null,
objectsToDump: []
},
editedDocs:"",
injected: false,
debug: false,
// Container of the code to analyze and of the analysis result
analysis: {},
helper: null, // Analyzer.KindHelper
closeAll: false,
create: function() {
ares.setupTraceLogger(this); // Setup this.trace() function according to this.debug value
this.inherited(arguments);
this.helper = new analyzer.Analyzer.KindHelper();
},
getProjectController: function() {
// create projectCtrl only when needed. In any case, there's only
// one Phobos and one projectCtrl in Ares
this.projectCtrl = this.projectData.getProjectCtrl();
if ( ! this.projectCtrl) {
this.projectCtrl = new ProjectCtrl({projectData: this.projectData});
// wire event propagation from there to Phobos
this.projectCtrl.setOwner(this);
this.projectData.setProjectCtrl(this.projectCtrl);
}
},
saveNeeded: function() {
return this.docData.getEdited();
},
openDoc: function(inDocData) {
// If we are changing documents, reparse any changes into the current projectIndexer
if (this.docData && this.docData.getEdited()) {
this.reparseUsersCode(true);
}
// Set up the new doucment
this.docData = inDocData;
this.setProjectData(this.docData.getProjectData());
this.getProjectController();
this.setAutoCompleteData();
// Save the value to set it again after data has been loaded into ACE
var edited = this.docData.getEdited();
var file = this.docData.getFile();
var extension = file.name.split(".").pop();
this.hideWaitPopup();
this.analysis = null;
var mode = {
json: "json",
design: "json",
js: "javascript",
html: "html",
css: "css",
coffee: "coffee",
dot: "dot",
patch: "diff",
diff: "diff",
jade: "jade",
less: "less",
md: "markdown",
markdown: "markdown",
svg: "svg",
xml: "xml",
jpeg: "image",
jpg: "image",
png: "image",
gif: "image"
}[extension] || "text";
this.docData.setMode(mode);
var hasAce = this.adjustPanelsForMode(mode, this.$.editorSettings.getSettingFromLS().rightpane);
if (hasAce) {
var aceSession = this.docData.getAceSession();
if (aceSession) {
this.$.aceWrapper.setSession(aceSession);
} else {
aceSession = this.$.aceWrapper.createSession(this.docData.getData(), mode);
this.$.aceWrapper.setSession(aceSession);
this.docData.setAceSession(aceSession);
}
// Pass to the autocomplete compononent a reference to ace
this.$.autocomplete.setAceWrapper(this.$.aceWrapper);
this.focusEditor();
/* set editor to user pref */
this.$.aceWrapper.applyAceSettings(this.$.editorSettings.getSettingFromLS());
this.$.aceWrapper.editingMode = mode;
} else {
var config = this.projectData.getService().getConfig();
var fileUrl = config.origin + config.pathname + "/file" + file.path;
this.$.imageViewer.setAttribute("src", fileUrl);
}
this.manageDesignerButton();
this.reparseUsersCode(true);
this.projectCtrl.buildProjectDb();
this.docData.setEdited(edited);
},
adjustPanelsForMode: function(mode, rightpane) {
this.trace("mode:", mode);
var showModes = {
javascript: {
imageViewer: false,
aceWrapper: true,
saveButton: true,
saveAsButton: true,
newKindButton: true,
designerDecorator: true,
right: rightpane
},
image: {
imageViewer: true,
aceWrapper: false,
saveButton: false,
saveAsButton: false,
newKindButton: false,
designerDecorator: false,
right: false
},
text: {
imageViewer: false,
aceWrapper: true,
saveButton: true,
saveAsButton: true,
newKindButton: false,
designerDecorator: false,
right: false
}
};
var showStuff, showSettings = showModes[mode] || showModes['text'];
for (var stuff in showSettings) {
showStuff = showSettings[stuff];
this.trace("show", stuff, ":", showStuff);
// FIXME need to clean up this code ENYO-3633
if(this.$[stuff] !== undefined){
if (typeof this.$[stuff].setShowing === 'function') {
this.$[stuff].setShowing(showStuff) ;
} else {
this.warn("BUG: attempting to show/hide a non existing element: ", stuff);
}
} else if (this.owner.$.editorFileMenu.$[stuff] !== undefined && this.owner.$.designerFileMenu.$[stuff] !== undefined) {
var editorFileMenu = this.owner.$.editorFileMenu.$[stuff];
var designerFileMenu = this.owner.$.designerFileMenu.$[stuff];
if (typeof editorFileMenu.setShowing === 'function' && typeof designerFileMenu.setShowing === 'function') {
editorFileMenu.setShowing(showStuff);
designerFileMenu.setShowing(showStuff);
} else {
this.warn("BUG: attempting to show/hide a non existing element: ", stuff);
}
} else {
if (typeof this.owner.$[stuff].setShowing === 'function') {
this.owner.$[stuff].setShowing(showStuff) ;
} else {
this.warn("BUG: attempting to show/hide a non existing element: ", stuff);
}
}
}
// xxxIndex: specify what to show in the "RightPanels" kinds (declared at the end of this file)
// xxxIndex is ignored when matching show setting is false
var modes = {
json: {rightIndex: 0},
javascript: {rightIndex: 1},
html: {rightIndex: 2},
css: {rightIndex: 3},
text: {rightIndex: 0},
image: {rightIndex: 0}
};
var settings = modes[mode]||modes['text'];
this.$.right.setIndex(settings.rightIndex);
this.resizeHandler();
return showSettings.aceWrapper ;
},
hideWaitPopup: function() {
this.doHideWaitPopup();
},
//
setAutoCompleteData: function() {
this.$.autocomplete.hide();
this.$.autocomplete.setProjectData(this.projectData);
this.manageDesignerButton();
},
resetAutoCompleteData: function() {
this.$.autocomplete.setProjectData(null);
},
/**
* Enable "Designer" button only if project & enyo index are both valid
*/
manageDesignerButton: function() {
this.doChildRequest({ task: [ "enableDesignerButton", this.projectCtrl.fullAnalysisDone ]} );
},
/**
* Receive the project data reference which allows to access the analyzer
* output for the project's files, enyo/onyx and all the other project
* related information shared between phobos and deimos.
* @param oldProjectData
* @protected
*/
projectDataChanged: function(oldProjectData) {
if (this.projectData) {
this.projectData.on('update:project-indexer', this.projectIndexerChanged, this);
}
if (oldProjectData) {
oldProjectData.off('update:project-indexer', this.projectIndexerChanged);
}
},
/**
* The current project analyzer output has changed
* Re-scan the indexer
* @param value the new analyzer output
* @protected
*/
projectIndexerChanged: function() {
this.trace("Project analysis ready");
this.manageDesignerButton();
},
dumpInfo: function(inObject) {
var h = [];
this.$.right.setDumpCount(0);
if (!inObject || !inObject.superkinds) {
h.push("no content");
this.objectsToDump = h;
this.$.right.setDumpCount(this.objectsToDump.length);
return;
}
var c = inObject;
h.push(c.name);
h.push("Extends");
for (var i=0, p; (p=c.superkinds[i]); i++) {
p = {name: c.superkinds[i], isExtended: true};
h.push(p);
}
if (c.components.length) {
h.push("Components");
for (i=0, p; (p=c.components[i]); i++) {
h.push(p);
}
}
h.push("Properties");
for (i=0, p; (p=c.properties[i]); i++) {
h.push(p);
}
this.objectsToDump = h;
this.$.right.setDumpCount(this.objectsToDump.length);
},
// invoked by reparse button in right panel (the file index)
reparseAction: function(inSender, inEvent) {
this.reparseUsersCode(true);
},
initNavigation: function(inSender, inEvent) {
var item = inEvent.item.$.item,
index = inEvent.item.index,
object = this.objectsToDump[index];
if (object.isExtended){
item.setFixedItem(object.name);
} else if (object.name){
item.setNavigateItem(object.name);
} else {
item.setTitle(object);
}
item.setIndex(index);
return true;
},
navigateInCodeEditor: function(inSender, inEvent) {
var itemToSelect = this.objectsToDump[inEvent.index];
if(itemToSelect.start && itemToSelect.end){
this.$.aceWrapper.navigateToPosition(itemToSelect.start, itemToSelect.end);
this.doAceFocus();
}
},
//* Updates the projectIndexer (notifying watchers by default) and resets the local analysis file
reparseUsersCode: function(inhibitUpdate) {
var mode = this.docData.getMode();
var codeLooksGood = false;
var module = {
name: this.docData.getFile().name,
code: this.$.aceWrapper.getValue(),
path: this.projectCtrl.projectUrl + this.docData.getFile().dir + this.docData.getFile().name
};
this.trace("called with mode " , mode , " inhibitUpdate " , inhibitUpdate , " on " + module.name);
switch(mode) {
case "javascript":
try {
this.analysis = module;
this.projectData.getProjectIndexer().reIndexModule(module);
if (inhibitUpdate !== true) {
this.projectData.updateProjectIndexer();
}
this.updateObjectsLines(module);
// dump the object where the cursor is positioned, if it exists
this.dumpInfo(module.objects && module.objects[module.currentObject]);
// Give the information to the autocomplete component
this.$.autocomplete.setAnalysis(this.analysis);
codeLooksGood = true;
} catch(error) {
enyo.log("An error occured during the code analysis: " , error);
this.dumpInfo(null);
this.$.autocomplete.setAnalysis(null);
}
break;
case "json":
if (module.name.slice(-7) == ".design") {
this.projectData.getProjectIndexer().reIndexDesign(module);
if (inhibitUpdate !== true) {
this.projectData.updateProjectIndexer();
}
}
this.analysis = null;
this.$.autocomplete.setAnalysis(null);
break;
default:
this.analysis = null;
this.$.autocomplete.setAnalysis(null);
break;
}
return codeLooksGood ;
},
/**
* Add for each object the corresponding range of lines in the file
* Update the information about the object currently referenced
* by the cursor position
* TODO: see if this should go in Analyzer2
*/
updateObjectsLines: function(module) {
module.ranges = [];
if (module.objects && module.objects.length > 0) {
var start = 0, range;
for( var idx = 1; idx < module.objects.length ; idx++ ) {
range = { first: start, last: module.objects[idx].line - 1};
module.ranges.push(range); // Push a range for previous object
start = module.objects[idx].line;
}
// Push a range for the last object
range = { first: start, last: 1000000000};
module.ranges.push(range);
}
var position = this.$.aceWrapper.getCursorPositionInDocument();
module.currentObject = this.findCurrentEditedObject(position);
module.currentRange = module.ranges[module.currentObject];
module.currentLine = position.row;
},
/**
* Return the index (in the analyzer result ) of the enyo kind
* currently edited (in which the cursor is)
* Otherwise return -1
* @returns {Number}
*/
findCurrentEditedObject: function(position) {
if (this.analysis && this.analysis.ranges) {
for( var idx = 0 ; idx < this.analysis.ranges.length ; idx++ ) {
if (position.row <= this.analysis.ranges[idx].last) {
return idx;
}
}
}
return -1;
},
/**
* Navigate from Phobos to Deimos. Pass Deimos all relevant info.
*/
designerAction: function() {
// Update the projectIndexer and notify watchers
this.reparseUsersCode();
var kinds = this.extractKindsData(),
data = {
kinds: kinds,
projectData: this.projectData,
fileIndexer: this.analysis
};
if (kinds.length > 0) {
// Request to design the current document, passing info about all kinds in the file
this.doChildRequest({ task: [ 'designDocument', data ]});
} // else - The error has been displayed by extractKindsData()
},
//* Extract info about kinds from the current file needed by the designer
extractKindsData: function() {
var c = this.$.aceWrapper.getValue(),
kinds = [];
if (this.analysis) {
var nbKinds = 0;
var errorMsg;
var i, o;
var oLen = this.analysis.objects.length;
for (i=0; i < oLen; i++) {
o = this.analysis.objects[i];
if (o.type !== "kind") {
errorMsg = $L("Ares does not support methods out of a kind. Please place '" + o.name + "' into a separate .js file");
} else {
nbKinds++;
}
}
if (nbKinds === 0) {
errorMsg = $L("No kinds found in this file");
}
if (errorMsg) {
this.doError({msg: errorMsg});
return [];
}
for (i=0; i < oLen; i++) {
o = this.analysis.objects[i];
var start = o.componentsBlockStart;
var end = o.componentsBlockEnd;
var kindBlock = enyo.json.codify.from(c.substring(o.block.start, o.block.end));
var name = o.name;
var kind = kindBlock.kind || o.superkind;
var comps = [];
if (start && end) {
var js = c.substring(start, end);
/* jshint evil: true */
comps = eval("(" + js + ")"); // TODO: ENYO-2074, replace eval. Why eval? Because JSON.parse doesn't support unquoted keys...
/* jshint evil: false */
}
var comp = {
name: name,
kind: kind,
components: comps
};
for (var j=0; j < o.properties.length; j++) {
var prop = o.properties[j];
var pName = prop.name;
var value = this.verifyValueType(analyzer.Documentor.stripQuotes(prop.value[0].name));
if ((start === undefined || prop.start < start) && pName !== "components") {
if (value === "{" || value === "[" || value === "function") {
comp[pName] = kindBlock[pName];
} else {
comp[pName] = value;
}
}
}
kinds.push(comp);
}
}
return kinds;
},
/**
* Converts string representation of boolean values
* to boolean
* TODO: Verify false-positives (ex: strings meant to be strings)
* @param inProps: the value to match
* @returns boolean value if match found: inValue if no matches
* @protected
*/
verifyValueType: function(inValue) {
if (inValue === "true") {
inValue = true;
} else if (inValue === "false") {
inValue = false;
}
return inValue;
},
/**
* Lists the handler methods mentioned in the "handlers"
* attributes and in the sub-components of the kind object
* passed as a parameter
* @param object: the kind definition to explore
* @param declared: list of handler methods already listed
* @returns the list of declared handler methods
*/
listHandlers: function(object, declared) {
try {
this.helper.setDefinition(object);
return this.helper.listHandlers(declared);
} catch(error) {
enyo.log("Unexpected error: " , error); // TODO TBC
}
},
/**
* This function checks all the kinds and add the missing
* handler functions listed in the "onXXXX" attributes
* @protected
* Note: This implies to reparse/analyze the file before
* and after the operation.
*/
insertMissingHandlers: function() {
if (this.analysis) {
// Reparse to get the definition of possibly added onXXXX attributes
this.reparseUsersCode(true);
/*
* Insert missing handlers starting from the end of the
* file to limit the need of reparsing/reanalysing
* the file
*/
for( var i = this.analysis.objects.length -1 ; i >= 0 ; i-- ) {
var obj = this.analysis.objects[i];
if (obj.components) {
this.insertMissingHandlersIntoKind(obj);
}
}
// Reparse to get the definition of the newly added methods
this.reparseUsersCode(true);
} else {
// There is no parser data for the current file
enyo.log("Unable to insert missing handler methods");
}
},
/**
* This function checks the kind passed as an inout parameter
* and add the missing handler functions listed in the "onXXXX" attributes
* @param object
* @protected
*/
insertMissingHandlersIntoKind: function(object) {
var commaTerminated = false,
commaInsertionIndex = 0;
// List existing handlers
var existing = {};
for(var j = 0 ; j < object.properties.length ; j++) {
var p = object.properties[j];
commaTerminated = p.commaTerminated;
if (p.value[0].name === 'function') {
existing[p.name] = "";
commaInsertionIndex = p.value[0].block.end; // Index of function block ending curly braket
} else {
commaInsertionIndex = p.value[0].end; // Index of property definition
}
}
// List the handler methods declared in the components and in handlers map
var declared = this.listHandlers(object, {});
// insert the missing handler methods code in the editor
if (object.block) {
var lineTermination = this.$.aceWrapper.getNewLineCharacter(),
codeInsertionIndex = object.block.end - 1, // Index before kind block ending curly braket
codeInsertionPosition;
var commaInsertionPosition = null;
if (!commaTerminated) {
commaInsertionPosition = this.$.aceWrapper.mapToLineColumns([commaInsertionIndex]);
commaTerminated = true;
}
// refresh editorSettings content from localStorage. This
// is necessary when editor settings were modified in the
// EditorSetting kind owned by Ares
this.$.editorSettings.initSettings();
var edSettings = this.$.editorSettings.settings;
// Prepare the code to insert
var codeToInsert = "";
for(var item in declared) {
if (item !== "" && existing[item] === undefined) {
// use correct line terminations
codeToInsert += (commaTerminated ? "" : "," + lineTermination);
commaTerminated = false;
codeToInsert += ("\t" + item + ": function(inSender, inEvent) {" + lineTermination);
// Auto trace line Insert's
if(edSettings.autoTrace === true && edSettings.autoTraceLine !== null){
codeToInsert += ('\t\t' + edSettings.autoTraceLine + lineTermination);
}
codeToInsert += ("\t\t// TO DO - Auto-generated code" + lineTermination + "\t}");
}
}
if (codeToInsert !== "") {
// add a comma after the last function/property if required
if (commaInsertionPosition) {
this.$.aceWrapper.insertPosition(commaInsertionPosition[0], ",");
codeInsertionIndex++; // position shifted because of comma character addition
}
commaInsertionIndex++; // index after comma character added or already present
// detect if block ending curly braket is contiguous to previous function or property ending: curly bracket w/wo comma, value w/wo comma
if (commaInsertionIndex === codeInsertionIndex) {
commaInsertionPosition = this.$.aceWrapper.mapToLineColumns([commaInsertionIndex]);
this.$.aceWrapper.insertNewLine(commaInsertionPosition[0]);
codeInsertionIndex += lineTermination.length; // shifted because of line termination addition, is sentitive to line termination mode
}
// Add a new line before kind block ending curly braket
codeInsertionPosition = this.$.aceWrapper.mapToLineColumns([codeInsertionIndex]);
this.$.aceWrapper.insertNewLine(codeInsertionPosition[0]);
// Get the corresponding Ace range to replace/insert the missing code
// NB: aceWrapper.replace() allow to use the undo/redo stack.
var codeInsertionRange = this.$.aceWrapper.mapToLineColumnRange(codeInsertionIndex, codeInsertionIndex);
this.$.aceWrapper.replaceRange(codeInsertionRange, codeToInsert);
}
} else {
// There is no block information for that kind - Parser is probably not up-to-date
enyo.log("Unable to insert missing handler methods");
}
},
// called when designer has modified the components
updateComponentsCode: function(kinds) {
this.injected = true;
for( var i = this.analysis.objects.length -1 ; i >= 0 ; i-- ) {
if (kinds[i]) {
// Insert the new version of components (replace components block, or insert at end)
var obj = this.analysis.objects[i];
var content = kinds[i];
var start = obj.componentsBlockStart;
var end = obj.componentsBlockEnd;
var kindStart = obj.block.start;
if (!(start && end)) {
// If this kind doesn't have a components block yet, insert a new one
// at the end of the file
var last = obj.properties[obj.properties.length-1];
if (last) {
content = (last.commaTerminated ? "" : ",") + "\n\t" + "components: []";
kindStart = obj.block.end - 2;
end = obj.block.end - 2;
}
}
// Get the corresponding Ace range to replace the component definition
// NB: aceWrapper.replace() allow to use the undo/redo stack.
var range = this.$.aceWrapper.mapToLineColumnRange(kindStart, end);
this.$.aceWrapper.replaceRange(range, content);
}
}
this.injected = false;
/*
* Insert the missing handlers
* NB: reparseUsersCode() is invoked by insertMissingHandlers()
*/
this.insertMissingHandlers();
//file is edited if only we have a difference between stored file data and editor value
if(this.getEditorContent().localeCompare(this.docData.getData())!==0){
this.docData.setEdited(true);
this.doFileEdited();
}
},
docChanged: function(inSender, inEvent) {
//this.injected === false then modification coming from user
if(!this.injected && !this.docData.getEdited()){
this.docData.setEdited(true);
this.doFileEdited();
}
this.trace("data:", enyo.json.stringify(inEvent.data));
if (this.analysis) {
// Call the autocomplete component
this.$.autocomplete.start(inEvent);
}
return true; // Stop the propagation of the event
},
editorUserSyntaxError:function(){
var userSyntaxError = [];
userSyntaxError = this.$.autocomplete.aceWrapper.editor.session.$annotations.length;
return userSyntaxError;
},
cursorChanged: function(inSender, inEvent) {
var position = this.$.aceWrapper.getCursorPositionInDocument();
this.trace(inSender.id, " ", inEvent.type, " ", enyo.json.stringify(position));
// Check if we moved to another enyo kind and display it in the right pane
var tempo = this.analysis;
if (tempo && tempo.currentLine !== undefined && tempo.currentLine != position.row) { // If no more on the same line
tempo.currentLine = position.row;
// Check if the cursor references another object
if (tempo.currentRange !== undefined && (position.row < tempo.currentRange.first || position.row > tempo.currentRange.last)) {
tempo.currentObject = this.findCurrentEditedObject(position);
tempo.currentRange = tempo.ranges[tempo.currentObject];
this.dumpInfo(tempo.objects && tempo.objects[tempo.currentObject]);
}
}
this.$.autocomplete.cursorChanged(position);
return true; // Stop the propagation of the event
},
startAutoCompletion: function() {
this.$.autocomplete.start(null);
},
newKindAction: function() {
// Insert a new empty enyo kind at the end of the file
var newKind = 'enyo.kind({\n name : "@cursor@",\n kind : "Control",\n components : []\n});';
this.$.aceWrapper.insertAtEndOfFile(newKind, '@cursor@');
},
newcssAction: function(inSender, inEvent){
this.$.aceWrapper.insertAtEndOfFile(inEvent.outPut);
},
/*
* cleanup data. Typically called when closing a document
* @protected
*/
closeSession: function() {
if (this.docData) {
this.$.aceWrapper.destroySession(this.docData.getAceSession());
this.resetAutoCompleteData();
this.docData = null;
}
},
// Show Find popup
findpop: function(){
var selected = this.$.aceWrapper.getSelection();
if(selected){
this.$.findpop.setFindInput(selected);
}
this.$.findpop.removeMessage();
this.$.findpop.disableReplaceButtons(true);
this.$.findpop.show();
return true;
},
findNext: function(inSender, inEvent){
var options = {backwards: false, wrap: true, caseSensitive: false, wholeWord: false, regExp: false};
this.$.aceWrapper.find(this.$.findpop.findValue, options);
this.$.findpop.updateAfterFind(this.$.aceWrapper.getSelection());
},
findPrevious: function(){
var options = {backwards: true, wrap: true, caseSensitive: false, wholeWord: false, regExp: false};
this.$.aceWrapper.find(this.$.findpop.findValue, options);
this.$.findpop.updateAfterFind(this.$.aceWrapper.getSelection());
},
replaceAll: function(){
var occurences = this.$.aceWrapper.replaceAll(this.$.findpop.findValue , this.$.findpop.replaceValue);
this.$.findpop.updateMessage(this.$.aceWrapper.getSelection(), occurences);
},
replacefind: function(){
var options = {backwards: false, wrap: true, caseSensitive: false, wholeWord: false, regExp: false};
this.$.aceWrapper.replacefind(this.$.findpop.findValue , this.$.findpop.replaceValue, options);
this.$.findpop.updateMessage(this.$.aceWrapper.getSelection());
},
//ACE replace doesn't replace the currently-selected match. It instead replaces the *next* match. Seems less-than-useful
//It was not working because ACE(Ace.js) was doing "find" action before "replace".
replace: function(){
this.$.aceWrapper.replace(this.$.findpop.findValue, this.$.findpop.replaceValue);
},
focusEditor: function(inSender, inEvent) {
this.$.aceWrapper.focus();
},
getEditorContent: function() {
return this.$.aceWrapper.getValue();
},
handleScroll: function(inSender, inEvent) {
this.$.autocomplete.hide();
},
findClose: function(){
this.$.findpop.hide();
},
toggleww: function(){
if(this.$.aceWrapper.wordWrap === "true" || this.$.aceWrapper.wordWrap === true){
this.$.aceWrapper.wordWrap = false;
this.$.aceWrapper.wordWrapChanged();
}else{
this.$.aceWrapper.wordWrap = true;
this.$.aceWrapper.wordWrapChanged();
}
},
/** @public */
requestSelectedText: function() {
return this.$.aceWrapper.requestSelectedText();
},
/**
* Add a new kind (requested from the designer)
* A switch to the designer is performed to fully reload the kinds in the designer.
* @param config
* @public
*/
addNewKind: function(config) {
var newKind = 'enyo.kind('+config+'\n);';
this.$.aceWrapper.insertAtEndOfFile(newKind, '@cursor@');
this.designerAction();
},
/**
* Insert a new kind (requested from the designer)
* A switch to the designer is performed to fully reload the kinds in the designer.
* @param kind_index, config
* @public
*/
replaceKind: function(kind_index, config){
var obj = this.analysis.objects[kind_index];
var range = this.$.aceWrapper.mapToLineColumnRange(obj.block.start, obj.block.end);
this.$.aceWrapper.replaceRange(range, config);
this.designerAction();
},
//* editor setting
editorSettings: function() {
this.$.editorSettingsPopup.show();
this.$.editorSettings.initSettings();
},
closeEditorPopup: function(){
this.$.editorSettingsPopup.hide();
this.doAceFocus();
return true;
},
changeRightPane: function(settings){
if(this.docData){
this.adjustPanelsForMode(this.docData.getMode(), settings.rightpane);
}
},
applySettings: function(settings){
//apply Ace settings
this.$.aceWrapper.applyAceSettings(settings);
if(this.docData){
this.adjustPanelsForMode(this.docData.getMode(), settings.rightpane);
}
},
fkeypressed: function(inSender, inEvent) {
var key = inEvent;
this.$.aceWrapper.insertAtCursor(this.$.editorSettings.settings.keys[ key ]);
},
//* Trigger an Ace undo and bubble updated code
undoAndUpdate: function(next) {
ares.assertCb(next);
this.$.aceWrapper.undo();
this.updateCodeInDesigner(next);
},
//* Trigger an Ace undo and bubble updated code
redoAndUpdate: function(next) {
ares.assertCb(next);
this.$.aceWrapper.redo();
this.updateCodeInDesigner(next);
},
//* Send up an updated copy of the code
updateCodeInDesigner: function(next) {
// Update the projectIndexer and notify watchers
ares.assertCb(next);
this.reparseUsersCode(true);
var data = {kinds: this.extractKindsData(), projectData: this.projectData, fileIndexer: this.analysis};
if (data.kinds.length > 0) {
this.doChildRequest({task: [ "loadDesignerUI", data, next ]});
} // else - The error has been displayed by extractKindsData()
else {
setTimeout(next,0);
}
},
resizeHandler: function() {
this.inherited(arguments);
this.$.body.reflow();
this.$.aceWrapper.resize();
}
});
enyo.kind({
name: "rightPanels",
kind: "Panels",
wrap: false,
draggable:false,
events: {
onCss: "",
onReparseAsked: "",
onInitNavigation: "",
onNavigateInCodeEditor: ""
},
components: [
{// right panel for JSON goes here
},
{kind: "enyo.Control", classes: "enyo-fit", components: [
{name: "right", classes: "enyo-fit ares_phobos_panel border", components: [
{kind: "onyx.Button", content: "Reparse", ontap: "doReparseAsked"},
{kind: "enyo.Scroller", classes: "enyo-fit ace-helper-panel",components: [
{tag:"ul", kind: "enyo.Repeater", name: "dump", onSetupItem: "sendInitHelperReapeter", ontap: "sendNavigate", components: [
{tag:"li", classes:"ace-helper-list", kind:"RightPanel.Helper", name: "item"}
]}
]}
]}
]},
{// right panel for HTML goes here
},
{kind: "enyo.Control", classes: "enyo-fit", components: [ // right panel for CSS here
{kind: "cssBuilder", classes: "enyo-fit ares_phobos_panel border", onInsert: "test"}
]}
],
create: function() {
this.inherited(arguments);
},
test: function(inEvent) {
this.doCss(inEvent);
},
sendInitHelperReapeter: function(inSender, inEvent) {
this.doInitNavigation({item: inEvent.item});
},
sendNavigate: function(inSender, inEvent){
this.doNavigateInCodeEditor({index:inEvent.index});
},
setDumpCount: function(count){
this.$.dump.setCount(count);
}
});
enyo.kind({
name: "RightPanel.Helper",
published: {
title: "",
fixedItem: "",
navigateItem: "",
index: -1
},
components: [
{name: "title", classes: "ace-title"},
{name: "fixedItem", classes: "ace-fixed-item"},
{name: "navigateItem", kind: "control.Link", classes: "ace-navigate-item"}
],
titleChanged: function() {
this.$.title.setContent(this.title);
},
fixedItemChanged: function() {
this.$.fixedItem.setContent(this.fixedItem);
},
navigateItemChanged: function() {
this.$.navigateItem.setContent(this.navigateItem);
}
});