/********************************************* * Copyright (c) 2014 AnsibleWorks, Inc. * * FormGenerator * Pass in a form definition from FormDefinitions and out pops an html template. * See js/form-definitions.js for form example. For now produces a Twitter Bootstrap * horizontal form. * */ 'use strict'; angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) .factory('GenerateForm', ['$rootScope', '$location', '$cookieStore', '$compile', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'Column', 'NavigationLink', 'HelpCollapse', 'Button', 'DropDown', 'Empty', 'SelectIcon', function($rootScope, $location, $cookieStore, $compile, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, HelpCollapse, Button, DropDown, Empty, SelectIcon) { return { setForm: function(form) { this.form = form; }, attr: Attr, icon: Icon, accordion_count: 0, scope: null, has: function(key) { return (this.form[key] && this.form[key] != null && this.form[key] != undefined) ? true : false; }, inject: function(form, options) { // // Use to inject the form as html into the view. View MUST have an ng-bind for 'htmlTemplate'.t // Returns scope of form. // var element; if (options.modal) { if (options.modal_body_id) { element = angular.element(document.getElementById(options.modal_body_id)); } else { // use default dialog element = angular.element(document.getElementById('form-modal-body')); } } else { if (options.id) { element = angular.element(document.getElementById(options.id)); } else { element = angular.element(document.getElementById('htmlTemplate')); } } this.mode = options.mode; this.modal = (options.modal) ? true : false; this.setForm(form); if (options.html) { element.html(options.html); } else { element.html(this.build(options)); } if (options.scope) { this.scope = options.scope; } else { this.scope = element.scope(); } $compile(element)(this.scope); if (!options.html) { // Reset the scope to prevent displaying old data from our last visit to this form for (var fld in form.fields) { this.scope[fld] = null; } for (var set in form.related) { this.scope[set] = null; } if ( ((!options.modal) && options.related) || this.form.forceListeners ) { this.addListeners(); } if (options.mode == 'add') { this.applyDefaults(); } } // Remove any lingering tooltip and popover
elements $('.tooltip').each( function(index) { $(this).remove(); }); $('.popover').each(function(index) { // remove lingering popover
. Seems to be a bug in TB3 RC1 $(this).remove(); }); $(window).unbind('resize'); // Prepend an asterisk to required field label $('.form-control[required], input[type="radio"][required]').each(function() { if ( Empty($(this).attr('aw-required-when')) ) { var label = $(this).parent().parent().find('label').first(); if ($(this).attr('type') == 'radio') { label = $(this).parent().parent().parent().find('label').first(); } if (label.length > 0) { var span = label.children('span'); if (span.length > 0 && !span.first().hasClass('prepend-asterisk')) { span.first().addClass('prepend-asterisk'); } else if (span.length <= 0 && !label.first().hasClass('prepend-asterisk')) { label.first().addClass('prepend-asterisk'); } } } }); try { $('#help-modal').empty().dialog('destroy'); } catch(e) { //ignore any errors should the dialog not be initialized } if (options.modal) { this.scope.formModalActionDisabled = false; this.scope.formModalInfo = false //Disable info button for default modal if (form) { if (options.modal_title_id) { this.scope[options.modal_title_id] = (options.mode == 'add') ? form.addTitle : form.editTitle; } else { this.scope.formModalHeader = (options.mode == 'add') ? form.addTitle : form.editTitle; //Default title for default modal } } if (options.modal_selector) { $(options.modal_selector).modal({ show: true, backdrop: 'static', keyboard: true }); $(options.modal_selector).on('shown.bs.modal', function() { $(options.modal_select + ' input:first').focus(); }); $(options.modal_selector).on('hidden.bs.modal', function() { $('.tooltip').each( function(index) { // Remove any lingering tooltip and popover
elements $(this).remove(); }); $('.popover').each(function(index) { // remove lingering popover
. Seems to be a bug in TB3 RC1 $(this).remove(); }); }); } else { var show = (options.show_modal == false) ? false : true; $('#form-modal').modal({ show: show, backdrop: 'static', keyboard: true }); $('#form-modal').on('shown.bs.modal', function() { $('#form-modal input:first').focus(); }); $('#form-modal').on('hidden.bs.modal', function() { $('.tooltip').each( function(index) { // Remove any lingering tooltip and popover
elements $(this).remove(); }); $('.popover').each(function(index) { // remove lingering popover
. Seems to be a bug in TB3 RC1 $(this).remove(); }); }); } $(document).bind('keydown', function(e) { if (e.keyCode === 27) { if (options.modal_selector) { $(options.modal_selector).modal('hide'); } $('#prompt-modal').modal('hide'); $('#form-modal').modal('hide'); } }); } if (!this.scope.$$phase) { this.scope.$digest(); } return this.scope; }, applyDefaults: function() { for (var fld in this.form.fields) { if (this.form.fields[fld]['default'] || this.form.fields[fld]['default'] == 0) { if (this.form.fields[fld].type == 'select' && this.scope[fld + '_options']) { this.scope[fld] = this.scope[fld + '_options'][this.form.fields[fld]['default']]; } else { this.scope[fld] = this.form.fields[fld]['default']; } } } }, reset: function() { // The form field values cannot be reset with jQuery. Each field is tied to a model, so to clear the field // value, you have to clear the model. if (this.scope[this.form.name + '_form']) { this.scope[this.form.name + '_form'].$setPristine(); } var scope = this.scope; var form = this.form; function resetField(f, fld) { // f is the field object, fld is the key if (f.type == 'checkbox_group') { for (var i=0; i < f.fields.length; i++) { scope[f.fields[i].name] = ''; scope[f.fields[i].name + '_api_error'] = ''; scope[form.name + '_form'][f.fields[i].name].$setValidity('apiError', true); } } else { scope[fld] = ''; scope[fld + '_api_error'] = ''; } if (f.sourceModel) { scope[f.sourceModel + '_' + f.sourceField] = ''; scope[f.sourceModel + '_' + f.sourceField + '_api_error'] = ''; if (scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField]) { scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); } } if (f.type == 'lookup' && scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField]) { scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setPristine(); scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); } if (scope[form.name + '_form'][fld]) { scope[form.name + '_form'][fld].$setPristine(); scope[form.name + '_form'][fld].$setValidity('apiError', true); } if (f.chkPass && scope[form.name + '_form'][fld]) { scope[form.name + '_form'][fld].$setValidity('complexity', true); $('#progbar').css({ width: 0 }); } if (f.awPassMatch && scope[form.name + '_form'][fld]) { scope[form.name + '_form'][fld].$setValidity('awpassmatch', true); } if (f.ask) { scope[fld + '_ask'] = false; } } for (var fld in form.fields) { resetField(form.fields[fld], fld); } if (form.statusFields) { for (var fld in form.statusFields) { resetField(form.statusFields[fld], fld); } } if (this.mode == 'add') { this.applyDefaults(); } }, addListeners: function() { if (this.modal) { $('.jqui-accordion-modal').accordion({ collapsible: false, heightStyle: 'content', active: 0 }); } else { this.scope.accordionToggle = function(selector) { $(selector).collapse('toggle'); if ( $(selector + '-icon').hasClass('fa-minus') ) { $(selector + '-icon').removeClass('fa-minus').addClass('fa-plus'); } else { $(selector + '-icon').removeClass('fa-plus').addClass('fa-minus') } } $('.jqui-accordion').each( function(index) { var active = false; var list = $cookieStore.get('accordions'); var found = false; if (list) { var id = $(this).attr('id'); var base = ($location.path().replace(/^\//,'').split('/')[0]); for (var i=0; i < list.length && found == false; i++) { if (list[i].base == base && list[i].id == id) { found = true; active = list[i].active; } } } if (found == false && $(this).attr('data-open') == 'true') { active = 0; } $(this).accordion({ collapsible: true, heightStyle: 'content', active: active, activate: function( event, ui ) { $('.jqui-accordion').each( function(index) { var active = $(this).accordion('option', 'active'); var id = $(this).attr('id'); var base = ($location.path().replace(/^\//,'').split('/')[0]); var list = $cookieStore.get('accordions'); if (list == null || list == undefined) { list = []; } var found = false; for (var i=0; i < list.length && found == false; i++) { if ( list[i].base == base && list[i].id == id) { found = true; list[i].active = active; } } if (found == false) { list.push({ base: base, id: id, active: active }); } $cookieStore.put('accordions',list); }); } }); }); } }, genID: function() { var id = new Date(); return id.getTime(); }, headerField: function(fld, field, options) { var html = ''; if (field.label) { html += "\n"; } html += ""; } html += "'; //html += (field.awPopOver && !field.awPopOverRight) ? Attr(field, 'awPopOver', fld) : ""; html += (field.icon) ? this.icon(field.icon) : ""; html += "" + field.label + ""; html += (field.awPopOver && !field.awPopOverRight) ? Attr(field, 'awPopOver', fld) : ""; html += "\n"; } return html; } var html = ''; var horizontal = (this.form.horizontal) ? true : false; if (field.type == 'alertblock') { html += "
\n"; html += "
\n"; html += "
×\n" : ""; html += field.alertTxt; html += "
\n"; html += "
\n"; html += "
\n"; } if (field.type == 'hidden') { if ( (options.mode == 'edit' && field.includeOnEdit) || (options.mode == 'add' && field.includeOnAdd) ) { html += ""; } } if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { html += "
\n" : ""; if (field.control === null || field.control === undefined || field.control) { html += "\n"; html += "\n
\n"; if (field.ask) { html += "
\n"; } // Add error messages if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) || field.awRequiredWhen ) { html += "
A value is required!
\n"; } if (field.type == "email") { html += "
A valid email address is required!
\n"; } if (field.awPassMatch) { html += "
Must match Password value
\n"; } if (field.awValidUrl) { html += "
URL must begin with ssh, http or https and may not contain '@'
\n"; } html += "
\n"; if (field.chkPass) { // complexity error html += "
Password must be stronger
\n"; // progress bar html += "
\n"; html += "
\n"; html += "
\n"; html += "
\n"; // help panel html += HelpCollapse({ hdr: 'Password Complexity', content: "

A password with reasonable strength is required. As you type the password " + "a progress bar will measure the strength. Sufficient strength is reached when the bar turns green.

" + "

Password strength is judged using the following:

" + "
    " + "
  • Minimum 8 characters in length
  • \n" + "
  • Contains a sufficient combination of the following items:\n" + "
      \n" + "
    • UPPERCASE letters
    • \n" + "
    • Numbers
    • \n" + "
    • Symbols _!@#$%^&*()
    • \n" + "
    \n" + "
  • \n" + "
\n", idx: this.accordion_count++, show: null }); html += "
\n"; } // Add help panel(s) html += (field.helpCollapse) ? this.buildHelpCollapse(field.helpCollapse) : ''; html += "
\n"; } //textarea fields if (field.type == 'textarea') { html += label(); html += "
Parse as: " + " YAML\n"; html += " JSON\n" html += "
\n"; } html += "