From fc14e3c993619461c756e4104d7bf747475eb02d Mon Sep 17 00:00:00 2001 From: Timur Sufiev Date: Thu, 29 Jan 2015 21:08:47 +0300 Subject: [PATCH] Take data for Angular.js from Barricade.js object Along with this perform major code cleanup and change directory structure. Change-Id: I1736ded46ab5b9b635acce7e938a803001884393 --- .../angular-templates/collapsible-panel.html | 17 - .../angular-templates/fields/dictionary.html | 15 - .../angular-templates/fields/frozendict.html | 10 - .../js/angular-templates/fields/group.html | 11 - .../js/angular-templates/fields/string.html | 4 - .../js/angular-templates/fields/text.html | 4 - .../static/mistral/js/mistral.directives.js | 36 + .../mistral/static/mistral/js/mistral.init.js | 17 + .../js/mistral.workbook.controllers.js | 47 + .../mistral/js/mistral.workbook.models.js | 440 ++++++ .../mistral/js/{ => obsolete}/controllers.js | 22 +- .../static/mistral/js/obsolete/schema-alt.js | 213 +++ .../mistral/js/{ => obsolete}/schema.js | 19 +- .../mistral/js/{ => obsolete}/services.js | 44 +- .../mistral/js/{ => obsolete}/workbook.js | 0 .../fields/varlist.html | 45 +- .../fields/yaqllist.html | 0 .../yaql-field-combined.html | 0 .../mistral/templates/mistral/create.html | 61 +- .../merlin/{ => js}/lib/angular-filter.js | 10 +- .../merlin/{ => js}/lib/angular-filter.min.js | 0 merlin/static/merlin/js/lib/barricade.js | 1354 +++++++++++++++++ merlin/static/merlin/{ => js}/lib/js-yaml.js | 0 .../static/merlin/js/merlin.directives.js | 55 +- .../static/merlin/js/merlin.field.models.js | 174 +++ merlin/static/merlin/js/merlin.init.js | 19 + .../static/merlin/js/merlin.panel.models.js | 152 ++ merlin/static/merlin/js/merlin.utils.js | 83 + merlin/static/merlin/lib/barricade.js | 820 ---------- .../merlin/templates}/collapsible-group.html | 0 .../merlin/templates/collapsible-panel.html | 11 + .../merlin/templates}/editable-popup.html | 0 .../merlin/templates/fields/dictionary.html | 19 + .../merlin/templates/fields/frozendict.html | 15 + .../static/merlin/templates/fields/group.html | 12 + .../static/merlin/templates}/fields/list.html | 8 +- .../merlin/templates/fields/number.html | 5 + .../merlin/templates/fields/string.html | 5 + .../static/merlin/templates/fields/text.html | 5 + 39 files changed, 2748 insertions(+), 1004 deletions(-) delete mode 100644 extensions/mistral/static/mistral/js/angular-templates/collapsible-panel.html delete mode 100644 extensions/mistral/static/mistral/js/angular-templates/fields/dictionary.html delete mode 100644 extensions/mistral/static/mistral/js/angular-templates/fields/frozendict.html delete mode 100644 extensions/mistral/static/mistral/js/angular-templates/fields/group.html delete mode 100644 extensions/mistral/static/mistral/js/angular-templates/fields/string.html delete mode 100644 extensions/mistral/static/mistral/js/angular-templates/fields/text.html create mode 100644 extensions/mistral/static/mistral/js/mistral.directives.js create mode 100644 extensions/mistral/static/mistral/js/mistral.init.js create mode 100644 extensions/mistral/static/mistral/js/mistral.workbook.controllers.js create mode 100644 extensions/mistral/static/mistral/js/mistral.workbook.models.js rename extensions/mistral/static/mistral/js/{ => obsolete}/controllers.js (95%) create mode 100644 extensions/mistral/static/mistral/js/obsolete/schema-alt.js rename extensions/mistral/static/mistral/js/{ => obsolete}/schema.js (96%) rename extensions/mistral/static/mistral/js/{ => obsolete}/services.js (63%) rename extensions/mistral/static/mistral/js/{ => obsolete}/workbook.js (100%) rename extensions/mistral/static/mistral/{js/angular-templates => templates}/fields/varlist.html (61%) rename extensions/mistral/static/mistral/{js/angular-templates => templates}/fields/yaqllist.html (100%) rename extensions/mistral/static/mistral/{js/angular-templates => templates}/yaql-field-combined.html (100%) rename merlin/static/merlin/{ => js}/lib/angular-filter.js (99%) rename merlin/static/merlin/{ => js}/lib/angular-filter.min.js (100%) create mode 100644 merlin/static/merlin/js/lib/barricade.js rename merlin/static/merlin/{ => js}/lib/js-yaml.js (100%) rename extensions/mistral/static/mistral/js/directives.js => merlin/static/merlin/js/merlin.directives.js (53%) create mode 100644 merlin/static/merlin/js/merlin.field.models.js create mode 100644 merlin/static/merlin/js/merlin.init.js create mode 100644 merlin/static/merlin/js/merlin.panel.models.js create mode 100644 merlin/static/merlin/js/merlin.utils.js delete mode 100644 merlin/static/merlin/lib/barricade.js rename {extensions/mistral/static/mistral/js/angular-templates => merlin/static/merlin/templates}/collapsible-group.html (100%) create mode 100644 merlin/static/merlin/templates/collapsible-panel.html rename {extensions/mistral/static/mistral/js/angular-templates => merlin/static/merlin/templates}/editable-popup.html (100%) create mode 100644 merlin/static/merlin/templates/fields/dictionary.html create mode 100644 merlin/static/merlin/templates/fields/frozendict.html create mode 100644 merlin/static/merlin/templates/fields/group.html rename {extensions/mistral/static/mistral/js/angular-templates => merlin/static/merlin/templates}/fields/list.html (55%) create mode 100644 merlin/static/merlin/templates/fields/number.html create mode 100644 merlin/static/merlin/templates/fields/string.html create mode 100644 merlin/static/merlin/templates/fields/text.html diff --git a/extensions/mistral/static/mistral/js/angular-templates/collapsible-panel.html b/extensions/mistral/static/mistral/js/angular-templates/collapsible-panel.html deleted file mode 100644 index aa60ea9..0000000 --- a/extensions/mistral/static/mistral/js/angular-templates/collapsible-panel.html +++ /dev/null @@ -1,17 +0,0 @@ -
-
-
-

- {$ title $} -

