diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.models.js b/extensions/mistral/static/mistral/js/mistral.workbook.models.js index 87df39d..e9111c8 100644 --- a/extensions/mistral/static/mistral/js/mistral.workbook.models.js +++ b/extensions/mistral/static/mistral/js/mistral.workbook.models.js @@ -148,7 +148,7 @@ '@meta': { 'index': 1, 'row': 0, - autocompletionUrl: '/project/mistral/actions/types' + 'autocompletionUrl': '/project/mistral/actions/types' } }) }, @@ -380,20 +380,94 @@ models.ActionTaskMixin = Barricade.Blueprint.create(function() { return this.extend({}, { 'action': { - '@class': fields.string.extend({}, { + '@class': fields.string.extend({ + create: function(json, parameters) { + var self = fields.string.create.call(this, json, parameters), + actionsCls = Barricade.create({ + '@type': String, + '@ref': { + to: function() { + return models.Actions; + }, + needs: function() { + return models.Workbook; + }, + getter: function(data) { + return data.needed.get('actions'); + } + } + }); + + self._actions = actionsCls.create().on( + 'replace', function(newValue) { + self._actions = newValue; + self._actions.on('change', function() { + self._choices = self._actions.getIDs(); + self.resetValues(); + }); + self._actions.emit('change'); + }); + return self; + }, + _choices: null + }, { + '@enum': function() { + if (!this._choices) { + this._choices = []; + this.emit('_resolveUp', this._actions); + } + return this._choices; + }, '@meta': { 'row': 0, 'index': 1 } }) } - }); + }) }); models.WorkflowTaskMixin = Barricade.Blueprint.create(function() { return this.extend({}, { 'workflow': { - '@class': fields.string.extend({}, { + '@class': fields.string.extend({ + create: function(json, parameters) { + var self = fields.string.create.call(this, json, parameters), + workflowsCls = Barricade.create({ + '@type': String, + '@ref': { + to: function() { + return models.Workflows; + }, + needs: function() { + return models.Workbook; + }, + getter: function(data) { + return data.needed.get('workflows'); + } + } + }); + + self._workflows = workflowsCls.create().on( + 'replace', function(newValue) { + self._workflows = newValue; + self._workflows.on('change', function() { + self._choices = self._workflows.getIDs(); + self.resetValues(); + }); + self._workflows.emit('change'); + }); + return self; + }, + _choices: null + }, { + '@enum': function() { + if ( !this._choices ) { + this._choices = []; + this.emit('_resolveUp', this._workflows); + } + return this._choices; + }, '@meta': { 'row': 0, 'index': 1 @@ -542,6 +616,44 @@ return workflowTypes[type].create(json, parameters); } + models.Actions = fields.dictionary.extend({}, { + '@required': false, + '@meta': { + 'index': 3, + 'panelIndex': 1 + }, + '?': { + '@class': models.Action + } + }); + + models.Workflows = fields.dictionary.extend({ + create: function(json, parameters) { + var self = fields.dictionary.create.call(this, json, parameters); + self.on('childChange', function(child, op) { + if ( op === 'workflowType' ) { + var workflowId = child.getID(), + workflowPos = self.getPosByID(workflowId), + params = child._parameters, + workflowData = child.toJSON(); + params.wfType = child.type; + params.id = workflowId; + self.set(workflowPos, workflowFactory(workflowData, params)); + } + }); + return self; + } + }, { + '@meta': { + 'index': 4, + 'panelIndex': 2 + }, + '?': { + '@class': models.Workflow, + '@factory': workflowFactory + } + }); + models.Workbook = fields.frozendict.extend({ toYAML: function() { return jsyaml.dump(this.toJSON({pretty: true})); @@ -583,44 +695,10 @@ }) }, 'actions': { - '@class': fields.dictionary.extend({}, { - '@required': false, - '@meta': { - 'index': 3, - 'panelIndex': 1 - }, - '?': { - '@class': models.Action - } - }) + '@class': models.Actions }, 'workflows': { - '@class': fields.dictionary.extend({ - create: function(json, parameters) { - var self = fields.dictionary.create.call(this, json, parameters); - self.on('childChange', function(child, op) { - if ( op === 'workflowType' ) { - var workflowId = child.getID(), - workflowPos = self.getPosByID(workflowId), - params = child._parameters, - workflowData = child.toJSON(); - params.wfType = child.type; - params.id = workflowId; - self.set(workflowPos, workflowFactory(workflowData, params)); - } - }); - return self; - } - }, { - '@meta': { - 'index': 4, - 'panelIndex': 2 - }, - '?': { - '@class': models.Workflow, - '@factory': workflowFactory - } - }) + '@class': models.Workflows } }); diff --git a/merlin/static/merlin/js/custom-libs/barricade.js b/merlin/static/merlin/js/custom-libs/barricade.js index 23e68f7..7703423 100644 --- a/merlin/static/merlin/js/custom-libs/barricade.js +++ b/merlin/static/merlin/js/custom-libs/barricade.js @@ -261,43 +261,78 @@ var Barricade = (function () { * @mixin * @memberof Barricade */ - Deferrable = Blueprint.create(function (schema) { - var self = this, - deferred; + Deferrable = Blueprint.create(function () { + var existingCreate = this.create; - function resolver(neededValue) { - var ref = schema['@ref'].resolver(self, neededValue); - if (ref === undefined) { - logError('Could not resolve ', JSON.stringify(self.toJSON())); - } - return ref; - } + this.create = function() { + var self = existingCreate.apply(this, arguments), + schema = self._schema, + needed, + deferred = schema.hasOwnProperty('@ref') + ? Deferred.create(schema['@ref'].needs, getter, resolver) + : null; - if (schema.hasOwnProperty('@ref')) { - deferred = Deferred.create(schema['@ref'].needs, resolver); - } - - this.resolveWith = function (obj) { - var allResolved = true; - - if (deferred && !deferred.isResolved()) { - if (deferred.needs(obj)) { - this.emit('replace', deferred.resolve(obj)); - } else { - allResolved = false; - } + if (schema.hasOwnProperty('@ref') && !schema['@ref'].processor) { + schema['@ref'].processor = function (o) { return o.val; }; } - if (this.instanceof(Container)) { - this.each(function (index, value) { - if (!value.resolveWith(obj)) { + function getter(neededVal) { + return schema['@ref'].getter( + {standIn: self, needed: neededVal}); + } + + function resolver(retrievedValue) { + self.emit('replace', schema['@ref'].processor({ + val: retrievedValue, + standIn: self, + needed: needed + })); + } + + self.resolveWith = function (obj) { + var allResolved = true; + + if (deferred && !deferred.isResolved()) { + if (deferred.needs(obj)) { + needed = obj; + deferred.resolve(obj); + } else { allResolved = false; } - }); - } + } - return allResolved; + if (this.instanceof(Container)) { + this.each(function (index, value) { + if (!value.resolveWith(obj)) { + allResolved = false; + } + }); + } + + return allResolved; + }; + + self.isPlaceholder = function () { + return !!deferred; + }; + + return self; }; + + this.isValidRef = function(instance) { + var clsRef = this._schema['@ref']; + if (!clsRef) { + return false; + } + if (typeof clsRef.to === 'function') { + return this._safeInstanceof(instance, clsRef.to()); + } else if (typeof clsRef.to === 'object') { + return this._safeInstanceof(instance, clsRef.to); + } + throw new Error('Ref.to was ' + clsRef.to); + }; + + return this; }); /** @@ -407,7 +442,7 @@ var Barricade = (function () { this.emit = function (eventName) { var args = arguments; // Must come from correct scope if (events.hasOwnProperty(eventName)) { - events[eventName].forEach(function (callback) { + events[eventName].slice().forEach(function (callback) { // Call with emitter as context and pass all but eventName callback.apply(this, Array.prototype.slice.call(args, 1)); }, this); @@ -469,10 +504,11 @@ var Barricade = (function () { Callback to execute when resolve happens. * @returns {Barricade.Deferred} */ - create: function (classGetter, onResolve) { + create: function (classGetter, getter, onResolve) { var self = Object.create(this); self._isResolved = false; self._classGetter = classGetter; + self._getter = getter; self._onResolve = onResolve; return self; }, @@ -502,17 +538,25 @@ var Barricade = (function () { * @param obj */ resolve: function (obj) { - var ref; + var self = this, + neededValue; + + function doResolve(realNeededValue) { + neededValue.off('replace', doResolve); + self._onResolve(realNeededValue); + self._isResolved = true; + } if (this._isResolved) { throw new Error('Deferred already resolved'); } - ref = this._onResolve(obj); + neededValue = this._getter(obj); - if (ref !== undefined) { - this._isResolved = true; - return ref; + if (neededValue.isPlaceholder()) { + neededValue.on('replace', doResolve); + } else { + doResolve(neededValue); } } }; @@ -537,7 +581,7 @@ var Barricade = (function () { * @mixes Barricade.Identifiable * @extends Barricade.Identifiable */ - Base = Extendable.call(InstanceofMixin.call({ + Base = Deferrable.call(Extendable.call(InstanceofMixin.call({ /** * Creates a `Base` instance * @memberof Barricade.Base @@ -564,7 +608,6 @@ var Barricade = (function () { Observable.call(self); Omittable.call(self, isUsed); - Deferrable.call(self, schema); Validatable.call(self, schema); if (schema.hasOwnProperty('@enum')) { @@ -625,7 +668,7 @@ var Barricade = (function () { * @private */ _safeInstanceof: function (instance, class_) { - return typeof instance === 'object' && + return getType(instance) === Object && ('instanceof' in instance) && instance.instanceof(class_); }, @@ -695,7 +738,7 @@ var Barricade = (function () { ? this._getPrettyJSON(options) : this._getJSON(options); } - })); + }))); /** * @class @@ -714,9 +757,15 @@ var Barricade = (function () { var self = Base.create.call(this, json, parameters); return self.on('_addedElement', function (key) { + // every time a new element is added we need to set the + // listeners for it and try to resolve any references, + // passing the resolution upward if needed self._attachListeners(key); self._tryResolveOn(self.get(key)); }).each(function (index, value) { + // also we bind the handlers and do reference resolution + // for all the children that are already here (no upward + // resolution here) self._attachListeners(index); value.resolveWith(self); }); @@ -776,21 +825,8 @@ var Barricade = (function () { * @private */ _isCorrectType: function (instance, class_) { - var self = this; - - function isRefTo() { - if (typeof class_._schema['@ref'].to === 'function') { - return self._safeInstanceof(instance, - class_._schema['@ref'].to()); - } else if (typeof class_._schema['@ref'].to === 'object') { - return self._safeInstanceof(instance, - class_._schema['@ref'].to); - } - throw new Error('Ref.to was ' + class_._schema['@ref'].to); - } - return this._safeInstanceof(instance, class_) || - (class_._schema.hasOwnProperty('@ref') && isRefTo()); + class_.isValidRef(instance); }, /** diff --git a/merlin/static/merlin/js/merlin.field.models.js b/merlin/static/merlin/js/merlin.field.models.js index dbc393b..c492d75 100644 --- a/merlin/static/merlin/js/merlin.field.models.js +++ b/merlin/static/merlin/js/merlin.field.models.js @@ -11,22 +11,37 @@ }); var restrictedChoicesMixin = Barricade.Blueprint.create(function() { - var values = this.getEnumValues(), - labels = this.getEnumLabels(), + var self = this, + values, labels, items; + + function fillItems() { + values = self.getEnumValues(); + labels = self.getEnumLabels(); items = {}; - values.forEach(function(value, index) { - items[value] = labels[index]; - }); + values && values.forEach(function (value, index) { + items[value] = labels[index]; + }); + } this.getLabel = function(value) { + if ( values === undefined ) { + fillItems(); + } return items[value]; }; this.getValues = function() { + if ( values === undefined ) { + fillItems(); + } return values; }; + this.resetValues = function() { + values = undefined; + }; + this.setType('choices'); return this; }); @@ -100,16 +115,15 @@ }, {'@type': String}); var autoCompletionMixin = Barricade.Blueprint.create(function(url) { - var suggestions = []; + var self = this; + this.getSuggestions = function() { return []; }; $http.get(url).success(function(data) { - suggestions = data; + self.getSuggestions = function() { + return data; + }; }); - this.getSuggestions = function() { - return suggestions; - }; - return this; }); @@ -251,8 +265,9 @@ dictionary: dictionaryModel, frozendict: frozendictModel, directeddictionary: directedDictionaryModel, + autocompletionmixin: autoCompletionMixin, wildcard: wildcardMixin // use for most general type-checks }; }]) -})(); \ No newline at end of file +})();