| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- // Licensed to Cloudera, Inc. under one
- // or more contributor license agreements. See the NOTICE file
- // distributed with this work for additional information
- // regarding copyright ownership. Cloudera, Inc. licenses this file
- // to you 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.
- /**
- * Design representation
- */
- var Design = (function($, ko, NodeFields) {
- var module = function(options) {
- var self = this;
- self.options = {};
- self.model = {};
- self.initialize(options);
- };
- // NodeFields is defined in oozie/static/js/workflow.node-fields.js.
- // It provides the more advanced field manipulation for the fields
- // that hava JSON representation.
- $.extend(module.prototype, NodeFields, {
- initialize: function(options) {
- var self = this;
- self.options = $.extend(self.options, options);
- self.model = options.model;
- self.model.errors = self.model.errors || {};
- for(var key in self.model) {
- switch(key) {
- case 'initialize':
- case 'toString':
- case 'errors':
- break;
- default:
- if (!(key in self.model.errors)) {
- self.model.errors[key] = [];
- }
- break;
- }
- }
- // @see http://knockoutjs.com/documentation/plugins-mapping.html
- // MAPPING_OPTIONS comes from /oozie/static/js/workflow.models.js
- // We don't update the observed object using ko.mapping because
- // the plugin does not work with mixed objects.
- ko.mapping.fromJS(self.model, $.extend({
- is_shared: {
- create: function(options) {
- return ko.observable(($.type(options.data) == "string") ? value.toLowerCase() == "true" : new Boolean(options.data).valueOf());
- },
- update: function(options) {
- return ($.type(options.data) == "string") ? value.toLowerCase() == "true" : new Boolean(options.data);
- }
- },
- parameters: {
- // Will receive individual objects to subscribe.
- // Containing array is mapped automagically
- create: function(options) {
- var parent = options.parent;
- var subscribe = function(mapping) {
- mapping.name.subscribe(function(value) {
- parent.parameters.valueHasMutated();
- });
- mapping.value.subscribe(function(value) {
- parent.parameters.valueHasMutated();
- });
- };
- return map_params(options, subscribe);
- },
- update: function(options) {
- var parent = options.parent;
- var subscribe = function(mapping) {
- mapping.name.subscribe(function(value) {
- parent.parameters.valueHasMutated();
- });
- mapping.value.subscribe(function(value) {
- parent.parameters.valueHasMutated();
- });
- };
- return map_params(options, subscribe);
- }
- }
- }, MAPPING_OPTIONS), self);
- // hack on '<key>ErrorClass' and '<key>Condition'.
- $.each(self.__ko_mapping__, function(key, enabled) {
- if (ko.isObservable(self[key])) {
- self[key+'_condition'] = ko.computed(function() {
- return self[key]().length > 0;
- });
- self[key+'_error_class'] = ko.computed(function() {
- return ( self.errors[key]().length > 0 ) ? 'control-group error' : 'control-group';
- });
- }
- });
- if (!('is_dirty' in self)) {
- self.is_dirty = ko.observable(true);
- }
- if (!('new' in self)) {
- self.new = ko.computed(function() {
- return !self.id();
- });
- }
- if (!('editable' in self)) {
- self.editable = ko.observable(false);
- }
- if ('files' in self) {
- var files = self.files();
- self.files = ko.observableArray([]);
- // ['file', ...] => [{'name': 'file', 'dummy': ''}, ...].
- $.each(files, function(index, filename) {
- var prop = { name: ko.observable(filename), dummy: ko.observable("") };
- prop.name.subscribe(function(value) {
- self.files.valueHasMutated();
- });
- prop.dummy.subscribe(function(value) {
- self.files.valueHasMutated();
- });
- self.files.push(prop);
- });
- }
- $(document).trigger('initialize.design', [options, self]);
- },
- request: function(url, options) {
- var self = this;
- var request = $.extend({
- url: url,
- dataType: 'json',
- type: 'GET',
- success: $.noop,
- error: $.noop
- }, options || {});
- $.ajax(request);
- },
- load: function(options) {
- var self = this;
- var options = $.extend({
- success: function(data) {
- self.is_dirty(false);
- $(document).trigger('load.design', [options, data, self]);
- }
- }, options);
- this.request('/jobsub/designs/' + self.id(), options);
- },
- save: function(options) {
- // First try to save, then update error list if fail.
- // Response should be json object. IE: {data: {errors: {files: ['example', ...], ... }}}
- var self = this;
- var model_dict = {};
- $.each(ko.mapping.toJS(self), function(key, value) {
- if (key != 'errors') {
- if (key == 'files') {
- // [{'name': 'file', 'dummy': ''}, ...] => ['file', ...].
- var files = ko.utils.unwrapObservable(value);
- model_dict[key] = [];
- $.each(files, function(i, value) {
- model_dict[key].push(value.name);
- });
- } else {
- model_dict[key] = ko.utils.unwrapObservable(value);
- }
- }
- });
- var data = normalize_model_fields($.parseJSON(JSON.stringify(model_dict)));
- var options = $.extend({
- type: 'POST',
- data: data,
- error: function(xhr) {
- var response = $.parseJSON(xhr.responseText);
- if (response) {
- var model = ko.mapping.toJS(self);
- $.extend(model.errors, response.data.errors);
- ko.mapping.fromJS(model, self);
- $(document).trigger('error.design', [options, data]);
- }
- },
- success: function(data) {
- $(document).trigger('save.design', [options, data]);
- }
- }, options);
- self.request((self.new()) ? '/jobsub/designs/'+self.node_type()+'/new' : '/jobsub/designs/'+self.id()+'/save', options);
- },
- clone: function(options) {
- var self = this;
- var options = $.extend({
- type: 'POST',
- success: function(data) {
- $(document).trigger('clone.design', [options, data]);
- }
- }, options);
- this.request('/jobsub/designs/' + self.id() + '/clone', options);
- },
- delete: function(options) {
- var self = this;
- var options = $.extend({
- type: 'POST',
- success: function(data) {
- $(document).trigger('delete.design', [options, data]);
- }
- }, options);
- this.request('/jobsub/designs/' + self.id() + '/delete', options);
- },
- // More node field methods
- addParameter: function(data) {
- var self = this;
- var prop = { name: ko.observable(""), value: ko.observable("") };
- prop.name.subscribe(function(value) {
- self.parameters.valueHasMutated();
- });
- prop.value.subscribe(function(value) {
- self.parameters.valueHasMutated();
- });
- self.parameters.push(prop);
- $(document).trigger('add.parameter.workflow', [data]);
- },
- removeParameter: function(data) {
- var self = this;
- self.parameters.remove(data);
- $(document).trigger('remove.parameter.workflow', [data]);
- }
- });
- return module;
- })($, ko, NodeFields);
- /**
- * List of designs
- */
- var Designs = (function($, ko, NodeModelChooser) {
- var module = function(options) {
- var self = this;
- self.options = options || {
- models: []
- };
- self.temporary = ko.observable();
- self.designs = ko.observableArray([]);
- self.selectedDesignObjects = ko.computed(function() {
- var selected = [];
- $.each(self.designs(), function(index, designObject) {
- if (designObject.selected()) {
- selected.push(designObject);
- }
- });
- return selected;
- });
- self.selectedDesignObject = ko.computed(function () {
- return self.selectedDesignObjects()[0];
- });
- self.selectedDesign = ko.computed(function() {
- if (self.selectedDesignObject()) {
- return self.selectedDesignObject().design();
- } else {
- return null;
- }
- });
- self.selectedIndex = ko.computed(function() {
- var selected = -1;
- $.each(self.designs(), function(index, designObject) {
- if (selected == -1 && designObject.selected()) {
- selected = index;
- }
- });
- return selected;
- });
- self.allSelected = ko.computed(function() {
- return self.selectedDesignObjects().length == self.designs().length && self.selectedDesignObjects().length > 0;
- });
- self.initialize(options);
- };
- $.extend(module.prototype, {
- initialize: function(options) {
- var self = this;
- self.options = $.extend(self.options, options);
- self.designs.removeAll();
- self.createDesigns(self.options.models);
- self.temporary({
- design: ko.observable(null),
- selected: ko.observable(false),
- template: ko.observable(null)
- })
- self.deselectAll();
- $(document).trigger('initialize.designs', [options, self]);
- },
- load: function(options) {
- // Fetch designs from backend.
- var self = this;
- var request = $.extend({
- url: '/jobsub/designs',
- dataType: 'json',
- type: 'GET',
- success: function(data) {
- $(document).trigger('load.designs', [options, data]);
- self.initialize({models: data});
- },
- error: $.noop
- }, options || {});
- $.ajax(request);
- },
- ensureListFields: function(model) {
- if (!('name' in model)) {
- model.name = '';
- }
- if (!('description' in model)) {
- model.description = '';
- }
- if (!('owner' in model)) {
- model.owner = '';
- }
- if (!('last_modified' in model)) {
- model.last_modified = 0;
- }
- return model;
- },
- createDesign: function(model) {
- var self = this;
- var NodeModel = NodeModelChooser(model.node_type);
- var node_model = new NodeModel(self.ensureListFields(model));
- var design = new Design({model: node_model});
- return design;
- },
- createDesigns: function(models) {
- var self = this;
- $.each(models, function(index, model) {
- self.designs.push({
- design: ko.observable(self.createDesign(model)),
- selected: ko.observable(false),
- template: ko.observable(model.node_type)
- });
- });
- },
- toggleSelect: function(index) {
- var self = this;
- self.designs()[index].selected(!self.designs()[index].selected());
- },
- select: function(index) {
- var self = this;
- self.designs()[index].selected(true);
- },
- toggleSelectAll: function() {
- var self = this;
- if (self.allSelected()) {
- self.deselectAll();
- } else {
- self.selectAll();
- }
- },
- selectAll: function() {
- var self = this;
- $.each(self.designs(), function(index, value) {
- value.selected(true);
- });
- },
- deselectAll: function() {
- var self = this;
- $.each(self.designs(), function(index, value) {
- value.selected(false);
- });
- },
- //// Design delegation
- newDesign: function(node_type) {
- var self = this;
- var design = self.createDesign({
- id: null,
- node_type: node_type,
- is_shared: true,
- parameters: '[{"name":"oozie.use.system.libpath","value":"true"}]'
- });
- // Reversing the order of the next two statements may cause KO to break.
- self.temporary().template(node_type);
- self.temporary().design(design);
- // Do not do any thing with any other design.
- self.deselectAll();
- $(document).trigger('new.design', [design]);
- },
- saveDesign: function(data, event) {
- var self = this;
- self.temporary().design().save();
- },
- cloneDesigns: function() {
- var self = this;
- $.each(self.selectedDesignObjects(), function(index, designObject) {
- designObject.design().clone();
- });
- },
- deleteDesigns: function() {
- var self = this;
- $.each(self.selectedDesignObjects(), function(index, designObject) {
- designObject.design().delete();
- });
- },
- editDesign: function(index) {
- var self = this;
- if (self.selectedDesignObject()) {
- var design = self.selectedDesignObject().design();
- if (design.is_dirty()) {
- $(document).one('load.design', function(e, options, data, design) {
- design.initialize({model: data});
- self.temporary().design(design);
- self.temporary().template(self.selectedDesignObject().template());
- $(document).trigger('edit.design', [design]);
- });
- design.load();
- } else {
- self.temporary().design(design);
- self.temporary().template(self.selectedDesignObject().template());
- $(document).trigger('edit.design', [design]);
- }
- }
- },
- closeDesign: function() {
- var self = this;
- self.temporary().design(null);
- self.temporary().template(null);
- }
- });
- return module;
- })($, ko, nodeModelChooser);
- var designs = new Designs({models: []});
|