ares-ide
Version:
A browser-based code editor and UI designer for Enyo 2 projects
735 lines (676 loc) • 24.2 kB
JavaScript
/*global enyo, ares, ProjectConfig, Phonegap */
/**
* This kind provide a widget to tune project properties
*
* By default, this widget is tuned for project modification. In case
* of project *creation*, the method setupCreate must be called after
* construction. Since the widget is re-used between call for creation
* or modification, the methos setupModif must be called also.
*/
enyo.kind({
name: "ProjectProperties",
debug: false,
classes: "enyo-unselectable ares-classic-popup",
fit: true,
events: {
onError: "",
onModifiedConfig: "",
onSaveGeneratedXml: "",
onDone: "",
onSelectFile: "",
onCheckPath: "",
onFileChoosersChecked: ""
},
handlers: {
onAddSource: "addNewSource",
onRemoveSource: "removeAddedSource",
onInitSource: "initAddedSource",
onInputButtonTap: "selectInputFile",
onDisableOkButton: "disableOkButton",
onEnableOkButton: "enableOkButton"
},
components: [
{classes:"title left-align", content:"Project properties", components:[
{kind: "onyx.RadioGroup", onActivate: "switchDrawers", name: "thumbnail", classes:"ares-radio-group", components: [
{serviceId: "project", active: true, attributes: { title: $L("project attributes...") }, components: [
{classes: "large-fixed", content: $L("Project")}, {tag: "span", classes:"ares-bottom-check"}
]},
{serviceId: "preview", attributes: { title: $L("project preview parameters...") }, components: [
{classes: "large-fixed", content: $L("Preview")}, {tag:"span", classes:"ares-bottom-check"}
]}
]}
]},
{name: "projectDrawer", kind: "onyx.Drawer", open: true, components: [
{classes: "ares-project-properties", kind: "Scroller", components:[
{kind: "FittableRows", components: [
{kind: "FittableColumns", classes: "ares-row", components: [
{components: [
{tag: "label", name: "projectPathLabel", classes : "ares-fixed-label ares-small-label", content: ""},
]},
{fit: true, components: [
{tag: "label", name: "projectPathValue", classes : "ares-label", content: ""},
]}
]},
{kind: "FittableColumns", name: "versions", classes: "ares-row", components: [
{components: [
{tag: "label", classes : "ares-fixed-label ares-small-label", content: "Versions: "},
]},
{fit: true, components: [
{tag: "label", name: "enyoVersions", classes : "ares-label", content: ""}
]}
]}
]},
{tag: "p", classes:"break"},
{kind: "FittableColumns", components: [
{kind: "FittableRows", components: [
{classes: "ares-row", components: [
{tag: "label", classes : "ares-fixed-label ares-small-label", content: $L("Name: ")},
{kind: "onyx.InputDecorator", components: [
{kind: "Input", defaultFocus: true, name: "projectName"}
]}
]},
{classes: "ares-row", components: [
{tag: "label", classes : "ares-fixed-label ares-small-label", content: $L("Version: ")},
{kind: "onyx.InputDecorator", components: [
{kind: "Input", defaultFocus: true, name: "projectVersion", placeholder:"0.0.1"}
]}
]},
{classes: "ares-row", components: [
{tag: "label", classes : "ares-fixed-label ares-small-label", content: $L("Author name: ")},
{kind: "onyx.InputDecorator", components: [
{kind: "Input", name: "projectAuthor", attributes: {title: $L("Vendor / Committer Name")}, placeholder: $L("My Company")}
]}
]},
{classes:"ares-row", name: "templatesEntry", showing: false, components: [
{tag: "label", classes :"ares-fixed-label ares-small-label", content: $L("Template:")},
{kind: "onyx.PickerDecorator", fit: true, components: [
{name: "templateButton", classes:"very-large-width", kind: "onyx.PickerButton", fit: true},
{kind: "onyx.FlyweightPicker", name: "templatePicker", components: [
{name: "template"}
], onSetupItem: "templateSetupItem", onSelect: "templateSelected"}
]}
]}
]},
{kind: "FittableRows", components: [
{classes: "ares-row", components: [
{tag: "label", classes: "ares-fixed-label ares-small-label", content: $L("Title: ")},
{kind: "onyx.InputDecorator", components: [
{kind: "Input", defaultFocus: true, name: "projectTitle", placeholder: $L("My Example App")}
]}
]},
{classes: "ares-row", components: [
{tag: "label", classes : "ares-fixed-label ares-small-label", content: $L("Id: ")},
{kind: "onyx.InputDecorator", components: [
{kind: "Input", defaultFocus: true, name: "projectId",
attributes: {title: $L("Application ID in reverse domain-name format: com.example.apps.myapp")}, placeholder: "com.example.apps.myapp"}
]}
]},
{classes: "ares-row", components: [
{tag: "label", classes : "ares-fixed-label ares-small-label", content: $L("Contact: ")},
{kind: "onyx.InputDecorator", components: [
{kind: "Input", name: "projectContact",
attributes: {title: $L("mail address or home page of the author")}, placeholder: $L("support@example.com")
}
]}
]}
]}
]},
{tag: "p", classes: "break"},
{kind: "FittableColumns", components: [
{classes: "ares-row", components: [
{tag: "label", classes: "ares-fixed-label ares-small-label", content: $L("Description:")},
{kind: "onyx.InputDecorator", classes: "ares-project-properties-textarea-wide", components: [
{kind: "onyx.TextArea", name: "projectDescription", fit: true, content: "", placeholder: $L("My project description...")}
]}
]}
]},
{tag:"p", classes:"break"},
{kind: "enyo.FittableColumns", classes: "ares-row", name: "servicesList"}
]}
]},
{name: "previewDrawer", kind: "onyx.Drawer", open: false, components: [
{classes: "ares-project-properties", components:[
{kind: "FittableRows", components: [
{kind: "ProjectProperties.PathInputRow",
name: "topFileRow",
label: $L("Top application file: "),
inputTip: $L("top file of your application. Typically '/index.html', but per default '/debug.html' for debug mode."),
buttonTip: $L("select file...")}
]}
]}
]},
{name: "errTooltipDecorator", kind: "onyx.TooltipDecorator", components: [
{name: "toolbarId", kind: "onyx.Toolbar", classes: "bottom-toolbar", components: [
{kind: "onyx.Button", content: $L("Cancel"), ontap: "doDone"},
{kind: "onyx.Button", name: "ok", classes: "right", content: $L("OK"), ontap: "confirmTap"}
]}
]},
{kind: "Signals", onServicesChange: "handleServicesChange"}
],
published: {
topFileStatus: "",
targetProject: "",
validatePhonegapUiValues: {}
},
templates: [],
selectedTemplate: undefined,
selectedAddedSource: undefined,
services: {},
addedSource:[],
fileChoosers: [],
create: function() {
ares.setupTraceLogger(this); // Setup this.trace() function according to this.debug value
this.inherited(arguments);
},
/**
* Set the default tab
*/
setDefaultTab: function() {
this.$.thumbnail.children[0].setActive(true);
},
setDisplayedTab: function(inIndex) {
this.$.thumbnail.children[inIndex].setActive(true);
},
/**
* Receive the {onServicesChange} broadcast notification
* @param {Object} inEvent.serviceRegistry
*/
handleServicesChange: function(inSender, inEvent) {
this.trace(inSender, "=>", inEvent);
this.services = enyo.clone(this.services) || {};
inEvent.serviceRegistry.forEach(enyo.bind(this, function(inService) {
var service = {
id: inService.id,
name: inService.getName() || inService.id,
kind: inService.getProjectPropertiesKind && inService.getProjectPropertiesKind()
};
this.trace("service:", service);
if (service.kind) {
this.services[service.id] = service;
}
}));
enyo.forEach(enyo.keys(this.services), function(serviceId) {
var service = this.services[serviceId];
var drawer = this.$[service.id + 'Drawer'] || this.createComponent({
name: service.id + 'Drawer',
kind: "onyx.Drawer",
open: false
},{
addBefore: this.$.toolbarId
});
service.panel = drawer.$[service.id] || drawer.createComponent({
name: service.id,
kind: service.kind
});
service.tab = this.$.thumbnail.$[service.id + 'Tab'] || this.$.thumbnail.createComponent({
name: service.id + 'Tab',
serviceId: service.id,
showing: false,
components:[{content:service.name, classes:"large-fixed"},{tag:"span", classes:"ares-bottom-check"}]
});
var frame = this.$.servicesList.$[service.id + 'Frame'];
if (typeof frame !== 'object') {
frame = this.$.servicesList.createComponent({
name: service.id + 'Frame'
});
frame.createComponent({
kind: 'onyx.Checkbox',
name: service.id + 'CheckBox',
onchange: 'toggleService',
serviceId: service.id
}, {
owner: this
});
frame.createComponent({
tag: 'label',
classes: 'ares-label',
content: service.name
});
}
service.checkBox = this.$[service.id + 'CheckBox'];
// Take the project configuration into account
// to show the service or not
this.showService(serviceId);
}, this);
this.trace("services:", this.services);
},
/**
* Toggle a service panel
*/
toggleService: function(inSender, inEvent) {
var serviceId = inEvent.originator.serviceId,
checked = inEvent.originator.checked;
this.trace("serviceId:", serviceId, 'checked:', checked);
this.config.providers[serviceId].enabled = checked;
this.showService(serviceId);
},
/**
* Tune the widget for project creation
*/
setupCreate: function() {
//this.$.ok.setDisabled(true) ;
this.$.templatesEntry.show();
this.notifyProjectPropertyStatus({status: "create"});
},
/**
* Tune the widget for project modification
*/
setupModif: function() {
//Set the selected Project Information to plugin service drawer
for(var serviceId in this.services){
var service = this.services[serviceId];
var serviceDrawer = this.$[service.id + 'Drawer'].$[service.id];
if(typeof serviceDrawer.setTargetProject === 'function'){
serviceDrawer.setTargetProject(this.targetProject);
}
}
this.$.templatesEntry.hide();
this.notifyProjectPropertyStatus({status: "modify"});
},
/**
* close one drawer and open the other depending on which radio button was tapped
*/
switchDrawers: function(inSender, inEvent) {
if (inEvent.originator.active === true ) {
enyo.forEach(inEvent.originator.parent.children, function(tab) {
var activate = (tab.serviceId === inEvent.originator.serviceId);
this.$[tab.serviceId + 'Drawer'].setOpen(activate);
}, this);
}
},
/**
* Show the drawer of the corresponding service.
*/
showServiceDrawer: function(inServiceId) {
this.$.thumbnail.$[inServiceId + "Tab"].setActive(true);
},
/**
* pre-fill the widget with configuration data
* @param {Object} config is configuration data (typically from project.json)
* can be a json string or an object.
*/
preFill: function(inData) {
var conf = typeof inData === 'object' ? inData : enyo.json.parse(inData);
this.config = ProjectConfig.checkConfig(conf);
// avoid storing 'undefined' in there
this.$.projectId.setValue(this.config.id || '' );
this.$.projectVersion.setValue(this.config.version || '' );
this.$.projectName.setValue(this.config.name || '' );
this.$.projectTitle.setValue(this.config.title || '' );
if (!this.config.author) {
this.config.author = {};
}
this.$.projectAuthor.setValue(this.config.author.name || '') ;
this.$.projectContact.setValue(this.config.author.href || '') ;
this.$.projectDescription.setValue(this.config.description || '') ;
// Load each provider service configuration into its
// respective ProjectProperties panel
enyo.forEach(enyo.keys(this.services), function(serviceId) {
this.showService(serviceId);
}, this);
if (!this.config.preview) {
this.config.preview = {};
}
this.$.topFileRow.setValue(this.config.preview.top_file);
return this ;
},
update: function(inData) {
var conf = this.config ;
enyo.forEach(enyo.keys(inData), function(key) {
conf[key] = inData[key];
});
this.$.projectId. setValue(conf.id || '' );
this.$.projectVersion.setValue(conf.version || '' );
this.$.projectTitle. setValue(conf.title || '' );
return this ;
},
showService: function(serviceId) {
var service = this.services[serviceId];
var config = this.config && this.config.providers && this.config.providers[serviceId];
var enabled = config && config.enabled;
service.checkBox.setChecked(enabled);
service.tab.setShowing(enabled);
if (config) {
service.panel.setProjectConfig(config);
}
},
notifyProjectPropertyStatus: function(inEvent) {
this.waterfallDown("onChangeProjectStatus", inEvent);
},
/** @private */
confirmTap: function(inSender, inEvent) {
// retrieve all configuration settings
this.config = {};
// Project settings
this.config.id = this.$.projectId.getValue();
this.config.version = this.$.projectVersion.getValue();
this.config.name = this.$.projectName.getValue();
this.config.title = this.$.projectTitle.getValue();
this.config.author = {};
this.config.author.name = this.$.projectAuthor.getValue();
this.config.author.href = this.$.projectContact.getValue();
this.config.description = this.$.projectDescription.getValue();
// Preview settings
this.config.preview = {};
this.config.preview.top_file = this.$.topFileRow.getValue();
// Dump each provider service configuration panel into
// the project configuration.
this.config.providers = {};
enyo.forEach(enyo.keys(this.services), function(serviceId) {
var service = this.services[serviceId];
this.config.providers[service.id] = {};
this.config.providers[service.id].enabled = service.checkBox.checked;
service.panel.saveProjectConfig(this.targetProject);
service.panel.getProjectConfig(this.config.providers[service.id]);
}, this);
if(this.config.name === ""){
this.doError({msg:"Please enter a valid Name value.", title:"User Error"});
return;
}else if(this.config.title === ""){
this.doError({msg: "Please enter a valid Title value." , title:"User Error"});
return;
}else if(this.config.version === ""){
this.doError({msg: "Please enter a valid Version value.", title:"User Error"});
return;
}else if(this.config.id === ""){
this.doError( {msg: "Please enter a valid Id value.", title: "User Error"});
return;
}
this.doModifiedConfig({data: this.config, template: this.selectedTemplate && this.selectedTemplate.id, addedSources: this.addedSource}) ;
this.doDone();
if (inEvent.callBack) {
inEvent.callBack() ;
}
// handled here (don't bubble)
return true;
},
disableOkButton: function(inSender, inEvent) {
var provider = Phonegap.ProjectProperties.getProvider();
this.trace("inSender:", inSender, "inEvent:", inEvent);
this.$.ok.setDisabled(true);
var contentError = inEvent.reason || "Check project properties tabs for errors";
if(!this.$.errTooltip){
this.$.errTooltipDecorator.createComponent({
kind: "onyx.Tooltip",
name: "errTooltip",
content: contentError,
showing: true,
showDelay: 0
}, {owner: this});
this.$.errTooltipDecorator.render();
}else{
this.$.errTooltip.setContent(contentError);
}
this.$.errTooltip.show();
provider.getSelectedProject().getValidPgbConf()[inEvent.originator.platform][inEvent.originator.name] = false;
provider.getSelectedProject().getValidPgbConf()[inEvent.originator.platform]["validDrawer"] = false;
},
/**
* Enable the OK button of the project properties Pop-up if all the values are
* correct.
* Called if the event onEnableOkButton is fired by a row in Phonegap build UI
* This event is bubbled in the case where the value set in the UI Row is corrected. *
*
* @param {Object} inSender container of the originator of the event
* @param {Object} inEvent contain the new state of the event originator
* @return {boolean} stop the bubbling.
*/
enableOkButton: function(inSender, inEvent){
this.trace("inSender:", inSender, "inEvent:", inEvent);
var okDisabled = false;
var validDrawer = true;
var provider = Phonegap.ProjectProperties.getProvider();
// Set in the array {this.validatePhonegapUiValues} the originator UI row as valid
provider.getSelectedProject().getValidPgbConf()[inEvent.originator.platform][inEvent.originator.name] = true;
// Check the validation state of all controled PGB attributes by platform (it includes also the shared configuration attributes)
// to update the validation state of the drawer
for(var option in provider.getSelectedProject().getValidPgbConf()[this.platform]){
if(!provider.getSelectedProject().getValidPgbConf()[this.platform][option]){
validDrawer = false;
}
}
if (validDrawer) {
provider.getSelectedProject().getValidPgbConf()[inEvent.originator.platform]["validDrawer"] = true;
}
// Check the validation state of all the controled PGB drawer
// to enable/disable the Ok button
for(var drawer in provider.getSelectedProject().getValidPgbConf()) {
if (!provider.getSelectedProject().getValidPgbConf()[drawer]["validDrawer"]) {
okDisabled = true;
}
}
if(this.$.errTooltip){
this.$.errTooltip.destroy();
}
this.$.ok.setDisabled(okDisabled);
},
/** @public */
setTemplateList: function(templates) {
this.templates = [].concat(templates);
this.$.templatePicker.setCount(this.templates.length);
this.$.templatePicker.setSelected(0);
this.selectedTemplate = this.templates[0];
},
/** @public */
setLibsList: function(inLibs) {
this.libs = this.libs || [];
enyo.forEach(inLibs, function(inLib) {
inLib = enyo.filter(this.libs, function(lib) {
return inLib.id !== lib.id;
})[0];
if (inLib) {
// new lib
this.libs.push(inLib.id);
this.$.libsChecker.createComponent({
kind: "ProjectProperties.LibCheckBox",
name: inLib.id + "Checker",
lib: inLib
}, {
onLibChecked: "_onLibCheckedAction",
owner: this
});
}
}, this);
},
_onLibCheckedAction: function(inSender, inEvent) {
this.trace("inSender:", inSender, "inEvent:", inEvent);
var selectedLibs = [];
enyo.forEach(this.libs, function(lib) {
if (lib.id === inEvent.lib.id) {
lib.selected = inEvent.checked;
}
if (lib.selected) {
selectedLibs.push(lib.id);
}
}, this);
this.setSelectedLibs(selectedLibs);
},
templateSetupItem: function(inSender, inEvent) {
this.trace("inSender:", inSender, "inEvent:", inEvent);
var template = this.templates[inEvent.index];
this.$.template.setContent(template.description);
this.$.template.setAttribute("x-template", template);
return true;
},
templateSelected: function(inSender, inEvent) {
this.trace("inSender:", inSender, "inEvent:", inEvent);
this.selectedTemplate = inEvent.selected.getAttribute("x-template");
},
topFileChanged: function() {
this.$.topFileRow.setValue(this.topFile);
},
topFileStatusChanged: function() {
this.$.topFileRow.setStatus(this.topFileStatus);
},
handleAdditionalSource: function(inSender, inEvent) {
this.selectedAddedSource = inEvent.source;
return true;
},
addNewSource: function(inSender, inEvent) {
this.addedSource.push(inEvent.source);
return true;
},
removeAddedSource: function(inSender, inEvent) {
var index = this.addedSource.indexOf(inEvent.source);
this.addedSource = this.addedSource.slice(0,index).concat(this.addedSource.slice(index+1,this.addedSource.lenght));
return true;
},
initAddedSource: function(inSender, inEvent) {
this.addedSource = [];
return true;
},
/** @public */
activateFileChoosers: function(status) {
this.$.topFileRow.setActivated(status);
// Activate PhoneGap (and other services) path chooser buttons in related rows
enyo.forEach(enyo.keys(this.services), function(serviceId) {
if (serviceId === "phonegap") {
this.services[serviceId].panel.activateInputRows(status);
}
/* to the same for further services to be intregrated in Project Properties*/
}, this);
},
/** @public */
checkFileChoosers: function() {
this.fileChoosers = [];
// get fileChooser triggering objects name for ares project properties
// currently only one: Top File's PathInputRow in Preview tab
for (var o in this.$) {
if (this.$[o].kind === "ProjectProperties.PathInputRow") {
this.fileChoosers.push(this.$[o]);
}
}
// find PhoneGap (and other services) rows containing path chooser buttons
enyo.forEach(enyo.keys(this.services), function(serviceId) {
if (serviceId === "phonegap") {
this.fileChoosers = this.fileChoosers.concat(this.services[serviceId].panel.findAllInputRows());
}
/* to the same for further services to be intregrated in Project Properties*/
}, this);
var chooser = this.fileChoosers.shift();
this.doCheckPath({input: chooser, value: chooser.getValue()});
},
/** @private */
fileChooserChecked: function() {
if (this.fileChoosers.length) {
var chooser = this.fileChoosers.shift();
this.doCheckPath({input: chooser, value: chooser.getValue()});
} else {
this.doFileChoosersChecked();
}
return true;
},
/** @private */
selectInputFile: function (inSender, inData) {
this.doSelectFile({input: inData.originator, value: inData.originator.getValue(), status: inData.originator.getStatus(), header: inData.header});
},
/** @public */
updateFileInput: function(input, value) {
input.setValue(value);
return true;
},
updatePathCheck: function(input, status) {
input.setStatus(status);
this.fileChooserChecked();
return true;
},
setVersionLabel: function(value){
this.$.enyoVersions.setContent(value);
},
hideVersions: function(){
this.$.versions.hide();
}
});
enyo.kind({
name: "ProjectProperties.LibCheckBox",
classes: "ares-row",
published: {
lib: ""
},
events: {
onLibChecked: ""
},
components: [
{name: "chkBx", kind: "onyx.Checkbox", onchange: "onCheckedAction"},
{tag:"label", classes:"ares-label", content: ""}
],
onCheckedAction: function(inSender, inEvent) {
this.doLibChecked({
lib: this.lib,
use: this.$.chkBx.checked
});
return true;
}
});
enyo.kind({
name: "ProjectProperties.PathInputRow",
classes: "ares-row",
published: {
label: "",
value: "",
inputTip: "",
activated: false,
status: false,
buttonTip: ""
},
events: {
onInputButtonTap: "",
onPathChecked: ""
},
components: [
{tag: "label", name: "pathInputLabel", classes:"ares-fixed-label"},
{kind: "onyx.InputDecorator", components: [
{kind: "Input", name: "pathInputValue", disabled: true}
]},
{kind: "onyx.IconButton", name:"pathInputButton", src: "$project-view/assets/images/file-32x32.png", ontap: "pathInputTap"}
],
debug: false,
create: function () {
this.inherited(arguments);
this.labelChanged();
this.valueChanged();
this.inputTipChanged();
this.activatedChanged();
this.statusChanged();
this.buttonTipChanged();
},
/** @private */
labelChanged: function () {
this.$.pathInputLabel.setContent(this.label);
},
/** @private */
valueChanged: function () {
this.$.pathInputValue.setValue(this.value);
this.setStatus(true);
},
/** @private */
inputTipChanged: function () {
this.$.pathInputValue.setAttribute("title", this.inputTip);
},
/** @private */
activatedChanged: function () {
if (this.activated) {
this.$.pathInputButton.show();
this.statusChanged();
} else {
this.$.pathInputButton.hide();
}
},
/** @private */
statusChanged: function () {
if (this.status) {
this.$.pathInputButton.setSrc("$project-view/assets/images/file-32x32.png");
} else {
this.$.pathInputButton.setSrc("$project-view/assets/images/file_broken-32x32.png");
}
},
/** @private */
buttonTipChanged: function () {
this.$.pathInputButton.setAttribute("title", this.buttonTip);
},
/** @private */
pathInputTap: function (inSender, inEvent) {
this.doInputButtonTap({header: $L("Select top file...")});
return true;
}
});