// AUTOGENERATED FILE - DO NOT EDIT!
// django-angular - v0.7.0 - 2014-05-25
// https://github.com/jrief/django-angular
// Copyright (c) 2014 Jacob Rief; Licensed MIT
(function (angular, undefined) {
  'use strict';
  // module: ng.django.forms
  // Correct Angular's form.FormController behavior after rendering bound forms.
  // Additional validators for form elements.
  var djng_forms_module = angular.module('ng.django.forms', []);
  // This directive overrides some of the internal behavior on forms if used together with AngularJS.
  // If not used, the content of bound forms is not displayed, because AngularJS does not know about
  // the concept of bound forms.
  // TODO: Find out, if the form was bound or unbound. This can be done looking at the fields values
  // or by adding a special value to each field. If forms are unbound, use that information to send
  // data via PUT rather than POST, since this is how new objects shall be created. An alternative
  // would be to create a directive djng-bound-form, which shall be added to a form, whenever it is
  // bound. This can easily be done from django.
  djng_forms_module.directive('form', function () {
    function restoreInputFields(form, fields) {
      angular.forEach(fields, function (field) {
        var model_field = form[field.name];
        if (model_field !== undefined) {
          // restore the field's content from the rendered content of bound fields
          switch (field.type) {
          case 'text':
          case 'email':
          case 'number':
          case 'url':
            model_field.$setViewValue(field.defaultValue);
            break;
          case 'radio':
            if (field.defaultChecked) {
              model_field.$setViewValue(field.defaultValue);
            }
            break;
          case 'checkbox':
            if (field.defaultChecked) {
              model_field.$setViewValue(true);
            }
            break;
          case 'password':
            // after an (un)successful submission, reset the password field
            model_field.$setViewValue(null);
            break;
          }
        }
      });
    }
    function restoreSelectOptions(form, selects) {
      angular.forEach(selects, function (field) {
        var multivalues = [];
        if (form[field.name] !== undefined) {
          angular.forEach(field.options, function (option) {
            if (option.defaultSelected) {
              // restore the select option to selected
              angular.element(option).prop('selected', 'selected');
              if (field.multiple) {
                multivalues.push(option.value);
              } else {
                form[field.name].$setViewValue(option.value);
              }
            }
          });
          if (field.multiple) {
            form[field.name].$setViewValue(multivalues);
          }
        }
      });
    }
    function restoreTextAreas(form, textareas) {
      angular.forEach(textareas, function (field) {
        if (form[field.name] !== undefined && field.defaultValue) {
          // restore the field's content from the rendered content of bound fields
          form[field.name].$setViewValue(field.defaultValue);
        }
      });
    }
    return {
      restrict: 'E',
      scope: 'isolate',
      priority: -1,
      link: function (scope, element, attrs) {
        var form = scope[attrs.name], ngelem = angular.element(element);
        restoreInputFields(form, ngelem.find('input'));
        restoreSelectOptions(form, ngelem.find('select'));
        restoreTextAreas(form, ngelem.find('textarea'));
        // restore the form's pristine state
        form.$setPristine();
      }
    };
  });
  // This directive can be added to an input field which shall validate inserted dates, for example:
  // <input ng-model="a_date" type="text" validate-date="^(\d{4})-(\d{1,2})-(\d{1,2})$" />
  // Now, such an input field is only considered valid, if the date is a valid date and if it matches
  // against the given regular expression.
  djng_forms_module.directive('validateDate', function () {
    var validDatePattern = null;
    function validateDate(date) {
      var matched, dateobj;
      if (!date)
        // empty field are validated by the "required" validator
        return true;
      dateobj = new Date(date);
      if (isNaN(dateobj))
        return false;
      if (validDatePattern) {
        matched = validDatePattern.exec(date);
        return matched && parseInt(matched[2]) === dateobj.getMonth() + 1;
      }
      return true;
    }
    return {
      require: 'ngModel',
      restrict: 'A',
      scope: 'isolate',
      link: function (scope, elem, attrs, controller) {
        if (attrs.validateDate) {
          // if a pattern is set, only valid dates with that pattern are accepted
          validDatePattern = new RegExp(attrs.validateDate, 'g');
        }
        // watch for modifications on input fields containing attribute 'validate-date="/pattern/"'
        scope.$watch(attrs.ngModel, function (date) {
          if (controller.$pristine)
            return;
          controller.$setValidity('date', validateDate(date));
        });
      }
    };
  });
  // If forms are validated using Ajax, the server shall return a dictionary of detected errors to the
  // client code. The success-handler of this Ajax call, now can set those error messages on their
  // prepared list-items. The simplest way, is to add this code snippet into the controllers function
  // which is responsible for submitting form data using Ajax:
  //  $http.post("/path/to/url", $scope.data).success(function(data) {
  //      djangoForm.setErrors($scope.form, data.errors);
  //  });
  // djangoForm.setErrors returns false, if no errors have been transferred.
  djng_forms_module.factory('djangoForm', function () {
    var NON_FIELD_ERRORS = '__all__';
    function isNotEmpty(obj) {
      for (var p in obj) {
        if (obj.hasOwnProperty(p))
          return true;
      }
      return false;
    }
    return {
      setErrors: function (form, errors) {
        // remove errors from this form, which may have been rejected by an earlier validation
        form.$message = '';
        if (form.$error.hasOwnProperty('rejected')) {
          angular.forEach(form.$error.rejected, function (rejected) {
            var field, key = rejected.$name;
            if (form.hasOwnProperty(key)) {
              field = form[key];
              field.$message = '';
              field.$setValidity('rejected', true);
              if (field.rejectedListenerPos !== undefined) {
                field.$viewChangeListeners.splice(field.rejectedListenerPos, 1);
                delete field.rejectedListenerPos;
              }
            }
          });
        }
        // add the new upstream errors
        angular.forEach(errors, function (errors, key) {
          var field;
          if (errors.length > 0) {
            if (key === NON_FIELD_ERRORS) {
              form.$message = errors[0];
              form.$setPristine();
            } else if (form.hasOwnProperty(key)) {
              field = form[key];
              field.$message = errors[0];
              field.$setValidity('rejected', false);
              field.$setPristine();
              field.rejectedListenerPos = field.$viewChangeListeners.push(function () {
                // changing the field the server complained about, resets the form into valid state
                field.$setValidity('rejected', true);
                field.$viewChangeListeners.splice(field.rejectedListenerPos, 1);
                delete field.rejectedListenerPos;
              }) - 1;
            }
          }
        });
        return isNotEmpty(errors);
      }
    };
  });
  // A simple wrapper to extend the $httpProvider for executing remote methods on the server side
  // for Django Views derived from JSONResponseMixin.
  // It can be used to invoke GET and POST request. The return value is the same promise as returned
  // by $http.get() and $http.post().
  // Usage:
  // djangoRMI.name.method(data).success(...).error(...)
  // @param data (optional): If set and @allowd_action was auto, then the call is performed as method
  //     POST. If data is unset, method GET is used. data must be a valid JavaScript object or undefined.
  djng_forms_module.provider('djangoRMI', function () {
    var remote_methods, http;
    this.configure = function (conf) {
      remote_methods = conf;
      convert_configuration(remote_methods);
    };
    function convert_configuration(obj) {
      angular.forEach(obj, function (val, key) {
        if (!angular.isObject(val))
          throw new Error('djangoRMI.configure got invalid data');
        if (val.hasOwnProperty('url')) {
          // convert config object into function
          val.headers['X-Requested-With'] = 'XMLHttpRequest';
          obj[key] = function (data) {
            var config = angular.copy(val);
            if (config.method === 'POST') {
              if (data === undefined)
                throw new Error('Calling remote method ' + key + ' without data object');
              config.data = data;
            } else if (config.method === 'auto') {
              if (data === undefined) {
                config.method = 'GET';
              } else {
                // TODO: distinguish between POST and PUT
                config.method = 'POST';
                config.data = data;
              }
            }
            return http(config);
          };
        } else {
          // continue to examine the values recursively
          convert_configuration(val);
        }
      });
    }
    this.$get = [
      '$http',
      function ($http) {
        http = $http;
        return remote_methods;
      }
    ];
  });
}(window.angular));
(function (angular, undefined) {
  'use strict';
  function noop() {
  }
  // Add three-way data-binding for AngularJS with Django using websockets.
  var djng_ws_module = angular.module('ng.django.websocket', []);
  // Wraps the built-in WebSocket into a replaceable provider suitable for dependency injection.
  djng_ws_module.service('$websocket', function () {
    var ws;
    this.connect = function (url) {
      ws = new WebSocket(url);
      ws.onopen = this.onopen;
      ws.onmessage = this.onmessage;
      ws.onerror = this.onerror;
      ws.onclose = this.onclose;
    };
    this.send = function (msg) {
      ws.send(msg);
    };
    this.close = function () {
      ws.close();
    };
  });
  djng_ws_module.provider('djangoWebsocket', function () {
    var _console = {
        log: noop,
        warn: noop,
        error: noop
      };
    var websocket_uri, heartbeat_msg = null;
    // Set prefix for the Websocket's URI.
    // This URI must be set during initialization using
    // djangoWebsocketProvider.setURI('{{ WEBSOCKET_URI }}');
    this.setURI = function (uri) {
      websocket_uri = uri;
      return this;
    };
    // Set the heartbeat message and activate the heartbeat interval to 5 seconds.
    // The heartbeat message shall be configured using
    // djangoWebsocketProvider.setHeartbeat({{ WS4REDIS_HEARTBEAT }});  // unquoted!
    // The default behavior is to not listen on heartbeats.
    this.setHeartbeat = function (msg) {
      heartbeat_msg = msg;
      return this;
    };
    this.setLogLevel = function (logLevel) {
      switch (logLevel) {
      case 'debug':
        _console = console;
        break;
      case 'log':
        _console.log = console.log;
      /* falls through */
      case 'warn':
        _console.warn = console.warn;
      /* falls through */
      case 'error':
        _console.error = console.error;
      /* falls through */
      default:
        break;
      }
      return this;
    };
    this.$get = [
      '$websocket',
      '$q',
      '$timeout',
      '$interval',
      function ($websocket, $q, $timeout, $interval) {
        var ws_url, deferred, scope, collection;
        var is_subscriber = false, is_publisher = false, receiving = false;
        var wait_for_reconnect = 0, heartbeat_promise = null, missed_heartbeats = 0;
        function connect() {
          _console.log('Connecting to ' + ws_url);
          deferred = $q.defer();
          $websocket.connect(ws_url);
        }
        $websocket.onopen = function (evt) {
          _console.log('Connected');
          deferred.resolve();
          wait_for_reconnect = 0;
          if (heartbeat_msg && heartbeat_promise === null) {
            missed_heartbeats = 0;
            heartbeat_promise = $interval(sendHeartbeat, 5000);
          }
        };
        $websocket.onclose = function (evt) {
          _console.log('Disconnected');
          deferred.reject();
          wait_for_reconnect = Math.min(wait_for_reconnect + 1000, 10000);
          $timeout(function () {
            $websocket.connect(ws_url);
          }, wait_for_reconnect);
        };
        $websocket.onerror = function (evt) {
          _console.error('Websocket connection is broken!');
          $websocket.close();
        };
        $websocket.onmessage = function (evt) {
          var data;
          if (evt.data === heartbeat_msg) {
            // reset the counter for missed heartbeats
            missed_heartbeats = 0;
            return;
          }
          try {
            data = angular.fromJson(evt.data);
          } catch (e) {
            _console.warn('Data received by server is invalid JSON: ' + evt.data);
            return;
          }
          if (is_subscriber) {
            // temporarily disable the function 'listener', so that message received
            // from the websocket, are not propagated back
            receiving = true;
            scope.$apply(function () {
              angular.extend(scope[collection], data);
            });
            receiving = false;
          }
        };
        function sendHeartbeat() {
          try {
            missed_heartbeats++;
            if (missed_heartbeats > 3)
              throw new Error('Too many missed heartbeats.');
            $websocket.send(heartbeat_msg);
          } catch (e) {
            $interval.cancel(heartbeat_promise);
            heartbeat_promise = null;
            _console.warn('Closing connection. Reason: ' + e.message);
            $websocket.close();
          }
        }
        function listener(newValue, oldValue) {
          if (!receiving && !angular.equals(oldValue, newValue)) {
            $websocket.send(angular.toJson(newValue));
          }
        }
        function setChannels(channels) {
          angular.forEach(channels, function (channel) {
            if (channel.substring(0, 9) === 'subscribe') {
              is_subscriber = true;
            } else if (channel.substring(0, 7) === 'publish') {
              is_publisher = true;
            }
          });
        }
        function watchCollection() {
          scope.$watchCollection(collection, listener);
        }
        function buildWebsocketURL(facility, channels) {
          var parts = [
              websocket_uri,
              facility,
              '?'
            ];
          parts.push(channels.join('&'));
          ws_url = parts.join('');
        }
        return {
          connect: function ($scope, scope_obj, facility, channels) {
            scope = $scope;
            setChannels(channels);
            collection = scope_obj;
            scope[collection] = scope[collection] || {};
            buildWebsocketURL(facility, channels);
            connect();
            if (is_publisher) {
              deferred.promise.then(watchCollection);
            }
            return deferred.promise;
          }
        };
      }
    ];
  });
}(window.angular));