diff --git a/extensions/enabled/_50_add_mistral_panel.py b/extensions/enabled/_50_add_mistral_panel.py index 61cc9e6..64f3ddb 100644 --- a/extensions/enabled/_50_add_mistral_panel.py +++ b/extensions/enabled/_50_add_mistral_panel.py @@ -9,3 +9,6 @@ ADD_INSTALLED_APPS = ['merlin', 'mistral'] # Python panel class of the PANEL to be added. ADD_PANEL = 'mistral.panel.MistralPanel' + +ADD_ANGULAR_MODULES = ['angular.filter'] +ADD_JS_FILES = ['merlin/lib/angular-filter.js'] \ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/angular-templates/collapsible-panel.html b/extensions/mistral/static/mistral/js/angular-templates/collapsible-panel.html index a954cdf..aa60ea9 100644 --- a/extensions/mistral/static/mistral/js/angular-templates/collapsible-panel.html +++ b/extensions/mistral/static/mistral/js/angular-templates/collapsible-panel.html @@ -1,10 +1,16 @@
-
-

- {$ title $} -

+
+
+

+ {$ title $} +

+
+
+
+
+
-
+
diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/dictionary.html b/extensions/mistral/static/mistral/js/angular-templates/fields/dictionary.html index 3fcd1b1..ce4e7dc 100644 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/dictionary.html +++ b/extensions/mistral/static/mistral/js/angular-templates/fields/dictionary.html @@ -1,13 +1,15 @@ -
-
-
- -
- - + +
+
+
+ +
+ + +
-
+ diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/frozendict.html b/extensions/mistral/static/mistral/js/angular-templates/fields/frozendict.html index f023f5e..ba71eab 100644 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/frozendict.html +++ b/extensions/mistral/static/mistral/js/angular-templates/fields/frozendict.html @@ -1,4 +1,4 @@ - +
diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/group.html b/extensions/mistral/static/mistral/js/angular-templates/fields/group.html new file mode 100644 index 0000000..7267e48 --- /dev/null +++ b/extensions/mistral/static/mistral/js/angular-templates/fields/group.html @@ -0,0 +1,11 @@ + +
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/text.html b/extensions/mistral/static/mistral/js/angular-templates/fields/text.html new file mode 100644 index 0000000..e6f6d4f --- /dev/null +++ b/extensions/mistral/static/mistral/js/angular-templates/fields/text.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/varlist.html b/extensions/mistral/static/mistral/js/angular-templates/fields/varlist.html index d228694..5b3d966 100644 --- a/extensions/mistral/static/mistral/js/angular-templates/fields/varlist.html +++ b/extensions/mistral/static/mistral/js/angular-templates/fields/varlist.html @@ -1,4 +1,5 @@ - +
diff --git a/extensions/mistral/static/mistral/js/angular-templates/fields/yaqllist.html b/extensions/mistral/static/mistral/js/angular-templates/fields/yaqllist.html new file mode 100644 index 0000000..89a81f1 --- /dev/null +++ b/extensions/mistral/static/mistral/js/angular-templates/fields/yaqllist.html @@ -0,0 +1,18 @@ + +
+ +
+
+
+ + + +
+
+
+
+
diff --git a/extensions/mistral/static/mistral/js/controllers.js b/extensions/mistral/static/mistral/js/controllers.js index ac2e9ac..37eb93a 100644 --- a/extensions/mistral/static/mistral/js/controllers.js +++ b/extensions/mistral/static/mistral/js/controllers.js @@ -65,33 +65,219 @@ type: 'list', value: ['', ''] }] - } - ] + }], + workflows: [{ + id: 'workflow1', + name: 'Workflow1', + base: '', // FIXME + input: [''], + output: [{ + id: 'varlist1', + type: 'string', + value: '' + }], + taskDefaults: { + onError: { + type: 'list', + value: ['', ''] + }, + onSuccess: { + type: 'list', + value: [''] + }, + onComplete: { + type: 'list', + value: ['', ''] + } + } + }] }; $scope.schema = { - action: [{ - name: 'name', + name: { type: 'string', - group: 'one' - }, { - name: 'base', - type: 'string', - group: 'one' - }, { - name: 'baseInput', - type: 'frozendict', - group: '' - }, { - name: 'input', - type: 'list', - group: '' - }, { - name: 'output', - type: 'varlist', - group: '' + index: 0, + panelIndex: 0, + row: 0 + }, + description: { + type: 'text', + index: 1, + panelIndex: 0, + row: 0 + }, + actions: { + index: 2, + type: 'panel', + multiple: true, + value: { + name: { + type: 'string', + row: 0, + index: 0 + }, + base: { + type: 'string', + row: 0, + index: 1 + }, + baseInput: { + type: 'frozendict', + title: 'Base Input', + index: 2 + }, + input: { + type: 'list', + index: 3 + }, + output: { + type: 'varlist', + index: 4 + } + } + }, + workflows: { + index: 3, + type: 'panel', + multiple: true, + value: { + name: { + type: 'string', + index: 0, + row: 0 + }, + base: { + type: 'string', + index: 1, + row: 0 + }, + input: { + type: 'list', + index: 2 + }, + output: { + type: 'varlist', + index: 3 + }, + taskDefaults: { + type: 'group', + title: 'Task defaults', + additive: false, + index: 4, + value: { + onError: { + type: 'yaqllist', + title: 'On error', + index: 0 + }, + onSuccess: { + type: 'yaqllist', + title: 'On success', + index: 1 + }, + onComplete: { + type: 'yaqllist', + title: 'On complete', + index: 2 + } + } + }, + tasks: { + type: 'group', + index: 5, + value: { + task: { + type: 'group', + additive: false, + multiple: true, + index: 0, + value: { + name: { + type: 'string', + index: 0, + row: 0 + }, + type: { + type: 'string', + index: 1, + row: 0 + }, + action: { + type: 'string', + index: 2, + row: 1 + }, + input: { + type: 'dictionary', + index: 3 + }, + publish: { + type: 'dictionary', + index: 4 + }, + onError: { + type: 'yaqllist', + title: 'On error', + index: 5 + }, + onSuccess: { + type: 'yaqllist', + title: 'On success', + index: 6 + }, + onComplete: { + type: 'yaqllist', + title: 'On complete', + index: 7 + }, + policies: { + type: 'group', + additive: false, + index: 8, + value: { + waitBefore: { + type: 'string', + title: 'Wait before', + index: 0, + row: 0 + }, + waitAfter: { + type: 'string', + title: 'Wait after', + index: 1, + row: 0 + }, + timeout: { + type: 'string', + index: 2, + row: 1 + }, + retryCount: { + type: 'string', + title: 'Retry count', + index: 3, + row: 2 + }, + retryDelay: { + type: 'string', + title: 'Retry delay', + index: 4, + row: 2 + }, + retryBreakOn: { + type: 'string', + title: 'Retry break on', + index: 5, + row: 3 + } + } + } + } + } + } + } + } } - ] }; $scope.makeTitle = function(str) { @@ -107,7 +293,7 @@ }; $scope.isAtomic = function(type) { - return ['string'].indexOf(type) > -1; + return ['string', 'text'].indexOf(type) > -1; }; $scope.remove = function(parent, item) { diff --git a/extensions/mistral/static/mistral/js/directives.js b/extensions/mistral/static/mistral/js/directives.js index 213e2b5..8b9bdb1 100644 --- a/extensions/mistral/static/mistral/js/directives.js +++ b/extensions/mistral/static/mistral/js/directives.js @@ -60,22 +60,25 @@ } }) - .directive('collapsiblePanel', function($parse, defaultSetter) { + .directive('panel', function() { return { restrict: 'E', templateUrl: '/static/mistral/js/angular-templates/collapsible-panel.html', transclude: true, scope: { title: '@', - removable: '&' + onRemove: '&' }, link: function(scope, element, attrs) { disableClickDefaultBehaviour(element); + if ( attrs.onRemove ) { + scope.removable = true; + } } } }) - .directive('collapsibleGroup', function($parse, defaultSetter) { + .directive('collapsibleGroup', function() { return { restrict: 'E', templateUrl: '/static/mistral/js/angular-templates/collapsible-group.html', diff --git a/extensions/mistral/static/mistral/js/schema.js b/extensions/mistral/static/mistral/js/schema.js index 7c30107..bfeb451 100644 --- a/extensions/mistral/static/mistral/js/schema.js +++ b/extensions/mistral/static/mistral/js/schema.js @@ -1,324 +1,329 @@ - /* Copyright (c) 2014 Mirantis, Inc. +/* 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 + 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 + 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. -*/ + 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. + */ - 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(); - } - }; +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 || []; +types.base.AcceptsMixin = Barricade.Blueprint.create(function (acceptsList) { + acceptsList = acceptsList || []; - this.getLabels = function() { - return acceptsList.map(function(item) { - return item.label; - }) - }; + 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; - } - }); + 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.Action = Barricade.create({ - '@type': Object, +types.Mistral.Action = Barricade.create({ + '@type': Object, + '@meta': {'groups': ['panel2']}, - 'name': {'@type': String}, - 'base': { - '@type': String, - '@enum': function() { - var predefinedActions = types.getOpenStackActions(), - actions = workbook.get('actions'), - currentItemIndex = actions.length() - 1; - actions.each(function(index, actionItem) { - var name = actionItem.get('name'); - if ( index < currentItemIndex && !name.isEmpty() ) { - predefinedActions = predefinedActions.concat(name.get()) - } - }); - return predefinedActions; - }, - '@default': types.getOpenStackActions()[0] - }, - 'base-input': { - '@type': Object, - '@required': false, - '?': {'@type': String} - } - }); + 'name': {'@type': String}, + 'base': { + '@type': String, + '@enum': function() { + var predefinedActions = types.getOpenStackActions(), + actions = workbook.get('actions'), + currentItemIndex = actions.length() - 1; + actions.each(function(index, actionItem) { + var name = actionItem.get('name'); + if ( index < currentItemIndex && !name.isEmpty() ) { + predefinedActions = predefinedActions.concat(name.get()) + } + }); + return predefinedActions; + }, + '@default': types.getOpenStackActions()[0] + }, + 'base-input': { + '@type': Object, + '@required': false, + '?': {'@type': String} + } +}); - types.Mistral.Policy = Barricade.create({ - '@type': Object, +types.Mistral.Policy = Barricade.create({ + '@type': Object, - 'wait-before': { - '@type': Number, - '@required': false - }, - 'wait-after': { - '@type': Number, - '@required': false - }, - 'retry': { - '@type': Object, - '@required': false, - 'count': {'@type': Number}, - 'delay': {'@type': Number}, - 'break-on': { - '@type': String, - '@required': false - } - }, - 'timeout': { - '@type': Number, - '@required': false - } - }); + 'wait-before': { + '@type': Number, + '@required': false + }, + 'wait-after': { + '@type': Number, + '@required': false + }, + 'retry': { + '@type': Object, + '@required': false, + 'count': {'@type': Number}, + 'delay': {'@type': Number}, + 'break-on': { + '@type': String, + '@required': false + } + }, + 'timeout': { + '@type': Number, + '@required': false + } +}); - types.Mistral.Task = Barricade.create({ - '@type': Object, +types.Mistral.Task = Barricade.create({ + '@type': Object, - '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 - } - }); + '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); +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(); - } + 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 []; - } - } - }) - } - } - }; + 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.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.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; } - )); - } - } - }); +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; } + )); + } + } + }); - types.Mistral.Workflow = Barricade.create({ - '@type': Object, +types.Mistral.Workflow = Barricade.create({ + '@type': Object, + '@meta': {'groups': 'panel3'}, - 'name': {'@type': String}, - 'type': { - '@type': String, - '@enum': ['reverse', 'direct'], - '@default': 'direct' - }, - 'input': { - '@type': Array, - '@required': false, - '*': { - '@class': Barricade.Primitive.extend({ - 'name': 'Primitive' - }, { - '@type': String - }) - } - }, - 'output': { - '@type': String, - '@required': false - }, - 'task-defaults': { - '@type': Object, - '@required': false, - 'on-error': {'@type': String}, - 'on-success': {'@type': String}, - 'on-complete': {'@type': String}, - 'policies': {'@class': types.Mistral.Policy} - }, - 'tasks': { - '@class': types.Mistral.Tasks - } + 'name': {'@type': String}, + 'type': { + '@type': String, + '@enum': ['reverse', 'direct'], + '@default': 'direct' + }, + 'input': { + '@type': Array, + '@required': false, + '*': { + '@class': Barricade.Primitive.extend({ + 'name': 'Primitive' + }, { + '@type': String + }) + } + }, + 'output': { + '@type': String, + '@required': false + }, + 'task-defaults': { + '@type': Object, + '@required': false, + 'on-error': {'@type': String}, + 'on-success': {'@type': String}, + 'on-complete': {'@type': String}, + 'policies': {'@class': types.Mistral.Policy} + }, + 'tasks': { + '@class': types.Mistral.Tasks + } - }); +}); - types.Mistral.Workbook = Barricade.create({ - '@type': Object, +types.Mistral.Workbook = Barricade.create({ + '@type': Object, - 'version': { - '@type': Number, - '@default': 2 - }, - 'name': { - '@type': String - }, - 'description': { - '@type': String, - '@required': false - }, - 'actions': { - '@type': Object, - '@required': false, - '?': { - '@class': types.Mistral.Action - } - }, - 'workflows': { - '@type': Object, - '?': { - '@class': types.Mistral.Workflow - } - } - }); + 'version': { + '@type': Number, + '@meta': {'groups': ['panel1']}, + '@default': 2 + }, + 'name': { + '@type': String, + '@meta': {'groups': ['panel1']} + }, + 'description': { + '@type': String, + '@meta': {'groups': ['panel1']}, + '@required': false + }, + 'actions': { + '@type': Object, + '@required': false, + '?': { + '@class': types.Mistral.Action + } + }, + 'workflows': { + '@type': Object, + '?': { + '@class': types.Mistral.Workflow + } + } +}); diff --git a/extensions/mistral/static/mistral/js/services.js b/extensions/mistral/static/mistral/js/services.js index 337d261..684bb5f 100644 --- a/extensions/mistral/static/mistral/js/services.js +++ b/extensions/mistral/static/mistral/js/services.js @@ -16,7 +16,8 @@ }) .run(function($http, $templateCache) { - var fields = ['dictionary', 'frozendict', 'list', 'string', 'varlist']; + var fields = ['dictionary', 'frozendict', 'list', 'string', + 'varlist', 'text', 'group', 'yaqllist']; fields.forEach(function(field) { var base = '/static/mistral/js/angular-templates/fields/'; $http.get(base + field + '.html').success(function(templateContent) { @@ -25,4 +26,42 @@ }) }) + .filter('prepareSchema', function($filter) { + var toArray = $filter('toArray'), + orderBy = $filter('orderBy'); + function schemaToArray(schema) { + return angular.isArray(schema) ? schema : orderBy( + toArray(schema, true), 'index').map(function(item) { + item.name = item.$key; + if ( item.type === 'panel' ) { + item.panelIndex = item.index; + } + if ( item.type === 'panel' || item.type === 'group' ) { + item.value = schemaToArray(item.value); + } + return item; + }); + } + return schemaToArray; + }) + + .filter('normalizePanels', function() { + return function(collection) { + return collection.map(function(panelSpec) { + if ( panelSpec[0].type === 'panel' ) { + var data = panelSpec[0]; + panelSpec.length = data.value.length; + for ( var i = 0; i < panelSpec.length; i++ ) { + panelSpec[i] = data.value[i]; + } + panelSpec.multiple = data.multiple; + panelSpec.name = data.name; + } + return panelSpec; + }); + } + }) + + + })(); \ No newline at end of file diff --git a/extensions/mistral/templates/mistral/create.html b/extensions/mistral/templates/mistral/create.html index df7ffb8..e2197f7 100644 --- a/extensions/mistral/templates/mistral/create.html +++ b/extensions/mistral/templates/mistral/create.html @@ -51,40 +51,34 @@
- -
-
-
-
-
- - -
-
-
-
- - +
+ +
+
+
+ +
-
+ + + +
+
+
+ +
+
+
+
+
- - -
-
-
- -
-
-
-
-
- +
@@ -267,7 +261,7 @@ - +
diff --git a/merlin/static/merlin/lib/angular-filter.min.js b/merlin/static/merlin/lib/angular-filter.min.js new file mode 100644 index 0000000..44aa6b2 --- /dev/null +++ b/merlin/static/merlin/lib/angular-filter.min.js @@ -0,0 +1,6 @@ +/** + * Bunch of useful filters for angularJS(with no external dependencies!) + * @version v0.5.1 - 2014-11-12 * @link https://github.com/a8m/angular-filter + * @author Ariel Mashraki + * @license MIT License, http://www.opensource.org/licenses/MIT + */!function(a,b,c){"use strict";function d(a){return D(a)?a:Object.keys(a).map(function(b){return a[b]})}function e(a){return null===a}function f(a,b){var c=Object.keys(a);return-1==c.map(function(c){return!(!b[c]||b[c]!=a[c])}).indexOf(!1)}function g(a,b){if(""===b)return a;var c=a.indexOf(b.charAt(0));return-1===c?!1:g(a.substr(c+1),b.substr(1))}function h(a,b,c){var d=0;return a.filter(function(a){var e=x(c)?b>d&&c(a):b>d;return d=e?d+1:d,e})}function i(a,b,c){return c.round(a*c.pow(10,b))/c.pow(10,b)}function j(a,b,c){b=b||[];var d=Object.keys(a);return d.forEach(function(d){if(C(a[d])&&!D(a[d])){var e=c?c+"."+d:c;j(a[d],b,e||d)}else{var f=c?c+"."+d:d;b.push(f)}}),b}function k(a){return a&&a.$evalAsync&&a.$watch}function l(){return function(a,b){return a>b}}function m(){return function(a,b){return a>=b}}function n(){return function(a,b){return b>a}}function o(){return function(a,b){return b>=a}}function p(){return function(a,b){return a==b}}function q(){return function(a,b){return a!=b}}function r(){return function(a,b){return a===b}}function s(){return function(a,b){return a!==b}}function t(a){return function(b,c){return b=C(b)?d(b):b,!D(b)||y(c)?!0:b.some(function(b){return C(b)||z(c)?a(c)(b):b===c})}}function u(a,b){return b=b||0,b>=a.length?a:D(a[b])?u(a.slice(0,b).concat(a[b],a.slice(b+1)),b):u(a,b+1)}function v(a){return function(b,c){function e(a,b){return y(b)?!1:a.some(function(a){return H(a,b)})}if(b=C(b)?d(b):b,!D(b))return b;var f=[],g=a(c);return b.filter(y(c)?function(a,b,c){return c.indexOf(a)===b}:function(a){var b=g(a);return e(f,b)?!1:(f.push(b),!0)})}}function w(a,b,c){return b?a+c+w(a,--b,c):a}var x=b.isDefined,y=b.isUndefined,z=b.isFunction,A=b.isString,B=b.isNumber,C=b.isObject,D=b.isArray,E=b.forEach,F=b.extend,G=b.copy,H=b.equals;String.prototype.contains||(String.prototype.contains=function(){return-1!==String.prototype.indexOf.apply(this,arguments)}),b.module("a8m.angular",[]).filter("isUndefined",function(){return function(a){return b.isUndefined(a)}}).filter("isDefined",function(){return function(a){return b.isDefined(a)}}).filter("isFunction",function(){return function(a){return b.isFunction(a)}}).filter("isString",function(){return function(a){return b.isString(a)}}).filter("isNumber",function(){return function(a){return b.isNumber(a)}}).filter("isArray",function(){return function(a){return b.isArray(a)}}).filter("isObject",function(){return function(a){return b.isObject(a)}}).filter("isEqual",function(){return function(a,c){return b.equals(a,c)}}),b.module("a8m.conditions",[]).filter({isGreaterThan:l,">":l,isGreaterThanOrEqualTo:m,">=":m,isLessThan:n,"<":n,isLessThanOrEqualTo:o,"<=":o,isEqualTo:p,"==":p,isNotEqualTo:q,"!=":q,isIdenticalTo:r,"===":r,isNotIdenticalTo:s,"!==":s}),b.module("a8m.is-null",[]).filter("isNull",function(){return function(a){return e(a)}}),b.module("a8m.after-where",[]).filter("afterWhere",function(){return function(a,b){if(a=C(a)?d(a):a,!D(a)||y(b))return a;var c=a.map(function(a){return f(b,a)}).indexOf(!0);return a.slice(-1===c?0:c)}}),b.module("a8m.after",[]).filter("after",function(){return function(a,b){return a=C(a)?d(a):a,D(a)?a.slice(b):a}}),b.module("a8m.before-where",[]).filter("beforeWhere",function(){return function(a,b){if(a=C(a)?d(a):a,!D(a)||y(b))return a;var c=a.map(function(a){return f(b,a)}).indexOf(!0);return a.slice(0,-1===c?a.length:++c)}}),b.module("a8m.before",[]).filter("before",function(){return function(a,b){return a=C(a)?d(a):a,D(a)?a.slice(0,b?--b:b):a}}),b.module("a8m.concat",[]).filter("concat",[function(){return function(a,b){if(y(b))return a;if(D(a))return a.concat(C(b)?d(b):b);if(C(a)){var c=d(a);return c.concat(C(b)?d(b):b)}return a}}]),b.module("a8m.contains",[]).filter({contains:["$parse",t],some:["$parse",t]}),b.module("a8m.count-by",[]).filter("countBy",["$parse",function(a){return function(b,c){var e,f={},g=a(c);return b=C(b)?d(b):b,!D(b)||y(c)?b:(b.forEach(function(a){e=g(a),f[e]||(f[e]=0),f[e]++}),f)}}]),b.module("a8m.defaults",[]).filter("defaults",["$parse",function(a){return function(b,c){if(b=C(b)?d(b):b,!D(b)||!C(c))return b;var e=j(c);return b.forEach(function(b){e.forEach(function(d){var e=a(d),f=e.assign;y(e(b))&&f(b,e(c))})}),b}}]),b.module("a8m.every",[]).filter("every",["$parse",function(a){return function(b,c){return b=C(b)?d(b):b,!D(b)||y(c)?!0:b.every(function(b){return C(b)||z(c)?a(c)(b):b===c})}}]),b.module("a8m.filter-by",[]).filter("filterBy",["$parse",function(a){return function(b,e,f){var g;return f=A(f)||B(f)?String(f).toLowerCase():c,b=C(b)?d(b):b,!D(b)||y(f)?b:b.filter(function(b){return e.some(function(c){if(~c.indexOf("+")){var d=c.replace(new RegExp("\\s","g"),"").split("+");g=d.reduce(function(c,d,e){return 1===e?a(c)(b)+" "+a(d)(b):c+" "+a(d)(b)})}else g=a(c)(b);return A(g)||B(g)?String(g).toLowerCase().contains(f):!1})})}}]),b.module("a8m.first",[]).filter("first",["$parse",function(a){return function(b){var e,f,g;return b=C(b)?d(b):b,D(b)?(g=Array.prototype.slice.call(arguments,1),e=B(g[0])?g[0]:1,f=B(g[0])?B(g[1])?c:g[1]:g[0],g.length?h(b,e,f?a(f):f):b[0]):b}}]),b.module("a8m.flatten",[]).filter("flatten",function(){return function(a,b){return b=b||!1,a=C(a)?d(a):a,D(a)?b?[].concat.apply([],a):u(a,0):a}}),b.module("a8m.fuzzy-by",[]).filter("fuzzyBy",["$parse",function(a){return function(b,c,e,f){var h,i,j=f||!1;return b=C(b)?d(b):b,!D(b)||y(c)||y(e)?b:(i=a(c),b.filter(function(a){return h=i(a),A(h)?(h=j?h:h.toLowerCase(),e=j?e:e.toLowerCase(),g(h,e)!==!1):!1}))}}]),b.module("a8m.fuzzy",[]).filter("fuzzy",function(){return function(a,b,c){function e(a,b){var c,d,e=Object.keys(a);return 0=0&&B(b)&&isFinite(b)?1024>b?i(b,c,a)+" B":1048576>b?i(b/1024,c,a)+" KB":1073741824>b?i(b/1048576,c,a)+" MB":i(b/1073741824,c,a)+" GB":"NaN"}}]),b.module("a8m.math.degrees",["a8m.math"]).filter("degrees",["$math",function(a){return function(b,c){if(B(c)&&isFinite(c)&&c%1===0&&c>=0&&B(b)&&isFinite(b)){var d=180*b/a.PI;return a.round(d*a.pow(10,c))/a.pow(10,c)}return"NaN"}}]),b.module("a8m.math.kbFmt",["a8m.math"]).filter("kbFmt",["$math",function(a){return function(b,c){return B(c)&&isFinite(c)&&c%1===0&&c>=0&&B(b)&&isFinite(b)?1024>b?i(b,c,a)+" KB":1048576>b?i(b/1024,c,a)+" MB":i(b/1048576,c,a)+" GB":"NaN"}}]),b.module("a8m.math",[]).factory("$math",["$window",function(a){return a.Math}]),b.module("a8m.math.max",["a8m.math"]).filter("max",["$math","$parse",function(a,b){function c(c,d){var e=c.map(function(a){return b(d)(a)});return e.indexOf(a.max.apply(a,e))}return function(b,d){return D(b)?y(d)?a.max.apply(a,b):b[c(b,d)]:b}}]),b.module("a8m.math.min",["a8m.math"]).filter("min",["$math","$parse",function(a,b){function c(c,d){var e=c.map(function(a){return b(d)(a)});return e.indexOf(a.min.apply(a,e))}return function(b,d){return D(b)?y(d)?a.min.apply(a,b):b[c(b,d)]:b}}]),b.module("a8m.math.percent",["a8m.math"]).filter("percent",["$math","$window",function(a,b){return function(c,d,e){var f=A(c)?b.Number(c):c;return d=d||100,e=e||!1,!B(f)||b.isNaN(f)?c:e?a.round(f/d*100):f/d*100}}]),b.module("a8m.math.radians",["a8m.math"]).filter("radians",["$math",function(a){return function(b,c){if(B(c)&&isFinite(c)&&c%1===0&&c>=0&&B(b)&&isFinite(b)){var d=3.14159265359*b/180;return a.round(d*a.pow(10,c))/a.pow(10,c)}return"NaN"}}]),b.module("a8m.math.radix",[]).filter("radix",function(){return function(a,b){var c=/^[2-9]$|^[1-2]\d$|^3[0-6]$/;return B(a)&&c.test(b)?a.toString(b).toUpperCase():a}}),b.module("a8m.math.shortFmt",["a8m.math"]).filter("shortFmt",["$math",function(a){return function(b,c){return B(c)&&isFinite(c)&&c%1===0&&c>=0&&B(b)&&isFinite(b)?1e3>b?b:1e6>b?i(b/1e3,c,a)+" K":1e9>b?i(b/1e6,c,a)+" M":i(b/1e9,c,a)+" B":"NaN"}}]),b.module("a8m.math.sum",[]).filter("sum",function(){return function(a,b){return D(a)?a.reduce(function(a,b){return a+b},b||0):a}}),b.module("a8m.ends-with",[]).filter("endsWith",function(){return function(a,b,c){var d,e=c||!1;return!A(a)||y(b)?a:(a=e?a:a.toLowerCase(),d=a.length-b.length,-1!==a.indexOf(e?b:b.toLowerCase(),d))}}),b.module("a8m.ltrim",[]).filter("ltrim",function(){return function(a,b){var c=b||"\\s";return A(a)?a.replace(new RegExp("^"+c+"+"),""):a}}),b.module("a8m.repeat",[]).filter("repeat",[function(){return function(a,b,c){var d=~~b;return A(a)&&d?w(a,--b,c||""):a}}]),b.module("a8m.rtrim",[]).filter("rtrim",function(){return function(a,b){var c=b||"\\s";return A(a)?a.replace(new RegExp(c+"+$"),""):a}}),b.module("a8m.slugify",[]).filter("slugify",[function(){return function(a,b){var c=y(b)?"-":b;return A(a)?a.toLowerCase().replace(/\s+/g,c):a}}]),b.module("a8m.starts-with",[]).filter("startsWith",function(){return function(a,b,c){var d=c||!1;return!A(a)||y(b)?a:(a=d?a:a.toLowerCase(),!a.indexOf(d?b:b.toLowerCase()))}}),b.module("a8m.stringular",[]).filter("stringular",function(){return function(a){var b=Array.prototype.slice.call(arguments,1);return a.replace(/{(\d+)}/g,function(a,c){return y(b[c])?a:b[c]})}}),b.module("a8m.strip-tags",[]).filter("stripTags",function(){return function(a){return A(a)?a.replace(/<\S[^><]*>/g,""):a}}),b.module("a8m.trim",[]).filter("trim",function(){return function(a,b){var c=b||"\\s";return A(a)?a.replace(new RegExp("^"+c+"+|"+c+"+$","g"),""):a}}),b.module("a8m.truncate",[]).filter("truncate",function(){return function(a,b,c,d){return b=y(b)?a.length:b,d=d||!1,c=c||"",!A(a)||a.length<=b?a:a.substring(0,d?-1===a.indexOf(" ",b)?a.length:a.indexOf(" ",b):b)+c}}),b.module("a8m.ucfirst",[]).filter("ucfirst",[function(){return function(a){return b.isString(a)?a.split(" ").map(function(a){return a.charAt(0).toUpperCase()+a.substring(1)}).join(" "):a}}]),b.module("a8m.uri-component-encode",[]).filter("uriComponentEncode",["$window",function(a){return function(b){return A(b)?a.encodeURIComponent(b):b}}]),b.module("a8m.uri-encode",[]).filter("uriEncode",["$window",function(a){return function(b){return A(b)?a.encodeURI(b):b}}]),b.module("a8m.wrap",[]).filter("wrap",function(){return function(a,b,c){return!A(a)||y(b)?a:[b,a,c||b].join("")}}),b.module("a8m.filter-watcher",[]).provider("filterWatcher",function(){this.$get=["$window","$rootScope",function(a,b){function c(a,b){return[a,JSON.stringify(b)].join("#").replace(/"/g,"")}function d(a){var b=a.targetScope.$id;E(j[b],function(a){delete i[a]}),delete j[b]}function e(){l(function(){b.$$phase||(i={})})}function f(a,b){var c=a.$id;return y(j[c])&&(a.$on("$destroy",d),j[c]=[]),j[c].push(b)}function g(a,b){var d=c(a,b);return i[d]}function h(a,b,d,g){var h=c(a,b);return i[h]=g,k(d)?f(d,h):e(),g}var i={},j={},l=a.setTimeout;return{isMemoized:g,memoize:h}}]}),b.module("angular.filter",["a8m.ucfirst","a8m.uri-encode","a8m.uri-component-encode","a8m.slugify","a8m.strip-tags","a8m.stringular","a8m.truncate","a8m.starts-with","a8m.ends-with","a8m.wrap","a8m.trim","a8m.ltrim","a8m.rtrim","a8m.repeat","a8m.to-array","a8m.concat","a8m.contains","a8m.unique","a8m.is-empty","a8m.after","a8m.after-where","a8m.before","a8m.before-where","a8m.defaults","a8m.where","a8m.reverse","a8m.remove","a8m.remove-with","a8m.group-by","a8m.count-by","a8m.search-field","a8m.fuzzy-by","a8m.fuzzy","a8m.omit","a8m.pick","a8m.every","a8m.filter-by","a8m.xor","a8m.map","a8m.first","a8m.last","a8m.flatten","a8m.math","a8m.math.max","a8m.math.min","a8m.math.percent","a8m.math.radix","a8m.math.sum","a8m.math.degrees","a8m.math.radians","a8m.math.byteFmt","a8m.math.kbFmt","a8m.math.shortFmt","a8m.angular","a8m.conditions","a8m.is-null","a8m.filter-watcher"])}(window,window.angular); \ No newline at end of file