text: 'use strict';
  
  ////////////////////////////////////
  
  /**
   * @ngdoc function
   * @name angular.lowercase
   * @function
   *
   * @description Converts the specified string to lowercase.
   * @param {string} string String to be converted to lowercase.
   * @returns {string} Lowercased string.
   */
  var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
  
  
  /**
   * @ngdoc function
   * @name angular.uppercase
   * @function
   *
   * @description Converts the specified string to uppercase.
   * @param {string} string String to be converted to uppercase.
   * @returns {string} Uppercased string.
   */
  var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;};
  
  
  var manualLowercase = function(s) {
    return isString(s)
        ? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
        : s;
  };
  var manualUppercase = function(s) {
    return isString(s)
        ? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
        : s;
  };
  
  
  // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
  // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
  // with correct but slower alternatives.
  if ('i' !== 'I'.toLowerCase()) {
    lowercase = manualLowercase;
    uppercase = manualUppercase;
  }
  
  function fromCharCode(code) {return String.fromCharCode(code);}
  
  
  var Error             = window.Error,
      /** holds major version number for IE or NaN for real browsers */
      msie              = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
      jqLite,           // delay binding since jQuery could be loaded after us.
      jQuery,           // delay binding
      slice             = [].slice,
      push              = [].push,
      toString          = Object.prototype.toString,
  
      /** @name angular */
      angular           = window.angular || (window.angular = {}),
      angularModule,
      nodeName_,
      uid               = ['0', '0', '0'];
  
  /**
   * @ngdoc function
   * @name angular.forEach
   * @function
   *
   * @description
   * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
   * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value`
   * is the value of an object property or an array element and `key` is the object property key or
   * array element index. Specifying a `context` for the function is optional.
   *
   * Note: this function was previously known as `angular.foreach`.
   *
     <pre>
       var values = {name: 'misko', gender: 'male'};
       var log = [];
       angular.forEach(values, function(value, key){
         this.push(key + ': ' + value);
       }, log);
       expect(log).toEqual(['name: misko', 'gender:male']);
     </pre>
   *
   * @param {Object|Array} obj Object to iterate over.
   * @param {Function} iterator Iterator function.
   * @param {Object=} context Object to become context (`this`) for the iterator function.
   * @returns {Object|Array} Reference to `obj`.
   */
  function forEach(obj, iterator, context) {
    var key;
    if (obj) {
      if (isFunction(obj)){
        for (key in obj) {
          if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) {
            iterator.call(context, obj[key], key);
          }
        }
      } else if (obj.forEach && obj.forEach !== forEach) {
        obj.forEach(iterator, context);
      } else if (isObject(obj) && isNumber(obj.length)) {
        for (key = 0; key < obj.length; key++)
          iterator.call(context, obj[key], key);
      } else {
        for (key in obj) {
          if (obj.hasOwnProperty(key)) {
            iterator.call(context, obj[key], key);
          }
        }
      }
    }
    return obj;
  }
  
  function sortedKeys(obj) {
    var keys = [];
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        keys.push(key);
      }
    }
    return keys.sort();
  }
  
  function forEachSorted(obj, iterator, context) {
    var keys = sortedKeys(obj);
    for ( var i = 0; i < keys.length; i++) {
      iterator.call(context, obj[keys[i]], keys[i]);
    }
    return keys;
  }
  
  
  /**
   * when using forEach the params are value, key, but it is often useful to have key, value.
   * @param {function(string, *)} iteratorFn
   * @returns {function(*, string)}
   */
  function reverseParams(iteratorFn) {
    return function(value, key) { iteratorFn(key, value) };
  }
  
  /**
   * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
   * characters such as '012ABC'. The reason why we are not using simply a number counter is that
   * the number string gets longer over time, and it can also overflow, where as the the nextId
   * will grow much slower, it is a string, and it will never overflow.
   *
   * @returns an unique alpha-numeric string
   */
  function nextUid() {
    var index = uid.length;
    var digit;
  
    while(index) {
      index--;
      digit = uid[index].charCodeAt(0);
      if (digit == 57 /*'9'*/) {
        uid[index] = 'A';
        return uid.join('');
      }
      if (digit == 90  /*'Z'*/) {
        uid[index] = '0';
      } else {
        uid[index] = String.fromCharCode(digit + 1);
        return uid.join('');
      }
    }
    uid.unshift('0');
    return uid.join('');
  }
  
  /**
   * @ngdoc function
   * @name angular.extend
   * @function
   *
   * @description
   * Extends the destination object `dst` by copying all of the properties from the `src` object(s)
   * to `dst`. You can specify multiple `src` objects.
   *
   * @param {Object} dst Destination object.
   * @param {...Object} src Source object(s).
   */
  function extend(dst) {
    forEach(arguments, function(obj){
      if (obj !== dst) {
        forEach(obj, function(value, key){
          dst[key] = value;
        });
      }
    });
    return dst;
  }
  
  function int(str) {
    return parseInt(str, 10);
  }
  
  
  function inherit(parent, extra) {
    return extend(new (extend(function() {}, {prototype:parent}))(), extra);
  }
  
  
  /**
   * @ngdoc function
   * @name angular.noop
   * @function
   *
   * @description
   * A function that performs no operations. This function can be useful when writing code in the
   * functional style.
     <pre>
       function foo(callback) {
         var result = calculateResult();
         (callback || angular.noop)(result);
       }
     </pre>
   */
  function noop() {}
  noop.$inject = [];
  
  
  /**
   * @ngdoc function
   * @name angular.identity
   * @function
   *
   * @description
   * A function that returns its first argument. This function is useful when writing code in the
   * functional style.
   *
     <pre>
       function transformer(transformationFn, value) {
         return (transformationFn || identity)(value);
       };
     </pre>
   */
  function identity($) {return $;}
  identity.$inject = [];
  
  
  function valueFn(value) {return function() {return value;};}
  
  /**
   * @ngdoc function
   * @name angular.isUndefined
   * @function
   *
   * @description
   * Determines if a reference is undefined.
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is undefined.
   */
  function isUndefined(value){return typeof value == 'undefined';}
  
  
  /**
   * @ngdoc function
   * @name angular.isDefined
   * @function
   *
   * @description
   * Determines if a reference is defined.
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is defined.
   */
  function isDefined(value){return typeof value != 'undefined';}
  
  
  /**
   * @ngdoc function
   * @name angular.isObject
   * @function
   *
   * @description
   * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
   * considered to be objects.
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is an `Object` but not `null`.
   */
  function isObject(value){return value != null && typeof value == 'object';}
  
  
  /**
   * @ngdoc function
   * @name angular.isString
   * @function
   *
   * @description
   * Determines if a reference is a `String`.
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is a `String`.
   */
  function isString(value){return typeof value == 'string';}
  
  
  /**
   * @ngdoc function
   * @name angular.isNumber
   * @function
   *
   * @description
   * Determines if a reference is a `Number`.
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is a `Number`.
   */
  function isNumber(value){return typeof value == 'number';}
  
  
  /**
   * @ngdoc function
   * @name angular.isDate
   * @function
   *
   * @description
   * Determines if a value is a date.
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is a `Date`.
   */
  function isDate(value){
    return toString.apply(value) == '[object Date]';
  }
  
  
  /**
   * @ngdoc function
   * @name angular.isArray
   * @function
   *
   * @description
   * Determines if a reference is an `Array`.
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is an `Array`.
   */
  function isArray(value) {
    return toString.apply(value) == '[object Array]';
  }
  
  
  /**
   * @ngdoc function
   * @name angular.isFunction
   * @function
   *
   * @description
   * Determines if a reference is a `Function`.
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is a `Function`.
   */
  function isFunction(value){return typeof value == 'function';}
  
  
  /**
   * Checks if `obj` is a window object.
   *
   * @private
   * @param {*} obj Object to check
   * @returns {boolean} True if `obj` is a window obj.
   */
  function isWindow(obj) {
    return obj && obj.document && obj.location && obj.alert && obj.setInterval;
  }
  
  
  function isScope(obj) {
    return obj && obj.$evalAsync && obj.$watch;
  }
  
  
  function isFile(obj) {
    return toString.apply(obj) === '[object File]';
  }
  
  
  function isBoolean(value) {
    return typeof value == 'boolean';
  }
  
  
  function trim(value) {
    return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
  }
  
  /**
   * @ngdoc function
   * @name angular.isElement
   * @function
   *
   * @description
   * Determines if a reference is a DOM element (or wrapped jQuery element).
   *
   * @param {*} value Reference to check.
   * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
   */
  function isElement(node) {
    return node &&
      (node.nodeName  // we are a direct element
      || (node.bind && node.find));  // we have a bind and find method part of jQuery API
  }
  
  /**
   * @param str 'key1,key2,...'
   * @returns {object} in the form of {key1:true, key2:true, ...}
   */
  function makeMap(str){
    var obj = {}, items = str.split(","), i;
    for ( i = 0; i < items.length; i++ )
      obj[ items[i] ] = true;
    return obj;
  }
  
  
  if (msie < 9) {
    nodeName_ = function(element) {
      element = element.nodeName ? element : element[0];
      return (element.scopeName && element.scopeName != 'HTML')
        ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
    };
  } else {
    nodeName_ = function(element) {
      return element.nodeName ? element.nodeName : element[0].nodeName;
    };
  }
  
  
  function map(obj, iterator, context) {
    var results = [];
    forEach(obj, function(value, index, list) {
      results.push(iterator.call(context, value, index, list));
    });
    return results;
  }
  
  
  /**
   * @description
   * Determines the number of elements in an array, the number of properties an object has, or
   * the length of a string.
   *
   * Note: This function is used to augment the Object type in Angular expressions. See
   * {@link angular.Object} for more information about Angular arrays.
   *
   * @param {Object|Array|string} obj Object, array, or string to inspect.
   * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object
   * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array.
   */
  function size(obj, ownPropsOnly) {
    var size = 0, key;
  
    if (isArray(obj) || isString(obj)) {
      return obj.length;
    } else if (isObject(obj)){
      for (key in obj)
        if (!ownPropsOnly || obj.hasOwnProperty(key))
          size++;
    }
  
    return size;
  }
  
  
  function includes(array, obj) {
    return indexOf(array, obj) != -1;
  }
  
  function indexOf(array, obj) {
    if (array.indexOf) return array.indexOf(obj);
  
    for ( var i = 0; i < array.length; i++) {
      if (obj === array[i]) return i;
    }
    return -1;
  }
  
  function arrayRemove(array, value) {
    var index = indexOf(array, value);
    if (index >=0)
      array.splice(index, 1);
    return value;
  }
  
  function isLeafNode (node) {
    if (node) {
      switch (node.nodeName) {
      case "OPTION":
      case "PRE":
      case "TITLE":
        return true;
      }
    }
    return false;
  }
  
  /**
   * @ngdoc function
   * @name angular.copy
   * @function
   *
   * @description
   * Creates a deep copy of `source`, which should be an object or an array.
   *
   * * If no destination is supplied, a copy of the object or array is created.
   * * If a destination is provided, all of its elements (for array) or properties (for objects)
   *   are deleted and then all elements/properties from the source are copied to it.
   * * If  `source` is not an object or array, `source` is returned.
   *
   * Note: this function is used to augment the Object type in Angular expressions. See
   * {@link ng.$filter} for more information about Angular arrays.
   *
   * @param {*} source The source that will be used to make a copy.
   *                   Can be any type, including primitives, `null`, and `undefined`.
   * @param {(Object|Array)=} destination Destination into which the source is copied. If
   *     provided, must be of the same type as `source`.
   * @returns {*} The copy or updated `destination`, if `destination` was specified.
   */
  function copy(source, destination){
    if (isWindow(source) || isScope(source)) throw Error("Can't copy Window or Scope");
    if (!destination) {
      destination = source;
      if (source) {
        if (isArray(source)) {
          destination = copy(source, []);
        } else if (isDate(source)) {
          destination = new Date(source.getTime());
        } else if (isObject(source)) {
          destination = copy(source, {});
        }
      }
    } else {
      if (source === destination) throw Error("Can't copy equivalent objects or arrays");
      if (isArray(source)) {
        while(destination.length) {
          destination.pop();
        }
        for ( var i = 0; i < source.length; i++) {
          destination.push(copy(source[i]));
        }
      } else {
        forEach(destination, function(value, key){
          delete destination[key];
        });
        for ( var key in source) {
          destination[key] = copy(source[key]);
        }
      }
    }
    return destination;
  }
  
  /**
   * Create a shallow copy of an object
   */
  function shallowCopy(src, dst) {
    dst = dst || {};
  
    for(var key in src) {
      if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
        dst[key] = src[key];
      }
    }
  
    return dst;
  }
  
  
  /**
   * @ngdoc function
   * @name angular.equals
   * @function
   *
   * @description
   * Determines if two objects or two values are equivalent. Supports value types, arrays and
   * objects.
   *
   * Two objects or values are considered equivalent if at least one of the following is true:
   *
   * * Both objects or values pass `===` comparison.
   * * Both objects or values are of the same type and all of their properties pass `===` comparison.
   * * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal)
   *
   * During a property comparision, properties of `function` type and properties with names
   * that begin with `$` are ignored.
   *
   * Scope and DOMWindow objects are being compared only be identify (`===`).
   *
   * @param {*} o1 Object or value to compare.
   * @param {*} o2 Object or value to compare.
   * @returns {boolean} True if arguments are equal.
   */
  function equals(o1, o2) {
    if (o1 === o2) return true;
    if (o1 === null || o2 === null) return false;
    if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
    var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
    if (t1 == t2) {
      if (t1 == 'object') {
        if (isArray(o1)) {
          if ((length = o1.length) == o2.length) {
            for(key=0; key<length; key++) {
              if (!equals(o1[key], o2[key])) return false;
            }
            return true;
          }
        } else if (isDate(o1)) {
          return isDate(o2) && o1.getTime() == o2.getTime();
        } else {
          if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
          keySet = {};
          for(key in o1) {
            if (key.charAt(0) !== '$' && !isFunction(o1[key]) && !equals(o1[key], o2[key])) {
              return false;
            }
            keySet[key] = true;
          }
          for(key in o2) {
            if (!keySet[key] && key.charAt(0) !== '$' && !isFunction(o2[key])) return false;
          }
          return true;
        }
      }
    }
    return false;
  }
  
  
  function concat(array1, array2, index) {
    return array1.concat(slice.call(array2, index));
  }
  
  function sliceArgs(args, startIndex) {
    return slice.call(args, startIndex || 0);
  }
  
  
  /**
   * @ngdoc function
   * @name angular.bind
   * @function
   *
   * @description
   * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
   * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
   * known as [function currying](http://en.wikipedia.org/wiki/Currying).
   *
   * @param {Object} self Context which `fn` should be evaluated in.
   * @param {function()} fn Function to be bound.
   * @param {...*} args Optional arguments to be prebound to the `fn` function call.
   * @returns {function()} Function that wraps the `fn` with all the specified bindings.
   */
  function bind(self, fn) {
    var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
    if (isFunction(fn) && !(fn instanceof RegExp)) {
      return curryArgs.length
        ? function() {
            return arguments.length
              ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
              : fn.apply(self, curryArgs);
          }
        : function() {
            return arguments.length
              ? fn.apply(self, arguments)
              : fn.call(self);
          };
    } else {
      // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
      return fn;
    }
  }
  
  
  function toJsonReplacer(key, value) {
    var val = value;
  
    if (/^\$+/.test(key)) {
      val = undefined;
    } else if (isWindow(value)) {
      val = '$WINDOW';
    } else if (value &&  document === value) {
      val = '$DOCUMENT';
    } else if (isScope(value)) {
      val = '$SCOPE';
    }
  
    return val;
  }
  
  
  /**
   * @ngdoc function
   * @name angular.toJson
   * @function
   *
   * @description
   * Serializes input into a JSON-formatted string.
   *
   * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
   * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
   * @returns {string} Jsonified string representing `obj`.
   */
  function toJson(obj, pretty) {
    return JSON.stringify(obj, toJsonReplacer, pretty ? '  ' : null);
  }
  
  
  /**
   * @ngdoc function
   * @name angular.fromJson
   * @function
   *
   * @description
   * Deserializes a JSON string.
   *
   * @param {string} json JSON string to deserialize.
   * @returns {Object|Array|Date|string|number} Deserialized thingy.
   */
  function fromJson(json) {
    return isString(json)
        ? JSON.parse(json)
        : json;
  }
  
  
  function toBoolean(value) {
    if (value && value.length !== 0) {
      var v = lowercase("" + value);
      value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
    } else {
      value = false;
    }
    return value;
  }
  
  /**
   * @returns {string} Returns the string representation of the element.
   */
  function startingTag(element) {
    element = jqLite(element).clone();
    try {
      // turns out IE does not let you set .html() on elements which
      // are not allowed to have children. So we just ignore it.
      element.html('');
    } catch(e) {}
    return jqLite('<div>').append(element).html().
        match(/^(<[^>]+>)/)[1].
        replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
  }
  
  
  /////////////////////////////////////////////////
  
  /**
   * Parses an escaped url query string into key-value pairs.
   * @returns Object.<(string|boolean)>
   */
  function parseKeyValue(/**string*/keyValue) {
    var obj = {}, key_value, key;
    forEach((keyValue || "").split('&'), function(keyValue){
      if (keyValue) {
        key_value = keyValue.split('=');
        key = decodeURIComponent(key_value[0]);
        obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true;
      }
    });
    return obj;
  }
  
  function toKeyValue(obj) {
    var parts = [];
    forEach(obj, function(value, key) {
      parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true)));
    });
    return parts.length ? parts.join('&') : '';
  }
  
  
  /**
   * We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow
   * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
   * segments:
   *    segment       = *pchar
   *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
   *    pct-encoded   = "%" HEXDIG HEXDIG
   *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
   *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
   *                     / "*" / "+" / "," / ";" / "="
   */
  function encodeUriSegment(val) {
    return encodeUriQuery(val, true).
               replace(/%26/gi, '&').
               replace(/%3D/gi, '=').
               replace(/%2B/gi, '+');
  }
  
  
  /**
   * This method is intended for encoding *key* or *value* parts of query component. We need a custom
   * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
   * encoded per http://tools.ietf.org/html/rfc3986:
   *    query       = *( pchar / "/" / "?" )
   *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
   *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
   *    pct-encoded   = "%" HEXDIG HEXDIG
   *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
   *                     / "*" / "+" / "," / ";" / "="
   */
  function encodeUriQuery(val, pctEncodeSpaces) {
    return encodeURIComponent(val).
               replace(/%40/gi, '@').
               replace(/%3A/gi, ':').
               replace(/%24/g, '$').
               replace(/%2C/gi, ',').
               replace((pctEncodeSpaces ? null : /%20/g), '+');
  }
  
  
  /**
   * @ngdoc directive
   * @name ng.directive:ngApp
   *
   * @element ANY
   * @param {angular.Module} ngApp on optional application
   *   {@link angular.module module} name to load.
   *
   * @description
   *
   * Use this directive to auto-bootstrap on application. Only
   * one directive can be used per HTML document. The directive
   * designates the root of the application and is typically placed
   * ot the root of the page.
   *
   * In the example below if the `ngApp` directive would not be placed
   * on the `html` element then the document would not be compiled
   * and the `{{ 1+2 }}` would not be resolved to `3`.
   *
   * `ngApp` is the easiest way to bootstrap an application.
   *
   <doc:example>
     <doc:source>
      I can add: 1 + 2 =  {{ 1+2 }}
     </doc:source>
   </doc:example>
   *
   */
  function angularInit(element, bootstrap) {
    var elements = [element],
        appElement,
        module,
        names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
        NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
  
    function append(element) {
      element && elements.push(element);
    }
  
    forEach(names, function(name) {
      names[name] = true;
      append(document.getElementById(name));
      name = name.replace(':', '\\:');
      if (element.querySelectorAll) {
        forEach(element.querySelectorAll('.' + name), append);
        forEach(element.querySelectorAll('.' + name + '\\:'), append);
        forEach(element.querySelectorAll('[' + name + ']'), append);
      }
    });
  
    forEach(elements, function(element) {
      if (!appElement) {
        var className = ' ' + element.className + ' ';
        var match = NG_APP_CLASS_REGEXP.exec(className);
        if (match) {
          appElement = element;
          module = (match[2] || '').replace(/\s+/g, ',');
        } else {
          forEach(element.attributes, function(attr) {
            if (!appElement && names[attr.name]) {
              appElement = element;
              module = attr.value;
            }
          });
        }
      }
    });
    if (appElement) {
      bootstrap(appElement, module ? [module] : []);
    }
  }
  
  /**
   * @ngdoc function
   * @name angular.bootstrap
   * @description
   * Use this function to manually start up angular application.
   *
   * See: {@link guide/bootstrap Bootstrap}
   *
   * @param {Element} element DOM element which is the root of angular application.
   * @param {Array<String|Function>=} modules an array of module declarations. See: {@link angular.module modules}
   * @returns {AUTO.$injector} Returns the newly created injector for this app.
   */
  function bootstrap(element, modules) {
    element = jqLite(element);
    modules = modules || [];
    modules.unshift(['$provide', function($provide) {
      $provide.value('$rootElement', element);
    }]);
    modules.unshift('ng');
    var injector = createInjector(modules);
    injector.invoke(
      ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
        scope.$apply(function() {
          element.data('$injector', injector);
          compile(element)(scope);
        });
      }]
    );
    return injector;
  }
  
  var SNAKE_CASE_REGEXP = /[A-Z]/g;
  function snake_case(name, separator){
    separator = separator || '_';
    return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
      return (pos ? separator : '') + letter.toLowerCase();
    });
  }
  
  function bindJQuery() {
    // bind to jQuery if present;
    jQuery = window.jQuery;
    // reset to jQuery or default to us.
    if (jQuery) {
      jqLite = jQuery;
      extend(jQuery.fn, {
        scope: JQLitePrototype.scope,
        controller: JQLitePrototype.controller,
        injector: JQLitePrototype.injector,
        inheritedData: JQLitePrototype.inheritedData
      });
      JQLitePatchJQueryRemove('remove', true);
      JQLitePatchJQueryRemove('empty');
      JQLitePatchJQueryRemove('html');
    } else {
      jqLite = JQLite;
    }
    angular.element = jqLite;
  }
  
  /**
   * throw error of the argument is falsy.
   */
  function assertArg(arg, name, reason) {
    if (!arg) {
      throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required"));
    }
    return arg;
  }
  
  function assertArgFn(arg, name, acceptArrayAnnotation) {
    if (acceptArrayAnnotation && isArray(arg)) {
        arg = arg[arg.length - 1];
    }
  
    assertArg(isFunction(arg), name, 'not a function, got ' +
        (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
    return arg;
  }
  