-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/dictionary.html b/extensions/mistral/static/mistral/js/angular-templates/fields/dictionary.html deleted file mode 100644 index ce4e7dc..0000000 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/dictionary.html +++ /dev/null @@ -1,15 +0,0 @@ - -
-
-
- -
- - -
-
-
-
-
diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/frozendict.html b/extensions/mistral/static/mistral/js/angular-templates/fields/frozendict.html deleted file mode 100644 index ba71eab..0000000 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/frozendict.html +++ /dev/null @@ -1,10 +0,0 @@ - -
-
-
- - -
-
-
-
diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/group.html b/extensions/mistral/static/mistral/js/angular-templates/fields/group.html deleted file mode 100644 index 45dc91e..0000000 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/group.html +++ /dev/null @@ -1,11 +0,0 @@ - -
-
-
- -
-
-
-
-
\ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/string.html b/extensions/mistral/static/mistral/js/angular-templates/fields/string.html deleted file mode 100644 index 8fc6ec8..0000000 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/string.html +++ /dev/null @@ -1,4 +0,0 @@ -
- - -
diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/text.html b/extensions/mistral/static/mistral/js/angular-templates/fields/text.html deleted file mode 100644 index e6f6d4f..0000000 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/text.html +++ /dev/null @@ -1,4 +0,0 @@ -
- - -
diff --git a/extensions/mistral/static/mistral/js/mistral.directives.js b/extensions/mistral/static/mistral/js/mistral.directives.js new file mode 100644 index 0000000..432ca10 --- /dev/null +++ b/extensions/mistral/static/mistral/js/mistral.directives.js @@ -0,0 +1,36 @@ +/** + * Created by tsufiev on 12/29/14. + */ + +(function() { + angular.module('hz') + + .directive('yaqlFieldCombined', function() { + return { + restrict: 'E', + templateUrl: '/static/mistral/templates/yaql-field-combined.html', + scope: { + yaqlExpression: '@', + value: '@' + }, + link: function(scope, element) { + angular.element(element).find('span.yaql-condition') + .on('click', function() { + var $elt = $(this), + $inputColumn = $elt.closest('.three-columns').children(':first-child'), + $input; + + $elt.hide(); + $input = $inputColumn.show().find('textarea'); + $input.focus().on('blur', function() { + $inputColumn.hide(); + $elt.toggleClass('fa-lock', $input.val() !== ''); + $elt.toggleClass('fa-unlock', $input.val() === ''); + $elt.show(); + }); + }); + } + } + }) + +})(); diff --git a/extensions/mistral/static/mistral/js/mistral.init.js b/extensions/mistral/static/mistral/js/mistral.init.js new file mode 100644 index 0000000..0b90c45 --- /dev/null +++ b/extensions/mistral/static/mistral/js/mistral.init.js @@ -0,0 +1,17 @@ +/** + * Created by tsufiev on 2/24/15. + */ +(function() { + angular.module('hz') + + .run(function($http, $templateCache) { + var fields = ['varlist', 'yaqllist']; + fields.forEach(function(field) { + var base = '/static/mistral/templates/fields/'; + $http.get(base + field + '.html').success(function(templateContent) { + $templateCache.put(field, templateContent); + }); + }) + }) + +})(); \ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js b/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js new file mode 100644 index 0000000..f26387e --- /dev/null +++ b/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js @@ -0,0 +1,47 @@ +/** + * Created by tsufiev on 2/24/15. + */ +(function() { + angular.module('hz') + + .controller('workbookCtrl', + ['$scope', 'mistral.workbook.models', function($scope, models) { + var workbook = models.Workbook.create(); + $scope.workbook = workbook; + + function getNextIDSuffix(container, regexp) { + var max = Math.max.apply(Math, container.getIDs().map(function(id) { + var match = regexp.exec(id); + return match && +match[2]; + })); + return max > 0 ? max + 1 : 1; + } + + function getWorkbookNextIDSuffix(base) { + var containerName = base + 's', + regexp = /(workflow|action)([0-9]+)/, + container = workbook.get(containerName); + if ( !container ) { + throw 'Base should be either "action" or "workflow"!'; + } + return getNextIDSuffix(container, regexp); + } + + var baseActionId = 'action', baseWorkflowId = 'workflow'; + + $scope.addAction = function() { + var nextSuffix = getWorkbookNextIDSuffix(baseActionId), + newID = baseActionId + nextSuffix; + workbook.get('actions').push({name: 'Action ' + nextSuffix}, {id: newID}); + workbook.addPanel(workbook.get('actions'), newID, workbook.get('actions').length()); + }; + + $scope.addWorkflow = function() { + var nextSuffix = getWorkbookNextIDSuffix(baseWorkflowId), + newID = baseWorkflowId + nextSuffix; + workbook.get('workflows').push({name: 'Workflow ' + nextSuffix}, {id: newID}); + workbook.addPanel(workbook.get('workflows'), newID); + }; + + }]) +})(); \ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.models.js b/extensions/mistral/static/mistral/js/mistral.workbook.models.js new file mode 100644 index 0000000..dad238d --- /dev/null +++ b/extensions/mistral/static/mistral/js/mistral.workbook.models.js @@ -0,0 +1,440 @@ +/** + * Created by tsufiev on 2/24/15. + */ +(function(){ + angular.module('hz') + + .factory('mistral.workbook.models', + ['merlin.field.models', 'merlin.panel.models', function(fields, panel) { + var models = {}; + + var varlistValueFactory = function(json, parameters) { + var type = Barricade.getType(json); + if ( json === undefined || type === String ) { + return fields.string.create(json, parameters); + } else if ( type === Array ) { + return fields.list.extend({}, { + '*': {'@class': fields.string} + }).create(json, parameters); + } else if ( type === Object ) { + return fields.dictionary.extend({}, { + '?': {'@class': fields.string} + }).create(json, parameters); + } + }; + + models.varlist = fields.list.extend({ + create: function(json, parameters) { + var self = fields.list.create.call(this, json, parameters); + self.setType('varlist'); + self.on('childChange', function(child, op) { + if ( op == 'empty' ) { + self.each(function(index, item) { + if ( child === item ) { + self.remove(index); + } + }) + } + }); + return self; + } + }, { + '*': { + '@class': fields.frozendict.extend({ + create: function(json, parameters) { + var self = fields.frozendict.create.call(this, json, parameters); + self.on('childChange', function(child) { + if ( child.instanceof(Barricade.Enumerated) ) { // type change + var value = self.get('value'); + switch ( child.get() ) { + case 'string': + self.set('value', varlistValueFactory('')); + break; + case 'list': + self.set('value', varlistValueFactory([''])); + break; + case 'dictionary': + self.set('value', varlistValueFactory({'key1': ''})); + break; + } + } else if ( child.instanceof(Barricade.Arraylike) && !child.length() ) { + self.emit('change', 'empty'); + } + }); + return self; + } + }, { + 'type': { + '@class': fields.string.extend({}, { + '@enum': ['string', 'list', 'dictionary'], + '@default': 'string' + }) + }, + 'value': { + '@class': fields.wildcard, + '@factory': varlistValueFactory + } + }) + } + }); + + models.Action = fields.frozendict.extend({}, { + 'name': { + '@class': fields.string.extend({}, { + '@meta': { + 'index': 0, + 'row': 0 + } + }) + }, + 'base': { + '@class': fields.string.extend({}, { + '@meta': { + 'index': 1, + 'row': 0 + } + }) + }, + 'baseInput': { + '@class': fields.frozendict.extend({}, { + '@required': false, + '@meta': { + 'index': 2, + 'title': 'Base Input' + }, + '?': {'@class': fields.string} + }) + }, + 'input': { + '@class': fields.list.extend({}, { + '@meta': { + 'index': 3 + }, + '*': {'@class': fields.string} + }) + }, + 'output': { + '@class': models.varlist.extend({}, { + '@meta': { + 'index': 4 + } + }) + } + }); + + models.Task = fields.frozendict.extend({}, { + '@meta': { + 'baseKey': 'task', + 'baseName': 'Task ', + 'group': true, + 'additive': false + }, + 'name': { + '@class': fields.string.extend({}, { + '@meta': { + 'index': 0, + 'row': 0 + } + }) + }, + 'type': { + '@class': fields.string.extend({}, { + '@enum': ['Action-based', 'Workflow-based'], + '@meta': { + 'index': 1, + 'row': 0 + } + }) + }, + 'action': { + '@class': fields.string.extend({}, { + '@meta': { + 'index': 2, + 'row': 1 + } + }) + }, + 'input': { + '@class': fields.dictionary.extend({}, { + '@meta': { + 'index': 3 + }, + '?': { + '@class': fields.string + } + }) + }, + 'publish': { + '@class': fields.dictionary.extend({}, { + '@meta': { + 'index': 4 + }, + '?': { + '@class': fields.string + } + }) + }, + 'onError': { + '@class': fields.list.extend({}, { + '@meta': { + 'title': 'On error', + 'index': 5 + }, + '*': { + '@class': fields.string + } + }) + }, + 'onSuccess': { + '@class': fields.list.extend({}, { + '@meta': { + 'title': 'On success', + 'index': 6 + }, + '*': { + '@class': fields.string + } + }) + }, + 'onComplete': { + '@class': fields.list.extend({}, { + '@meta': { + 'title': 'On complete', + 'index': 7 + }, + '*': { + '@class': fields.string + } + }) + }, + 'policies': { + '@class': fields.frozendict.extend({}, { + '@meta': { + 'index': 8 + }, + '@required': false, + 'waitBefore': { + '@class': fields.number.extend({}, { + '@required': false, + '@meta': { + 'index': 0, + 'row': 0, + 'title': 'Wait before' + } + }) + }, + 'waitAfter': { + '@class': fields.number.extend({}, { + '@required': false, + '@meta': { + 'index': 1, + 'row': 0, + 'title': 'Wait after' + } + }) + }, + 'timeout': { + '@class': fields.number.extend({}, { + '@required': false, + '@meta': { + 'index': 2, + 'row': 1 + } + }) + }, + 'retryCount': { + '@class': fields.number.extend({}, { + '@required': false, + '@meta': { + 'index': 3, + 'row': 2, + 'title': 'Retry count' + } + }) + }, + 'retryDelay': { + '@class': fields.number.extend({}, { + '@required': false, + '@meta': { + 'index': 4, + 'row': 2, + 'title': 'Retry delay' + } + }) + }, + 'retryBreakOn': { + '@class': fields.number.extend({}, { + '@required': false, + '@meta': { + 'index': 5, + 'row': 3, + 'title': 'Retry break on' + } + }) + } + }) + } + }); + + models.Workflow = fields.frozendict.extend({}, { + 'name': { + '@class': fields.string.extend({}, { + '@meta': { + 'index': 0, + 'row': 0 + } + }) + }, + 'type': { + '@class': fields.string.extend({}, { + '@enum': ['reverse', 'direct'], + '@default': 'direct', + '@meta': { + 'index': 1, + 'row': 0 + } + }) + }, + 'input': { + '@class': fields.list.extend({}, { + '@required': false, + '@meta': { + 'index': 2 + }, + '*': { + '@class': fields.string + } + }) + }, + 'output': { + '@class': fields.list.extend({}, { + '@required': false, + '@meta': { + 'index': 3 + }, + '*': { + '@class': fields.string + } + }) + }, + 'taskDefaults': { + '@class': fields.frozendict.extend({}, { + '@required': false, + '@meta': { + 'index': 4, + 'group': true, + 'additive': false + }, + 'onError': { + '@class': fields.list.extend({}, { + '@meta': { + 'title': 'On error', + 'index': 0 + }, + '*': { + '@class': fields.string + } + }) + }, + 'onSuccess': { + '@class': fields.list.extend({}, { + '@meta': { + 'title': 'On success', + 'index': 1 + }, + '*': { + '@class': fields.string + } + }) + }, + 'onComplete': { + '@class': fields.list.extend({}, { + '@meta': { + 'title': 'On complete', + 'index': 2 + }, + '*': { + '@class': fields.string + } + }) + } + }) + }, + 'tasks': { + '@class': fields.dictionary.extend({}, { + '@meta': { + 'index': 5, + 'group': true + }, + '?': { + '@class': models.Task + } + }) + } + + }); + + models.Workbook = fields.frozendict.extend({ + create: function(json, parameters) { + var self = fields.frozendict.create.call(this, json, parameters); + return panel.panelmixin.call(self); + } + }, { + 'version': { + '@class': fields.number.extend({}, { + '@enum': [2], + '@meta': { + 'index': 2, + 'panelIndex': 0, + 'row': 1 + }, + '@default': 2 + }) + }, + 'name': { + '@class': fields.string.extend({}, { + '@meta': { + 'index': 0, + 'panelIndex': 0, + 'row': 0 + } + }) + }, + 'description': { + '@class': fields.text.extend({}, { + '@meta': { + 'index': 1, + 'panelIndex': 0, + 'row': 0 + }, + '@required': false + }) + }, + 'actions': { + '@class': fields.dictionary.extend({}, { + '@required': false, + '@meta': { + 'index': 3, + 'panelIndex': 1 + }, + '?': { + '@class': models.Action + } + }) + }, + 'workflows': { + '@class': fields.dictionary.extend({}, { + '@meta': { + 'index': 4, + 'panelIndex': 2 + }, + '?': { + '@class': models.Workflow + } + }) + } + }); + + return models; + }]) +})(); \ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/controllers.js b/extensions/mistral/static/mistral/js/obsolete/controllers.js similarity index 95% rename from extensions/mistral/static/mistral/js/controllers.js rename to extensions/mistral/static/mistral/js/obsolete/controllers.js index 37eb93a..8a6c95b 100644 --- a/extensions/mistral/static/mistral/js/controllers.js +++ b/extensions/mistral/static/mistral/js/obsolete/controllers.js @@ -14,7 +14,9 @@ angular.module('hz') - .controller('workbookCtrl', function($scope) { + .controller('workbookCtrl', function($scope, workbook, $filter) { + $scope.workbook = workbook; + $scope.defaults = { 'actions': { name: 'Action1', @@ -280,6 +282,8 @@ } }; + + $scope.makeTitle = function(str) { if ( !str ) { return ''; @@ -293,7 +297,7 @@ }; $scope.isAtomic = function(type) { - return ['string', 'text'].indexOf(type) > -1; + return ['string', 'text', 'number'].indexOf(type) > -1; }; $scope.remove = function(parent, item) { @@ -367,20 +371,6 @@ } }; - - - //$scope.getBaseInput = function() { - // return baseTypes[actionBase] || []; - //}; - // - //$scope.updateBase = function(newBase) { - // actionBase = newBase; - // var values = []; - // angular.forEach($scope.getBaseInput(), function(item) { - // values.push(item.value || ''); - // }); - // $scope.item.baseInput.value = values; - //} }) .controller('dictionaryCtrl', function($scope) { diff --git a/extensions/mistral/static/mistral/js/obsolete/schema-alt.js b/extensions/mistral/static/mistral/js/obsolete/schema-alt.js new file mode 100644 index 0000000..aff2f63 --- /dev/null +++ b/extensions/mistral/static/mistral/js/obsolete/schema-alt.js @@ -0,0 +1,213 @@ + +/* Copyright (c) 2014 Mirantis, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + */ + +(function() { + angular.module('hz') + + .factory('workbook', function() { + var types = { + Mistral: {}, + base: {}, + OpenStack: { + // TODO: obtain list of predefined OpenStack actions from Mistral server-side + // for now a stubbed list of predefined actions suffices + actions: ['createInstance', 'terminateInstance'] + }, + getOpenStackActions: function() { + return this.OpenStack.actions.slice(); + } + }; + + //types.base.AcceptsMixin = Barricade.Blueprint.create(function (acceptsList) { + // acceptsList = acceptsList || []; + // + // this.getLabels = function() { + // return acceptsList.map(function(item) { + // return item.label; + // }) + // }; + // + // this.getValue = function(label) { + // for ( var i = 0; i < acceptsList.length; i++ ) { + // if ( acceptsList[i].label === label ) { + // return acceptsList[i].value; + // } + // } + // return null; + // } + //}); + +//types.Mistral.Task = Barricade.create({ +// '@class': types.Mistral.dictionary, +// +// 'name': {'@type': String}, +// 'input': { +// '@type': Array, +// '*': { +// '@class': Barricade.Primitive.extend({ +// 'name': 'Parameter' +// }, { +// '@type': String +// }) +// } +// }, +// 'publish': { +// '@type': String, +// '@required': false +// }, +// 'policies': { +// '@class': types.Mistral.Policy, +// '@required': false +// } +//}); +// +//types.Mistral.Tasks = Barricade.MutableObject.extend({ +// create: function(json, parameters) { +// var self = Barricade.MutableObject.create.call(this); +// +// function getParentWorkflowType() { +// var container = self._container, +// workflow; +// while ( container ) { +// if ( container.instanceof(types.Mistral.Workflow) ) { +// workflow = container; +// break; +// } +// container = container._container; +// } +// return workflow && workflow.get('type').get(); +// } +// +// var directSpecificData = { +// 'on-complete': { +// '@type': String, +// '@required': false +// }, +// 'on-success': { +// '@type': String, +// '@required': false +// }, +// 'on-error': { +// '@type': String, +// '@required': false +// } +// }, +// reverseSpecificData = { +// 'requires': { +// '@type': Array, +// '*': { +// '@class': Barricade.Primitive.extend({ +// 'name': 'Action' +// }, { +// '@type': String, +// '@enum': function() { +// var container = this._container, +// workflow, task; +// while ( container ) { +// if ( container.instanceof(types.Mistral.Task) ) { +// task = container; +// } +// if ( container.instanceof(types.Mistral.Workflow) ) { +// workflow = container; +// break; +// } +// container = container._container; +// } +// if ( workflow && task ) { +// return workflow.get('tasks').toArray().filter(function(taskItem) { +// return !(taskItem === task) && taskItem.get('name').get(); +// }).map(function(taskItem) { +// return taskItem.get('name').get(); +// }); +// } else { +// return []; +// } +// } +// }) +// } +// } +// }; +// +// types.base.AcceptsMixin.call(self, [ +// { +// label: 'Action-based', +// value: function() { +// var workflowType = getParentWorkflowType(); +// if ( workflowType === 'direct' ) { +// return types.Mistral.ActionTask.extend({}, directSpecificData); +// } else if ( workflowType === 'reverse' ) { +// return types.Mistral.ActionTask.extend({}, reverseSpecificData); +// } else { +// return types.Mistral.ActionTask; +// } +// } +// }, { +// label: 'Workflow-based', +// value: function() { +// var workflowType = getParentWorkflowType(); +// if ( workflowType === 'direct' ) { +// return types.Mistral.WorkflowTask.extend({}, directSpecificData); +// } else if ( workflowType === 'reverse' ) { +// return types.Mistral.WorkflowTask.extend({}, reverseSpecificData); +// } else { +// return types.Mistral.WorkflowTask; +// } +// } +// } +// ]); +// return self; +// } +//}, { +// '@type': Object, +// '?': {'@class': types.Mistral.Task} +//}); +// +//types.Mistral.WorkflowTask = types.Mistral.Task.extend({}, +// { +// 'workflow': { +// '@type': String, +// '@enum': function() { +// var workflows = workbook.get('workflows').toArray(); +// return workflows.map(function(workflowItem) { +// return workflowItem.get('name').get(); +// }).filter(function (name) { +// return name; +// }); +// } +// } +// }); +// +//types.Mistral.ActionTask = types.Mistral.Task.extend({}, +// { +// 'action': { +// '@type': String, +// '@enum': function() { +// var predefinedActions = types.getOpenStackActions(), +// actions = workbook.get('actions').toArray(); +// return predefinedActions.concat(actions.map(function(actionItem) { +// return actionItem.get('name').get(); +// }).filter(function(name) { +// return name; } +// )); +// } +// } +// }); + + + return types.Mistral.Workbook.create(); + }) +})(); + diff --git a/extensions/mistral/static/mistral/js/schema.js b/extensions/mistral/static/mistral/js/obsolete/schema.js similarity index 96% rename from extensions/mistral/static/mistral/js/schema.js rename to extensions/mistral/static/mistral/js/obsolete/schema.js index bfeb451..27f8718 100644 --- a/extensions/mistral/static/mistral/js/schema.js +++ b/extensions/mistral/static/mistral/js/obsolete/schema.js @@ -301,16 +301,29 @@ types.Mistral.Workbook = Barricade.create({ 'version': { '@type': Number, - '@meta': {'groups': ['panel1']}, + '@enum': function() { return [2]; }, + '@meta': { + 'index': 2, + 'panelIndex': 0, + 'row': 1 + }, '@default': 2 }, 'name': { '@type': String, - '@meta': {'groups': ['panel1']} + '@meta': { + 'index': 0, + 'panelIndex': 0, + 'row': 0 + } }, 'description': { '@type': String, - '@meta': {'groups': ['panel1']}, + '@meta': { + 'index': 1, + 'panelIndex': 0, + 'row': 0 + }, '@required': false }, 'actions': { diff --git a/extensions/mistral/static/mistral/js/services.js b/extensions/mistral/static/mistral/js/obsolete/services.js similarity index 63% rename from extensions/mistral/static/mistral/js/services.js rename to extensions/mistral/static/mistral/js/obsolete/services.js index 684bb5f..eaab9e1 100644 --- a/extensions/mistral/static/mistral/js/services.js +++ b/extensions/mistral/static/mistral/js/obsolete/services.js @@ -17,9 +17,10 @@ .run(function($http, $templateCache) { var fields = ['dictionary', 'frozendict', 'list', 'string', - 'varlist', 'text', 'group', 'yaqllist']; + 'varlist', 'text', 'group', 'yaqllist', 'number' + ]; fields.forEach(function(field) { - var base = '/static/mistral/js/angular-templates/fields/'; + var base = '/static/mistral/js/templates-templates/fields/'; $http.get(base + field + '.html').success(function(templateContent) { $templateCache.put(field, templateContent); }); @@ -62,6 +63,45 @@ } }) + .filter('getPanels', function($filter) { + var orderBy = $filter('orderBy'), + groupBy = $filter('groupBy'), + toArray = $filter('toArray'); + return function(container) { + var seq = groupBy(container, 'getMeta().panelIndex'); + console.log('seq', seq); + var seq1 = toArray(seq, true); + console.log('seq1', seq1); + var seq2 = orderBy(seq1, '$key'); + console.log('seq2', seq2); +// var seq = orderBy( +// toArray(, true), +// '$key'); + return seq2; + } + }) + + + function sign(x) { + if ( x > 0 ) { + return 1; + } else { + return x < 0 ? -1 : 0; + } + } + + function comparePanelIndices(item1, item2) { + var index1 = item1.getMeta().panelIndex, + index2 = item2.getMeta().panelIndex; + + if ( index1 === undefined || index2 === undefined ) { + return -1; + } else { + return sign(index1-index2); + } + + } + })(); \ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/workbook.js b/extensions/mistral/static/mistral/js/obsolete/workbook.js similarity index 100% rename from extensions/mistral/static/mistral/js/workbook.js rename to extensions/mistral/static/mistral/js/obsolete/workbook.js diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/varlist.html b/extensions/mistral/static/mistral/templates/fields/varlist.html similarity index 61% rename from extensions/mistral/static/mistral/js/angular-templates/fields/varlist.html rename to extensions/mistral/static/mistral/templates/fields/varlist.html index 5b3d966..52f9d29 100644 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/varlist.html +++ b/extensions/mistral/static/mistral/templates/fields/varlist.html @@ -1,25 +1,28 @@ - -
+ +
- +
-
+
- + - @@ -28,8 +31,8 @@
-
-
+
+
@@ -37,9 +40,10 @@
- + - @@ -48,7 +52,7 @@
-
@@ -56,16 +60,17 @@
-
-
+
+
- + - @@ -74,7 +79,7 @@
-
diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/yaqllist.html b/extensions/mistral/static/mistral/templates/fields/yaqllist.html similarity index 100% rename from extensions/mistral/static/mistral/js/angular-templates/fields/yaqllist.html rename to extensions/mistral/static/mistral/templates/fields/yaqllist.html diff --git a/extensions/mistral/static/mistral/js/angular-templates/yaql-field-combined.html b/extensions/mistral/static/mistral/templates/yaql-field-combined.html similarity index 100% rename from extensions/mistral/static/mistral/js/angular-templates/yaql-field-combined.html rename to extensions/mistral/static/mistral/templates/yaql-field-combined.html diff --git a/extensions/mistral/templates/mistral/create.html b/extensions/mistral/templates/mistral/create.html index e2197f7..01b1988 100644 --- a/extensions/mistral/templates/mistral/create.html +++ b/extensions/mistral/templates/mistral/create.html @@ -11,9 +11,18 @@ {% block js %} {% include "horizon/_scripts.html" %} - - - + + + + + + + + + + + + {% endblock %} {% block css %} @@ -32,12 +41,12 @@
-

Workbook1

+

{$ workbook.get('name') $}

- - + +
@@ -51,33 +60,8 @@
-
- -
-
-
- -
-
-
-
-
- - -
-
-
- -
-
-
-
-
-
-
+
@@ -274,6 +258,19 @@ name: Workbook1 description:
+ +
+
+
+ +
+
+
+
+
diff --git a/merlin/static/merlin/lib/angular-filter.js b/merlin/static/merlin/js/lib/angular-filter.js similarity index 99% rename from merlin/static/merlin/lib/angular-filter.js rename to merlin/static/merlin/js/lib/angular-filter.js index e45ab7e..66bb33c 100644 --- a/merlin/static/merlin/lib/angular-filter.js +++ b/merlin/static/merlin/js/lib/angular-filter.js @@ -155,11 +155,11 @@ function isScope(obj) { } /** * @ngdoc filter - * @name a8m.angular + * @name a8m.templates * @kind function * * @description - * reference to angular function + * reference to templates function */ angular.module('a8m.angular', []) @@ -2022,7 +2022,7 @@ angular.module('a8m.filter-watcher', []) /** * @description - * for angular version that greater than v.1.3.0 + * for templates version that greater than v.1.3.0 * if clear cache when the digest cycle end. */ function cleanStateless() { @@ -2075,7 +2075,7 @@ angular.module('a8m.filter-watcher', []) var hashKey = getHashKey(filterName, args); //store result in `$$cache` container $$cache[hashKey] = result; - // for angular versions that less than 1.3 + // for templates versions that less than 1.3 // add to `$destroy` listener, a cleaner callback if(isScope(scope)) { addListener(scope, hashKey); @@ -2114,7 +2114,7 @@ angular.module('a8m.filter-watcher', []) /** * @ngdoc module - * @name angular.filters + * @name templates.filters * @description * Bunch of useful filters for angularJS */ diff --git a/merlin/static/merlin/lib/angular-filter.min.js b/merlin/static/merlin/js/lib/angular-filter.min.js similarity index 100% rename from merlin/static/merlin/lib/angular-filter.min.js rename to merlin/static/merlin/js/lib/angular-filter.min.js diff --git a/merlin/static/merlin/js/lib/barricade.js b/merlin/static/merlin/js/lib/barricade.js new file mode 100644 index 0000000..226d117 --- /dev/null +++ b/merlin/static/merlin/js/lib/barricade.js @@ -0,0 +1,1354 @@ +// Copyright 2014 Rackspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** @namespace */ +var Barricade = (function () { + "use strict"; + + var Array_, Arraylike, BarricadeMain, Base, Blueprint, Container, + Deferrable, Deferred, Enumerated, Extendable, Identifiable, + ImmutableObject, InstanceofMixin, MutableObject, Observable, Omittable, + Primitive, Validatable; + + /** + * Blueprints are used to define mixins. They can be used to enable private + * state. Blueprints are meant to be applied to new instances of a class to + * provide instance methods or wrapped around a class itself to provide + * static methods. + * + * Blueprints can be applied using + * `SomeBlueprint.call(instance, arg1, arg2, ...)` + * + * Instances (and classes) can be checked to see if a + * Blueprint has been applied to them using `instanceof()`
+ * ex: `someInstance.instanceof(SomeBlueprint)` + * @class + * @memberof Barricade + */ + Blueprint = { + /** + * Creates a Blueprint. + * @memberof Barricade.Blueprint + * @param {function} f + A function that will be run when the Blueprint is applied. When + `.call(instance, arg1, arg2, ...)` is used, `instance` will be + set to `this` and the arguments will be passed to `f`. + */ + create: function (f) { + return function g() { + if (!this.hasOwnProperty('_parents')) { + Object.defineProperty(this, '_parents', {value: []}); + } + this._parents.push(g); + return f.apply(this, arguments); + }; + } + }; + + /** + * @mixin + * @memberof Barricade + */ + Extendable = Blueprint.create(function () { + function deepClone(object) { + if (isPlainObject(object)) { + return forInKeys(object).reduce(function (clone, key) { + clone[key] = deepClone(object[key]); + return clone; + }, {}); + } + return object; + } + + function extend(extension) { + return Object.keys(extension).reduce(function (object, prop) { + return Object.defineProperty(object, prop, { + enumerable: true, + writable: true, + configurable: true, + value: extension[prop] + }); + }, Object.create(this)); + } + + function forInKeys(obj) { + var key, keys = []; + for (key in obj) { keys.push(key); } + return keys; + } + + function isPlainObject(obj) { + return getType(obj) === Object && + Object.getPrototypeOf(Object.getPrototypeOf(obj)) === null; + } + + function merge(target, source) { + forInKeys(source).forEach(function (key) { + if (target.hasOwnProperty(key) && + isPlainObject(target[key]) && + isPlainObject(source[key])) { + merge(target[key], source[key]); + } else { + target[key] = deepClone(source[key]); + } + }); + } + + /** + * Extends the object, returning a new object with the original object as + its prototype. + * @method extend + * @memberof Barricade.Extendable + * @instance + * @param {Object} extension A set of properties to add to the new + object. + * @param {Object} [schema] Barricade schema. + * @returns {Object} + */ + return Object.defineProperty(this, 'extend', { + enumerable: false, + writable: false, + value: function (extension, schema) { + if (schema) { + extension._schema = deepClone(this._schema) || {}; + merge(extension._schema, schema); + } + return extend.call(this, extension); + } + }); + }); + + /** + * @mixin + * @memberof Barricade + */ + InstanceofMixin = Blueprint.create(function () { + return Object.defineProperty(this, 'instanceof', { + enumerable: false, + value: function (proto) { + var _instanceof = this.instanceof, + subject = this; + + function hasMixin(obj, mixin) { + return obj.hasOwnProperty('_parents') && + obj._parents.some(function (_parent) { + return _instanceof.call(_parent, mixin); + }); + } + + do { + if (subject === proto || hasMixin(subject, proto)) { + return true; + } + subject = Object.getPrototypeOf(subject); + } while (subject); + + return false; + } + }); + }); + + /** + * Attaches an identifier to an object. Used as an alternative to key/value + pairs in JSON objects when the key is user-defined. This way the key (ID) + stays with the value. + * @mixin + * @memberof Barricade + */ + Identifiable = Blueprint.create(function (id) { + /** + * Returns the ID + * @method getID + * @memberof Barricade.Identifiable + * @instance + * @returns {String} + */ + this.getID = function () { + return id; + }; + + this.hasID = function() { + return id !== undefined; + }; + + /** + * Sets the ID. + * @method setID + * @memberof Barricade.Identifiable + * @instance + * @param {String} newID + * @returns {self} + */ + this.setID = function (newID) { + id = newID; + return this.emit('change', 'id'); + }; + }); + + /** + * Tracks whether an object is being "used" or not, which is a state that + updates whenever the object changes, but also can be explicitly set. + * @mixin + * @memberof Barricade + */ + Omittable = Blueprint.create(function (isUsed) { + /** + * Returns whether object is being used or not. + * @method isUsed + * @memberof Barricade.Omittable + * @instance + * @returns {Boolean} + */ + this.isUsed = function () { + return this.isRequired() || isUsed; + }; + + /** + * Explicitly sets whether object is being used or not. + * @method setIsUsed + * @memberof Barricade.Omittable + * @instance + * @param {Boolean} newUsedValue + * @returns {self} + */ + this.setIsUsed = function (newUsedValue) { + isUsed = !!newUsedValue; + return this; + }; + + this.on('change', function () { + isUsed = !this.isEmpty(); + }); + }); + + /** + * @mixin + * @memberof Barricade + */ + Deferrable = Blueprint.create(function (schema) { + var self = this, + deferred; + + function resolver(neededValue) { + var ref = schema['@ref'].resolver(self, neededValue); + if (ref === undefined) { + logError('Could not resolve ', JSON.stringify(self.toJSON())); + } + return ref; + } + + 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 (this.instanceof(Container)) { + this.each(function (index, value) { + if (!value.resolveWith(obj)) { + allResolved = false; + } + }); + } + + return allResolved; + }; + }); + + /** + * @mixin + * @memberof Barricade + */ + Validatable = Blueprint.create(function (schema) { + var constraints = schema['@constraints'], + error = null; + + if (getType(constraints) !== Array) { + constraints = []; + } + + this.getError = function () { return error || ''; }; + this.hasError = function () { return error !== null; }; + + this._validate = function (value) { + function getConstraintMessage(i, lastMessage) { + if (lastMessage !== true) { + return lastMessage; + } else if (i < constraints.length) { + return getConstraintMessage(i + 1, constraints[i](value)); + } + return null; + } + error = getConstraintMessage(0, true); + return !this.hasError(); + }; + + this.addConstraint = function (newConstraint) { + constraints.push(newConstraint); + return this; + }; + }); + + /** + * Defines a constraint on the possible values a Barricade object can take. + Enums can be defined simply as an array of values or an array of objects + of the form `{label: someLabel, value: someValue}`. + * @mixin + * @memberof Barricade + */ + Enumerated = Blueprint.create(function(enum_) { + var self = this; + + function getEnum() { + return (typeof enum_ === 'function') ? enum_.call(self) : enum_; + } + + /** + * Returns an array of labels. If the enum has defined labels, those are + returned. If the enum is simply a set of values, the values are + returned as the labels. + * @method getEnumLabels + * @memberof Barricade.Enumerated + * @instance + * @returns {Array} + */ + this.getEnumLabels = function () { + var curEnum = getEnum(); + return getType(curEnum[0]) === Object + ? curEnum.map(function (value) { return value.label; }) + : curEnum; + }; + + /** + * Returns an array of only the enum's values. + * @method getEnumValues + * @memberof Barricade.Enumerated + * @instance + * @returns {Array} + */ + this.getEnumValues = function () { + var curEnum = getEnum(); + return getType(curEnum[0]) === Object + ? curEnum.map(function (value) { return value.value; }) + : curEnum; + }; + + this.addConstraint(function (value) { + return (self.getEnumValues().indexOf(value) > -1) || + 'Value can only be one of ' + self.getEnumLabels().join(', '); + }); + }); + + /** + * @mixin + * @memberof Barricade + */ + Observable = Blueprint.create(function () { + var events = {}; + + function hasEvent(eventName) { + return events.hasOwnProperty(eventName); + } + + /** + * Executes all callbacks associated with an event in the order that they + were added. + * @method emit + * @memberof Barricade.Observable + * @instance + * @param {String} eventName + * @returns {self} + */ + this.emit = function (eventName) { + var args = arguments; // Must come from correct scope + if (events.hasOwnProperty(eventName)) { + events[eventName].forEach(function (callback) { + // Call with emitter as context and pass all but eventName + callback.apply(this, Array.prototype.slice.call(args, 1)); + }, this); + } + return this; + }; + + /** + * Removes a callback for a particular event. + * @method off + * @memberof Barricade.Observable + * @instance + * @param {String} eventName + * @param {Function} callback + * @returns {self} + */ + this.off = function (eventName, callback) { + var index; + + if (hasEvent(eventName)) { + index = events[eventName].indexOf(callback); + + if (index > -1) { + events[eventName].splice(index, 1); + } + } + return this; + }; + + /** + * Specifies a callback to be executed when the Observable emits + a particular event + * @method on + * @memberof Barricade.Observable + * @instance + * @param {String} eventName + * @param {Function} callback + * @returns {self} + */ + this.on = function (eventName, callback) { + if (!hasEvent(eventName)) { + events[eventName] = []; + } + events[eventName].push(callback); + return this; + }; + }); + + /** + * @class + * @memberof Barricade + */ + Deferred = { + /** + * @memberof Barricade.Deferred + * @instance + * @param {Function} classGetter + * @param {Function} onResolve + Callback to execute when resolve happens. + * @returns {Barricade.Deferred} + */ + create: function (classGetter, onResolve) { + var self = Object.create(this); + self._isResolved = false; + self._classGetter = classGetter; + self._onResolve = onResolve; + return self; + }, + + /** + * @memberof Barricade.Deferred + * @instance + * @returns {Boolean} + */ + isResolved: function () { + return this._isResolved; + }, + + /** + * @memberof Barricade.Deferred + * @instance + * @param obj + * @returns {Boolean} + */ + needs: function (obj) { + return obj.instanceof(this._classGetter()); + }, + + /** + * @memberof Barricade.Deferred + * @instance + * @param obj + */ + resolve: function (obj) { + var ref; + + if (this._isResolved) { + throw new Error('Deferred already resolved'); + } + + ref = this._onResolve(obj); + + if (ref !== undefined) { + this._isResolved = true; + return ref; + } + } + }; + + /** + * @class + * @memberof Barricade + * @mixes Barricade.Extendable + * @extends Barricade.Extendable + * @mixes Barricade.InstanceofMixin + * @extends Barricade.InstanceofMixin + * @mixes Barricade.Observable + * @extends Barricade.Observable + * @mixes Barricade.Omittable + * @extends Barricade.Omittable + * @mixes Barricade.Deferrable + * @extends Barricade.Deferrable + * @mixes Barricade.Validatable + * @extends Barricade.Validatable + * @mixes Barricade.Enumerated + * @extends Barricade.Enumerated + * @mixes Barricade.Identifiable + * @extends Barricade.Identifiable + */ + Base = Extendable.call(InstanceofMixin.call({ + /** + * Creates a `Base` instance + * @memberof Barricade.Base + * @param {JSON} json + * @param {Object} parameters + * @returns {Barricade.Base} + */ + create: function (json, parameters) { + var self = this.extend({}), + schema = self._schema, + isUsed; + + self._parameters = parameters = parameters || {}; + + if (schema.hasOwnProperty('@inputMassager')) { + json = schema['@inputMassager'](json); + } + + isUsed = self._setData(json); + + if (schema.hasOwnProperty('@toJSON')) { + self.toJSON = schema['@toJSON']; + } + + Observable.call(self); + Omittable.call(self, isUsed); + Deferrable.call(self, schema); + Validatable.call(self, schema); + + if (schema.hasOwnProperty('@enum')) { + Enumerated.call(self, schema['@enum']); + } + + Identifiable.call(self, parameters.id); + + return self; + }, + + /** + * @memberof Barricade.Base + * @private + */ + _getDefaultValue: function () { + return this._schema.hasOwnProperty('@default') + ? typeof this._schema['@default'] === 'function' + ? this._schema['@default']() + : this._schema['@default'] + : this._schema['@type'](); + }, + + /** + * @memberof Barricade.Base + * @private + */ + _setData: function(json) { + var isUsed = true, + type = this._schema['@type']; + + if (getType(json) !== type) { + if (json) { + logError("Type mismatch. JSON: ", json, + "schema: ", this._schema); + } else { + isUsed = false; + } + // Replace bad type (does not change original) + json = this._getDefaultValue(); + } + this._data = this._sift(json, this._parameters); + + return isUsed; + }, + + /** + * @memberof Barricade.Base + * @private + */ + _safeInstanceof: function (instance, class_) { + return typeof instance === 'object' && + ('instanceof' in instance) && + instance.instanceof(class_); + }, + + /** + * @memberof Barricade.Base + * @private + */ + _sift: function () { + throw new Error("sift() must be overridden in subclass"); + }, + + /** + * Returns the primitive type of the Barricade object. + * @memberof Barricade.Base + * @instance + * @returns {constructor} + */ + getPrimitiveType: function () { + return this._schema['@type']; + }, + + /** + * @memberof Barricade.Base + * @instance + * @virtual + */ + isEmpty: function () { + throw new Error('Subclass should override isEmpty()'); + }, + + /** + * Returns whether the Barricade object is required or not. Usually + affects output of `toJSON()`. Use the `@required` tag in the schema to + specify this option. + * @memberof Barricade.Base + * @instance + * @returns {Boolean} + */ + isRequired: function () { + return this._schema['@required'] !== false; + } + })); + + /** + * @class + * @memberof Barricade + * @extends Barricade.Base + */ + Container = Base.extend({ + /** + * Creates a `Container` instance. + * @memberof Barricade.Container + * @param {JSON} json + * @param {Object} parameters + * @returns {Barricade.Container} + */ + create: function (json, parameters) { + var self = Base.create.call(this, json, parameters); + + return self.on('_addedElement', function (key) { + self._attachListeners(key); + self._tryResolveOn(self.get(key)); + }).each(function (index, value) { + self._attachListeners(index); + value.resolveWith(self); + }); + }, + + /** + * @memberof Barricade.Container + * @private + */ + _attachListeners: function (key) { + var self = this, + element = this.get(key), + slice = Array.prototype.slice, + events = { + 'childChange': function () { + self.emit.apply(self, + ['childChange'].concat(slice.call(arguments))); + }, + 'change': function () { + // 'this' is set to callee, no typo + events.childChange.apply(events, + [this].concat(slice.call(arguments))); + }, + 'replace': function (newValue) { + self.set(key, newValue); + self._tryResolveOn(newValue); + }, + '_resolveUp': function (value) { + self._tryResolveOn(value); + }, + 'removeFrom': function (container) { + if (container === self) { + Object.keys(events).forEach(function (eName) { + element.off(eName, events[eName]); + }); + } + } + }; + + Object.keys(events).forEach(function (eName) { + element.on(eName, events[eName]); + }); + }, + + /** + * @memberof Barricade.Container + * @private + */ + _getKeyClass: function (key) { + return this._schema[key].hasOwnProperty('@class') + ? this._schema[key]['@class'] + : BarricadeMain.create(this._schema[key]); + }, + + /** + * @memberof Barricade.Container + * @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()); + }, + + /** + * @memberof Barricade.Container + * @private + */ + _keyClassCreate: function (key, keyClass, json, parameters) { + return this._schema[key].hasOwnProperty('@factory') + ? this._schema[key]['@factory'](json, parameters) + : keyClass.create(json, parameters); + }, + + /** + * @memberof Barricade.Container + * @private + */ + _tryResolveOn: function (value) { + if (!value.resolveWith(this)) { + this.emit('_resolveUp', value); + } + }, + + /** + * @memberof Barricade.Container + * @instance + * @param key + * @param {Element} value + * @returns {self} + */ + set: function (key, value) { + this.get(key).emit('removeFrom', this); + this._doSet(key, value); + this._attachListeners(key); + return this; + } + }); + + /** + * @class + * @memberof Barricade + * @extends Barricade.Container + */ + Arraylike = Container.extend({ + /** + * Creates an Arraylike. + * @memberof Barricade.Arraylike + * @returns {Barricade.Arraylike} New Arraylike instance. + */ + create: function (json, parameters) { + if (!this.hasOwnProperty('_elementClass')) { + Object.defineProperty(this, '_elementClass', { + enumerable: false, + writable: true, + value: this._getKeyClass(this._elSymbol) + }); + } + return Container.create.call(this, json, parameters); + }, + + /** + * @memberof Barricade.Arraylike + * @private + */ + _doSet: function (index, newVal, newParameters) { + var oldVal = this._data[index]; + + this._data[index] = this._isCorrectType(newVal, this._elementClass) + ? this._data[index] = newVal + : this._keyClassCreate(this._elSymbol, this._elementClass, + newVal, newParameters); + + this.emit('change', 'set', index, this._data[index], oldVal); + }, + + /** + * @memberof Barricade.Arraylike + * @private + */ + _elSymbol: '*', + + /** + * @memberof Barricade.Arraylike + * @private + */ + _sift: function (json) { + return json.map(function (el) { + return this._keyClassCreate( + this._elSymbol, this._elementClass, el); + }, this); + }, + + /** + * @callback Barricade.Arraylike.eachCB + * @param {Number} index + * @param {Element} value + Instance of the Arraylike's Element class at index + */ + + /** + * @memberof Barricade.Arraylike + * @instance + * @param {Barricade.Arraylike.eachCB} functionIn + A function to be called for each element in the array + * @param {Function} comparatorIn + Comparator in the form that JavaScript's Array.sort() expects + * @returns {self} + */ + each: function (functionIn, comparatorIn) { + var arr = this._data.slice(); + + if (comparatorIn) { + arr.sort(comparatorIn); + } + + arr.forEach(function (value, index) { + functionIn(index, value); + }); + + return this; + }, + + /** + * @memberof Barricade.Arraylike + * @instance + * @param {Integer} index + * @returns {Element} + */ + get: function (index) { + return this._data[index]; + }, + + /** + * Returns true if no elements are present, false otherwise. + * @memberof Barricade.Arraylike + * @instance + * @returns {Boolean} + */ + isEmpty: function () { + return !this._data.length; + }, + + /** + * Returns number of elements in Arraylike + * @memberof Barricade.Arraylike + * @instance + * @returns {Number} + */ + length: function () { + return this._data.length; + }, + + /** + * Appends an element to the end of the Arraylike. + * @memberof Barricade.Arraylike + * @instance + * @param {JSON|Element} newValue + JSON in the form that the element schema expects, or an + instance of the Arraylike's element class. + * @param {Object} [newParameters] + If JSON was passed in for newValue, a parameters object can be + passed in. + * @returns {self} + */ + push: function (newValue, newParameters) { + this._data.push( + this._isCorrectType(newValue, this._elementClass) + ? newValue + : this._keyClassCreate(this._elSymbol, this._elementClass, + newValue, newParameters)); + + return this.emit('_addedElement', this._data.length - 1) + .emit('change', 'add', this._data.length - 1); + }, + + /** + * Removes element at specified index. + * @memberof Barricade.Arraylike + * @instance + * @param {Integer} index + * @returns {self} + */ + remove: function (index) { + this._data[index].emit('removeFrom', this); + this._data.splice(index, 1); + return this.emit('change', 'remove', index); + }, + + /** + * Returns an array containing the Arraylike's elements + * @memberof Barricade.Arraylike + * @instance + * @returns {Array} + */ + toArray: function () { + return this._data.slice(); // Shallow copy to prevent mutation + }, + + /** + * Converts the Arraylike and all of its elements to JSON. + * @memberof Barricade.Arraylike + * @instance + * @param {Boolean} [ignoreUnused] + Whether to include unused entries. Has no effect at Arraylike's + level, but is passed into each element for them to decide. + * @returns {Array} JSON array containing JSON representations of each + element. + */ + toJSON: function (ignoreUnused) { + return this._data.map(function (el) { + return el.toJSON(ignoreUnused); + }); + } + }); + + /** + * Array_ is provided to simply differentiate between Barricade arrays and + * other classes that are array-like, such as MutableObject. + * @class + * @extends Barricade.Arraylike + * @memberof Barricade + */ + Array_ = Arraylike.extend({}); + + /** + * @class + * @memberof Barricade + * @extends Barricade.Container + */ + ImmutableObject = Container.extend({ + create: function (json, parameters) { + var self = this; + if (!this.hasOwnProperty('_keyClasses')) { + Object.defineProperty(this, '_keyClasses', { + enumerable: false, + writable: true, + value: this.getKeys().reduce(function (classes, key) { + classes[key] = self._getKeyClass(key); + return classes; + }, {}) + }); + } + + return Container.create.call(this, json, parameters); + }, + + /** + * @memberof Barricade.ImmutableObject + * @private + */ + _sift: function (json) { + var self = this; + return this.getKeys().reduce(function (objOut, key) { + objOut[key] = + self._keyClassCreate(key, self._keyClasses[key], json[key]); + return objOut; + }, {}); + }, + + /** + * @memberof Barricade.ImmutableObject + * @private + */ + _doSet: function (key, newValue, newParameters) { + var oldVal = this._data[key]; + + if (this._schema.hasOwnProperty(key)) { + if (this._isCorrectType(newValue, this._keyClasses[key])) { + this._data[key] = newValue; + } else { + this._data[key] = + this._keyClassCreate(key, this._keyClasses[key], + newValue, newParameters); + } + + this.emit('change', 'set', key, this._data[key], oldVal); + } else { + logError('object does not have key: ', key, + ' schema: ', this._schema); + } + }, + + /** + * @callback Barricade.ImmutableObject.eachCB + * @param {String} key + * @param {Element} value + Instance of the ImmutableObject's Element class at index + */ + + /** + * @memberof Barricade.ImmutableObject + * @instance + * @param {Barricade.ImmutableObject.eachCB} functionIn + A function to be called for each element in the array + * @param {Function} comparatorIn + Comparator in the form that JavaScript's Array.sort() expects + * @returns {self} + */ + each: function (functionIn, comparatorIn) { + var self = this, + keys = this.getKeys(); + + if (comparatorIn) { + keys.sort(comparatorIn); + } + + keys.forEach(function (key) { + functionIn(key, self._data[key]); + }); + + return this; + }, + + /** + * @memberof Barricade.Arraylike + * @instance + * @param {String} key + * @returns {Element} + */ + get: function (key) { + return this._data[key]; + }, + + /** + * Returns all keys in the ImmutableObject + * @memberof Barricade.ImmutableObject + * @instance + * @returns {Array} + */ + getKeys: function () { + return Object.keys(this._schema).filter(function (key) { + return key.charAt(0) !== '@'; + }); + }, + + /** + * Returns true if ImmutableObject has no keys, false otherwise. + * @memberof Barricade.ImmutableObject + * @instance + * @returns {Boolean} + */ + isEmpty: function () { + return !Object.keys(this._data).length; + }, + + /** + * @memberof Barricade.ImmutableObject + * @instance + * @param {Boolean} [ignoreUnused] + Whether to include unused entries. If true, unused values will + not have their key/value pairs show up in the resulting JSON. + * @returns {Object} JSON representation of the ImmutableObject and its + values. + */ + toJSON: function (ignoreUnused) { + var data = this._data; + return this.getKeys().reduce(function (jsonOut, key) { + if (ignoreUnused !== true || data[key].isUsed()) { + jsonOut[key] = data[key].toJSON(ignoreUnused); + } + return jsonOut; + }, {}); + } + }); + + /** + * @class + * @memberof Barricade + * @extends Barricade.Arraylike + */ + MutableObject = Arraylike.extend({ + /** + * @memberof Barricade.MutableObject + * @private + */ + _elSymbol: '?', + + /** + * @memberof Barricade.MutableObject + * @private + */ + _sift: function (json) { + return Object.keys(json).map(function (key) { + return this._keyClassCreate(this._elSymbol, this._elementClass, + json[key], {id: key}); + }, this); + }, + + /** + * Returns true if MutableObject contains `element`, false otherwise. + * @memberof Barricade.MutableObject + * @instance + * @param element Element to check for. + * @returns {Boolean} + */ + contains: function (element) { + return this.toArray().some(function (value) { + return element === value; + }); + }, + + /** + * Retrieves element with specified ID. + * @memberof Barricade.MutableObject + * @instance + * @param {String} id + * @returns {Element} + */ + getByID: function (id) { + return this.get(this.getPosByID(id)); + }, + + /** + * Returns an array of the IDs of the elements of the MutableObject. + * @memberof Barricade.MutableObject + * @instance + * @returns {Array} + */ + getIDs: function () { + return this.toArray().map(function (value) { + return value.getID(); + }); + }, + + /** + * Returns index of the element with the specified ID. + * @memberof Barricade.MutableObject + * @instance + * @param {String} id + * @returns {Integer} + */ + getPosByID: function (id) { + return this.getIDs().indexOf(id); + }, + + /** + * Adds a new element to the MutableObject. + * @memberof Barricade.MutableObject + * @instance + * @param {JSON|Element} newJson + JSON in the form that the element schema expects, or an + instance of the MutableObject's element class. + * @param {Object} [newParameters] + If JSON was passed in for newJson, a parameters object with at + least an `id` property is required. + * @returns {self} + */ + push: function (newJson, newParameters) { + if (!this._safeInstanceof(newJson, this._elementClass) && + (getType(newParameters) !== Object || + !newParameters.hasOwnProperty('id'))) { + logError('ID should be passed in with parameters object'); + } else { + return Arraylike.push.call(this, newJson, newParameters); + } + }, + + /** + * Converts the MutableObject and all of its elements to JSON. + * @memberof Barricade.MutableObject + * @instance + * @param {Boolean} [ignoreUnused] + Whether to include unused entries. If true, elements that are + unused will not be included in the return value. This parameter + is also passed to each element's `toJSON()` method. + * @returns {Object} JSON object containing JSON representations of each + element. + */ + toJSON: function (ignoreUnused) { + return this.toArray().reduce(function (jsonOut, element) { + if (jsonOut.hasOwnProperty(element.getID())) { + logError("ID found multiple times: " + element.getID()); + } else { + jsonOut[element.getID()] = element.toJSON(ignoreUnused); + } + return jsonOut; + }, {}); + } + }); + + /** + * @class + * @memberof Barricade + * @extends Barricade.Base + */ + Primitive = Base.extend({ + /** + * @memberof Barricade.Primitive + * @private + */ + _sift: function (json) { + return json; + }, + + /** + * Retrieves the Primitive's value. + * @memberof Barricade.Primitive + * @instance + * @returns {JSON} + */ + get: function () { + return this._data; + }, + + /** + * Returns true if the Primitive's data is empty. This depends on the + type; Arrays and Objects are considered empty if they have no + elements, while Strings, Numbers, and Booleans are empty if they are + equivalent to a newly-constructed instance. + * @memberof Barricade.Primitive + * @instance + * @returns {Boolean} + */ + isEmpty: function () { + if (this._schema['@type'] === Array) { + return !this._data.length; + } else if (this._schema['@type'] === Object) { + return !Object.keys(this._data).length; + } + return this._data === this._schema['@type'](); + }, + + /** + * @memberof Barricade.Primitive + * @instance + * @param newVal + * @returns {self} + */ + set: function (newVal) { + var schema = this._schema; + + function typeMatches(newVal) { + return getType(newVal) === schema['@type']; + } + + if (typeMatches(newVal) && this._validate(newVal)) { + this._data = newVal; + return this.emit('validation', 'succeeded') + .emit('change'); + } else if (this.hasError()) { + return this.emit('validation', 'failed'); + } + + logError("Setter - new value (", newVal, ")", + " did not match schema: ", schema); + return this; + }, + + /** + * Converts the Primitive to JSON (which is simply the value itself). + * @memberof Barricade.Primitive + * @instance + * @returns {JSON} + */ + toJSON: function () { + return this._data; + } + }); + + var getType = (function () { + var toString = Object.prototype.toString, + types = { + 'boolean': Boolean, + 'number': Number, + 'string': String, + '[object Array]': Array, + '[object Date]': Date, + '[object Function]': Function, + '[object RegExp]': RegExp + }; + + return function (val) { + return types[typeof val] || + types[toString.call(val)] || + (val ? Object : null); + }; + }()); + + function logError() { + console.error.apply(console, Array.prototype.slice.call(arguments) + .unshift('Barricade: ')); + } + + BarricadeMain = { + 'Array': Array_, + 'Arraylike': Arraylike, + 'Base': Base, + 'Blueprint': Blueprint, + 'Container': Container, + 'Deferrable': Deferrable, + 'Enumerated': Enumerated, + 'getType': getType, // Very helpful function + 'Identifiable': Identifiable, + 'ImmutableObject': ImmutableObject, + 'MutableObject': MutableObject, + 'Observable': Observable, + 'Omittable': Omittable, + 'Primitive': Primitive, + 'create': function (schema) { + function schemaIsMutable() { + return schema.hasOwnProperty('?'); + } + + function schemaIsImmutable() { + return Object.keys(schema).some(function (key) { + return key.charAt(0) !== '@' && key !== '?'; + }); + } + + if (schema['@type'] === Object && schemaIsImmutable()) { + return ImmutableObject.extend({}, schema); + } else if (schema['@type'] === Object && schemaIsMutable()) { + return MutableObject.extend({}, schema); + } else if (schema['@type'] === Array && '*' in schema) { + return Array_.extend({}, schema); + } + return Primitive.extend({}, schema); + } + }; + + return BarricadeMain; + +}()); diff --git a/merlin/static/merlin/lib/js-yaml.js b/merlin/static/merlin/js/lib/js-yaml.js similarity index 100% rename from merlin/static/merlin/lib/js-yaml.js rename to merlin/static/merlin/js/lib/js-yaml.js diff --git a/extensions/mistral/static/mistral/js/directives.js b/merlin/static/merlin/js/merlin.directives.js similarity index 53% rename from extensions/mistral/static/mistral/js/directives.js rename to merlin/static/merlin/js/merlin.directives.js index 7bea1dd..2e51599 100644 --- a/extensions/mistral/static/mistral/js/directives.js +++ b/merlin/static/merlin/js/merlin.directives.js @@ -1,10 +1,9 @@ /** - * Created by tsufiev on 12/29/14. + * Created by tsufiev on 2/24/15. */ - (function() { function disableClickDefaultBehaviour(element) { - angular.element(element).find('a[data-toggle="collapse"]') + element.find('a[data-toggle="collapse"]') .on('click', function(e) { e.preventDefault(); return true; @@ -16,7 +15,7 @@ .directive('editable', function() { return { restrict: 'E', - templateUrl: '/static/mistral/js/angular-templates/editable-popup.html', + templateUrl: '/static/merlin/templates/editable-popup.html', scope: { label: '@', value: '=' @@ -32,48 +31,18 @@ }; }) - .directive('yaqlFieldCombined', function() { + .directive('panel', function($parse) { return { restrict: 'E', - templateUrl: '/static/mistral/js/angular-templates/yaql-field-combined.html', - scope: { - yaqlExpression: '@', - value: '@' - }, - link: function(scope, element) { - angular.element(element).find('span.yaql-condition') - .on('click', function() { - var $elt = $(this), - $inputColumn = $elt.closest('.three-columns').children(':first-child'), - $input; - - $elt.hide(); - $input = $inputColumn.show().find('textarea'); - $input.focus().on('blur', function() { - $inputColumn.hide(); - $elt.toggleClass('fa-lock', $input.val() !== ''); - $elt.toggleClass('fa-unlock', $input.val() === ''); - $elt.show(); - }); - }); - } - } - }) - - .directive('panel', function() { - return { - restrict: 'E', - templateUrl: '/static/mistral/js/angular-templates/collapsible-panel.html', + templateUrl: '/static/merlin/templates/collapsible-panel.html', transclude: true, scope: { title: '@', onRemove: '&' }, link: function(scope, element, attrs) { + scope.removable = $parse(attrs.removable)(); disableClickDefaultBehaviour(element); - if ( attrs.onRemove ) { - scope.removable = true; - } } } }) @@ -81,7 +50,7 @@ .directive('collapsibleGroup', function() { return { restrict: 'E', - templateUrl: '/static/mistral/js/angular-templates/collapsible-group.html', + templateUrl: '/static/merlin/templates/collapsible-group.html', transclude: true, scope: { title: '@', @@ -103,12 +72,16 @@ .directive('typedField', function($http, $templateCache, $compile) { return { restrict: 'E', - scope: true, + scope: { + title: '@', + value: '=', + type: '@' + }, link: function(scope, element) { - var template = $templateCache.get(scope.spec.type); + var template = $templateCache.get(scope.type); element.replaceWith($compile(template)(scope)); } } }) -})(); +})(); \ No newline at end of file diff --git a/merlin/static/merlin/js/merlin.field.models.js b/merlin/static/merlin/js/merlin.field.models.js new file mode 100644 index 0000000..e68fd13 --- /dev/null +++ b/merlin/static/merlin/js/merlin.field.models.js @@ -0,0 +1,174 @@ + +(function() { + angular.module('hz') + + .factory('merlin.field.models', + ['merlin.utils', 'merlin.panel.models', function(utils, panels) { + + var wildcardMixin = Barricade.Blueprint.create(function() { + return this; + }); + + var modelMixin = Barricade.Blueprint.create(function(type) { + this.value = function() { + if ( !arguments.length ) { + return this.get(); + } else { + this.set(arguments[0]); + } + }; + this.id = utils.getNewId(); + + this.getType = function() { + return type; + }; + + this.setType = function(_type) { + type = _type; + }; + + this.isAtomic = function() { + return ['number', 'string', 'text'].indexOf(this.getType()) > -1; + }; + this.getTitle = function() { + var title = utils.getMeta(this, 'title'); + if ( !title ) { + if ( this.instanceof(Barricade.ImmutableObject) ) { + if ( this.getKeys().indexOf('name') > -1 ) { + return this.get('name').get(); + } + } + title = utils.makeTitle(this.getID()) || ''; + } + return title; + }; + wildcardMixin.call(this); + return this; + }); + + function meldGroup() { + if ( utils.getMeta(this, 'group') ) { + panels.groupmixin.call(this); + } + } + + var stringModel = Barricade.Primitive.extend({ + create: function(json, parameters) { + var self = Barricade.Primitive.create.call(this, json, parameters); + return modelMixin.call(self, 'string'); + } + }, {'@type': String}); + + var textModel = Barricade.Primitive.extend({ + create: function(json, parameters) { + var self = Barricade.Primitive.create.call(this, json, parameters); + return modelMixin.call(self, 'text'); + } + }, {'@type': String}); + + var numberModel = Barricade.Primitive.extend({ + create: function(json, parameters) { + var self = Barricade.Primitive.create.call(this, json, parameters); + return modelMixin.call(self, 'number'); + } + }, {'@type': Number}); + + var listModel = Barricade.Array.extend({ + create: function(json, parameters) { + var self = Barricade.Array.create.call(this, json, parameters); + + modelMixin.call(self, 'list'); + + self.add = function() { + self.push(); + }; + self.getValues = function() { + return self.toArray(); + }; + self._getContents = function() { + return self.toArray(); + }; + meldGroup.call(self); + return self; + } + }, {'@type': Array}); + + var frozendictModel = Barricade.ImmutableObject.extend({ + create: function(json, parameters) { + var self = Barricade.ImmutableObject.create.call(this, json, parameters); + self.getKeys().forEach(function(key) { + utils.enhanceItemWithID(self.get(key), key); + }); + + modelMixin.call(self, 'frozendict'); + self.getValues = function() { + return self._data; + }; + self._getContents = function() { + return self.getKeys().map(function(key) { + return self.get(key); + }) + }; + panels.rowmixin.call(self); + meldGroup.call(self); + return self; + } + }, {'@type': Object}); + + var dictionaryModel = Barricade.MutableObject.extend({ + create: function(json, parameters) { + var self = Barricade.MutableObject.create.call(this, json, parameters), + _items = {}, + _elClass = self._elementClass, + baseKey = utils.getMeta(_elClass, 'baseKey') || 'key', + baseName = utils.getMeta(_elClass, 'baseName') || utils.makeTitle(baseKey); + + modelMixin.call(self, 'dictionary'); + + self.add = function() { + var newID = baseKey + utils.getNextIDSuffix(self, /(key)([0-9]+)/), + newValue; + if ( _elClass.instanceof(Barricade.ImmutableObject) ) { + if ( 'name' in _elClass._schema ) { + var nameNum = utils.getNextIDSuffix(self, new RegExp('(' + baseName + ')([0-9]+)')); + newValue = {name: baseName + nameNum}; + } else { + newValue = {}; + } + } else { // usually, it's either frozendict inside or string + newValue = ''; + } + self.push(newValue, {id: newID}); + _items[newID] = self.getByID(newID); + }; + self.getValues = function() { + if ( !Object.keys(_items).length ) { + self.getIDs().forEach(function(id) { + _items[id] = self.getByID(id); + }); + } + return _items; + }; + self._getContents = function() { + return self.toArray(); + }; + self.remove = function(key) { + delete _items[key]; + Barricade.MutableObject.remove.call(self, self.getPosByID(key)); + }; + meldGroup.call(self); + return self; + } + }, {'@type': Object}); + + return { + string: stringModel, + text: textModel, + number: numberModel, + list: listModel, + dictionary: dictionaryModel, + frozendict: frozendictModel, + wildcard: wildcardMixin // use for most general type-checks + }; + }]) +})(); \ No newline at end of file diff --git a/merlin/static/merlin/js/merlin.init.js b/merlin/static/merlin/js/merlin.init.js new file mode 100644 index 0000000..7319457 --- /dev/null +++ b/merlin/static/merlin/js/merlin.init.js @@ -0,0 +1,19 @@ +/** + * Created by tsufiev on 2/24/15. + */ +(function() { + angular.module('hz') + + .run(function($http, $templateCache) { + var fields = ['dictionary', 'frozendict', 'list', 'string', + 'text', 'group', 'number' + ]; + fields.forEach(function(field) { + var base = '/static/merlin/templates/fields/'; + $http.get(base + field + '.html').success(function(templateContent) { + $templateCache.put(field, templateContent); + }); + }) + }) + +})(); \ No newline at end of file diff --git a/merlin/static/merlin/js/merlin.panel.models.js b/merlin/static/merlin/js/merlin.panel.models.js new file mode 100644 index 0000000..8c73aad --- /dev/null +++ b/merlin/static/merlin/js/merlin.panel.models.js @@ -0,0 +1,152 @@ +/** + * Created by tsufiev on 2/24/15. + */ +(function(){ + angular.module('hz') + + .factory('merlin.panel.models', ['merlin.utils', function(utils) { + var rowProto = { + create: function(items) { + this.id = utils.getNewId(); + this.index = items.row; + items = items.slice(); + this._items = items.sort(function(item1, item2) { + return utils.getMeta(item1, 'index') - utils.getMeta(item2, 'index'); + }); + return this; + }, + getItems: function() { + return this._items; + } + }; + + var panelMixin = Barricade.Blueprint.create(function (schema) { + var self = this, + panelProto = { + create: function(itemsOrContainer, id) { + if ( angular.isArray(itemsOrContainer) && !itemsOrContainer.length ) { + return null; + } + this.id = utils.getNewId(); + if ( angular.isArray(itemsOrContainer) ) { + this._items = itemsOrContainer; + } else { + this._barricadeContainer = itemsOrContainer; + this._barricadeId = id; + var barricadeObj = itemsOrContainer.getByID(id); + this._items = barricadeObj.getKeys().map(function(key) { + return utils.enhanceItemWithID(barricadeObj.get(key), key); + }); + this.removable = true; + } + return this; + }, + getTitle: function() { + if ( this._barricadeContainer ) { + return this._barricadeContainer.getByID(this._barricadeId).get('name'); + } + }, + getRows: function() { + if ( this._rows === undefined ) { + this._rows = utils.groupByMetaKey(this._items, 'row').map(function(items) { + return Object.create(rowProto).create(items); + }); + } + return this._rows; + }, + remove: function(id) { + for ( var i = 0; i < panels.length; i++ ) { + if ( panels[i].id === id ) { + var container = this._barricadeContainer; + container.remove.call(container, this._barricadeId); + panels.splice(i, 1); + break; + } + } + } + }, + panels; + + this.getPanels = function(filterKey) { + if ( panels === undefined ) { + panels = []; + var items = self._getContents(); + utils.groupByMetaKey(items, 'panelIndex').forEach(function(items) { + // check for 'actions' and 'workflows' containers + if ( items[0].instanceof(Barricade.MutableObject) ) { + items[0].getIDs().forEach(function(id) { + panels.push(Object.create(panelProto).create(items[0], id)); + }); + } else { + panels.push(Object.create(panelProto).create(items)); + } + }); + panels = panels.condense(); + } + if ( filterKey ) { + panels.filter(function(panel) { + return panel._barricadeId && panel._barricadeId.match(filterKey); + }) + } + return panels; + }; + + this.addPanel = function(barricadeContainer, itemID, panelIndex) { + var panel = Object.create(panelProto).create(barricadeContainer, itemID); + if ( panelIndex ) { + panels.splice(panelIndex, 0, panel); + }else { + panels.push(panel); + } + }; + return this; + }); + + var rowMixin = Barricade.Blueprint.create(function() { + var self = this, + items = self._getContents(), + rows; + + self.getRows = function() { + if ( rows === undefined ) { + rows = utils.groupByMetaKey(items, 'row').map(function(items) { + return Object.create(rowProto).create(items); + }); + } + return rows; + }; + self.on('change', function(op) { + console.log(arguments); + if ( op == 'add' ) { + var items = self._getContents(); + utils.groupByMetaKey(items, 'row').forEach(function(items) { + rows.push(Object.create(rowProto).create(items)); + }) + } else if ( op == 'remove' ) { + } + }); + }); + + var groupMixin = Barricade.Blueprint.create(function() { + var self = this, + additive = utils.getMeta(self, 'additive'); + + rowMixin.call(self); + if ( additive === undefined ) { + additive = true; + } + self.isAdditive = function() { + return additive; + }; + self.setType('group'); + + return self; + }); + + return { + panelmixin: panelMixin, + groupmixin: groupMixin, + rowmixin: rowMixin + } + }]) +})(); \ No newline at end of file diff --git a/merlin/static/merlin/js/merlin.utils.js b/merlin/static/merlin/js/merlin.utils.js new file mode 100644 index 0000000..cb4a829 --- /dev/null +++ b/merlin/static/merlin/js/merlin.utils.js @@ -0,0 +1,83 @@ +/** + * Created by tsufiev on 2/24/15. + */ +(function(){ + angular.module('hz') + + .factory('merlin.utils', function() { + Array.prototype.condense = function() { + return this.filter(function(el) { + return el !== undefined && el != null; + }); + }; + + var _id_counter = 0; + + function getNewId() { + _id_counter++; + return 'id-' + _id_counter; + } + + function groupByMetaKey(sequence, metaKey, insertAtBeginning) { + var newSequence = [], defaultBucket = [], + index; + sequence.forEach(function(item) { + index = getMeta(item, metaKey); + if ( index !== undefined ) { + if ( !newSequence[index] ) { + newSequence[index] = []; + newSequence[index][metaKey] = index; + } + newSequence[index].push(item); + } else { + defaultBucket.push(item); + } + }); + newSequence = newSequence.condense(); + // insert default bucket at the beginning/end of sequence + if ( defaultBucket.length ) { + if ( insertAtBeginning ) { + newSequence.splice(0, 0, defaultBucket); + } else { + newSequence.push(defaultBucket); + } + } + return newSequence; + } + + function getMeta(item, key) { + var meta = item._schema['@meta']; + return meta && meta[key]; + } + + function makeTitle(str) { + if ( !str ) { + return ''; + } + var firstLetter = str.substr(0, 1).toUpperCase(); + return firstLetter + str.substr(1); + } + + function getNextIDSuffix(container, regexp) { + var max = Math.max.apply(Math, container.getIDs().map(function(id) { + var match = regexp.exec(id); + return match && +match[2]; + })); + return max > 0 ? max + 1 : 1; + } + + function enhanceItemWithID(item, id) { + item.setID(id); + return item; + } + + return { + getMeta: getMeta, + getNewId: getNewId, + groupByMetaKey: groupByMetaKey, + makeTitle: makeTitle, + getNextIDSuffix: getNextIDSuffix, + enhanceItemWithID: enhanceItemWithID + } + }) +})(); \ No newline at end of file diff --git a/merlin/static/merlin/lib/barricade.js b/merlin/static/merlin/lib/barricade.js deleted file mode 100644 index b8e7949..0000000 --- a/merlin/static/merlin/lib/barricade.js +++ /dev/null @@ -1,820 +0,0 @@ -// Copyright 2014 Drago Rosson -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -Barricade = (function () { - "use strict"; - - var Blueprint = { - create: function (f) { - var g = function () { - if (this.hasOwnProperty('_parents')) { - this._parents.push(g); - } else { - Object.defineProperty(this, '_parents', { - value: [g] - }); - } - - return f.apply(this, arguments); - }; - - return g; - } - }; - - var Extendable = Blueprint.create(function () { - function forInKeys(obj) { - var key, - keys = []; - - for (key in obj) { - keys.push(key); - } - - return keys; - } - - function isPlainObject(obj) { - return getType(obj) === Object && - Object.getPrototypeOf(Object.getPrototypeOf(obj)) === null; - } - - function extend(extension) { - function addProperty(object, prop) { - return Object.defineProperty(object, prop, { - enumerable: true, - writable: true, - configurable: true, - value: extension[prop] - }); - } - - // add properties to extended object - return Object.keys(extension).reduce(addProperty, - Object.create(this)); - } - - function deepClone(object) { - if (isPlainObject(object)) { - return forInKeys(object).reduce(function (clone, key) { - clone[key] = deepClone(object[key]); - return clone; - }, {}); - } - return object; - } - - function merge(target, source) { - forInKeys(source).forEach(function (key) { - if (target.hasOwnProperty(key) && - isPlainObject(target[key]) && - isPlainObject(source[key])) { - merge(target[key], source[key]); - } else { - target[key] = deepClone(source[key]); - } - }); - } - - return Object.defineProperty(this, 'extend', { - enumerable: false, - writable: false, - value: function (extension, schema) { - if (schema) { - extension._schema = '_schema' in this ? - deepClone(this._schema) : {}; - merge(extension._schema, schema); - } - - return extend.call(this, extension); - } - }); - }); - - var InstanceofMixin = Blueprint.create(function () { - return Object.defineProperty(this, 'instanceof', { - enumerable: false, - value: function (proto) { - var _instanceof = this.instanceof, - subject = this; - - function hasMixin(obj, mixin) { - return obj.hasOwnProperty('_parents') && - obj._parents.some(function (_parent) { - return _instanceof.call(_parent, mixin); - }); - } - - do { - if (subject === proto || - hasMixin(subject, proto)) { - return true; - } - subject = Object.getPrototypeOf(subject); - } while (subject); - - return false; - } - }); - }); - - var Identifiable = Blueprint.create(function (id) { - this.getID = function () { - return id; - }; - - this.setID = function (newID) { - id = newID; - this.emit('change', 'id'); - }; - }); - - var Omittable = Blueprint.create(function (isUsed) { - this.isUsed = function () { - // If required, it has to be used. - return this.isRequired() || isUsed; - }; - - this.setIsUsed = function (newUsedValue) { - isUsed = !!newUsedValue; - }; - - this.on('change', function () { - isUsed = !this.isEmpty(); - }); - }); - - var Deferrable = Blueprint.create(function (schema) { - var self = this, - deferred; - - function resolver(neededValue) { - var ref = schema['@ref'].resolver(self, neededValue); - if (ref === undefined) { - logError('Could not resolve "' + - JSON.stringify(self.toJSON()) + '"'); - } - return ref; - } - - 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 (this.instanceof(Container)) { - this.each(function (index, value) { - if (!value.resolveWith(obj)) { - allResolved = false; - } - }); - } - - return allResolved; - }; - }); - - var Validatable = Blueprint.create(function (schema) { - var constraints = schema['@constraints'], - error = null; - - if (getType(constraints) !== Array) { - constraints = []; - } - - this.hasError = function () { return error !== null; }; - this.getError = function () { return error || ''; }; - - this._validate = function (value) { - function getConstraintMessage(i, lastMessage) { - if (lastMessage !== true) { - return lastMessage; - } else if (i < constraints.length) { - return getConstraintMessage(i + 1, constraints[i](value)); - } - return null; - } - error = getConstraintMessage(0, true); - return !this.hasError(); - }; - - this.addConstraint = function (newConstraint) { - constraints.push(newConstraint); - }; - }); - - var Enumerated = Blueprint.create(function(enum_) { - var self = this; - - function getEnum() { - return (typeof enum_ === 'function') ? enum_.call(self) : enum_; - } - - this.getEnumLabels = function () { - var curEnum = getEnum(); - if (getType(curEnum[0]) === Object) { - return curEnum.map(function (value) { return value.label; }); - } else { - return curEnum; - } - }; - - this.getEnumValues = function () { - var curEnum = getEnum(); - if (getType(curEnum[0]) === Object) { - return curEnum.map(function (value) { return value.value; }); - } else { - return curEnum; - } - }; - - this.addConstraint(function (value) { - return (self.getEnumValues().indexOf(value) > -1) || - 'Value can only be one of ' + self.getEnumLabels().join(', '); - }); - }); - - var Observable = Blueprint.create(function () { - var events = {}; - - function hasEvent(eventName) { - return events.hasOwnProperty(eventName); - } - - // Adds listener for event - this.on = function (eventName, callback) { - if (!hasEvent(eventName)) { - events[eventName] = []; - } - - events[eventName].push(callback); - }; - - // Removes listener for event - this.off = function (eventName, callback) { - var index; - - if (hasEvent(eventName)) { - index = events[eventName].indexOf(callback); - - if (index > -1) { - events[eventName].splice(index, 1); - } - } - }; - - this.emit = function (eventName) { - var args = arguments; // Must come from correct scope - if (events.hasOwnProperty(eventName)) { - events[eventName].forEach(function (callback) { - // Call with emitter as context and pass all but eventName - callback.apply(this, Array.prototype.slice.call(args, 1)); - }, this); - } - }; - }); - - var Deferred = { - create: function (classGetter, onResolve) { - var self = Object.create(this); - self._isResolved = false; - self._classGetter = classGetter; - self._onResolve = onResolve; - return self; - }, - resolve: function (obj) { - var ref; - - if (this._isResolved) { - throw new Error('Deferred already resolved'); - } - - ref = this._onResolve(obj); - - if (ref !== undefined) { - this._isResolved = true; - return ref; - } - }, - isResolved: function () { - return this._isResolved; - }, - needs: function (obj) { - return obj.instanceof(this._classGetter()); - } - }; - - var Base = Extendable.call(InstanceofMixin.call({ - create: function (json, parameters) { - var self = this.extend({}), - schema = self._schema, - type = schema['@type'], - isUsed; - - self._parameters = parameters = parameters || {}; - - if (schema.hasOwnProperty('@inputMassager')) { - json = schema['@inputMassager'](json); - } - - isUsed = self._setData(json); - - if (schema.hasOwnProperty('@toJSON')) { - self.toJSON = schema['@toJSON']; - } - - Observable.call(self); - Omittable.call(self, isUsed); - Deferrable.call(self, schema); - Validatable.call(self, schema); - - if (schema.hasOwnProperty('@enum')) { - Enumerated.call(self, schema['@enum']); - } - - if (parameters.hasOwnProperty('id')) { - Identifiable.call(self, parameters.id); - } - - return self; - }, - _setData: function(json) { - var isUsed = true, - type = this._schema['@type']; - - if (getType(json) !== type) { - if (json) { - logError("Type mismatch (json, schema)"); - logVal(json, this._schema); - } else { - isUsed = false; - } - // Replace bad type (does not change original) - json = this._getDefaultValue(); - } - this._data = this._sift(json, this._parameters); - - return isUsed; - }, - _getDefaultValue: function () { - return this._schema.hasOwnProperty('@default') - ? typeof this._schema['@default'] === 'function' - ? this._schema['@default']() - : this._schema['@default'] - : this._schema['@type'](); - }, - _sift: function () { - throw new Error("sift() must be overridden in subclass"); - }, - _safeInstanceof: function (instance, class_) { - return typeof instance === 'object' && - ('instanceof' in instance) && - instance.instanceof(class_); - }, - getPrimitiveType: function () { - return this._schema['@type']; - }, - isRequired: function () { - return this._schema['@required'] !== false; - }, - isEmpty: function () { - throw new Error('Subclass should override isEmpty()'); - } - })); - - var Container = Base.extend({ - create: function (json, parameters) { - var self = Base.create.call(this, json, parameters), - allDeferred = []; - - function attachListeners(key) { - self._attachListeners(key); - self.get(key)._container = self; - } - - self.on('_addedElement', function (key) { - attachListeners(key); - self._tryResolveOn(self.get(key)); - }); - - self.each(attachListeners); - - self.each(function (index, value) { - value.resolveWith(self); - }); - - return self; - }, - _tryResolveOn: function (value) { - if (!value.resolveWith(this)) { - this.emit('_resolveUp', value); - } - }, - _attachListeners: function (key) { - var self = this, - element = this.get(key), - events = { - 'childChange': function (child) { - self.emit('childChange', child); - }, - 'change': function () { - // 'this' is set to callee, no typo - events.childChange(this); - }, - 'replace': function (newValue) { - self.set(key, newValue); - self._tryResolveOn(newValue); - }, - '_resolveUp': function (value) { - self._tryResolveOn(value); - }, - 'removeFrom': function (container) { - if (container === self) { - Object.keys(events).forEach(function (eName) { - element.on(eName, events[eName]); - }); - } - } - }; - - Object.keys(events).forEach(function (eName) { - element.on(eName, events[eName]); - }); - }, - _getKeyClass: function (key) { - return this._schema[key].hasOwnProperty('@class') - ? this._schema[key]['@class'] - : BarricadeMain.create(this._schema[key]); - }, - _keyClassCreate: function (key, keyClass, json, parameters) { - return this._schema[key].hasOwnProperty('@factory') - ? this._schema[key]['@factory'](json, parameters) - : keyClass.create(json, parameters); - }, - _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()); - }, - set: function (key, value) { - this.get(key).emit('removeFrom', this); - this._doSet(key, value); - this._attachListeners(key); - } - }); - - var Arraylike = Container.extend({ - create: function (json, parameters) { - if (!this.hasOwnProperty('_elementClass')) { - Object.defineProperty(this, '_elementClass', { - enumerable: false, - writable: true, - value: this._getKeyClass(this._elSymbol) - }); - } - - return Container.create.call(this, json, parameters); - }, - _elSymbol: '*', - _sift: function (json, parameters) { - return json.map(function (el) { - return this._keyClassCreate(this._elSymbol, - this._elementClass, el); - }, this); - }, - get: function (index) { - return this._data[index]; - }, - each: function (functionIn, comparatorIn) { - var arr = this._data.slice(); - - if (comparatorIn) { - arr.sort(comparatorIn); - } - - arr.forEach(function (value, index) { - functionIn(index, value); - }); - }, - toArray: function () { - return this._data.slice(); // Shallow copy to prevent mutation - }, - _doSet: function (index, newVal, newParameters) { - var oldVal = this._data[index]; - - if (this._isCorrectType(newVal, this._elementClass)) { - this._data[index] = newVal; - } else { - this._data[index] = this._keyClassCreate( - this._elSymbol, this._elementClass, - newVal, newParameters); - } - - this.emit('change', 'set', index, this._data[index], oldVal); - }, - length: function () { - return this._data.length; - }, - isEmpty: function () { - return this._data.length === 0; - }, - toJSON: function (ignoreUnused) { - return this._data.map(function (el) { - return el.toJSON(ignoreUnused); - }); - }, - push: function (newValue, newParameters) { - if (this._isCorrectType(newValue, this._elementClass)) { - this._data.push(newValue); - } else { - this._data.push(this._keyClassCreate( - this._elSymbol, this._elementClass, - newValue, newParameters)); - } - - this.emit('_addedElement', this._data.length - 1); - this.emit('change', 'add', this._data.length - 1); - }, - remove: function (index) { - this._data[index].emit('removeFrom', this); - this._data.splice(index, 1); - this.emit('change', 'remove', index); - } - }); - - var Array_ = Arraylike.extend({}); - - var ImmutableObject = Container.extend({ - create: function (json, parameters) { - var self = this; - if (!this.hasOwnProperty('_keyClasses')) { - Object.defineProperty(this, '_keyClasses', { - enumerable: false, - writable: true, - value: this.getKeys().reduce(function (classes, key) { - classes[key] = self._getKeyClass(key); - return classes; - }, {}) - }); - } - - return Container.create.call(this, json, parameters); - }, - _sift: function (json, parameters) { - var self = this; - return this.getKeys().reduce(function (objOut, key) { - objOut[key] = self._keyClassCreate( - key, self._keyClasses[key], json[key]); - return objOut; - }, {}); - }, - get: function (key) { - return this._data[key]; - }, - _doSet: function (key, newValue, newParameters) { - var oldVal = this._data[key]; - - if (this._schema.hasOwnProperty(key)) { - if (this._isCorrectType(newValue, - this._keyClasses[key])) { - this._data[key] = newValue; - } else { - this._data[key] = this._keyClassCreate( - key, this._keyClasses[key], - newValue, newParameters); - } - - this.emit('change', 'set', key, this._data[key], oldVal); - } else { - console.error('object does not have key (key, schema)'); - console.log(key, this._schema); - } - }, - each: function (functionIn, comparatorIn) { - var self = this, - keys = this.getKeys(); - - if (comparatorIn) { - keys.sort(comparatorIn); - } - - keys.forEach(function (key) { - functionIn(key, self._data[key]); - }); - }, - isEmpty: function () { - return Object.keys(this._data).length === 0; - }, - toJSON: function (ignoreUnused) { - var data = this._data; - return this.getKeys().reduce(function (jsonOut, key) { - if (ignoreUnused !== true || data[key].isUsed()) { - jsonOut[key] = data[key].toJSON(ignoreUnused); - } - return jsonOut; - }, {}); - }, - getKeys: function () { - return Object.keys(this._schema).filter(function (key) { - return key.charAt(0) !== '@'; - }); - } - }); - - var MutableObject = Arraylike.extend({ - _elSymbol: '?', - _sift: function (json, parameters) { - return Object.keys(json).map(function (key) { - return this._keyClassCreate( - this._elSymbol, this._elementClass, - json[key], {id: key}); - }, this); - }, - getIDs: function () { - return this.toArray().map(function (value) { - return value.getID(); - }); - }, - getPosByID: function(id) { - return this.toArray().map(function (value) { - return value.getID(); - }).indexOf(id); - }, - getByID: function (id) { - var pos = this.getPosByID(id); - return this.get(pos); - }, - contains: function (element) { - return this.toArray().some(function (value) { - return element === value; - }); - }, - toJSON: function (ignoreUnused) { - return this.toArray().reduce(function (jsonOut, element) { - if (jsonOut.hasOwnProperty(element.getID())) { - logError("ID encountered multiple times: " + - element.getID()); - } else { - jsonOut[element.getID()] = - element.toJSON(ignoreUnused); - } - return jsonOut; - }, {}); - }, - push: function (newJson, newParameters) { - if (getType(newParameters) !== Object || - !newParameters.hasOwnProperty('id')) { - logError('ID should be passed in ' + - 'with parameters object'); - } else { - Array_.push.call(this, newJson, newParameters); - } - }, - }); - - var Primitive = Base.extend({ - _sift: function (json, parameters) { - return json; - }, - get: function () { - return this._data; - }, - set: function (newVal) { - var schema = this._schema; - - function typeMatches(newVal) { - return getType(newVal) === schema['@type']; - } - - if (typeMatches(newVal) && this._validate(newVal)) { - this._data = newVal; - this.emit('validation', 'succeeded'); - this.emit('change'); - } else if (this.hasError()) { - this.emit('validation', 'failed'); - } else { - logError("Setter - new value did not match " + - "schema (newVal, schema)"); - logVal(newVal, schema); - } - }, - isEmpty: function () { - if (this._schema['@type'] === Array) { - return this._data.length === 0; - } else if (this._schema['@type'] === Object) { - return Object.keys(this._data).length === 0; - } else { - return this._data === this._schema['@type'](); - } - }, - toJSON: function () { - return this._data; - } - }); - - var getType = (function () { - var toString = Object.prototype.toString, - types = { - 'boolean': Boolean, - 'number': Number, - 'string': String, - '[object Array]': Array, - '[object Date]': Date, - '[object Function]': Function, - '[object RegExp]': RegExp - }; - - return function (val) { - return types[typeof val] || - types[toString.call(val)] || - (val ? Object : null); - }; - }()); - - function logError(msg) { - console.error("Barricade: " + msg); - } - - function logVal(val1, val2) { - if (val2) { - console.log(val1, val2); - } else { - console.log(val1); - } - } - - var BarricadeMain = {}; - - BarricadeMain.create = function (schema) { - function schemaIsMutable() { - return schema.hasOwnProperty('?'); - } - - function schemaIsImmutable() { - return Object.keys(schema).some(function (key) { - return key.charAt(0) !== '@' && key !== '?'; - }); - } - - if (schema['@type'] === Object && schemaIsImmutable()) { - return ImmutableObject.extend({_schema: schema}); - } else if (schema['@type'] === Object && schemaIsMutable()) { - return MutableObject.extend({_schema: schema}); - } else if (schema['@type'] === Array && schema.hasOwnProperty('*')) { - return Array_.extend({_schema: schema}); - } else { - return Primitive.extend({_schema: schema}); - } - }; - - BarricadeMain.getType = getType; // Very helpful function - - BarricadeMain.Base = Base; - BarricadeMain.Container = Container; - BarricadeMain.Array = Array_; - BarricadeMain.ImmutableObject = ImmutableObject; - BarricadeMain.MutableObject = MutableObject; - BarricadeMain.Primitive = Primitive; - BarricadeMain.Blueprint = Blueprint; - BarricadeMain.Observable = Observable; - BarricadeMain.Deferrable = Deferrable; - BarricadeMain.Omittable = Omittable; - BarricadeMain.Identifiable = Identifiable; - BarricadeMain.Enumerated = Enumerated; - - return BarricadeMain; - -}()); diff --git a/extensions/mistral/static/mistral/js/angular-templates/collapsible-group.html b/merlin/static/merlin/templates/collapsible-group.html similarity index 100% rename from extensions/mistral/static/mistral/js/angular-templates/collapsible-group.html rename to merlin/static/merlin/templates/collapsible-group.html diff --git a/merlin/static/merlin/templates/collapsible-panel.html b/merlin/static/merlin/templates/collapsible-panel.html new file mode 100644 index 0000000..bc16ec0 --- /dev/null +++ b/merlin/static/merlin/templates/collapsible-panel.html @@ -0,0 +1,11 @@ +
+
+

+ {$ title $} +

+
+
+
+
+
+
\ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/angular-templates/editable-popup.html b/merlin/static/merlin/templates/editable-popup.html similarity index 100% rename from extensions/mistral/static/mistral/js/angular-templates/editable-popup.html rename to merlin/static/merlin/templates/editable-popup.html diff --git a/merlin/static/merlin/templates/fields/dictionary.html b/merlin/static/merlin/templates/fields/dictionary.html new file mode 100644 index 0000000..c0a2ebb --- /dev/null +++ b/merlin/static/merlin/templates/fields/dictionary.html @@ -0,0 +1,19 @@ + +
+
+
+ +
+ + + + +
+
+
+
+
diff --git a/merlin/static/merlin/templates/fields/frozendict.html b/merlin/static/merlin/templates/fields/frozendict.html new file mode 100644 index 0000000..f3a4407 --- /dev/null +++ b/merlin/static/merlin/templates/fields/frozendict.html @@ -0,0 +1,15 @@ + +
+
+
+
+ + +
+
+
+
+
+
diff --git a/merlin/static/merlin/templates/fields/group.html b/merlin/static/merlin/templates/fields/group.html new file mode 100644 index 0000000..23e997c --- /dev/null +++ b/merlin/static/merlin/templates/fields/group.html @@ -0,0 +1,12 @@ + +
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/list.html b/merlin/static/merlin/templates/fields/list.html similarity index 55% rename from extensions/mistral/static/mistral/js/angular-templates/fields/list.html rename to merlin/static/merlin/templates/fields/list.html index 4db9d73..d0ecd2e 100644 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/list.html +++ b/merlin/static/merlin/templates/fields/list.html @@ -1,11 +1,11 @@ - +
-
+
- + - diff --git a/merlin/static/merlin/templates/fields/number.html b/merlin/static/merlin/templates/fields/number.html new file mode 100644 index 0000000..4f6a4ff --- /dev/null +++ b/merlin/static/merlin/templates/fields/number.html @@ -0,0 +1,5 @@ +
+ + +
diff --git a/merlin/static/merlin/templates/fields/string.html b/merlin/static/merlin/templates/fields/string.html new file mode 100644 index 0000000..a12b803 --- /dev/null +++ b/merlin/static/merlin/templates/fields/string.html @@ -0,0 +1,5 @@ +
+ + +
diff --git a/merlin/static/merlin/templates/fields/text.html b/merlin/static/merlin/templates/fields/text.html new file mode 100644 index 0000000..5670edf --- /dev/null +++ b/merlin/static/merlin/templates/fields/text.html @@ -0,0 +1,5 @@ +
+ + +