Code coverage report for src/directives/schema-form.js

Statements: 98.28% (57 / 58)      Branches: 83.33% (25 / 30)      Functions: 100% (10 / 10)      Lines: 98.28% (57 / 58)      Ignored: none     

All files » src/directives/ » schema-form.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147          1         36 36 89 89 89       36               47 37                   47             47 47 47 47   47 47 47 47 1   1             47 47 47   283 283     283     54 54   54 54           54 7   54     54     54     54 54   54 1       54 89   89     89 68 68 1     1 1         88       54     54     54 167 6 6 5         283            
/*
FIXME: real documentation
<form sf-form="form"  sf-schema="schema" sf-decorator="foobar"></form>
*/
 
angular.module('schemaForm')
       .directive('sfSchema',
['$compile', 'schemaForm', 'schemaFormDecorators', 'sfSelect', 'sfPath',
  function($compile,  schemaForm,  schemaFormDecorators, sfSelect, sfPath) {
 
    var SNAKE_CASE_REGEXP = /[A-Z]/g;
    var snakeCase = function(name, separator) {
      separator = separator || '_';
      return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
        return (pos ? separator : '') + letter.toLowerCase();
      });
    };
 
    return {
      scope: {
        schema: '=sfSchema',
        initialForm: '=sfForm',
        model: '=sfModel',
        options: '=sfOptions'
      },
      controller: ['$scope', function($scope) {
        this.evalInParentScope = function(expr, locals) {
          return $scope.$parent.$eval(expr, locals);
        };
      }],
      replace: false,
      restrict: 'A',
      transclude: true,
      require: '?form',
      link: function(scope, element, attrs, formCtrl, transclude) {
 
        //expose form controller on scope so that we don't force authors to use name on form
        scope.formCtrl = formCtrl;
 
        //We'd like to handle existing markup,
        //besides using it in our template we also
        //check for ng-model and add that to an ignore list
        //i.e. even if form has a definition for it or form is ["*"]
        //we don't generate it.
        var ignore = {};
        transclude(scope, function(clone) {
          clone.addClass('schema-form-ignore');
          element.prepend(clone);
 
          Eif (element[0].querySelectorAll) {
            var models = element[0].querySelectorAll('[ng-model]');
            Eif (models) {
              for (var i = 0; i < models.length; i++) {
                var key = models[i].getAttribute('ng-model');
                //skip first part before .
                ignore[key.substring(key.indexOf('.') + 1)] = true;
              }
            }
          }
        });
        //Since we are dependant on up to three
        //attributes we'll do a common watch
        var lastDigest = {};
        var childScope;
        scope.$watch(function() {
 
          var schema = scope.schema;
          var form   = scope.initialForm || ['*'];
 
          //The check for schema.type is to ensure that schema is not {}
          if (form && schema && schema.type &&
              (lastDigest.form !== form || lastDigest.schema !== schema) &&
              Object.keys(schema.properties).length > 0) {
            lastDigest.schema = schema;
            lastDigest.form = form;
 
            var merged = schemaForm.merge(schema, form, ignore, scope.options);
            var frag = document.createDocumentFragment();
 
            // Create a new form and destroy the old one.
            // Not doing keeps old form elements hanging around after
            // they have been removed from the DOM
            // https://github.com/Textalk/angular-schema-form/issues/200
            if (childScope) {
              childScope.$destroy();
            }
            childScope = scope.$new();
 
            //make the form available to decorators
            childScope.schemaForm  = {form:  merged, schema: schema};
 
            //clean all but pre existing html.
            element.children(':not(.schema-form-ignore)').remove();
 
            // Find all slots.
            var slots = {};
            var slotsFound = element[0].querySelectorAll('*[sf-insert-field]');
 
            for (var i = 0; i < slotsFound.length; i++) {
              slots[slotsFound[i].getAttribute('sf-insert-field')] = slotsFound[i];
            }
 
            //Create directives from the form definition
            angular.forEach(merged, function(obj, i) {
              var n = document.createElement(attrs.sfDecorator ||
                                             snakeCase(schemaFormDecorators.defaultDecorator, '-'));
              n.setAttribute('form','schemaForm.form['+i+']');
 
              // Check if there is a slot to put this in...
              if (obj.key) {
                var slot = slots[sfPath.stringify(obj.key)];
                if (slot) {
                  while (slot.firstChild) {
                    slot.removeChild(slot.firstChild);
                  }
                  slot.appendChild(n);
                  return;
                }
              }
 
              // ...otherwise add it to the frag
              frag.appendChild(n);
 
            });
 
            element[0].appendChild(frag);
 
            //compile only children
            $compile(element.children())(childScope);
 
            //ok, now that that is done let's set any defaults
            schemaForm.traverseSchema(schema, function(prop, path) {
              if (angular.isDefined(prop['default'])) {
                var val = sfSelect(path, scope.model);
                if (angular.isUndefined(val)) {
                  sfSelect(path, scope.model, prop['default']);
                }
              }
            });
          };
          scope.$emit('sf-render-finished', element);
        });
      }
    };
  }
]);