Compare commits

...

27 Commits

Author SHA1 Message Date
b4dc50295c Merge pull request #1131 from nasa/open1094
Resolve synchronization issues with MutableObject
2016-08-25 13:26:50 -07:00
382dde300a Merge remote-tracking branch 'origin/api-tutorials' into open1094
Conflicts:
	index.html
	tutorials/todo/todo.js
2016-08-25 13:25:54 -07:00
02aa08a3ef Merge pull request #1121 from nasa/api-containment
[API] Containment
2016-08-25 13:19:14 -07:00
c95d9c7956 Merge pull request #1107 from nasa/api-type-forms
[API] Handle forms with a "properties" region
2016-08-25 13:17:26 -07:00
b1b8df4d87 Use MutationCapability 2016-08-23 13:57:12 +01:00
bd3c6665fb Added bridge between old and new event models 2016-08-22 16:14:01 +01:00
10e90519c0 Tidy todo views, remove unnecessary code 2016-08-22 16:10:45 +01:00
d341a8be97 Selection changes include new selection 2016-08-22 16:10:45 +01:00
8c439d8109 Adding compatibility between old and new style mutation events 2016-08-22 07:59:17 +01:00
9c88b7ce1d Removed setters from MutableObject and fixed non-working todo list tutorial
Refactoring MutableObject

Fixed non-working todo example
2016-08-19 13:39:23 -07:00
2463e4d59f [API] Update Dialog API usage 2016-08-12 12:54:39 -07:00
d73c505bea [API] Fix typo, add missing this 2016-08-12 11:17:00 -07:00
831ecc59d9 [API] Wire in canContain via policy 2016-08-12 10:24:59 -07:00
1de26d3c5d [API] Throw error on containment violation 2016-08-12 09:30:02 -07:00
11409ce509 [API] Add containment methods 2016-08-12 09:27:46 -07:00
93872ce074 [API] Expose Dialog as constructor
...and use it that way from todo plugin
2016-08-11 16:04:26 -07:00
8861644f2d [API] Adjust Dialog API
...to allow OK button to be enabled/disabled.
2016-08-11 16:02:04 -07:00
d4948f771b Merge branch 'api-todo-update' into api-type-forms 2016-08-11 15:31:11 -07:00
8295a0bed1 [API] Update todo tutorial
...to expect new domain object API (instead of explicitly
wrapping it.)
2016-08-11 15:29:46 -07:00
0656a298da [API] Remove test usage of properties region 2016-08-04 14:00:48 -07:00
fe2ce91d50 [API] Show a custom view in dialog 2016-07-28 16:16:23 -07:00
14f30b2489 [API] Restrict dialog overrides
...to those domain objects which have some view for the
properties region registered.
2016-07-28 16:05:02 -07:00
62d90a8114 [API] Show dialog via mct 2016-07-28 15:57:15 -07:00
87682607a5 [API] Rename dependency in adapter layer 2016-07-28 15:53:07 -07:00
7bf265b478 [API] Move mct service up 2016-07-28 15:52:52 -07:00
1d31fe8d02 [API] Override dialogService in actions
An ugly hack to allow dialogs to be shown for Save As
and Edit Properties, without requiring form generation.
This will permit views to be shown instead in certain
cases, https://github.com/nasa/openmct/pull/999#issuecomment-236045158
2016-07-28 15:48:30 -07:00
bfdbc71e40 [API] Define a properties region 2016-07-28 15:34:03 -07:00
19 changed files with 413 additions and 188 deletions

View File

@ -31,10 +31,12 @@
<script type="text/javascript">
require(['main'], function (mct) {
require([
'./tutorials/todo/todo',
'./example/imagery/bundle',
'./example/eventGenerator/bundle',
'./example/generator/bundle',
], function () {
'./example/generator/bundle'
], function (todoPlugin) {
mct.install(todoPlugin);
mct.run();
})
});

View File

