Browse Source

Tech: updated angular libs

Torkel Ödegaard 11 years ago
parent
commit
7baad7ff10

+ 1 - 0
src/vendor/angular/angular-dragdrop.js

@@ -39,6 +39,7 @@ angular.module("ang-drag-drop",[])
                 element.attr("draggable", false);
 
                 attrs.$observe("uiDraggable", function (newValue) {
+                  console.log(newValue);
                     if(newValue){
                         element.attr("draggable", newValue);
                     }

+ 172 - 61
src/vendor/angular/angular-mocks.js

@@ -1,5 +1,5 @@
 /**
- * @license AngularJS v1.3.0-rc.3
+ * @license AngularJS v1.3.4
  * (c) 2010-2014 Google, Inc. http://angularjs.org
  * License: MIT
  */
@@ -53,9 +53,10 @@ angular.mock.$Browser = function() {
   self.onUrlChange = function(listener) {
     self.pollFns.push(
       function() {
-        if (self.$$lastUrl != self.$$url) {
+        if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) {
           self.$$lastUrl = self.$$url;
-          listener(self.$$url);
+          self.$$lastState = self.$$state;
+          listener(self.$$url, self.$$state);
         }
       }
     );
@@ -73,7 +74,7 @@ angular.mock.$Browser = function() {
   self.defer = function(fn, delay) {
     delay = delay || 0;
     self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
-    self.deferredFns.sort(function(a,b){ return a.time - b.time;});
+    self.deferredFns.sort(function(a, b) { return a.time - b.time;});
     return self.deferredNextId++;
   };
 
@@ -116,7 +117,7 @@ angular.mock.$Browser = function() {
       self.defer.now += delay;
     } else {
       if (self.deferredFns.length) {
-        self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
+        self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
       } else {
         throw new Error('No deferred tasks to be flushed');
       }
@@ -141,7 +142,7 @@ angular.mock.$Browser.prototype = {
   * run all fns in pollFns
   */
   poll: function poll() {
-    angular.forEach(this.pollFns, function(pollFn){
+    angular.forEach(this.pollFns, function(pollFn) {
       pollFn();
     });
   },
@@ -151,15 +152,24 @@ angular.mock.$Browser.prototype = {
     return pollFn;
   },
 
-  url: function(url, replace) {
+  url: function(url, replace, state) {
+    if (angular.isUndefined(state)) {
+      state = null;
+    }
     if (url) {
       this.$$url = url;
+      // Native pushState serializes & copies the object; simulate it.
+      this.$$state = angular.copy(state);
       return this;
     }
 
     return this.$$url;
   },
 
+  state: function() {
+    return this.$$state;
+  },
+
   cookies:  function(name, value) {
     if (name) {
       if (angular.isUndefined(value)) {
@@ -191,7 +201,7 @@ angular.mock.$Browser.prototype = {
  *
  * @description
  * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
- * passed into the `$exceptionHandler`.
+ * passed to the `$exceptionHandler`.
  */
 
 /**
@@ -200,7 +210,7 @@ angular.mock.$Browser.prototype = {
  *
  * @description
  * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
- * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
+ * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
  * information.
  *
  *
@@ -240,16 +250,15 @@ angular.mock.$ExceptionHandlerProvider = function() {
    *
    * @param {string} mode Mode of operation, defaults to `rethrow`.
    *
-   *   - `rethrow`: If any errors are passed into the handler in tests, it typically
-   *                means that there is a bug in the application or test, so this mock will
-   *                make these tests fail.
+   *   - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
+   *                is a bug in the application or test, so this mock will make these tests fail.
    *   - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
    *            mode stores an array of errors in `$exceptionHandler.errors`, to allow later
    *            assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
    *            {@link ngMock.$log#reset reset()}
    */
   this.mode = function(mode) {
-    switch(mode) {
+    switch (mode) {
       case 'rethrow':
         handler = function(e) {
           throw e;
@@ -307,7 +316,7 @@ angular.mock.$LogProvider = function() {
     }
   };
 
-  this.$get = function () {
+  this.$get = function() {
     var $log = {
       log: function() { $log.log.logs.push(concat([], arguments, 0)); },
       warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
@@ -327,13 +336,13 @@ angular.mock.$LogProvider = function() {
      * @description
      * Reset all of the logging arrays to empty.
      */
-    $log.reset = function () {
+    $log.reset = function() {
       /**
        * @ngdoc property
        * @name $log#log.logs
        *
        * @description
-       * Array of messages logged using {@link ngMock.$log#log}.
+       * Array of messages logged using {@link ng.$log#log `log()`}.
        *
        * @example
        * ```js
@@ -347,7 +356,7 @@ angular.mock.$LogProvider = function() {
        * @name $log#info.logs
        *
        * @description
-       * Array of messages logged using {@link ngMock.$log#info}.
+       * Array of messages logged using {@link ng.$log#info `info()`}.
        *
        * @example
        * ```js
@@ -361,7 +370,7 @@ angular.mock.$LogProvider = function() {
        * @name $log#warn.logs
        *
        * @description
-       * Array of messages logged using {@link ngMock.$log#warn}.
+       * Array of messages logged using {@link ng.$log#warn `warn()`}.
        *
        * @example
        * ```js
@@ -375,7 +384,7 @@ angular.mock.$LogProvider = function() {
        * @name $log#error.logs
        *
        * @description
-       * Array of messages logged using {@link ngMock.$log#error}.
+       * Array of messages logged using {@link ng.$log#error `error()`}.
        *
        * @example
        * ```js
@@ -389,7 +398,7 @@ angular.mock.$LogProvider = function() {
        * @name $log#debug.logs
        *
        * @description
-       * Array of messages logged using {@link ngMock.$log#debug}.
+       * Array of messages logged using {@link ng.$log#debug `debug()`}.
        *
        * @example
        * ```js
@@ -405,21 +414,21 @@ angular.mock.$LogProvider = function() {
      * @name $log#assertEmpty
      *
      * @description
-     * Assert that the all of the logging methods have no logged messages. If messages present, an
-     * exception is thrown.
+     * Assert that all of the logging methods have no logged messages. If any messages are present,
+     * an exception is thrown.
      */
     $log.assertEmpty = function() {
       var errors = [];
       angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
         angular.forEach($log[logLevel].logs, function(log) {
-          angular.forEach(log, function (logItem) {
+          angular.forEach(log, function(logItem) {
             errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
                         (logItem.stack || ''));
           });
         });
       });
       if (errors.length) {
-        errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
+        errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " +
           "an expected log message was not checked and removed:");
         errors.push('');
         throw new Error(errors.join('\n---------\n'));
@@ -452,17 +461,17 @@ angular.mock.$LogProvider = function() {
  * @returns {promise} A promise which will be notified on each iteration.
  */
 angular.mock.$IntervalProvider = function() {
-  this.$get = ['$rootScope', '$q',
-       function($rootScope,   $q) {
+  this.$get = ['$browser', '$rootScope', '$q', '$$q',
+       function($browser,   $rootScope,   $q,   $$q) {
     var repeatFns = [],
         nextRepeatId = 0,
         now = 0;
 
     var $interval = function(fn, delay, count, invokeApply) {
-      var deferred = $q.defer(),
-          promise = deferred.promise,
-          iteration = 0,
-          skipApply = (angular.isDefined(invokeApply) && !invokeApply);
+      var iteration = 0,
+          skipApply = (angular.isDefined(invokeApply) && !invokeApply),
+          deferred = (skipApply ? $$q : $q).defer(),
+          promise = deferred.promise;
 
       count = (angular.isDefined(count)) ? count : 0;
       promise.then(null, null, fn);
@@ -485,7 +494,11 @@ angular.mock.$IntervalProvider = function() {
           }
         }
 
-        if (!skipApply) $rootScope.$apply();
+        if (skipApply) {
+          $browser.defer.flush();
+        } else {
+          $rootScope.$apply();
+        }
       }
 
       repeatFns.push({
@@ -495,7 +508,7 @@ angular.mock.$IntervalProvider = function() {
         id: nextRepeatId,
         deferred: deferred
       });
-      repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
+      repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
 
       nextRepeatId++;
       return promise;
@@ -511,7 +524,7 @@ angular.mock.$IntervalProvider = function() {
      * @returns {boolean} Returns `true` if the task was successfully cancelled.
      */
     $interval.cancel = function(promise) {
-      if(!promise) return false;
+      if (!promise) return false;
       var fnIndex;
 
       angular.forEach(repeatFns, function(fn, index) {
@@ -544,7 +557,7 @@ angular.mock.$IntervalProvider = function() {
         var task = repeatFns[0];
         task.fn();
         task.nextTime += task.delay;
-        repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
+        repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
       }
       return millis;
     };
@@ -572,10 +585,10 @@ function jsonStringToDate(string) {
       tzMin = int(match[9] + match[11]);
     }
     date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
-    date.setUTCHours(int(match[4]||0) - tzHour,
-                     int(match[5]||0) - tzMin,
-                     int(match[6]||0),
-                     int(match[7]||0));
+    date.setUTCHours(int(match[4] || 0) - tzHour,
+                     int(match[5] || 0) - tzMin,
+                     int(match[6] || 0),
+                     int(match[7] || 0));
     return date;
   }
   return string;
@@ -592,7 +605,7 @@ function padNumber(num, digits, trim) {
     num = -num;
   }
   num = '' + num;
-  while(num.length < digits) num = '0' + num;
+  while (num.length < digits) num = '0' + num;
   if (trim)
     num = num.substr(num.length - digits);
   return neg + num;
@@ -636,7 +649,7 @@ function padNumber(num, digits, trim) {
  * ```
  *
  */
-angular.mock.TzDate = function (offset, timestamp) {
+angular.mock.TzDate = function(offset, timestamp) {
   var self = new Date(0);
   if (angular.isString(timestamp)) {
     var tsStr = timestamp;
@@ -654,7 +667,7 @@ angular.mock.TzDate = function (offset, timestamp) {
   }
 
   var localOffset = new Date(timestamp).getTimezoneOffset();
-  self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
+  self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
   self.date = new Date(timestamp + self.offsetDiff);
 
   self.getTime = function() {
@@ -779,20 +792,20 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
     $provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser',
                             function($delegate,   $$asyncCallback,   $timeout,   $browser) {
       var animate = {
-        queue : [],
-        cancel : $delegate.cancel,
-        enabled : $delegate.enabled,
-        triggerCallbackEvents : function() {
+        queue: [],
+        cancel: $delegate.cancel,
+        enabled: $delegate.enabled,
+        triggerCallbackEvents: function() {
           $$asyncCallback.flush();
         },
-        triggerCallbackPromise : function() {
+        triggerCallbackPromise: function() {
           $timeout.flush(0);
         },
-        triggerCallbacks : function() {
+        triggerCallbacks: function() {
           this.triggerCallbackEvents();
           this.triggerCallbackPromise();
         },
-        triggerReflow : function() {
+        triggerReflow: function() {
           angular.forEach(reflowQueue, function(fn) {
             fn();
           });
@@ -801,12 +814,13 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
       };
 
       angular.forEach(
-        ['enter','leave','move','addClass','removeClass','setClass'], function(method) {
+        ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
         animate[method] = function() {
           animate.queue.push({
-            event : method,
-            element : arguments[0],
-            args : arguments
+            event: method,
+            element: arguments[0],
+            options: arguments[arguments.length - 1],
+            args: arguments
           });
           return $delegate[method].apply($delegate, arguments);
         };
@@ -873,13 +887,13 @@ angular.mock.dump = function(object) {
   function serializeScope(scope, offset) {
     offset = offset ||  '  ';
     var log = [offset + 'Scope(' + scope.$id + '): {'];
-    for ( var key in scope ) {
+    for (var key in scope) {
       if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
         log.push('  ' + key + ': ' + angular.toJson(scope[key]));
       }
     }
     var child = scope.$$childHead;
-    while(child) {
+    while (child) {
       log.push(serializeScope(child, offset + '  '));
       child = child.$$nextSibling;
     }
@@ -991,6 +1005,11 @@ angular.mock.dump = function(object) {
  * First we create the controller under test:
  *
   ```js
+  // The module code
+  angular
+    .module('MyApp', [])
+    .controller('MyController', MyController);
+
   // The controller code
   function MyController($scope, $http) {
     var authToken;
@@ -1020,6 +1039,9 @@ angular.mock.dump = function(object) {
     describe('MyController', function() {
        var $httpBackend, $rootScope, createController, authRequestHandler;
 
+       // Set up the module
+       beforeEach(module('MyApp'));
+
        beforeEach(inject(function($injector) {
          // Set up the mock http service responses
          $httpBackend = $injector.get('$httpBackend');
@@ -1368,7 +1390,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
   $httpBackend.expect = function(method, url, data, headers) {
     var expectation = new MockHttpExpectation(method, url, data, headers),
         chain = {
-          respond: function (status, data, headers, statusText) {
+          respond: function(status, data, headers, statusText) {
             expectation.response = createResponse(status, data, headers, statusText);
             return chain;
           }
@@ -1618,7 +1640,9 @@ function MockHttpExpectation(method, url, data, headers) {
     if (angular.isUndefined(data)) return true;
     if (data && angular.isFunction(data.test)) return data.test(d);
     if (data && angular.isFunction(data)) return data(d);
-    if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
+    if (data && !angular.isString(data)) {
+      return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
+    }
     return data == d;
   };
 
@@ -1691,7 +1715,7 @@ function MockXhr() {
  * that adds a "flush" and "verifyNoPendingTasks" methods.
  */
 
-angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function ($delegate, $browser) {
+angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
 
   /**
    * @ngdoc method
@@ -1745,12 +1769,12 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
   rafFn.supported = $delegate.supported;
 
   rafFn.flush = function() {
-    if(queue.length === 0) {
+    if (queue.length === 0) {
       throw new Error('No rAF callbacks present');
     }
 
     var length = queue.length;
-    for(var i=0;i<length;i++) {
+    for (var i = 0; i < length; i++) {
       queue[i]();
     }
 
@@ -1810,6 +1834,7 @@ angular.module('ngMock', ['ng']).provider({
   $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
   $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
   $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
+  $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
 }]);
 
 /**
@@ -2018,7 +2043,93 @@ angular.mock.e2e.$httpBackendDecorator =
   ['$rootScope', '$delegate', '$browser', createHttpBackendMock];
 
 
-if(window.jasmine || window.mocha) {
+/**
+ * @ngdoc type
+ * @name $rootScope.Scope
+ * @module ngMock
+ * @description
+ * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
+ * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
+ * `ngMock` module is loaded.
+ *
+ * In addition to all the regular `Scope` methods, the following helper methods are available:
+ */
+angular.mock.$RootScopeDecorator = function($delegate) {
+
+  var $rootScopePrototype = Object.getPrototypeOf($delegate);
+
+  $rootScopePrototype.$countChildScopes = countChildScopes;
+  $rootScopePrototype.$countWatchers = countWatchers;
+
+  return $delegate;
+
+  // ------------------------------------------------------------------------------------------ //
+
+  /**
+   * @ngdoc method
+   * @name $rootScope.Scope#$countChildScopes
+   * @module ngMock
+   * @description
+   * Counts all the direct and indirect child scopes of the current scope.
+   *
+   * The current scope is excluded from the count. The count includes all isolate child scopes.
+   *
+   * @returns {number} Total number of child scopes.
+   */
+  function countChildScopes() {
+    // jshint validthis: true
+    var count = 0; // exclude the current scope
+    var pendingChildHeads = [this.$$childHead];
+    var currentScope;
+
+    while (pendingChildHeads.length) {
+      currentScope = pendingChildHeads.shift();
+
+      while (currentScope) {
+        count += 1;
+        pendingChildHeads.push(currentScope.$$childHead);
+        currentScope = currentScope.$$nextSibling;
+      }
+    }
+
+    return count;
+  }
+
+
+  /**
+   * @ngdoc method
+   * @name $rootScope.Scope#$countWatchers
+   * @module ngMock
+   * @description
+   * Counts all the watchers of direct and indirect child scopes of the current scope.
+   *
+   * The watchers of the current scope are included in the count and so are all the watchers of
+   * isolate child scopes.
+   *
+   * @returns {number} Total number of watchers.
+   */
+  function countWatchers() {
+    // jshint validthis: true
+    var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
+    var pendingChildHeads = [this.$$childHead];
+    var currentScope;
+
+    while (pendingChildHeads.length) {
+      currentScope = pendingChildHeads.shift();
+
+      while (currentScope) {
+        count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
+        pendingChildHeads.push(currentScope.$$childHead);
+        currentScope = currentScope.$$nextSibling;
+      }
+    }
+
+    return count;
+  }
+};
+
+
+if (window.jasmine || window.mocha) {
 
   var currentSpec = null,
       isSpecRunning = function() {
@@ -2226,7 +2337,7 @@ if(window.jasmine || window.mocha) {
         injector = currentSpec.$injector = angular.injector(modules, strictDi);
         currentSpec.$injectorStrict = strictDi;
       }
-      for(var i = 0, ii = blockFns.length; i < ii; i++) {
+      for (var i = 0, ii = blockFns.length; i < ii; i++) {
         if (currentSpec.$injectorStrict) {
           // If the injector is strict / strictDi, and the spec wants to inject using automatic
           // annotation, then annotate the function here.

+ 104 - 60
src/vendor/angular/angular-route.js

@@ -1,5 +1,5 @@
 /**
- * @license AngularJS v1.3.0-rc.3
+ * @license AngularJS v1.3.4
  * (c) 2010-2014 Google, Inc. http://angularjs.org
  * License: MIT
  */
@@ -39,9 +39,9 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
  * ## Dependencies
  * Requires the {@link ngRoute `ngRoute`} module to be installed.
  */
-function $RouteProvider(){
+function $RouteProvider() {
   function inherit(parent, extra) {
-    return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
+    return angular.extend(Object.create(parent), extra);
   }
 
   var routes = {};
@@ -146,27 +146,45 @@ function $RouteProvider(){
    * Adds a new route definition to the `$route` service.
    */
   this.when = function(path, route) {
+    //copy original route object to preserve params inherited from proto chain
+    var routeCopy = angular.copy(route);
+    if (angular.isUndefined(routeCopy.reloadOnSearch)) {
+      routeCopy.reloadOnSearch = true;
+    }
+    if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
+      routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
+    }
     routes[path] = angular.extend(
-      {reloadOnSearch: true},
-      route,
-      path && pathRegExp(path, route)
+      routeCopy,
+      path && pathRegExp(path, routeCopy)
     );
 
     // create redirection for trailing slashes
     if (path) {
-      var redirectPath = (path[path.length-1] == '/')
-            ? path.substr(0, path.length-1)
-            : path +'/';
+      var redirectPath = (path[path.length - 1] == '/')
+            ? path.substr(0, path.length - 1)
+            : path + '/';
 
       routes[redirectPath] = angular.extend(
         {redirectTo: path},
-        pathRegExp(redirectPath, route)
+        pathRegExp(redirectPath, routeCopy)
       );
     }
 
     return this;
   };
 
+  /**
+   * @ngdoc property
+   * @name $routeProvider#caseInsensitiveMatch
+   * @description
+   *
+   * A boolean property indicating if routes defined
+   * using this provider should be matched using a case sensitive
+   * algorithm. Defaults to `false`.
+   */
+  this.caseInsensitiveMatch = false;
+
    /**
     * @param path {string} path
     * @param opts {Object} options
@@ -188,7 +206,7 @@ function $RouteProvider(){
 
     path = path
       .replace(/([().])/g, '\\$1')
-      .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){
+      .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
         var optional = option === '?' ? option : null;
         var star = option === '*' ? option : null;
         keys.push({ name: key, optional: !!optional });
@@ -380,6 +398,10 @@ function $RouteProvider(){
      * defined in `resolve` route property. Once  all of the dependencies are resolved
      * `$routeChangeSuccess` is fired.
      *
+     * The route change (and the `$location` change that triggered it) can be prevented
+     * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
+     * for more details about event object.
+     *
      * @param {Object} angularEvent Synthetic event object.
      * @param {Route} next Future route information.
      * @param {Route} current Current route information.
@@ -424,6 +446,8 @@ function $RouteProvider(){
      */
 
     var forceReload = false,
+        preparedRoute,
+        preparedRouteIsUpdateOnly,
         $route = {
           routes: routes,
 
@@ -436,11 +460,15 @@ function $RouteProvider(){
            * {@link ng.$location $location} hasn't changed.
            *
            * As a result of that, {@link ngRoute.directive:ngView ngView}
-           * creates new scope, reinstantiates the controller.
+           * creates new scope and reinstantiates the controller.
            */
           reload: function() {
             forceReload = true;
-            $rootScope.$evalAsync(updateRoute);
+            $rootScope.$evalAsync(function() {
+              // Don't support cancellation of a reload for now...
+              prepareRoute();
+              commitRoute();
+            });
           },
 
           /**
@@ -474,7 +502,8 @@ function $RouteProvider(){
           }
         };
 
-    $rootScope.$on('$locationChangeSuccess', updateRoute);
+    $rootScope.$on('$locationChangeStart', prepareRoute);
+    $rootScope.$on('$locationChangeSuccess', commitRoute);
 
     return $route;
 
@@ -512,36 +541,50 @@ function $RouteProvider(){
       return params;
     }
 
-    function updateRoute() {
-      var next = parseRoute(),
-          last = $route.current;
-
-      if (next && last && next.$$route === last.$$route
-          && angular.equals(next.pathParams, last.pathParams)
-          && !next.reloadOnSearch && !forceReload) {
-        last.params = next.params;
-        angular.copy(last.params, $routeParams);
-        $rootScope.$broadcast('$routeUpdate', last);
-      } else if (next || last) {
+    function prepareRoute($locationEvent) {
+      var lastRoute = $route.current;
+
+      preparedRoute = parseRoute();
+      preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
+          && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
+          && !preparedRoute.reloadOnSearch && !forceReload;
+
+      if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
+        if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
+          if ($locationEvent) {
+            $locationEvent.preventDefault();
+          }
+        }
+      }
+    }
+
+    function commitRoute() {
+      var lastRoute = $route.current;
+      var nextRoute = preparedRoute;
+
+      if (preparedRouteIsUpdateOnly) {
+        lastRoute.params = nextRoute.params;
+        angular.copy(lastRoute.params, $routeParams);
+        $rootScope.$broadcast('$routeUpdate', lastRoute);
+      } else if (nextRoute || lastRoute) {
         forceReload = false;
-        $rootScope.$broadcast('$routeChangeStart', next, last);
-        $route.current = next;
-        if (next) {
-          if (next.redirectTo) {
-            if (angular.isString(next.redirectTo)) {
-              $location.path(interpolate(next.redirectTo, next.params)).search(next.params)
+        $route.current = nextRoute;
+        if (nextRoute) {
+          if (nextRoute.redirectTo) {
+            if (angular.isString(nextRoute.redirectTo)) {
+              $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
                        .replace();
             } else {
-              $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
+              $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
                        .replace();
             }
           }
         }
 
-        $q.when(next).
+        $q.when(nextRoute).
           then(function() {
-            if (next) {
-              var locals = angular.extend({}, next.resolve),
+            if (nextRoute) {
+              var locals = angular.extend({}, nextRoute.resolve),
                   template, templateUrl;
 
               angular.forEach(locals, function(value, key) {
@@ -549,17 +592,17 @@ function $RouteProvider(){
                     $injector.get(value) : $injector.invoke(value, null, null, key);
               });
 
-              if (angular.isDefined(template = next.template)) {
+              if (angular.isDefined(template = nextRoute.template)) {
                 if (angular.isFunction(template)) {
-                  template = template(next.params);
+                  template = template(nextRoute.params);
                 }
-              } else if (angular.isDefined(templateUrl = next.templateUrl)) {
+              } else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
                 if (angular.isFunction(templateUrl)) {
-                  templateUrl = templateUrl(next.params);
+                  templateUrl = templateUrl(nextRoute.params);
                 }
                 templateUrl = $sce.getTrustedResourceUrl(templateUrl);
                 if (angular.isDefined(templateUrl)) {
-                  next.loadedTemplateUrl = templateUrl;
+                  nextRoute.loadedTemplateUrl = templateUrl;
                   template = $templateRequest(templateUrl);
                 }
               }
@@ -571,16 +614,16 @@ function $RouteProvider(){
           }).
           // after route change
           then(function(locals) {
-            if (next == $route.current) {
-              if (next) {
-                next.locals = locals;
-                angular.copy(next.params, $routeParams);
+            if (nextRoute == $route.current) {
+              if (nextRoute) {
+                nextRoute.locals = locals;
+                angular.copy(nextRoute.params, $routeParams);
               }
-              $rootScope.$broadcast('$routeChangeSuccess', next, last);
+              $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
             }
           }, function(error) {
-            if (next == $route.current) {
-              $rootScope.$broadcast('$routeChangeError', next, last, error);
+            if (nextRoute == $route.current) {
+              $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
             }
           });
       }
@@ -610,11 +653,11 @@ function $RouteProvider(){
      */
     function interpolate(string, params) {
       var result = [];
-      angular.forEach((string||'').split(':'), function(segment, i) {
+      angular.forEach((string || '').split(':'), function(segment, i) {
         if (i === 0) {
           result.push(segment);
         } else {
-          var segmentMatch = segment.match(/(\w+)(.*)/);
+          var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
           var key = segmentMatch[1];
           result.push(params[key]);
           result.push(segmentMatch[2] || '');
@@ -845,7 +888,7 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
  * Emitted every time the ngView content is reloaded.
  */
 ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
-function ngViewFactory(   $route,   $anchorScroll,   $animate) {
+function ngViewFactory($route, $anchorScroll, $animate) {
   return {
     restrict: 'ECA',
     terminal: true,
@@ -854,7 +897,7 @@ function ngViewFactory(   $route,   $anchorScroll,   $animate) {
     link: function(scope, $element, attr, ctrl, $transclude) {
         var currentScope,
             currentElement,
-            previousElement,
+            previousLeaveAnimation,
             autoScrollExp = attr.autoscroll,
             onloadExp = attr.onload || '';
 
@@ -862,19 +905,20 @@ function ngViewFactory(   $route,   $anchorScroll,   $animate) {
         update();
 
         function cleanupLastView() {
-          if(previousElement) {
-            previousElement.remove();
-            previousElement = null;
+          if (previousLeaveAnimation) {
+            $animate.cancel(previousLeaveAnimation);
+            previousLeaveAnimation = null;
           }
-          if(currentScope) {
+
+          if (currentScope) {
             currentScope.$destroy();
             currentScope = null;
           }
-          if(currentElement) {
-            $animate.leave(currentElement).then(function() {
-              previousElement = null;
+          if (currentElement) {
+            previousLeaveAnimation = $animate.leave(currentElement);
+            previousLeaveAnimation.then(function() {
+              previousLeaveAnimation = null;
             });
-            previousElement = currentElement;
             currentElement = null;
           }
         }
@@ -894,7 +938,7 @@ function ngViewFactory(   $route,   $anchorScroll,   $animate) {
             // function is called before linking the content, which would apply child
             // directives to non existing elements.
             var clone = $transclude(newScope, function(clone) {
-              $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter () {
+              $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
                 if (angular.isDefined(autoScrollExp)
                   && (!autoScrollExp || scope.$eval(autoScrollExp))) {
                   $anchorScroll();

+ 678 - 0
src/vendor/angular/angular-sanitize.js

@@ -0,0 +1,678 @@
+/**
+ * @license AngularJS v1.3.4
+ * (c) 2010-2014 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular, undefined) {'use strict';
+
+var $sanitizeMinErr = angular.$$minErr('$sanitize');
+
+/**
+ * @ngdoc module
+ * @name ngSanitize
+ * @description
+ *
+ * # ngSanitize
+ *
+ * The `ngSanitize` module provides functionality to sanitize HTML.
+ *
+ *
+ * <div doc-module-components="ngSanitize"></div>
+ *
+ * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
+ */
+
+/*
+ * HTML Parser By Misko Hevery (misko@hevery.com)
+ * based on:  HTML Parser By John Resig (ejohn.org)
+ * Original code by Erik Arvidsson, Mozilla Public License
+ * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
+ *
+ * // Use like so:
+ * htmlParser(htmlString, {
+ *     start: function(tag, attrs, unary) {},
+ *     end: function(tag) {},
+ *     chars: function(text) {},
+ *     comment: function(text) {}
+ * });
+ *
+ */
+
+
+/**
+ * @ngdoc service
+ * @name $sanitize
+ * @kind function
+ *
+ * @description
+ *   The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
+ *   then serialized back to properly escaped html string. This means that no unsafe input can make
+ *   it into the returned string, however, since our parser is more strict than a typical browser
+ *   parser, it's possible that some obscure input, which would be recognized as valid HTML by a
+ *   browser, won't make it through the sanitizer. The input may also contain SVG markup.
+ *   The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
+ *   `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
+ *
+ * @param {string} html HTML input.
+ * @returns {string} Sanitized HTML.
+ *
+ * @example
+   <example module="sanitizeExample" deps="angular-sanitize.js">
+   <file name="index.html">
+     <script>
+         angular.module('sanitizeExample', ['ngSanitize'])
+           .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
+             $scope.snippet =
+               '<p style="color:blue">an html\n' +
+               '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
+               'snippet</p>';
+             $scope.deliberatelyTrustDangerousSnippet = function() {
+               return $sce.trustAsHtml($scope.snippet);
+             };
+           }]);
+     </script>
+     <div ng-controller="ExampleController">
+        Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
+       <table>
+         <tr>
+           <td>Directive</td>
+           <td>How</td>
+           <td>Source</td>
+           <td>Rendered</td>
+         </tr>
+         <tr id="bind-html-with-sanitize">
+           <td>ng-bind-html</td>
+           <td>Automatically uses $sanitize</td>
+           <td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
+           <td><div ng-bind-html="snippet"></div></td>
+         </tr>
+         <tr id="bind-html-with-trust">
+           <td>ng-bind-html</td>
+           <td>Bypass $sanitize by explicitly trusting the dangerous value</td>
+           <td>
+           <pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
+&lt;/div&gt;</pre>
+           </td>
+           <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
+         </tr>
+         <tr id="bind-default">
+           <td>ng-bind</td>
+           <td>Automatically escapes</td>
+           <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
+           <td><div ng-bind="snippet"></div></td>
+         </tr>
+       </table>
+       </div>
+   </file>
+   <file name="protractor.js" type="protractor">
+     it('should sanitize the html snippet by default', function() {
+       expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
+         toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
+     });
+
+     it('should inline raw snippet if bound to a trusted value', function() {
+       expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
+         toBe("<p style=\"color:blue\">an html\n" +
+              "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
+              "snippet</p>");
+     });
+
+     it('should escape snippet without any filter', function() {
+       expect(element(by.css('#bind-default div')).getInnerHtml()).
+         toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
+              "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
+              "snippet&lt;/p&gt;");
+     });
+
+     it('should update', function() {
+       element(by.model('snippet')).clear();
+       element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
+       expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
+         toBe('new <b>text</b>');
+       expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
+         'new <b onclick="alert(1)">text</b>');
+       expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
+         "new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
+     });
+   </file>
+   </example>
+ */
+function $SanitizeProvider() {
+  this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
+    return function(html) {
+      var buf = [];
+      htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
+        return !/^unsafe/.test($$sanitizeUri(uri, isImage));
+      }));
+      return buf.join('');
+    };
+  }];
+}
+
+function sanitizeText(chars) {
+  var buf = [];
+  var writer = htmlSanitizeWriter(buf, angular.noop);
+  writer.chars(chars);
+  return buf.join('');
+}
+
+
+// Regular Expressions for parsing tags and attributes
+var START_TAG_REGEXP =
+       /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
+  END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
+  ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
+  BEGIN_TAG_REGEXP = /^</,
+  BEGING_END_TAGE_REGEXP = /^<\//,
+  COMMENT_REGEXP = /<!--(.*?)-->/g,
+  DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
+  CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
+  SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
+  // Match everything outside of normal chars and " (quote character)
+  NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
+
+
+// Good source of info about elements and attributes
+// http://dev.w3.org/html5/spec/Overview.html#semantics
+// http://simon.html5.org/html-elements
+
+// Safe Void Elements - HTML5
+// http://dev.w3.org/html5/spec/Overview.html#void-elements
+var voidElements = makeMap("area,br,col,hr,img,wbr");
+
+// Elements that you can, intentionally, leave open (and which close themselves)
+// http://dev.w3.org/html5/spec/Overview.html#optional-tags
+var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
+    optionalEndTagInlineElements = makeMap("rp,rt"),
+    optionalEndTagElements = angular.extend({},
+                                            optionalEndTagInlineElements,
+                                            optionalEndTagBlockElements);
+
+// Safe Block Elements - HTML5
+var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
+        "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
+        "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
+
+// Inline Elements - HTML5
+var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
+        "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
+        "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
+
+// SVG Elements
+// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
+var svgElements = makeMap("animate,animateColor,animateMotion,animateTransform,circle,defs," +
+        "desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient," +
+        "line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,set," +
+        "stop,svg,switch,text,title,tspan,use");
+
+// Special Elements (can contain anything)
+var specialElements = makeMap("script,style");
+
+var validElements = angular.extend({},
+                                   voidElements,
+                                   blockElements,
+                                   inlineElements,
+                                   optionalEndTagElements,
+                                   svgElements);
+
+//Attributes that have href and hence need to be sanitized
+var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
+
+var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
+    'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
+    'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
+    'scope,scrolling,shape,size,span,start,summary,target,title,type,' +
+    'valign,value,vspace,width');
+
+// SVG attributes (without "id" and "name" attributes)
+// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
+var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
+    'attributeName,attributeType,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,' +
+    'color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,' +
+    'font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,' +
+    'gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,' +
+    'keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,' +
+    'markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,' +
+    'overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,' +
+    'repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,' +
+    'stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,' +
+    'stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,' +
+    'stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,' +
+    'underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,' +
+    'viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,' +
+    'xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,' +
+    'zoomAndPan');
+
+var validAttrs = angular.extend({},
+                                uriAttrs,
+                                svgAttrs,
+                                htmlAttrs);
+
+function makeMap(str) {
+  var obj = {}, items = str.split(','), i;
+  for (i = 0; i < items.length; i++) obj[items[i]] = true;
+  return obj;
+}
+
+
+/**
+ * @example
+ * htmlParser(htmlString, {
+ *     start: function(tag, attrs, unary) {},
+ *     end: function(tag) {},
+ *     chars: function(text) {},
+ *     comment: function(text) {}
+ * });
+ *
+ * @param {string} html string
+ * @param {object} handler
+ */
+function htmlParser(html, handler) {
+  if (typeof html !== 'string') {
+    if (html === null || typeof html === 'undefined') {
+      html = '';
+    } else {
+      html = '' + html;
+    }
+  }
+  var index, chars, match, stack = [], last = html, text;
+  stack.last = function() { return stack[ stack.length - 1 ]; };
+
+  while (html) {
+    text = '';
+    chars = true;
+
+    // Make sure we're not in a script or style element
+    if (!stack.last() || !specialElements[ stack.last() ]) {
+
+      // Comment
+      if (html.indexOf("<!--") === 0) {
+        // comments containing -- are not allowed unless they terminate the comment
+        index = html.indexOf("--", 4);
+
+        if (index >= 0 && html.lastIndexOf("-->", index) === index) {
+          if (handler.comment) handler.comment(html.substring(4, index));
+          html = html.substring(index + 3);
+          chars = false;
+        }
+      // DOCTYPE
+      } else if (DOCTYPE_REGEXP.test(html)) {
+        match = html.match(DOCTYPE_REGEXP);
+
+        if (match) {
+          html = html.replace(match[0], '');
+          chars = false;
+        }
+      // end tag
+      } else if (BEGING_END_TAGE_REGEXP.test(html)) {
+        match = html.match(END_TAG_REGEXP);
+
+        if (match) {
+          html = html.substring(match[0].length);
+          match[0].replace(END_TAG_REGEXP, parseEndTag);
+          chars = false;
+        }
+
+      // start tag
+      } else if (BEGIN_TAG_REGEXP.test(html)) {
+        match = html.match(START_TAG_REGEXP);
+
+        if (match) {
+          // We only have a valid start-tag if there is a '>'.
+          if (match[4]) {
+            html = html.substring(match[0].length);
+            match[0].replace(START_TAG_REGEXP, parseStartTag);
+          }
+          chars = false;
+        } else {
+          // no ending tag found --- this piece should be encoded as an entity.
+          text += '<';
+          html = html.substring(1);
+        }
+      }
+
+      if (chars) {
+        index = html.indexOf("<");
+
+        text += index < 0 ? html : html.substring(0, index);
+        html = index < 0 ? "" : html.substring(index);
+
+        if (handler.chars) handler.chars(decodeEntities(text));
+      }
+
+    } else {
+      html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
+        function(all, text) {
+          text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
+
+          if (handler.chars) handler.chars(decodeEntities(text));
+
+          return "";
+      });
+
+      parseEndTag("", stack.last());
+    }
+
+    if (html == last) {
+      throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
+                                        "of html: {0}", html);
+    }
+    last = html;
+  }
+
+  // Clean up any remaining tags
+  parseEndTag();
+
+  function parseStartTag(tag, tagName, rest, unary) {
+    tagName = angular.lowercase(tagName);
+    if (blockElements[ tagName ]) {
+      while (stack.last() && inlineElements[ stack.last() ]) {
+        parseEndTag("", stack.last());
+      }
+    }
+
+    if (optionalEndTagElements[ tagName ] && stack.last() == tagName) {
+      parseEndTag("", tagName);
+    }
+
+    unary = voidElements[ tagName ] || !!unary;
+
+    if (!unary)
+      stack.push(tagName);
+
+    var attrs = {};
+
+    rest.replace(ATTR_REGEXP,
+      function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
+        var value = doubleQuotedValue
+          || singleQuotedValue
+          || unquotedValue
+          || '';
+
+        attrs[name] = decodeEntities(value);
+    });
+    if (handler.start) handler.start(tagName, attrs, unary);
+  }
+
+  function parseEndTag(tag, tagName) {
+    var pos = 0, i;
+    tagName = angular.lowercase(tagName);
+    if (tagName)
+      // Find the closest opened tag of the same type
+      for (pos = stack.length - 1; pos >= 0; pos--)
+        if (stack[ pos ] == tagName)
+          break;
+
+    if (pos >= 0) {
+      // Close all the open elements, up the stack
+      for (i = stack.length - 1; i >= pos; i--)
+        if (handler.end) handler.end(stack[ i ]);
+
+      // Remove the open elements from the stack
+      stack.length = pos;
+    }
+  }
+}
+
+var hiddenPre=document.createElement("pre");
+var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/;
+/**
+ * decodes all entities into regular string
+ * @param value
+ * @returns {string} A string with decoded entities.
+ */
+function decodeEntities(value) {
+  if (!value) { return ''; }
+
+  // Note: IE8 does not preserve spaces at the start/end of innerHTML
+  // so we must capture them and reattach them afterward
+  var parts = spaceRe.exec(value);
+  var spaceBefore = parts[1];
+  var spaceAfter = parts[3];
+  var content = parts[2];
+  if (content) {
+    hiddenPre.innerHTML=content.replace(/</g,"&lt;");
+    // innerText depends on styling as it doesn't display hidden elements.
+    // Therefore, it's better to use textContent not to cause unnecessary
+    // reflows. However, IE<9 don't support textContent so the innerText
+    // fallback is necessary.
+    content = 'textContent' in hiddenPre ?
+      hiddenPre.textContent : hiddenPre.innerText;
+  }
+  return spaceBefore + content + spaceAfter;
+}
+
+/**
+ * Escapes all potentially dangerous characters, so that the
+ * resulting string can be safely inserted into attribute or
+ * element text.
+ * @param value
+ * @returns {string} escaped text
+ */
+function encodeEntities(value) {
+  return value.
+    replace(/&/g, '&amp;').
+    replace(SURROGATE_PAIR_REGEXP, function(value) {
+      var hi = value.charCodeAt(0);
+      var low = value.charCodeAt(1);
+      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
+    }).
+    replace(NON_ALPHANUMERIC_REGEXP, function(value) {
+      return '&#' + value.charCodeAt(0) + ';';
+    }).
+    replace(/</g, '&lt;').
+    replace(/>/g, '&gt;');
+}
+
+/**
+ * create an HTML/XML writer which writes to buffer
+ * @param {Array} buf use buf.jain('') to get out sanitized html string
+ * @returns {object} in the form of {
+ *     start: function(tag, attrs, unary) {},
+ *     end: function(tag) {},
+ *     chars: function(text) {},
+ *     comment: function(text) {}
+ * }
+ */
+function htmlSanitizeWriter(buf, uriValidator) {
+  var ignore = false;
+  var out = angular.bind(buf, buf.push);
+  return {
+    start: function(tag, attrs, unary) {
+      tag = angular.lowercase(tag);
+      if (!ignore && specialElements[tag]) {
+        ignore = tag;
+      }
+      if (!ignore && validElements[tag] === true) {
+        out('<');
+        out(tag);
+        angular.forEach(attrs, function(value, key) {
+          var lkey=angular.lowercase(key);
+          var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
+          if (validAttrs[lkey] === true &&
+            (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
+            out(' ');
+            out(key);
+            out('="');
+            out(encodeEntities(value));
+            out('"');
+          }
+        });
+        out(unary ? '/>' : '>');
+      }
+    },
+    end: function(tag) {
+        tag = angular.lowercase(tag);
+        if (!ignore && validElements[tag] === true) {
+          out('</');
+          out(tag);
+          out('>');
+        }
+        if (tag == ignore) {
+          ignore = false;
+        }
+      },
+    chars: function(chars) {
+        if (!ignore) {
+          out(encodeEntities(chars));
+        }
+      }
+  };
+}
+
+
+// define ngSanitize module and register $sanitize service
+angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
+
+/* global sanitizeText: false */
+
+/**
+ * @ngdoc filter
+ * @name linky
+ * @kind function
+ *
+ * @description
+ * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
+ * plain email address links.
+ *
+ * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
+ *
+ * @param {string} text Input text.
+ * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
+ * @returns {string} Html-linkified text.
+ *
+ * @usage
+   <span ng-bind-html="linky_expression | linky"></span>
+ *
+ * @example
+   <example module="linkyExample" deps="angular-sanitize.js">
+     <file name="index.html">
+       <script>
+         angular.module('linkyExample', ['ngSanitize'])
+           .controller('ExampleController', ['$scope', function($scope) {
+             $scope.snippet =
+               'Pretty text with some links:\n'+
+               'http://angularjs.org/,\n'+
+               'mailto:us@somewhere.org,\n'+
+               'another@somewhere.org,\n'+
+               'and one more: ftp://127.0.0.1/.';
+             $scope.snippetWithTarget = 'http://angularjs.org/';
+           }]);
+       </script>
+       <div ng-controller="ExampleController">
+       Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
+       <table>
+         <tr>
+           <td>Filter</td>
+           <td>Source</td>
+           <td>Rendered</td>
+         </tr>
+         <tr id="linky-filter">
+           <td>linky filter</td>
+           <td>
+             <pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
+           </td>
+           <td>
+             <div ng-bind-html="snippet | linky"></div>
+           </td>
+         </tr>
+         <tr id="linky-target">
+          <td>linky target</td>
+          <td>
+            <pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
+          </td>
+          <td>
+            <div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
+          </td>
+         </tr>
+         <tr id="escaped-html">
+           <td>no filter</td>
+           <td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
+           <td><div ng-bind="snippet"></div></td>
+         </tr>
+       </table>
+     </file>
+     <file name="protractor.js" type="protractor">
+       it('should linkify the snippet with urls', function() {
+         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
+             toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
+                  'another@somewhere.org, and one more: ftp://127.0.0.1/.');
+         expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
+       });
+
+       it('should not linkify snippet without the linky filter', function() {
+         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
+             toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
+                  'another@somewhere.org, and one more: ftp://127.0.0.1/.');
+         expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
+       });
+
+       it('should update', function() {
+         element(by.model('snippet')).clear();
+         element(by.model('snippet')).sendKeys('new http://link.');
+         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
+             toBe('new http://link.');
+         expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
+         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
+             .toBe('new http://link.');
+       });
+
+       it('should work with the target property', function() {
+        expect(element(by.id('linky-target')).
+            element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
+            toBe('http://angularjs.org/');
+        expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
+       });
+     </file>
+   </example>
+ */
+angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
+  var LINKY_URL_REGEXP =
+        /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/,
+      MAILTO_REGEXP = /^mailto:/;
+
+  return function(text, target) {
+    if (!text) return text;
+    var match;
+    var raw = text;
+    var html = [];
+    var url;
+    var i;
+    while ((match = raw.match(LINKY_URL_REGEXP))) {
+      // We can not end in these as they are sometimes found at the end of the sentence
+      url = match[0];
+      // if we did not match ftp/http/mailto then assume mailto
+      if (match[2] == match[3]) url = 'mailto:' + url;
+      i = match.index;
+      addText(raw.substr(0, i));
+      addLink(url, match[0].replace(MAILTO_REGEXP, ''));
+      raw = raw.substring(i + match[0].length);
+    }
+    addText(raw);
+    return $sanitize(html.join(''));
+
+    function addText(text) {
+      if (!text) {
+        return;
+      }
+      html.push(sanitizeText(text));
+    }
+
+    function addLink(url, text) {
+      html.push('<a ');
+      if (angular.isDefined(target)) {
+        html.push('target="',
+                  target,
+                  '" ');
+      }
+      html.push('href="',
+                url.replace('"', '&quot;'),
+                '">');
+      addText(text);
+      html.push('</a>');
+    }
+  };
+}]);
+
+
+})(window, window.angular);

File diff suppressed because it is too large
+ 203 - 177
src/vendor/angular/angular.js


+ 300 - 300
src/vendor/angular/bindonce.js

@@ -1,321 +1,321 @@
 (function () {
-  "use strict";
-  /**
-   * Bindonce - Zero watches binding for AngularJs
-   * @version v0.3.1
-   * @link https://github.com/Pasvaz/bindonce
-   * @author Pasquale Vazzana <pasqualevazzana@gmail.com>
-   * @license MIT License, http://www.opensource.org/licenses/MIT
-   */
+	"use strict";
+	/**
+	 * Bindonce - Zero watches binding for AngularJs
+	 * @version v0.3.2
+	 * @link https://github.com/Pasvaz/bindonce
+	 * @author Pasquale Vazzana <pasqualevazzana@gmail.com>
+	 * @license MIT License, http://www.opensource.org/licenses/MIT
+	 */
 
-  var bindonceModule = angular.module('pasvaz.bindonce', []);
+	var bindonceModule = angular.module('pasvaz.bindonce', []);
 
-  bindonceModule.directive('bindonce', function ()
-  {
-    var toBoolean = function (value)
-    {
-      if (value && value.length !== 0)
-      {
-        var v = angular.lowercase("" + value);
-        value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]');
-      }
-      else
-      {
-        value = false;
-      }
-      return value;
-    };
+	bindonceModule.directive('bindonce', function ()
+	{
+		var toBoolean = function (value)
+		{
+			if (value && value.length !== 0)
+			{
+				var v = angular.lowercase("" + value);
+				value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]');
+			}
+			else
+			{
+				value = false;
+			}
+			return value;
+		};
 
-    var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
-    if (isNaN(msie))
-    {
-      msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
-    }
+		var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
+		if (isNaN(msie))
+		{
+			msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
+		}
 
-    var bindonceDirective =
-    {
-      restrict: "AM",
-      controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate)
-      {
-        var showHideBinder = function (elm, attr, value)
-        {
-          var show = (attr === 'show') ? '' : 'none';
-          var hide = (attr === 'hide') ? '' : 'none';
-          elm.css('display', toBoolean(value) ? show : hide);
-        };
-        var classBinder = function (elm, value)
-        {
-          if (angular.isObject(value) && !angular.isArray(value))
-          {
-            var results = [];
-            angular.forEach(value, function (value, index)
-            {
-              if (value) results.push(index);
-            });
-            value = results;
-          }
-          if (value)
-          {
-            elm.addClass(angular.isArray(value) ? value.join(' ') : value);
-          }
-        };
-        var transclude = function (transcluder, scope)
-        {
-          transcluder.transclude(scope, function (clone)
-          {
-            var parent = transcluder.element.parent();
-            var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1];
-            var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
-            var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
-            angular.forEach(clone, function (node)
-            {
-              parentNode.insertBefore(node, afterNextSibling);
-            });
-          });
-        };
+		var bindonceDirective =
+		{
+			restrict: "AM",
+			controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate)
+			{
+				var showHideBinder = function (elm, attr, value)
+				{
+					var show = (attr === 'show') ? '' : 'none';
+					var hide = (attr === 'hide') ? '' : 'none';
+					elm.css('display', toBoolean(value) ? show : hide);
+				};
+				var classBinder = function (elm, value)
+				{
+					if (angular.isObject(value) && !angular.isArray(value))
+					{
+						var results = [];
+						angular.forEach(value, function (value, index)
+						{
+							if (value) results.push(index);
+						});
+						value = results;
+					}
+					if (value)
+					{
+						elm.addClass(angular.isArray(value) ? value.join(' ') : value);
+					}
+				};
+				var transclude = function (transcluder, scope)
+				{
+					transcluder.transclude(scope, function (clone)
+					{
+						var parent = transcluder.element.parent();
+						var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1];
+						var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
+						var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
+						angular.forEach(clone, function (node)
+						{
+							parentNode.insertBefore(node, afterNextSibling);
+						});
+					});
+				};
 
-        var ctrl =
-        {
-          watcherRemover: undefined,
-          binders: [],
-          group: $attrs.boName,
-          element: $element,
-          ran: false,
+				var ctrl =
+				{
+					watcherRemover: undefined,
+					binders: [],
+					group: $attrs.boName,
+					element: $element,
+					ran: false,
 
-          addBinder: function (binder)
-          {
-            this.binders.push(binder);
+					addBinder: function (binder)
+					{
+						this.binders.push(binder);
 
-            // In case of late binding (when using the directive bo-name/bo-parent)
-            // it happens only when you use nested bindonce, if the bo-children
-            // are not dom children the linking can follow another order
-            if (this.ran)
-            {
-              this.runBinders();
-            }
-          },
+						// In case of late binding (when using the directive bo-name/bo-parent)
+						// it happens only when you use nested bindonce, if the bo-children
+						// are not dom children the linking can follow another order
+						if (this.ran)
+						{
+							this.runBinders();
+						}
+					},
 
-          setupWatcher: function (bindonceValue)
-          {
-            var that = this;
-            this.watcherRemover = $scope.$watch(bindonceValue, function (newValue)
-            {
-              if (newValue === undefined) return;
-              that.removeWatcher();
-              that.checkBindonce(newValue);
-            }, true);
-          },
+					setupWatcher: function (bindonceValue)
+					{
+						var that = this;
+						this.watcherRemover = $scope.$watch(bindonceValue, function (newValue)
+						{
+							if (newValue === undefined) return;
+							that.removeWatcher();
+							that.checkBindonce(newValue);
+						}, true);
+					},
 
-          checkBindonce: function (value)
-          {
-            var that = this, promise = (value.$promise) ? value.$promise.then : value.then;
-            // since Angular 1.2 promises are no longer
-            // undefined until they don't get resolved
-            if (typeof promise === 'function')
-            {
-              promise(function ()
-              {
-                that.runBinders();
-              });
-            }
-            else
-            {
-              that.runBinders();
-            }
-          },
+					checkBindonce: function (value)
+					{
+						var that = this, promise = (value.$promise) ? value.$promise.then : value.then;
+						// since Angular 1.2 promises are no longer
+						// undefined until they don't get resolved
+						if (typeof promise === 'function')
+						{
+							promise(function ()
+							{
+								that.runBinders();
+							});
+						}
+						else
+						{
+							that.runBinders();
+						}
+					},
 
-          removeWatcher: function ()
-          {
-            if (this.watcherRemover !== undefined)
-            {
-              this.watcherRemover();
-              this.watcherRemover = undefined;
-            }
-          },
+					removeWatcher: function ()
+					{
+						if (this.watcherRemover !== undefined)
+						{
+							this.watcherRemover();
+							this.watcherRemover = undefined;
+						}
+					},
 
-          runBinders: function ()
-          {
-            while (this.binders.length > 0)
-            {
-              var binder = this.binders.shift();
-              if (this.group && this.group != binder.group) continue;
-              var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value);
-              switch (binder.attr)
-              {
-                case 'boIf':
-                  if (toBoolean(value))
-                  {
-                    transclude(binder, binder.scope.$new());
-                  }
-                  break;
-                case 'boSwitch':
-                  var selectedTranscludes, switchCtrl = binder.controller[0];
-                  if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?']))
-                  {
-                    binder.scope.$eval(binder.attrs.change);
-                    angular.forEach(selectedTranscludes, function (selectedTransclude)
-                    {
-                      transclude(selectedTransclude, binder.scope.$new());
-                    });
-                  }
-                  break;
-                case 'boSwitchWhen':
-                  var ctrl = binder.controller[0];
-                  ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []);
-                  ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element });
-                  break;
-                case 'boSwitchDefault':
-                  var ctrl = binder.controller[0];
-                  ctrl.cases['?'] = (ctrl.cases['?'] || []);
-                  ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element });
-                  break;
-                case 'hide':
-                case 'show':
-                  showHideBinder(binder.element, binder.attr, value);
-                  break;
-                case 'class':
-                  classBinder(binder.element, value);
-                  break;
-                case 'text':
-                  binder.element.text(value);
-                  break;
-                case 'html':
-                  binder.element.html(value);
-                  break;
-                case 'style':
-                  binder.element.css(value);
-                  break;
-                case 'src':
-                  binder.element.attr(binder.attr, value);
-                  if (msie) binder.element.prop('src', value);
-                  break;
-                case 'attr':
-                  angular.forEach(binder.attrs, function (attrValue, attrKey)
-                  {
-                    var newAttr, newValue;
-                    if (attrKey.match(/^boAttr./) && binder.attrs[attrKey])
-                    {
-                      newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
-                      newValue = binder.scope.$eval(binder.attrs[attrKey]);
-                      binder.element.attr(newAttr, newValue);
-                    }
-                  });
-                  break;
-                case 'href':
-                case 'alt':
-                case 'title':
-                case 'id':
-                case 'value':
-                  binder.element.attr(binder.attr, value);
-                  break;
-              }
-            }
-            this.ran = true;
-          }
-        };
+					runBinders: function ()
+					{
+						while (this.binders.length > 0)
+						{
+							var binder = this.binders.shift();
+							if (this.group && this.group != binder.group) continue;
+							var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value);
+							switch (binder.attr)
+							{
+								case 'boIf':
+									if (toBoolean(value))
+									{
+										transclude(binder, binder.scope.$new());
+									}
+									break;
+								case 'boSwitch':
+									var selectedTranscludes, switchCtrl = binder.controller[0];
+									if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?']))
+									{
+										binder.scope.$eval(binder.attrs.change);
+										angular.forEach(selectedTranscludes, function (selectedTransclude)
+										{
+											transclude(selectedTransclude, binder.scope.$new());
+										});
+									}
+									break;
+								case 'boSwitchWhen':
+									var ctrl = binder.controller[0];
+									ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []);
+									ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element });
+									break;
+								case 'boSwitchDefault':
+									var ctrl = binder.controller[0];
+									ctrl.cases['?'] = (ctrl.cases['?'] || []);
+									ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element });
+									break;
+								case 'hide':
+								case 'show':
+									showHideBinder(binder.element, binder.attr, value);
+									break;
+								case 'class':
+									classBinder(binder.element, value);
+									break;
+								case 'text':
+									binder.element.text(value);
+									break;
+								case 'html':
+									binder.element.html(value);
+									break;
+								case 'style':
+									binder.element.css(value);
+									break;
+								case 'src':
+									binder.element.attr(binder.attr, value);
+									if (msie) binder.element.prop('src', value);
+									break;
+								case 'attr':
+									angular.forEach(binder.attrs, function (attrValue, attrKey)
+									{
+										var newAttr, newValue;
+										if (attrKey.match(/^boAttr./) && binder.attrs[attrKey])
+										{
+											newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
+											newValue = binder.scope.$eval(binder.attrs[attrKey]);
+											binder.element.attr(newAttr, newValue);
+										}
+									});
+									break;
+								case 'href':
+								case 'alt':
+								case 'title':
+								case 'id':
+								case 'value':
+									binder.element.attr(binder.attr, value);
+									break;
+							}
+						}
+						this.ran = true;
+					}
+				};
 
-        angular.extend(this, ctrl);
-      }],
+				angular.extend(this, ctrl);
+			}],
 
-      link: function (scope, elm, attrs, bindonceController)
-      {
-        var value = attrs.bindonce && scope.$eval(attrs.bindonce);
-        if (value !== undefined)
-        {
-          bindonceController.checkBindonce(value);
-        }
-        else
-        {
-          bindonceController.setupWatcher(attrs.bindonce);
-          elm.bind("$destroy", bindonceController.removeWatcher);
-        }
-      }
-    };
+			link: function (scope, elm, attrs, bindonceController)
+			{
+				var value = attrs.bindonce && scope.$eval(attrs.bindonce);
+				if (value !== undefined)
+				{
+					bindonceController.checkBindonce(value);
+				}
+				else
+				{
+					bindonceController.setupWatcher(attrs.bindonce);
+					elm.bind("$destroy", bindonceController.removeWatcher);
+				}
+			}
+		};
 
-    return bindonceDirective;
-  });
+		return bindonceDirective;
+	});
 
-  angular.forEach(
-  [
-    { directiveName: 'boShow', attribute: 'show' },
-    { directiveName: 'boHide', attribute: 'hide' },
-    { directiveName: 'boClass', attribute: 'class' },
-    { directiveName: 'boText', attribute: 'text' },
-    { directiveName: 'boBind', attribute: 'text' },
-    { directiveName: 'boHtml', attribute: 'html' },
-    { directiveName: 'boSrcI', attribute: 'src', interpolate: true },
-    { directiveName: 'boSrc', attribute: 'src' },
-    { directiveName: 'boHrefI', attribute: 'href', interpolate: true },
-    { directiveName: 'boHref', attribute: 'href' },
-    { directiveName: 'boAlt', attribute: 'alt' },
-    { directiveName: 'boTitle', attribute: 'title' },
-    { directiveName: 'boId', attribute: 'id' },
-    { directiveName: 'boStyle', attribute: 'style' },
-    { directiveName: 'boValue', attribute: 'value' },
-    { directiveName: 'boAttr', attribute: 'attr' },
+	angular.forEach(
+	[
+		{ directiveName: 'boShow', attribute: 'show' },
+		{ directiveName: 'boHide', attribute: 'hide' },
+		{ directiveName: 'boClass', attribute: 'class' },
+		{ directiveName: 'boText', attribute: 'text' },
+		{ directiveName: 'boBind', attribute: 'text' },
+		{ directiveName: 'boHtml', attribute: 'html' },
+		{ directiveName: 'boSrcI', attribute: 'src', interpolate: true },
+		{ directiveName: 'boSrc', attribute: 'src' },
+		{ directiveName: 'boHrefI', attribute: 'href', interpolate: true },
+		{ directiveName: 'boHref', attribute: 'href' },
+		{ directiveName: 'boAlt', attribute: 'alt' },
+		{ directiveName: 'boTitle', attribute: 'title' },
+		{ directiveName: 'boId', attribute: 'id' },
+		{ directiveName: 'boStyle', attribute: 'style' },
+		{ directiveName: 'boValue', attribute: 'value' },
+		{ directiveName: 'boAttr', attribute: 'attr' },
 
-    { directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 },
-    { directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } },
-    { directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' },
-    { directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' }
-  ],
-  function (boDirective)
-  {
-    var childPriority = 200;
-    return bindonceModule.directive(boDirective.directiveName, function ()
-    {
-      var bindonceDirective =
-      {
-        priority: boDirective.priority || childPriority,
-        transclude: boDirective.transclude || false,
-        terminal: boDirective.terminal || false,
-        require: ['^bindonce'].concat(boDirective.require || []),
-        controller: boDirective.controller,
-        compile: function (tElement, tAttrs, transclude)
-        {
-          return function (scope, elm, attrs, controllers)
-          {
-            var bindonceController = controllers[0];
-            var name = attrs.boParent;
-            if (name && bindonceController.group !== name)
-            {
-              var element = bindonceController.element.parent();
-              bindonceController = undefined;
-              var parentValue;
+		{ directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 },
+		{ directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } },
+		{ directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' },
+		{ directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' }
+	],
+	function (boDirective)
+	{
+		var childPriority = 200;
+		return bindonceModule.directive(boDirective.directiveName, function ()
+		{
+			var bindonceDirective =
+			{
+				priority: boDirective.priority || childPriority,
+				transclude: boDirective.transclude || false,
+				terminal: boDirective.terminal || false,
+				require: ['^bindonce'].concat(boDirective.require || []),
+				controller: boDirective.controller,
+				compile: function (tElement, tAttrs, transclude)
+				{
+					return function (scope, elm, attrs, controllers)
+					{
+						var bindonceController = controllers[0];
+						var name = attrs.boParent;
+						if (name && bindonceController.group !== name)
+						{
+							var element = bindonceController.element.parent();
+							bindonceController = undefined;
+							var parentValue;
 
-              while (element[0].nodeType !== 9 && element.length)
-              {
-                if ((parentValue = element.data('$bindonceController'))
-                  && parentValue.group === name)
-                {
-                  bindonceController = parentValue;
-                  break;
-                }
-                element = element.parent();
-              }
-              if (!bindonceController)
-              {
-                throw new Error("No bindonce controller: " + name);
-              }
-            }
+							while (element[0].nodeType !== 9 && element.length)
+							{
+								if ((parentValue = element.data('$bindonceController'))
+									&& parentValue.group === name)
+								{
+									bindonceController = parentValue;
+									break;
+								}
+								element = element.parent();
+							}
+							if (!bindonceController)
+							{
+								throw new Error("No bindonce controller: " + name);
+							}
+						}
 
-            bindonceController.addBinder(
-            {
-              element: elm,
-              attr: boDirective.attribute || boDirective.directiveName,
-              attrs: attrs,
-              value: attrs[boDirective.directiveName],
-              interpolate: boDirective.interpolate,
-              group: name,
-              transclude: transclude,
-              controller: controllers.slice(1),
-              scope: scope
-            });
-          };
-        }
-      };
+						bindonceController.addBinder(
+						{
+							element: elm,
+							attr: boDirective.attribute || boDirective.directiveName,
+							attrs: attrs,
+							value: attrs[boDirective.directiveName],
+							interpolate: boDirective.interpolate,
+							group: name,
+							transclude: transclude,
+							controller: controllers.slice(1),
+							scope: scope
+						});
+					};
+				}
+			};
 
-      return bindonceDirective;
-    });
-  })
+			return bindonceDirective;
+		});
+	})
 })();

Some files were not shown because too many files changed in this diff