@ -4,7 +4,6 @@ define([
'uuid',
'./api/api',
'text!./adapter/templates/edit-object-replacement.html',
'./ui/Dialog',
'./Selection',
'./api/objects/object-utils'
], function (
@ -13,13 +12,21 @@ define([
uuid,
api,
editObjectTemplate,
Dialog,
Selection,
objectUtils
) {
function MCT() {
EventEmitter.call(this);
this.legacyBundle = { extensions: {} };
this.legacyBundle = { extensions: {
services: [
{
key: "mct",
implementation: function () {
return this;
}.bind(this)
}
]
} };
this.selection = new Selection();
this.on('navigation', this.selection.clear.bind(this.selection));
@ -94,13 +101,6 @@ define([
region: region,
key: viewKey
});
this.legacyExtension('services', {
key: 'PublicAPI',
implementation: function () {
return this;
}.bind(this)
});
};
MCT.prototype.type = function (key, type) {
@ -117,10 +117,6 @@ define([
});
};
MCT.prototype.dialog = function (view, title) {
return new Dialog(view, title).show();
};
MCT.prototype.start = function () {
this.legacyExtension('runs', {
depends: ['navigationService'],
@ -147,6 +143,7 @@ define([
MCT.prototype.regions = {
main: "MAIN",
properties: "PROPERTIES",
toolbar: "TOOLBAR"
};

View File

@ -8,7 +8,7 @@ define(['EventEmitter'], function (EventEmitter) {
Selection.prototype.select = function (value) {
this.selectedValues.push(value);
this.emit('change');
this.emit('change', this.selectedValues);
return this.deselect.bind(this, value);
};
@ -16,7 +16,7 @@ define(['EventEmitter'], function (EventEmitter) {
this.selectedValues = this.selectedValues.filter(function (v) {
return v !== value;
});
this.emit('change');
this.emit('change', this.selectedValues);
};
Selection.prototype.selected = function () {
@ -25,7 +25,7 @@ define(['EventEmitter'], function (EventEmitter) {
Selection.prototype.clear = function () {
this.selectedValues = [];
this.emit('change');
this.emit('change', this.selectedValues);
};
return Selection;

View File

@ -0,0 +1,44 @@
define([
'../../api/objects/object-utils'
], function (objectUtils) {
function ActionDialogDecorator(mct, newViews, actionService) {
this.actionService = actionService;
this.mct = mct;
this.definitions = newViews.filter(function (newView) {
return newView.region === mct.regions.properties;
}).map(function (newView) {
return newView.factory;
});
}
ActionDialogDecorator.prototype.getActions = function (context) {
var mct = this.mct;
var definitions = this.definitions;
return this.actionService.getActions(context).map(function (action) {
if (action.dialogService) {
var domainObject = objectUtils.toNewFormat(
context.domainObject.getModel(),
objectUtils.parseKeyString(context.domainObject.getId())
);
definitions = definitions.filter(function (definition) {
return definition.canView(domainObject);
});
if (definitions.length > 0) {
action.dialogService = Object.create(action.dialogService);
action.dialogService.getUserInput = function (form, value) {
return new mct.Dialog(
definitions[0].view(context.domainObject),
form.title
).show();
};
}
}
return action;
});
};
return ActionDialogDecorator;
});

View File

@ -1,13 +1,17 @@
define([
'legacyRegistry',
'./actions/ActionDialogDecorator',
'./directives/MCTView',
'./services/Instantiate',
'./capabilities/APICapabilityDecorator'
'./capabilities/APICapabilityDecorator',
'./policies/AdapterCompositionPolicy'
], function (
legacyRegistry,
ActionDialogDecorator,
MCTView,
Instantiate,
APICapabilityDecorator
APICapabilityDecorator,
AdapterCompositionPolicy
) {
legacyRegistry.register('src/adapter', {
"extensions": {
@ -17,7 +21,7 @@ define([
implementation: MCTView,
depends: [
"newViews[]",
"PublicAPI"
"mct"
]
}
],
@ -41,6 +45,19 @@ define([
depends: [
"$injector"
]
},
{
type: "decorator",
provides: "actionService",
implementation: ActionDialogDecorator,
depends: [ "mct", "newViews[]" ]
}
],
policies: [
{
category: "composition",
implementation: AdapterCompositionPolicy,
depends: [ "mct" ]
}
]
}

View File

@ -0,0 +1,26 @@
define([], function () {
function AdapterCompositionPolicy(mct) {
this.mct = mct;
}
AdapterCompositionPolicy.prototype.allow = function (
containerType,
childType
) {
var containerObject = containerType.getInitialModel();
var childObject = childType.getInitialModel();
containerObject.type = containerType.getKey();
childObject.type = childType.getKey();
var composition = this.mct.Composition(containerObject);
if (composition) {
return composition.canContain(childObject);
}
return true;
};
return AdapterCompositionPolicy;
});

View File

@ -3,19 +3,22 @@ define([
'./TimeConductor',
'./View',
'./objects/ObjectAPI',
'./composition/CompositionAPI'
'./composition/CompositionAPI',
'./ui/Dialog'
], function (
Type,
TimeConductor,
View,
ObjectAPI,
CompositionAPI
CompositionAPI,
Dialog
) {
return {
Type: Type,
TimeConductor: new TimeConductor(),
View: View,
Objects: ObjectAPI,
Composition: CompositionAPI
Composition: CompositionAPI,
Dialog: Dialog
};
});

View File

@ -52,6 +52,9 @@ define([
if (!this._children) {
throw new Error("Must load composition before you can add!");
}
if (!this.canContain(child)) {
throw new Error("This object cannot contain that object.");
}
if (this.contains(child)) {
if (skipMutate) {
return; // don't add twice, don't error.
@ -94,6 +97,10 @@ define([
}
};
CompositionCollection.prototype.canContain = function (domainObject) {
return this.provider.canContain(this.domainObject, domainObject);
};
CompositionCollection.prototype.destroy = function () {
if (this.provider.off) {
this.provider.off(

View File

@ -59,6 +59,10 @@ define([
);
};
DefaultCompositionProvider.prototype.canContain = function (domainObject, child) {
return true;
};
DefaultCompositionProvider.prototype.remove = function (domainObject, child) {
// TODO: this needs to be synchronized via mutation
var index = domainObject.composition.indexOf(child);

View File

@ -1,15 +1,54 @@
define([
'./object-utils',
'./ObjectAPI'
'./ObjectAPI',
'./objectEventEmitter'
], function (
utils,
ObjectAPI
ObjectAPI,
objectEventEmitter
) {
function ObjectServiceProvider(objectService, instantiate) {
function ObjectServiceProvider(objectService, instantiate, topic) {
this.objectService = objectService;
this.instantiate = instantiate;
this.generalTopic = topic('mutation');
this.bridgeEventBuses();
}
/**
* Bridges old and new style mutation events to provide compatibility between the two APIs
* @private
*/
ObjectServiceProvider.prototype.bridgeEventBuses = function () {
var removeGeneralTopicListener;
var handleMutation = function (newStyleObject) {
var keyString = utils.makeKeyString(newStyleObject.key);
var oldStyleObject = this.instantiate(utils.toOldFormat(newStyleObject), keyString);
// Don't trigger self
removeGeneralTopicListener();
oldStyleObject.getCapability('mutation').mutate(function () {
return utils.toOldFormat(newStyleObject);
});
removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
}.bind(this);
var handleLegacyMutation = function (legacyObject){
var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId());
//Don't trigger self
objectEventEmitter.off('mutation', handleMutation);
objectEventEmitter.emit(newStyleObject.key.identifier + ":*", newStyleObject);
objectEventEmitter.on('mutation', handleMutation);
}.bind(this);
objectEventEmitter.on('mutation', handleMutation);
removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
};
ObjectServiceProvider.prototype.save = function (object) {
var key = object.key,
keyString = utils.makeKeyString(key),
@ -37,7 +76,7 @@ define([
// Injects new object API as a decorator so that it hijacks all requests.
// Object providers implemented on new API should just work, old API should just work, many things may break.
function LegacyObjectAPIInterceptor(ROOTS, instantiate, objectService) {
function LegacyObjectAPIInterceptor(ROOTS, instantiate, topic, objectService) {
this.getObjects = function (keys) {
var results = {},
promises = keys.map(function (keyString) {
@ -56,7 +95,7 @@ define([
};
ObjectAPI._supersecretSetFallbackProvider(
new ObjectServiceProvider(objectService, instantiate)
new ObjectServiceProvider(objectService, instantiate, topic)
);
ROOTS.forEach(function (r) {

View File

@ -1,10 +1,21 @@
define([
'lodash'
'lodash',
'./objectEventEmitter'
], function (
_
_,
objectEventEmitter
) {
function MutableObject(eventEmitter, object) {
this.eventEmitter = eventEmitter;
var ANY_OBJECT_EVENT = "mutation";
/**
* The MutableObject wraps a DomainObject and provides getters and
* setters for
* @param eventEmitter
* @param object
* @constructor
*/
function MutableObject(object) {
this.object = object;
this.unlisteners = [];
}
@ -21,22 +32,22 @@ define([
MutableObject.prototype.on = function(path, callback) {
var fullPath = qualifiedEventName(this.object, path);
this.eventEmitter.on(fullPath, callback);
this.unlisteners.push(this.eventEmitter.off.bind(this.eventEmitter, fullPath, callback));
objectEventEmitter.on(fullPath, callback);
this.unlisteners.push(objectEventEmitter.off.bind(objectEventEmitter, fullPath, callback));
};
MutableObject.prototype.set = function (path, value) {
_.set(this.object, path, value);
_.set(this.object, 'modified', Date.now());
//Emit event specific to property
this.eventEmitter.emit(qualifiedEventName(this.object, path), value);
objectEventEmitter.emit(qualifiedEventName(this.object, path), value);
//Emit wildcare event
this.eventEmitter.emit(qualifiedEventName(this.object, '*'), this.object);
};
objectEventEmitter.emit(qualifiedEventName(this.object, '*'), this.object);
MutableObject.prototype.get = function (path) {
return _.get(this.object, path);
//Emit a general "any object" event
objectEventEmitter.emit(ANY_OBJECT_EVENT, this.object);
};
return MutableObject;

View File

@ -60,25 +60,25 @@ define(['./MutableObject'], function (MutableObject) {
});
it('Supports getting and setting of object properties', function () {
expect(mutableObject.get('stringProperty')).toEqual('stringValue');
expect(domainObject.stringProperty).toEqual('stringValue');
mutableObject.set('stringProperty', 'updated');
expect(mutableObject.get('stringProperty')).toEqual('updated');
expect(domainObject.stringProperty).toEqual('updated');
var newArrayProperty = [];
expect(mutableObject.get('arrayProperty')).toEqual(arrayProperty);
expect(domainObject.arrayProperty).toEqual(arrayProperty);
mutableObject.set('arrayProperty', newArrayProperty);
expect(mutableObject.get('arrayProperty')).toEqual(newArrayProperty);
expect(domainObject.arrayProperty).toEqual(newArrayProperty);
var newObjectProperty = [];
expect(mutableObject.get('objectProperty')).toEqual(objectProperty);
expect(domainObject.objectProperty).toEqual(objectProperty);
mutableObject.set('objectProperty', newObjectProperty);
expect(mutableObject.get('objectProperty')).toEqual(newObjectProperty);
expect(domainObject.objectProperty).toEqual(newObjectProperty);
});
it('Supports getting and setting of nested properties', function () {
expect(mutableObject.get('objectProperty')).toEqual(objectProperty);
expect(mutableObject.get('objectProperty.prop1')).toEqual(objectProperty.prop1);
expect(mutableObject.get('objectProperty.prop3.propA')).toEqual(objectProperty.prop3.propA);
expect(domainObject.objectProperty).toEqual(objectProperty);
expect(domainObject.objectProperty.prop1).toEqual(objectProperty.prop1);
expect(domainObject.objectProperty.prop3.propA).toEqual(objectProperty.prop3.propA);
mutableObject.set('objectProperty.prop1', 'new-prop-1');
expect(domainObject.objectProperty.prop1).toEqual('new-prop-1');

View File

@ -1,11 +1,9 @@
define([
'lodash',
'EventEmitter',
'./object-utils',
'./MutableObject'
], function (
_,
EventEmitter,
utils,
MutableObject
) {
@ -18,8 +16,7 @@ define([
var Objects = {},
ROOT_REGISTRY = [],
PROVIDER_REGISTRY = {},
FALLBACK_PROVIDER,
eventEmitter = new EventEmitter();
FALLBACK_PROVIDER;
Objects._supersecretSetFallbackProvider = function (p) {
FALLBACK_PROVIDER = p;
@ -83,7 +80,7 @@ define([
};
Objects.getMutable = function (object) {
return new MutableObject(eventEmitter, object);
return new MutableObject(object);
};
return Objects;

View File

@ -40,7 +40,8 @@ define([
implementation: LegacyObjectAPIInterceptor,
depends: [
"roots[]",
"instantiate"
"instantiate",
"topic"
]
}
]

View File

@ -0,0 +1,10 @@
define([
"EventEmitter"
], function (
EventEmitter
) {
/**
* Provides a singleton event bus for sharing between objects.
*/
return new EventEmitter();
});

View File

@ -2,9 +2,15 @@ define(['text!./dialog.html', 'zepto'], function (dialogTemplate, $) {
function Dialog(view, title) {
this.view = view;
this.title = title;
this.showing = false;
this.enabledState = true;
}
Dialog.prototype.show = function () {
if (this.showing) {
throw new Error("Dialog already showing.");
}
var $body = $('body');
var $dialog = $(dialogTemplate);
var $contents = $dialog.find('.contents .editor');
@ -13,31 +19,45 @@ define(['text!./dialog.html', 'zepto'], function (dialogTemplate, $) {
var $ok = $dialog.find('.ok');
var $cancel = $dialog.find('.cancel');
var view = this.view;
function dismiss() {
$dialog.remove();
view.destroy();
}
if (this.title) {
$dialog.find('.title').text(this.title);
}
$body.append($dialog);
this.view.show($contents[0]);
this.$dialog = $dialog;
this.$ok = $ok;
this.showing = true;
[$ok, $cancel, $close].forEach(function ($button) {
$button.on('click', this.hide.bind(this));
}.bind(this));
return new Promise(function (resolve, reject) {
$ok.on('click', resolve);
$ok.on('click', dismiss);
$cancel.on('click', reject);
$cancel.on('click', dismiss);
$close.on('click', reject);
$close.on('click', dismiss);
});
};
Dialog.prototype.hide = function () {
if (!this.showing) {
return;
}
this.$dialog.remove();
this.view.destroy();
this.showing = false;
};
Dialog.prototype.enabled = function (state) {
if (state !== undefined) {
this.enabledState = state;
if (this.showing) {
this.$ok.toggleClass('disabled', !state);
}
}
return this.enabledState;
};
return Dialog;
});

View File

@ -1,14 +1,14 @@
<div class="example-todo">
<div class="example-button-group">
<a class="example-todo-button-all">All</a>
<a class="example-todo-button-incomplete">Incomplete</a>
<a class="example-todo-button-complete">Complete</a>
<a class="example-todo-button" data-filter="all">All</a>
<a class="example-todo-button" data-filter="incomplete">Incomplete</a>
<a class="example-todo-button" data-filter="complete">Complete</a>
</div>
<ul class="example-todo-task-list">
</ul>
<div class="example-message">
<div class="example-no-tasks">
There are no tasks to show.
</div>
</div>

View File

@ -25,21 +25,16 @@ define([
});
function TodoView(domainObject) {
this.domainObject = domainObject;
this.mutableObject = undefined;
this.tasks = domainObject.tasks;
this.filterValue = "all";
this.render = this.render.bind(this);
this.objectChanged = this.objectChanged.bind(this);
}
TodoView.prototype.objectChanged = function (object) {
if (this.mutableObject) {
this.mutableObject.stopListening();
}
this.mutableObject = mct.Objects.getMutable(object);
this.render();
this.setTaskStatus = this.setTaskStatus.bind(this);
this.selectTask = this.selectTask.bind(this);
<<<<<<< HEAD
this.mutableObject = mct.Objects.getMutable(domainObject);
this.mutableObject.on('tasks', this.updateTasks.bind(this));
=======
//If anything on object changes, re-render view
this.mutableObject.on("*", this.objectChanged);
};
@ -48,50 +43,79 @@ define([
var self = this;
this.destroy();
mct.Objects.get(utils.parseKeyString(self.domainObject.getId())).then(function (object) {
self.$els = $(todoTemplate);
self.$buttons = {
all: self.$els.find('.example-todo-button-all'),
incomplete: self.$els.find('.example-todo-button-incomplete'),
complete: self.$els.find('.example-todo-button-complete')
};
self.$els = $(todoTemplate);
self.$buttons = {
all: self.$els.find('.example-todo-button-all'),
incomplete: self.$els.find('.example-todo-button-incomplete'),
complete: self.$els.find('.example-todo-button-complete')
};
$(container).empty().append(self.$els);
$(container).empty().append(self.$els);
>>>>>>> origin/api-tutorials
this.$el = $(todoTemplate);
this.$emptyMessage = this.$el.find('.example-no-tasks');
this.$taskList = this.$el.find('.example-todo-task-list');
this.$el.on('click', '[data-filter]', this.updateFilter.bind(this));
this.$el.on('change', 'li', this.setTaskStatus.bind(this));
this.$el.on('click', '.example-task-description', this.selectTask.bind(this));
self.initialize();
self.objectChanged(object);
<<<<<<< HEAD
this.updateSelection = this.updateSelection.bind(this);
mct.selection.on('change', this.updateSelection);
}
mct.selection.on('change', self.render);
});
TodoView.prototype.show = function (container) {
$(container).empty().append(this.$el);
this.render();
=======
self.initialize();
self.objectChanged(this.domainObject);
mct.selection.on('change', self.render);
>>>>>>> origin/api-tutorials
};
TodoView.prototype.destroy = function () {
if (this.mutableObject) {
this.mutableObject.stopListening();
}
mct.selection.off('change', this.render);
this.mutableObject.stopListening();
mct.selection.off('change', this.updateSelection);
};
TodoView.prototype.setFilter = function (value) {
this.filterValue = value;
TodoView.prototype.updateSelection = function (selection) {
if (selection && selection.length) {
this.selection = selection[0].index;
} else {
this.selection = -1;
}
this.render();
};
TodoView.prototype.initialize = function () {
Object.keys(this.$buttons).forEach(function (k) {
this.$buttons[k].on('click', this.setFilter.bind(this, k));
}, this);
TodoView.prototype.updateTasks = function (tasks) {
this.tasks = tasks;
this.render();
};
TodoView.prototype.render = function () {
var $els = this.$els;
var mutableObject = this.mutableObject;
var tasks = mutableObject.get('tasks');
var $message = $els.find('.example-message');
var $list = $els.find('.example-todo-task-list');
var $buttons = this.$buttons;
var filters = {
TodoView.prototype.updateFilter = function (e) {
this.filterValue = $(e.target).data('filter');
this.render();
};
TodoView.prototype.setTaskStatus = function (e) {
var $checkbox = $(e.target);
var taskIndex = $checkbox.data('taskIndex');
var completed = !!$checkbox.prop('checked');
this.tasks[taskIndex].completed = completed;
this.mutableObject.set('tasks[' + taskIndex + '].checked', completed);
};
TodoView.prototype.selectTask = function (e) {
var taskIndex = $(e.target).data('taskIndex');
mct.selection.clear();
mct.selection.select({index: taskIndex});
};
TodoView.prototype.getFilteredTasks = function () {
return this.tasks.filter({
all: function () {
return true;
},
@ -101,94 +125,119 @@ define([
complete: function (task) {
return task.completed;
}
};
var filterValue = this.filterValue;
var selected = mct.selection.selected();
}[this.filterValue]);
};
Object.keys($buttons).forEach(function (k) {
$buttons[k].toggleClass('selected', filterValue === k);
});
tasks = tasks.filter(filters[filterValue]);
TodoView.prototype.render = function () {
var filteredTasks = this.getFilteredTasks();
this.$emptyMessage.toggle(filteredTasks.length === 0);
this.$taskList.empty();
filteredTasks
.forEach(function (task) {
var $taskEl = $(taskTemplate),
taskIndex = this.tasks.indexOf(task);
$taskEl.find('.example-task-checked')
.prop('checked', task.completed)
.attr('data-task-index', taskIndex);
$taskEl.find('.example-task-description')
.text(task.description)
.toggleClass('selected', taskIndex === this.selection)
.attr('data-task-index', taskIndex);
$list.empty();
tasks.forEach(function (task, index) {
var $taskEls = $(taskTemplate);
var $checkbox = $taskEls.find('.example-task-checked');
var $desc = $taskEls.find('.example-task-description');
$checkbox.prop('checked', task.completed);
$desc.text(task.description);
$checkbox.on('change', function () {
var checked = !!$checkbox.prop('checked');
mutableObject.set("tasks." + index + ".completed", checked);
});
$desc.on('click', function () {
mct.selection.clear();
mct.selection.select({ index: index });
});
if (selected.length > 0 && selected[0].index === index) {
$desc.addClass('selected');
}
$list.append($taskEls);
});
$message.toggle(tasks.length < 1);
this.$taskList.append($taskEl);
}, this);
};
function TodoToolbarView(domainObject) {
this.domainObject = domainObject;
this.mutableObject = undefined;
this.tasks = domainObject.tasks;
this.mutableObject = mct.Objects.getMutable(domainObject);
this.handleSelectionChange = this.handleSelectionChange.bind(this);
this.mutableObject.on('tasks', this.updateTasks.bind(this));
mct.selection.on('change', this.handleSelectionChange);
this.$el = $(toolbarTemplate);
this.$remove = this.$el.find('.example-remove');
this.$el.on('click', '.example-add', this.addTask.bind(this));
this.$el.on('click', '.example-remove', this.removeTask.bind(this));
}
TodoToolbarView.prototype.updateTasks = function (tasks) {
this.tasks = tasks;
};
TodoToolbarView.prototype.addTask = function () {
var $dialog = $(dialogTemplate),
view = {
show: function (container) {
$(container).append($dialog);
},
destroy: function () {}
};
mct.dialog(view, "Add a Task").then(function () {
var description = $dialog.find('input').val();
this.tasks.push({description: description});
this.mutableObject.set('tasks', this.tasks);
}.bind(this));
};
TodoToolbarView.prototype.removeTask = function () {
if (this.selection >= 0) {
this.tasks.splice(this.selection, 1);
this.mutableObject.set('tasks', this.tasks);
mct.selection.clear();
this.render();
}
};
TodoToolbarView.prototype.show = function (container) {
var self = this;
this.destroy();
this.$els = $(toolbarTemplate);
this.render();
$(container).append(this.$els);
};
mct.Objects.get(utils.parseKeyString(this.domainObject.getId())).then(function (wrappedObject){
TodoToolbarView.prototype.render = function () {
var self = this;
var $els = this.$els;
self.mutableObject = mct.Objects.getMutable(wrappedObject);
self.mutableObject = mct.Objects.getMutable(this.domainObject);
var $els = $(toolbarTemplate);
var $add = $els.find('a.example-add');
var $remove = $els.find('a.example-remove');
var $add = $els.find('a.example-add');
var $remove = $els.find('a.example-remove');
$(container).append($els);
$add.on('click', function () {
var $dialog = $(dialogTemplate),
view = {
show: function (container) {
$(container).append($dialog);
},
destroy: function () {}
};
$add.on('click', function () {
var $dialog = $(dialogTemplate),
view = {
show: function (container) {
$(container).append($dialog);
},
destroy: function () {}
};
mct.dialog(view, "Add a Task").then(function () {
var description = $dialog.find('input').val();
var tasks = self.mutableObject.get('tasks');
tasks.push({ description: description });
self.mutableObject.set('tasks', tasks);
});
new mct.Dialog(view, "Add a Task").show().then(function () {
var description = $dialog.find('input').val();
var tasks = self.mutableObject.get('tasks');
tasks.push({ description: description });
self.mutableObject.set('tasks', tasks);
});
$remove.on('click', function () {
var index = mct.selection.selected()[0].index;
if (index !== undefined) {
var tasks = self.mutableObject.get('tasks').filter(function (t, i) {
return i !== index;
});
self.mutableObject.set("tasks", tasks);
self.mutableObject.set("selected", undefined);
mct.selection.clear();
}
});
self.$remove = $remove;
self.handleSelectionChange();
mct.selection.on('change', self.handleSelectionChange);
});
$remove.on('click', function () {
var index = mct.selection.selected()[0].index;
if (index !== undefined) {
var tasks = self.mutableObject.get('tasks').filter(function (t, i) {
return i !== index;
});
self.mutableObject.set("tasks", tasks);
self.mutableObject.set("selected", undefined);
mct.selection.clear();
}
});
self.$remove = $remove;
self.handleSelectionChange();
mct.selection.on('change', self.handleSelectionChange);
};
TodoToolbarView.prototype.handleSelectionChange = function () {
@ -196,14 +245,12 @@ define([
if (this.$remove) {
this.$remove.toggle(selected.length > 0);
}
this.render();
};
TodoToolbarView.prototype.destroy = function () {
mct.selection.off('change', this.handleSelectionChange);
this.$remove = undefined;
if (this.mutableObject) {
this.mutableObject.stopListening();
}
this.mutableObject.stopListening();
};
mct.type('example.todo', todoType);