You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by om...@apache.org on 2015/12/08 07:37:35 UTC

[11/51] [partial] incubator-metron git commit: Initial import of code from https://github.com/OpenSOC/opensoc at ac0b00373f8f56dfae03a8109af5feb373ea598e.

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/font/fontawesome-webfont.woff
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/font/fontawesome-webfont.woff b/opensoc-ui/lib/public/font/fontawesome-webfont.woff
new file mode 100755
index 0000000..b9bd17e
Binary files /dev/null and b/opensoc-ui/lib/public/font/fontawesome-webfont.woff differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/annotation-icon.png
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/annotation-icon.png b/opensoc-ui/lib/public/img/annotation-icon.png
new file mode 100755
index 0000000..a6607d6
Binary files /dev/null and b/opensoc-ui/lib/public/img/annotation-icon.png differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/cubes.png
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/cubes.png b/opensoc-ui/lib/public/img/cubes.png
new file mode 100755
index 0000000..30712ea
Binary files /dev/null and b/opensoc-ui/lib/public/img/cubes.png differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/glyphicons-halflings-white.png
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/glyphicons-halflings-white.png b/opensoc-ui/lib/public/img/glyphicons-halflings-white.png
new file mode 100755
index 0000000..3bf6484
Binary files /dev/null and b/opensoc-ui/lib/public/img/glyphicons-halflings-white.png differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/glyphicons-halflings.png
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/glyphicons-halflings.png b/opensoc-ui/lib/public/img/glyphicons-halflings.png
new file mode 100755
index 0000000..a996999
Binary files /dev/null and b/opensoc-ui/lib/public/img/glyphicons-halflings.png differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/kibana.png
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/kibana.png b/opensoc-ui/lib/public/img/kibana.png
new file mode 100755
index 0000000..02dca8c
Binary files /dev/null and b/opensoc-ui/lib/public/img/kibana.png differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/light.png
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/light.png b/opensoc-ui/lib/public/img/light.png
new file mode 100755
index 0000000..cc65dba
Binary files /dev/null and b/opensoc-ui/lib/public/img/light.png differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/load.gif
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/load.gif b/opensoc-ui/lib/public/img/load.gif
new file mode 100755
index 0000000..d636d04
Binary files /dev/null and b/opensoc-ui/lib/public/img/load.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/load_big.gif
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/load_big.gif b/opensoc-ui/lib/public/img/load_big.gif
new file mode 100755
index 0000000..e6daac3
Binary files /dev/null and b/opensoc-ui/lib/public/img/load_big.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/img/small.png
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/img/small.png b/opensoc-ui/lib/public/img/small.png
new file mode 100755
index 0000000..8c5222b
Binary files /dev/null and b/opensoc-ui/lib/public/img/small.png differ

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/vendor/LICENSE.json
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/vendor/LICENSE.json b/opensoc-ui/lib/public/vendor/LICENSE.json
new file mode 100755
index 0000000..2adbae2
--- /dev/null
+++ b/opensoc-ui/lib/public/vendor/LICENSE.json
@@ -0,0 +1,90 @@
+{
+  "angular": {
+    "version":"1.1.5",
+    "license":"MIT"
+  },
+  "angular-dragdrop": {
+    "version":"1.0.4",
+    "license":"MIT"
+  },
+  "angular-strap": {
+    "version":"0.7.5",
+    "license":"MIT"
+  },
+  "bindonce": {
+    "version":"0.2.1",
+    "license":"MIT"
+  },
+  "datepicker": {
+    "version":"12/3/2013",
+    "license":"Apache 2.0"
+  },
+  "timepicker": {
+    "version":"0.2.6",
+    "license":"MIT"
+  },
+  "bootstrap": {
+    "version":"2.3.2",
+    "license":"Apache 2.0"
+  },
+  "elasticjs": {
+    "version":"1.1.1",
+    "license":"MIT"
+  },
+  "jquery": {
+    "version":"1.8.0",
+    "license":"MIT"
+  },
+  "jquery-ui": {
+    "version":"1.10.3",
+    "license":"MIT"
+  },
+  "flot": {
+    "version":"0.8.1",
+    "license":"MIT"
+  },
+  "require": {
+    "version":"2.1.8",
+    "license":"MIT"
+  },
+  "chromath": {
+    "version":"0.0.5",
+    "license":"MIT"
+  },
+  "blob": {
+    "version":"2013-06-20",
+    "license":"MIT"
+  },
+  "filesaver": {
+    "version":"2013-01-23",
+    "license":"MIT"
+  },
+  "modernizr": {
+    "version":"2.6.1",
+    "license":"MIT"
+  },
+  "moment": {
+    "version":"2.1.0",
+    "license":"MIT"
+  },
+  "numeral": {
+    "version":"1.5.2",
+    "license":"MIT"
+  },
+  "timezone": {
+    "version":"2010",
+    "license":"Apache 2"
+  },
+  "lodash": {
+    "version":"2.4.1",
+    "license":"https://github.com/lodash/lodash/blob/2.4.1/LICENSE.txt"
+  },
+  "leaflet": {
+    "version":"0.6",
+    "license":"https://github.com/Leaflet/Leaflet/blob/v0.6/LICENSE"
+  },
+  "jvectormap": {
+    "version":"1.2.2",
+    "license":"MIT"
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/vendor/angular/angular-cookies.js
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/vendor/angular/angular-cookies.js b/opensoc-ui/lib/public/vendor/angular/angular-cookies.js
new file mode 100755
index 0000000..3cb0248
--- /dev/null
+++ b/opensoc-ui/lib/public/vendor/angular/angular-cookies.js
@@ -0,0 +1,185 @@
+/**
+ * @license AngularJS v1.1.5
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular, undefined) {
+'use strict';
+
+/**
+ * @ngdoc overview
+ * @name ngCookies
+ */
+
+
+angular.module('ngCookies', ['ng']).
+  /**
+   * @ngdoc object
+   * @name ngCookies.$cookies
+   * @requires $browser
+   *
+   * @description
+   * Provides read/write access to browser's cookies.
+   *
+   * Only a simple Object is exposed and by adding or removing properties to/from
+   * this object, new cookies are created/deleted at the end of current $eval.
+   *
+   * @example
+   <doc:example>
+     <doc:source>
+       <script>
+         function ExampleController($cookies) {
+           // Retrieving a cookie
+           var favoriteCookie = $cookies.myFavorite;
+           // Setting a cookie
+           $cookies.myFavorite = 'oatmeal';
+         }
+       </script>
+     </doc:source>
+   </doc:example>
+   */
+   factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
+      var cookies = {},
+          lastCookies = {},
+          lastBrowserCookies,
+          runEval = false,
+          copy = angular.copy,
+          isUndefined = angular.isUndefined;
+
+      //creates a poller fn that copies all cookies from the $browser to service & inits the service
+      $browser.addPollFn(function() {
+        var currentCookies = $browser.cookies();
+        if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
+          lastBrowserCookies = currentCookies;
+          copy(currentCookies, lastCookies);
+          copy(currentCookies, cookies);
+          if (runEval) $rootScope.$apply();
+        }
+      })();
+
+      runEval = true;
+
+      //at the end of each eval, push cookies
+      //TODO: this should happen before the "delayed" watches fire, because if some cookies are not
+      //      strings or browser refuses to store some cookies, we update the model in the push fn.
+      $rootScope.$watch(push);
+
+      return cookies;
+
+
+      /**
+       * Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
+       */
+      function push() {
+        var name,
+            value,
+            browserCookies,
+            updated;
+
+        //delete any cookies deleted in $cookies
+        for (name in lastCookies) {
+          if (isUndefined(cookies[name])) {
+            $browser.cookies(name, undefined);
+          }
+        }
+
+        //update all cookies updated in $cookies
+        for(name in cookies) {
+          value = cookies[name];
+          if (!angular.isString(value)) {
+            if (angular.isDefined(lastCookies[name])) {
+              cookies[name] = lastCookies[name];
+            } else {
+              delete cookies[name];
+            }
+          } else if (value !== lastCookies[name]) {
+            $browser.cookies(name, value);
+            updated = true;
+          }
+        }
+
+        //verify what was actually stored
+        if (updated){
+          updated = false;
+          browserCookies = $browser.cookies();
+
+          for (name in cookies) {
+            if (cookies[name] !== browserCookies[name]) {
+              //delete or reset all cookies that the browser dropped from $cookies
+              if (isUndefined(browserCookies[name])) {
+                delete cookies[name];
+              } else {
+                cookies[name] = browserCookies[name];
+              }
+              updated = true;
+            }
+          }
+        }
+      }
+    }]).
+
+
+  /**
+   * @ngdoc object
+   * @name ngCookies.$cookieStore
+   * @requires $cookies
+   *
+   * @description
+   * Provides a key-value (string-object) storage, that is backed by session cookies.
+   * Objects put or retrieved from this storage are automatically serialized or
+   * deserialized by angular's toJson/fromJson.
+   * @example
+   */
+   factory('$cookieStore', ['$cookies', function($cookies) {
+
+      return {
+        /**
+         * @ngdoc method
+         * @name ngCookies.$cookieStore#get
+         * @methodOf ngCookies.$cookieStore
+         *
+         * @description
+         * Returns the value of given cookie key
+         *
+         * @param {string} key Id to use for lookup.
+         * @returns {Object} Deserialized cookie value.
+         */
+        get: function(key) {
+          var value = $cookies[key];
+          return value ? angular.fromJson(value) : value;
+        },
+
+        /**
+         * @ngdoc method
+         * @name ngCookies.$cookieStore#put
+         * @methodOf ngCookies.$cookieStore
+         *
+         * @description
+         * Sets a value for given cookie key
+         *
+         * @param {string} key Id for the `value`.
+         * @param {Object} value Value to be stored.
+         */
+        put: function(key, value) {
+          $cookies[key] = angular.toJson(value);
+        },
+
+        /**
+         * @ngdoc method
+         * @name ngCookies.$cookieStore#remove
+         * @methodOf ngCookies.$cookieStore
+         *
+         * @description
+         * Remove given cookie
+         *
+         * @param {string} key Id of the key-value pair to delete.
+         */
+        remove: function(key) {
+          delete $cookies[key];
+        }
+      };
+
+    }]);
+
+
+})(window, window.angular);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/vendor/angular/angular-dragdrop.js
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/vendor/angular/angular-dragdrop.js b/opensoc-ui/lib/public/vendor/angular/angular-dragdrop.js
new file mode 100755
index 0000000..1238627
--- /dev/null
+++ b/opensoc-ui/lib/public/vendor/angular/angular-dragdrop.js
@@ -0,0 +1,333 @@
+/**
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/**
+ * Implementing Drag and Drop functionality in AngularJS is easier than ever.
+ * Demo: http://codef0rmer.github.com/angular-dragdrop/
+ *
+ * @version 1.0.4
+ *
+ * (c) 2013 Amit Gharat a.k.a codef0rmer <am...@gmail.com> - amitgharat.wordpress.com
+ */
+
+/**
+ * This has been modified from the default angular-draganddrop to provide the original
+ * model and some other stuff as the 'data' arguement to callEventCallback
+ */
+
+(function (window, angular, undefined) {
+'use strict';
+
+var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$timeout', '$parse', function($timeout, $parse) {
+    this.callEventCallback = function (scope, callbackName, event, ui, data) {
+      if (!callbackName) {
+        return;
+      }
+      var args = [event, ui, data];
+      var match = callbackName.match(/^(.+)\((.+)\)$/);
+      if (match !== null) {
+        callbackName = match[1];
+        var values = eval('[' + match[0].replace(/^(.+)\(/, '').replace(/\)/, '') + ']');
+        args.push.apply(args, values);
+      }
+      if(scope[callbackName]) {
+        scope[callbackName].apply(scope, args);
+      }
+    };
+
+    this.invokeDrop = function ($draggable, $droppable, event, ui) {
+      var dragModel = '',
+        dropModel = '',
+        dragSettings = {},
+        dropSettings = {},
+        jqyoui_pos = null,
+        dragItem = {},
+        dropItem = {},
+        dragModelValue,
+        dropModelValue,
+        $droppableDraggable = null,
+        droppableScope = $droppable.scope(),
+        draggableScope = $draggable.scope(),
+        data = {};
+
+      dragModel = $draggable.ngattr('ng-model');
+      dropModel = $droppable.ngattr('ng-model');
+      dragModelValue = draggableScope.$eval(dragModel);
+      dropModelValue = droppableScope.$eval(dropModel);
+
+      $droppableDraggable = $droppable.find('[jqyoui-draggable]:last');
+      dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable')) || [];
+      dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable')) || [];
+
+      // Helps pick up the right item
+      dragSettings.index = this.fixIndex(draggableScope, dragSettings, dragModelValue);
+      dropSettings.index = this.fixIndex(droppableScope, dropSettings, dropModelValue);
+
+      jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null;
+      dragItem = angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue;
+
+      if (angular.isArray(dropModelValue) && dropSettings && dropSettings.index !== undefined) {
+        dropItem = dropModelValue[dropSettings.index];
+      } else if (!angular.isArray(dropModelValue)) {
+        dropItem = dropModelValue;
+      } else {
+        dropItem = {};
+      }
+
+      data = {
+        dragModel: dragModel,
+        dropModel: dropModel,
+        dragSettings: dragSettings,
+        dropSettings: dropSettings,
+        jqyoui_pos: jqyoui_pos,
+        dragItem: dragItem,
+        dropItem: dropItem,
+        dragModelValue: dragModelValue,
+        dropModelValue: dropModelValue,
+        droppableScope: $droppable.scope(),
+        draggableScope: $draggable.scope()
+      };
+
+
+      if (dragSettings.animate === true) {
+
+        this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null);
+        this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, function() {
+          $timeout(function() {
+            // Do not move this into move() to avoid flickering issue
+            $draggable.css({'position': 'relative', 'left': '', 'top': ''});
+            $droppableDraggable.css({'position': 'relative', 'left': '', 'top': ''});
+
+            if(dragSettings.mutate !== false) {
+              this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
+            }
+
+            if(dropSettings.mutate !== false) {
+              this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
+            }
+
+            this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui, data);
+          }.bind(this));
+        }.bind(this));
+      } else {
+        $timeout(function() {
+
+          if(dragSettings.mutate !== false) {
+            this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
+          }
+
+          if(dropSettings.mutate !== false) {
+            this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
+          }
+
+          this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui, data);
+        }.bind(this));
+      }
+    };
+
+    this.move = function($fromEl, $toEl, toPos, duration, dropSettings, callback) {
+      if ($fromEl.length === 0) {
+        if (callback) {
+          window.setTimeout(function() {
+            callback();
+          }, 300);
+        }
+        return false;
+      }
+
+      var zIndex = 9999,
+        fromPos = $fromEl.offset(),
+        wasVisible = $toEl && $toEl.is(':visible');
+
+      if (toPos === null && $toEl.length > 0) {
+        if ($toEl.attr('jqyoui-draggable') !== undefined && $toEl.ngattr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) {
+          toPos = $toEl.offset();
+          if (dropSettings.stack === false) {
+            toPos.left+= $toEl.outerWidth(true);
+          } else {
+            toPos.top+= $toEl.outerHeight(true);
+          }
+        } else {
+          toPos = $toEl.css({'visibility': 'hidden', 'display': 'block'}).offset();
+          $toEl.css({'visibility': '','display': wasVisible ? '' : 'none'});
+        }
+      }
+
+      $fromEl.css({'position': 'absolute', 'z-index': zIndex})
+        .css(fromPos)
+        .animate(toPos, duration, function() {
+          if (callback) callback();
+        });
+    };
+
+    this.mutateDroppable = function(scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) {
+      var dropModelValue = scope.$eval(dropModel);
+
+      scope.__dragItem = dragItem;
+
+      if (angular.isArray(dropModelValue)) {
+        if (dropSettings && dropSettings.index >= 0) {
+          dropModelValue[dropSettings.index] = dragItem;
+        } else {
+          dropModelValue.push(dragItem);
+        }
+        if (dragSettings && dragSettings.placeholder === true) {
+          dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos;
+        }
+      } else {
+        $parse(dropModel + ' = __dragItem')(scope);
+        if (dragSettings && dragSettings.placeholder === true) {
+          dropModelValue['jqyoui_pos'] = jqyoui_pos;
+        }
+      }
+    };
+
+    this.mutateDraggable = function(scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) {
+      var isEmpty = angular.equals(angular.copy(dropItem), {}),
+        dragModelValue = scope.$eval(dragModel);
+
+      scope.__dropItem = dropItem;
+
+      if (dragSettings && dragSettings.placeholder) {
+        if (dragSettings.placeholder != 'keep'){
+          if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) {
+            dragModelValue[dragSettings.index] = dropItem;
+          } else {
+            $parse(dragModel + ' = __dropItem')(scope);
+          }
+        }
+      } else {
+        if (angular.isArray(dragModelValue)) {
+          if (isEmpty) {
+            if (dragSettings && ( dragSettings.placeholder !== true && dragSettings.placeholder !== 'keep' )) {
+              dragModelValue.splice(dragSettings.index, 1);
+            }
+          } else {
+            dragModelValue[dragSettings.index] = dropItem;
+          }
+        } else {
+          // Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = {...}
+          // P.S.: Could not figure out why it happened
+          $parse(dragModel + ' = __dropItem')(scope);
+          if (scope.$parent) {
+            $parse(dragModel + ' = __dropItem')(scope.$parent);
+          }
+        }
+      }
+
+      $draggable.css({'z-index': '', 'left': '', 'top': ''});
+    };
+
+    this.fixIndex = function(scope, settings, modelValue) {
+      if (settings.applyFilter && angular.isArray(modelValue) && modelValue.length > 0) {
+        var dragModelValueFiltered = scope[settings.applyFilter](),
+            lookup = dragModelValueFiltered[settings.index],
+            actualIndex = undefined;
+
+        modelValue.forEach(function(item, i) {
+           if (angular.equals(item, lookup)) {
+             actualIndex = i;
+           }
+        });
+
+        return actualIndex;
+      }
+
+      return settings.index;
+    };
+  }]).directive('jqyouiDraggable', ['ngDragDropService', function(ngDragDropService) {
+    return {
+      require: '?jqyouiDroppable',
+      restrict: 'A',
+      link: function(scope, element, attrs) {
+        var dragSettings, zIndex;
+        var updateDraggable = function(newValue, oldValue) {
+          if (newValue) {
+            dragSettings = scope.$eval(element.attr('jqyoui-draggable')) || [];
+            element
+              .draggable({disabled: false})
+              .draggable(scope.$eval(attrs.jqyouiOptions) || {})
+              .draggable({
+                start: function(event, ui) {
+                  zIndex = angular.element(this).css('z-index');
+                  angular.element(this).css('z-index', 99999);
+                  jqyoui.startXY = angular.element(this).offset();
+                  ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui);
+                },
+                stop: function(event, ui) {
+                  angular.element(this).css('z-index', zIndex);
+                  ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui);
+                },
+                drag: function(event, ui) {
+                  ngDragDropService.callEventCallback(scope, dragSettings.onDrag, event, ui);
+                }
+              });
+          } else {
+            element.draggable({disabled: true});
+          }
+        };
+        scope.$watch(function() { return scope.$eval(attrs.drag); }, updateDraggable);
+        updateDraggable();
+      }
+    };
+  }]).directive('jqyouiDroppable', ['ngDragDropService', function(ngDragDropService) {
+    return {
+      restrict: 'A',
+      priority: 1,
+      link: function(scope, element, attrs) {
+        var updateDroppable = function(newValue, oldValue) {
+          if (newValue) {
+            element
+              .droppable({disabled: false})
+              .droppable(scope.$eval(attrs.jqyouiOptions) || {})
+              .droppable({
+                over: function(event, ui) {
+                  var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || [];
+                  ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui);
+                },
+                out: function(event, ui) {
+                  var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || [];
+                  ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui);
+                },
+                drop: function(event, ui) {
+                  if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) {
+                    ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui);
+                  } else {
+                    ngDragDropService.callEventCallback(scope, (scope.$eval(angular.element(this).attr('jqyoui-droppable')) || []).onDrop, event, ui);
+                  }
+                }
+              });
+          } else {
+            element.droppable({disabled: true});
+          }
+        };
+
+        scope.$watch(function() { return scope.$eval(attrs.drop); }, updateDroppable);
+        updateDroppable();
+      }
+    };
+  }]);
+
+  $.fn.ngattr = function(name, value) {
+    var element = angular.element(this).get(0);
+
+    return element.getAttribute(name) || element.getAttribute('data-' + name);
+  };
+})(window, window.angular);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/vendor/angular/angular-sanitize.js
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/vendor/angular/angular-sanitize.js b/opensoc-ui/lib/public/vendor/angular/angular-sanitize.js
new file mode 100755
index 0000000..a2d8d34
--- /dev/null
+++ b/opensoc-ui/lib/public/vendor/angular/angular-sanitize.js
@@ -0,0 +1,558 @@
+/**
+ * @license AngularJS v1.1.5
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular, undefined) {
+'use strict';
+
+/**
+ * @ngdoc overview
+ * @name ngSanitize
+ * @description
+ */
+
+/*
+ * 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 ngSanitize.$sanitize
+ * @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.
+ *
+ * @param {string} html Html input.
+ * @returns {string} Sanitized html.
+ *
+ * @example
+   <doc:example module="ngSanitize">
+     <doc:source>
+       <script>
+         function Ctrl($scope) {
+           $scope.snippet =
+             '<p style="color:blue">an html\n' +
+             '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
+             'snippet</p>';
+         }
+       </script>
+       <div ng-controller="Ctrl">
+          Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
+           <table>
+             <tr>
+               <td>Filter</td>
+               <td>Source</td>
+               <td>Rendered</td>
+             </tr>
+             <tr id="html-filter">
+               <td>html filter</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="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>
+             <tr id="html-unsafe-filter">
+               <td>unsafe html filter</td>
+               <td><pre>&lt;div ng-bind-html-unsafe="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
+               <td><div ng-bind-html-unsafe="snippet"></div></td>
+             </tr>
+           </table>
+         </div>
+     </doc:source>
+     <doc:scenario>
+       it('should sanitize the html snippet ', function() {
+         expect(using('#html-filter').element('div').html()).
+           toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
+       });
+
+       it('should escape snippet without any filter', function() {
+         expect(using('#escaped-html').element('div').html()).
+           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 inline raw snippet if filtered as unsafe', function() {
+         expect(using('#html-unsafe-filter').element("div").html()).
+           toBe("<p style=\"color:blue\">an html\n" +
+                "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
+                "snippet</p>");
+       });
+
+       it('should update', function() {
+         input('snippet').enter('new <b>text</b>');
+         expect(using('#html-filter').binding('snippet')).toBe('new <b>text</b>');
+         expect(using('#escaped-html').element('div').html()).toBe("new &lt;b&gt;text&lt;/b&gt;");
+         expect(using('#html-unsafe-filter').binding("snippet")).toBe('new <b>text</b>');
+       });
+     </doc:scenario>
+   </doc:example>
+ */
+var $sanitize = function(html) {
+  var buf = [];
+    htmlParser(html, htmlSanitizeWriter(buf));
+    return buf.join('');
+};
+
+
+// Regular Expressions for parsing tags and attributes
+var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
+  END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/,
+  ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
+  BEGIN_TAG_REGEXP = /^</,
+  BEGING_END_TAGE_REGEXP = /^<\s*\//,
+  COMMENT_REGEXP = /<!--(.*?)-->/g,
+  CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
+  URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/,
+  NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
+
+
+// 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"));
+
+
+// Special Elements (can contain anything)
+var specialElements = makeMap("script,style");
+
+var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements);
+
+//Attributes that have href and hence need to be sanitized
+var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap");
+var validAttrs = angular.extend({}, uriAttrs, 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,span,start,summary,target,title,type,'+
+    'valign,value,vspace,width'));
+
+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 ) {
+  var index, chars, match, stack = [], last = html;
+  stack.last = function() { return stack[ stack.length - 1 ]; };
+
+  while ( html ) {
+    chars = true;
+
+    // Make sure we're not in a script or style element
+    if ( !stack.last() || !specialElements[ stack.last() ] ) {
+
+      // Comment
+      if ( html.indexOf("<!--") === 0 ) {
+        index = html.indexOf("-->");
+
+        if ( index >= 0 ) {
+          if (handler.comment) handler.comment( html.substring( 4, index ) );
+          html = html.substring( index + 3 );
+          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 ) {
+          html = html.substring( match[0].length );
+          match[0].replace( START_TAG_REGEXP, parseStartTag );
+          chars = false;
+        }
+      }
+
+      if ( chars ) {
+        index = html.indexOf("<");
+
+        var 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 "Parse Error: " + 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, singleQoutedValue, unqoutedValue) {
+      var value = doubleQuotedValue
+        || singleQoutedValue
+        || unqoutedValue
+        || '';
+
+      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;
+    }
+  }
+}
+
+/**
+ * decodes all entities into regular string
+ * @param value
+ * @returns {string} A string with decoded entities.
+ */
+var hiddenPre=document.createElement("pre");
+function decodeEntities(value) {
+  hiddenPre.innerHTML=value.replace(/</g,"&lt;");
+  return hiddenPre.innerText || hiddenPre.textContent || '';
+}
+
+/**
+ * Escapes all potentially dangerous characters, so that the
+ * resulting string can be safely inserted into attribute or
+ * element text.
+ * @param value
+ * @returns escaped text
+ */
+function encodeEntities(value) {
+  return value.
+    replace(/&/g, '&amp;').
+    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){
+  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);
+          if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
+            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', []).value('$sanitize', $sanitize);
+
+/**
+ * @ngdoc directive
+ * @name ngSanitize.directive:ngBindHtml
+ *
+ * @description
+ * Creates a binding that will sanitize the result of evaluating the `expression` with the
+ * {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element.
+ *
+ * See {@link ngSanitize.$sanitize $sanitize} docs for examples.
+ *
+ * @element ANY
+ * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
+ */
+angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) {
+  return function(scope, element, attr) {
+    element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
+    scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) {
+      value = $sanitize(value);
+      element.html(value || '');
+    });
+  };
+}]);
+
+/**
+ * @ngdoc filter
+ * @name ngSanitize.filter:linky
+ * @function
+ *
+ * @description
+ *   Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
+ *   plain email address links.
+ *
+ * @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
+   <doc:example module="ngSanitize">
+     <doc:source>
+       <script>
+         function Ctrl($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="Ctrl">
+       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>
+     </doc:source>
+     <doc:scenario>
+       it('should linkify the snippet with urls', function() {
+         expect(using('#linky-filter').binding('snippet | linky')).
+           toBe('Pretty text with some links:&#10;' +
+                '<a href="http://angularjs.org/">http://angularjs.org/</a>,&#10;' +
+                '<a href="mailto:us@somewhere.org">us@somewhere.org</a>,&#10;' +
+                '<a href="mailto:another@somewhere.org">another@somewhere.org</a>,&#10;' +
+                'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.');
+       });
+
+       it ('should not linkify snippet without the linky filter', function() {
+         expect(using('#escaped-html').binding('snippet')).
+           toBe("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/.");
+       });
+
+       it('should update', function() {
+         input('snippet').enter('new http://link.');
+         expect(using('#linky-filter').binding('snippet | linky')).
+           toBe('new <a href="http://link">http://link</a>.');
+         expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
+       });
+
+       it('should work with the target property', function() {
+        expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")).
+          toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>');
+       });
+     </doc:scenario>
+   </doc:example>
+ */
+angular.module('ngSanitize').filter('linky', function() {
+  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 = [];
+    // TODO(vojta): use $sanitize instead
+    var writer = htmlSanitizeWriter(html);
+    var url;
+    var i;
+    var properties = {};
+    if (angular.isDefined(target)) {
+      properties.target = target;
+    }
+    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;
+      writer.chars(raw.substr(0, i));
+      properties.href = url;
+      writer.start('a', properties);
+      writer.chars(match[0].replace(MAILTO_REGEXP, ''));
+      writer.end('a');
+      raw = raw.substring(i + match[0].length);
+    }
+    writer.chars(raw);
+    return html.join('');
+  };
+});
+
+
+})(window, window.angular);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/vendor/angular/angular-strap.js
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/vendor/angular/angular-strap.js b/opensoc-ui/lib/public/vendor/angular/angular-strap.js
new file mode 100755
index 0000000..281ce11
--- /dev/null
+++ b/opensoc-ui/lib/public/vendor/angular/angular-strap.js
@@ -0,0 +1,878 @@
+/**
+ * AngularStrap - Twitter Bootstrap directives for AngularJS
+ * @version v0.7.5 - 2013-07-21
+ * @link http://mgcrea.github.com/angular-strap
+ * @author Olivier Louvignes <ol...@mg-crea.com>
+ * @license MIT License, http://www.opensource.org/licenses/MIT
+ */
+angular.module('$strap.config', []).value('$strapConfig', {});
+angular.module('$strap.filters', ['$strap.config']);
+angular.module('$strap.directives', ['$strap.config']);
+angular.module('$strap', [
+  '$strap.filters',
+  '$strap.directives',
+  '$strap.config'
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsAlert', [
+  '$parse',
+  '$timeout',
+  '$compile',
+  function ($parse, $timeout, $compile) {
+    return {
+      restrict: 'A',
+      link: function postLink(scope, element, attrs) {
+        var getter = $parse(attrs.bsAlert), setter = getter.assign, value = getter(scope);
+        var closeAlert = function closeAlertFn(delay) {
+          $timeout(function () {
+            element.alert('close');
+          }, delay * 1);
+        };
+        if (!attrs.bsAlert) {
+          if (angular.isUndefined(attrs.closeButton) || attrs.closeButton !== '0' && attrs.closeButton !== 'false') {
+            element.prepend('<button type="button" class="close" data-dismiss="alert">&times;</button>');
+          }
+          if (attrs.closeAfter)
+            closeAlert(attrs.closeAfter);
+        } else {
+          scope.$watch(attrs.bsAlert, function (newValue, oldValue) {
+            value = newValue;
+            element.html((newValue.title ? '<strong>' + newValue.title + '</strong>&nbsp;' : '') + newValue.content || '');
+            if (!!newValue.closed) {
+              element.hide();
+            }
+            $compile(element.contents())(scope);
+            if (newValue.type || oldValue.type) {
+              oldValue.type && element.removeClass('alert-' + oldValue.type);
+              newValue.type && element.addClass('alert-' + newValue.type);
+            }
+            if (angular.isDefined(newValue.closeAfter))
+              closeAlert(newValue.closeAfter);
+            else if (attrs.closeAfter)
+              closeAlert(attrs.closeAfter);
+            if (angular.isUndefined(attrs.closeButton) || attrs.closeButton !== '0' && attrs.closeButton !== 'false') {
+              element.prepend('<button type="button" class="close" data-dismiss="alert">&times;</button>');
+            }
+          }, true);
+        }
+        element.addClass('alert').alert();
+        if (element.hasClass('fade')) {
+          element.removeClass('in');
+          setTimeout(function () {
+            element.addClass('in');
+          });
+        }
+        var parentArray = attrs.ngRepeat && attrs.ngRepeat.split(' in ').pop();
+        element.on('close', function (ev) {
+          var removeElement;
+          if (parentArray) {
+            ev.preventDefault();
+            element.removeClass('in');
+            removeElement = function () {
+              element.trigger('closed');
+              if (scope.$parent) {
+                scope.$parent.$apply(function () {
+                  var path = parentArray.split('.');
+                  var curr = scope.$parent;
+                  for (var i = 0; i < path.length; ++i) {
+                    if (curr) {
+                      curr = curr[path[i]];
+                    }
+                  }
+                  if (curr) {
+                    curr.splice(scope.$index, 1);
+                  }
+                });
+              }
+            };
+            $.support.transition && element.hasClass('fade') ? element.on($.support.transition.end, removeElement) : removeElement();
+          } else if (value) {
+            ev.preventDefault();
+            element.removeClass('in');
+            removeElement = function () {
+              element.trigger('closed');
+              scope.$apply(function () {
+                value.closed = true;
+              });
+            };
+            $.support.transition && element.hasClass('fade') ? element.on($.support.transition.end, removeElement) : removeElement();
+          } else {
+          }
+        });
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsButton', [
+  '$parse',
+  '$timeout',
+  function ($parse, $timeout) {
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      link: function postLink(scope, element, attrs, controller) {
+        if (controller) {
+          if (!element.parent('[data-toggle="buttons-checkbox"], [data-toggle="buttons-radio"]').length) {
+            element.attr('data-toggle', 'button');
+          }
+          var startValue = !!scope.$eval(attrs.ngModel);
+          if (startValue) {
+            element.addClass('active');
+          }
+          scope.$watch(attrs.ngModel, function (newValue, oldValue) {
+            var bNew = !!newValue, bOld = !!oldValue;
+            if (bNew !== bOld) {
+              $.fn.button.Constructor.prototype.toggle.call(button);
+            } else if (bNew && !startValue) {
+              element.addClass('active');
+            }
+          });
+        }
+        if (!element.hasClass('btn')) {
+          element.on('click.button.data-api', function (ev) {
+            element.button('toggle');
+          });
+        }
+        element.button();
+        var button = element.data('button');
+        button.toggle = function () {
+          if (!controller) {
+            return $.fn.button.Constructor.prototype.toggle.call(this);
+          }
+          var $parent = element.parent('[data-toggle="buttons-radio"]');
+          if ($parent.length) {
+            element.siblings('[ng-model]').each(function (k, v) {
+              $parse($(v).attr('ng-model')).assign(scope, false);
+            });
+            scope.$digest();
+            if (!controller.$modelValue) {
+              controller.$setViewValue(!controller.$modelValue);
+              scope.$digest();
+            }
+          } else {
+            scope.$apply(function () {
+              controller.$setViewValue(!controller.$modelValue);
+            });
+          }
+        };
+      }
+    };
+  }
+]).directive('bsButtonsCheckbox', [
+  '$parse',
+  function ($parse) {
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      compile: function compile(tElement, tAttrs, transclude) {
+        tElement.attr('data-toggle', 'buttons-checkbox').find('a, button').each(function (k, v) {
+          $(v).attr('bs-button', '');
+        });
+      }
+    };
+  }
+]).directive('bsButtonsRadio', [
+  '$timeout',
+  function ($timeout) {
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      compile: function compile(tElement, tAttrs, transclude) {
+        tElement.attr('data-toggle', 'buttons-radio');
+        if (!tAttrs.ngModel) {
+          tElement.find('a, button').each(function (k, v) {
+            $(v).attr('bs-button', '');
+          });
+        }
+        return function postLink(scope, iElement, iAttrs, controller) {
+          if (controller) {
+            $timeout(function () {
+              iElement.find('[value]').button().filter('[value="' + controller.$viewValue + '"]').addClass('active');
+            });
+            iElement.on('click.button.data-api', function (ev) {
+              scope.$apply(function () {
+                controller.$setViewValue($(ev.target).closest('button').attr('value'));
+              });
+            });
+            scope.$watch(iAttrs.ngModel, function (newValue, oldValue) {
+              if (newValue !== oldValue) {
+                var $btn = iElement.find('[value="' + scope.$eval(iAttrs.ngModel) + '"]');
+                if ($btn.length) {
+                  $btn.button('toggle');
+                }
+              }
+            });
+          }
+        };
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsButtonSelect', [
+  '$parse',
+  '$timeout',
+  function ($parse, $timeout) {
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      link: function postLink(scope, element, attrs, ctrl) {
+        var getter = $parse(attrs.bsButtonSelect), setter = getter.assign;
+        if (ctrl) {
+          element.text(scope.$eval(attrs.ngModel));
+          scope.$watch(attrs.ngModel, function (newValue, oldValue) {
+            element.text(newValue);
+          });
+        }
+        var values, value, index, newValue;
+        element.bind('click', function (ev) {
+          values = getter(scope);
+          value = ctrl ? scope.$eval(attrs.ngModel) : element.text();
+          index = values.indexOf(value);
+          newValue = index > values.length - 2 ? values[0] : values[index + 1];
+          scope.$apply(function () {
+            element.text(newValue);
+            if (ctrl) {
+              ctrl.$setViewValue(newValue);
+            }
+          });
+        });
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsDatepicker', [
+  '$timeout',
+  '$strapConfig',
+  function ($timeout, $strapConfig) {
+    var isAppleTouch = /(iP(a|o)d|iPhone)/g.test(navigator.userAgent);
+    var regexpMap = function regexpMap(language) {
+      language = language || 'en';
+      return {
+        '/': '[\\/]',
+        '-': '[-]',
+        '.': '[.]',
+        ' ': '[\\s]',
+        'dd': '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))',
+        'd': '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))',
+        'mm': '(?:[0]?[1-9]|[1][012])',
+        'm': '(?:[0]?[1-9]|[1][012])',
+        'DD': '(?:' + $.fn.datepicker.dates[language].days.join('|') + ')',
+        'D': '(?:' + $.fn.datepicker.dates[language].daysShort.join('|') + ')',
+        'MM': '(?:' + $.fn.datepicker.dates[language].months.join('|') + ')',
+        'M': '(?:' + $.fn.datepicker.dates[language].monthsShort.join('|') + ')',
+        'yyyy': '(?:(?:[1]{1}[0-9]{1}[0-9]{1}[0-9]{1})|(?:[2]{1}[0-9]{3}))(?![[0-9]])',
+        'yy': '(?:(?:[0-9]{1}[0-9]{1}))(?![[0-9]])'
+      };
+    };
+    var regexpForDateFormat = function regexpForDateFormat(format, language) {
+      var re = format, map = regexpMap(language), i;
+      i = 0;
+      angular.forEach(map, function (v, k) {
+        re = re.split(k).join('${' + i + '}');
+        i++;
+      });
+      i = 0;
+      angular.forEach(map, function (v, k) {
+        re = re.split('${' + i + '}').join(v);
+        i++;
+      });
+      return new RegExp('^' + re + '$', ['i']);
+    };
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      link: function postLink(scope, element, attrs, controller) {
+        var options = angular.extend({ autoclose: true }, $strapConfig.datepicker || {}), type = attrs.dateType || options.type || 'date';
+        angular.forEach([
+          'format',
+          'weekStart',
+          'calendarWeeks',
+          'startDate',
+          'endDate',
+          'daysOfWeekDisabled',
+          'autoclose',
+          'startView',
+          'minViewMode',
+          'todayBtn',
+          'todayHighlight',
+          'keyboardNavigation',
+          'language',
+          'forceParse'
+        ], function (key) {
+          if (angular.isDefined(attrs[key]))
+            options[key] = attrs[key];
+        });
+        var language = options.language || 'en', readFormat = attrs.dateFormat || options.format || $.fn.datepicker.dates[language] && $.fn.datepicker.dates[language].format || 'mm/dd/yyyy', format = isAppleTouch ? 'yyyy-mm-dd' : readFormat, dateFormatRegexp = regexpForDateFormat(format, language);
+        if (controller) {
+          controller.$formatters.unshift(function (modelValue) {
+            return type === 'date' && angular.isString(modelValue) && modelValue ? $.fn.datepicker.DPGlobal.parseDate(modelValue, $.fn.datepicker.DPGlobal.parseFormat(readFormat), language) : modelValue;
+          });
+          controller.$parsers.unshift(function (viewValue) {
+            if (!viewValue) {
+              controller.$setValidity('date', true);
+              return null;
+            } else if (type === 'date' && angular.isDate(viewValue)) {
+              controller.$setValidity('date', true);
+              return viewValue;
+            } else if (angular.isString(viewValue) && dateFormatRegexp.test(viewValue)) {
+              controller.$setValidity('date', true);
+              if (isAppleTouch)
+                return new Date(viewValue);
+              return type === 'string' ? viewValue : $.fn.datepicker.DPGlobal.parseDate(viewValue, $.fn.datepicker.DPGlobal.parseFormat(format), language);
+            } else {
+              controller.$setValidity('date', false);
+              return undefined;
+            }
+          });
+          controller.$render = function ngModelRender() {
+            if (isAppleTouch) {
+              var date = controller.$viewValue ? $.fn.datepicker.DPGlobal.formatDate(controller.$viewValue, $.fn.datepicker.DPGlobal.parseFormat(format), language) : '';
+              element.val(date);
+              return date;
+            }
+            if (!controller.$viewValue)
+              element.val('');
+            return element.datepicker('update', controller.$viewValue);
+          };
+        }
+        if (isAppleTouch) {
+          element.prop('type', 'date').css('-webkit-appearance', 'textfield');
+        } else {
+          if (controller) {
+            element.on('changeDate', function (ev) {
+              scope.$apply(function () {
+                controller.$setViewValue(type === 'string' ? element.val() : ev.date);
+              });
+            });
+          }
+          element.datepicker(angular.extend(options, {
+            format: format,
+            language: language
+          }));
+          scope.$on('$destroy', function () {
+            var datepicker = element.data('datepicker');
+            if (datepicker) {
+              datepicker.picker.remove();
+              element.data('datepicker', null);
+            }
+          });
+          attrs.$observe('startDate', function (value) {
+            element.datepicker('setStartDate', value);
+          });
+          attrs.$observe('endDate', function (value) {
+            element.datepicker('setEndDate', value);
+          });
+        }
+        var component = element.siblings('[data-toggle="datepicker"]');
+        if (component.length) {
+          component.on('click', function () {
+            if (!element.prop('disabled')) {
+              element.trigger('focus');
+            }
+          });
+        }
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsDropdown', [
+  '$parse',
+  '$compile',
+  '$timeout',
+  function ($parse, $compile, $timeout) {
+    var buildTemplate = function (items, ul) {
+      if (!ul)
+        ul = [
+          '<ul class="dropdown-menu" role="menu" aria-labelledby="drop1">',
+          '</ul>'
+        ];
+      angular.forEach(items, function (item, index) {
+        if (item.divider)
+          return ul.splice(index + 1, 0, '<li class="divider"></li>');
+        var li = '<li' + (item.submenu && item.submenu.length ? ' class="dropdown-submenu"' : '') + '>' + '<a tabindex="-1" ng-href="' + (item.href || '') + '"' + (item.click ? '" ng-click="' + item.click + '"' : '') + (item.target ? '" target="' + item.target + '"' : '') + (item.method ? '" data-method="' + item.method + '"' : '') + '>' + (item.text || '') + '</a>';
+        if (item.submenu && item.submenu.length)
+          li += buildTemplate(item.submenu).join('\n');
+        li += '</li>';
+        ul.splice(index + 1, 0, li);
+      });
+      return ul;
+    };
+    return {
+      restrict: 'EA',
+      scope: true,
+      link: function postLink(scope, iElement, iAttrs) {
+        var getter = $parse(iAttrs.bsDropdown), items = getter(scope);
+        $timeout(function () {
+          if (!angular.isArray(items)) {
+          }
+          var dropdown = angular.element(buildTemplate(items).join(''));
+          dropdown.insertAfter(iElement);
+          $compile(iElement.next('ul.dropdown-menu'))(scope);
+        });
+        iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').factory('$modal', [
+  '$rootScope',
+  '$compile',
+  '$http',
+  '$timeout',
+  '$q',
+  '$templateCache',
+  '$strapConfig',
+  function ($rootScope, $compile, $http, $timeout, $q, $templateCache, $strapConfig) {
+    var ModalFactory = function ModalFactory(config) {
+      function Modal(config) {
+        var options = angular.extend({ show: true }, $strapConfig.modal, config), scope = options.scope ? options.scope : $rootScope.$new(), templateUrl = options.template;
+        return $q.when($templateCache.get(templateUrl) || $http.get(templateUrl, { cache: true }).then(function (res) {
+          return res.data;
+        })).then(function onSuccess(template) {
+          var id = templateUrl.replace('.html', '').replace(/[\/|\.|:]/g, '-') + '-' + scope.$id;
+          var $modal = $('<div class="modal hide" tabindex="-1"></div>').attr('id', id).addClass('fade').html(template);
+          if (options.modalClass)
+            $modal.addClass(options.modalClass);
+          $('body').append($modal);
+          $timeout(function () {
+            $compile($modal)(scope);
+          });
+          scope.$modal = function (name) {
+            $modal.modal(name);
+          };
+          angular.forEach([
+            'show',
+            'hide'
+          ], function (name) {
+            scope[name] = function () {
+              $modal.modal(name);
+            };
+          });
+          scope.dismiss = scope.hide;
+          angular.forEach([
+            'show',
+            'shown',
+            'hide',
+            'hidden'
+          ], function (name) {
+            $modal.on(name, function (ev) {
+              scope.$emit('modal-' + name, ev);
+            });
+          });
+          $modal.on('shown', function (ev) {
+            $('input[autofocus], textarea[autofocus]', $modal).first().trigger('focus');
+          });
+          $modal.on('hidden', function (ev) {
+            if (!options.persist)
+              scope.$destroy();
+          });
+          scope.$on('$destroy', function () {
+            $modal.remove();
+          });
+          $modal.modal(options);
+          return $modal;
+        });
+      }
+      return new Modal(config);
+    };
+    return ModalFactory;
+  }
+]).directive('bsModal', [
+  '$q',
+  '$modal',
+  function ($q, $modal) {
+    return {
+      restrict: 'A',
+      scope: true,
+      link: function postLink(scope, iElement, iAttrs, controller) {
+        var options = {
+            template: scope.$eval(iAttrs.bsModal),
+            persist: true,
+            show: false,
+            scope: scope
+          };
+        angular.forEach([
+          'modalClass',
+          'backdrop',
+          'keyboard'
+        ], function (key) {
+          if (angular.isDefined(iAttrs[key]))
+            options[key] = iAttrs[key];
+        });
+        $q.when($modal(options)).then(function onSuccess(modal) {
+          iElement.attr('data-target', '#' + modal.attr('id')).attr('data-toggle', 'modal');
+        });
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsNavbar', [
+  '$location',
+  function ($location) {
+    return {
+      restrict: 'A',
+      link: function postLink(scope, element, attrs, controller) {
+        scope.$watch(function () {
+          return $location.path();
+        }, function (newValue, oldValue) {
+          $('li[data-match-route]', element).each(function (k, li) {
+            var $li = angular.element(li), pattern = $li.attr('data-match-route'), regexp = new RegExp('^' + pattern + '$', ['i']);
+            if (regexp.test(newValue)) {
+              $li.addClass('active').find('.collapse.in').collapse('hide');
+            } else {
+              $li.removeClass('active');
+            }
+          });
+        });
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsPopover', [
+  '$parse',
+  '$compile',
+  '$http',
+  '$timeout',
+  '$q',
+  '$templateCache',
+  function ($parse, $compile, $http, $timeout, $q, $templateCache) {
+    $('body').on('keyup', function (ev) {
+      if (ev.keyCode === 27) {
+        $('.popover.in').each(function () {
+          $(this).popover('hide');
+        });
+      }
+    });
+    return {
+      restrict: 'A',
+      scope: true,
+      link: function postLink(scope, element, attr, ctrl) {
+        var getter = $parse(attr.bsPopover), setter = getter.assign, value = getter(scope), options = {};
+        if (angular.isObject(value)) {
+          options = value;
+        }
+        $q.when(options.content || $templateCache.get(value) || $http.get(value, { cache: true })).then(function onSuccess(template) {
+          if (angular.isObject(template)) {
+            template = template.data;
+          }
+          if (!!attr.unique) {
+            element.on('show', function (ev) {
+              $('.popover.in').each(function () {
+                var $this = $(this), popover = $this.data('popover');
+                if (popover && !popover.$element.is(element)) {
+                  $this.popover('hide');
+                }
+              });
+            });
+          }
+          if (!!attr.hide) {
+            scope.$watch(attr.hide, function (newValue, oldValue) {
+              if (!!newValue) {
+                popover.hide();
+              } else if (newValue !== oldValue) {
+                popover.show();
+              }
+            });
+          }
+          if (!!attr.show) {
+            scope.$watch(attr.show, function (newValue, oldValue) {
+              if (!!newValue) {
+                $timeout(function () {
+                  popover.show();
+                });
+              } else if (newValue !== oldValue) {
+                popover.hide();
+              }
+            });
+          }
+          element.popover(angular.extend({}, options, {
+            content: template,
+            html: true
+          }));
+          var popover = element.data('popover');
+          popover.hasContent = function () {
+            return this.getTitle() || template;
+          };
+          popover.getPosition = function () {
+            var r = $.fn.popover.Constructor.prototype.getPosition.apply(this, arguments);
+            $compile(this.$tip)(scope);
+            scope.$digest();
+            this.$tip.data('popover', this);
+            return r;
+          };
+          scope.$popover = function (name) {
+            popover(name);
+          };
+          angular.forEach([
+            'show',
+            'hide'
+          ], function (name) {
+            scope[name] = function () {
+              popover[name]();
+            };
+          });
+          scope.dismiss = scope.hide;
+          angular.forEach([
+            'show',
+            'shown',
+            'hide',
+            'hidden'
+          ], function (name) {
+            element.on(name, function (ev) {
+              scope.$emit('popover-' + name, ev);
+            });
+          });
+        });
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsSelect', [
+  '$timeout',
+  function ($timeout) {
+    var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/;
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      link: function postLink(scope, element, attrs, controller) {
+        var options = scope.$eval(attrs.bsSelect) || {};
+        $timeout(function () {
+          element.selectpicker(options);
+          element.next().removeClass('ng-scope');
+        });
+        if (controller) {
+          scope.$watch(attrs.ngModel, function (newValue, oldValue) {
+            if (!angular.equals(newValue, oldValue)) {
+              element.selectpicker('refresh');
+            }
+          });
+        }
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsTabs', [
+  '$parse',
+  '$compile',
+  '$timeout',
+  function ($parse, $compile, $timeout) {
+    var template = '<div class="tabs">' + '<ul class="nav nav-tabs">' + '<li ng-repeat="pane in panes" ng-class="{active:pane.active}">' + '<a data-target="#{{pane.id}}" data-index="{{$index}}" data-toggle="tab">{{pane.title}}</a>' + '</li>' + '</ul>' + '<div class="tab-content" ng-transclude>' + '</div>';
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      priority: 0,
+      scope: true,
+      template: template,
+      replace: true,
+      transclude: true,
+      compile: function compile(tElement, tAttrs, transclude) {
+        return function postLink(scope, iElement, iAttrs, controller) {
+          var getter = $parse(iAttrs.bsTabs), setter = getter.assign, value = getter(scope);
+          scope.panes = [];
+          var $tabs = iElement.find('ul.nav-tabs');
+          var $panes = iElement.find('div.tab-content');
+          var activeTab = 0, id, title, active;
+          $timeout(function () {
+            $panes.find('[data-title], [data-tab]').each(function (index) {
+              var $this = angular.element(this);
+              id = 'tab-' + scope.$id + '-' + index;
+              title = $this.data('title') || $this.data('tab');
+              active = !active && $this.hasClass('active');
+              $this.attr('id', id).addClass('tab-pane');
+              if (iAttrs.fade)
+                $this.addClass('fade');
+              scope.panes.push({
+                id: id,
+                title: title,
+                content: this.innerHTML,
+                active: active
+              });
+            });
+            if (scope.panes.length && !active) {
+              $panes.find('.tab-pane:first-child').addClass('active' + (iAttrs.fade ? ' in' : ''));
+              scope.panes[0].active = true;
+            }
+          });
+          if (controller) {
+            iElement.on('show', function (ev) {
+              var $target = $(ev.target);
+              scope.$apply(function () {
+                controller.$setViewValue($target.data('index'));
+              });
+            });
+            scope.$watch(iAttrs.ngModel, function (newValue, oldValue) {
+              if (angular.isUndefined(newValue))
+                return;
+              activeTab = newValue;
+              setTimeout(function () {
+                // Check if we're still on the same tab before making the switch
+                if(activeTab === newValue) {
+                  var $next = $($tabs[0].querySelectorAll('li')[newValue * 1]);
+                  if (!$next.hasClass('active')) {
+                    $next.children('a').tab('show');
+                  }
+                }
+              });
+            });
+          }
+        };
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsTimepicker', [
+  '$timeout',
+  '$strapConfig',
+  function ($timeout, $strapConfig) {
+    var TIME_REGEXP = '((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)';
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      link: function postLink(scope, element, attrs, controller) {
+        if (controller) {
+          element.on('changeTime.timepicker', function (ev) {
+            $timeout(function () {
+              controller.$setViewValue(element.val());
+            });
+          });
+          var timeRegExp = new RegExp('^' + TIME_REGEXP + '$', ['i']);
+          controller.$parsers.unshift(function (viewValue) {
+            if (!viewValue || timeRegExp.test(viewValue)) {
+              controller.$setValidity('time', true);
+              return viewValue;
+            } else {
+              controller.$setValidity('time', false);
+              return;
+            }
+          });
+        }
+        element.attr('data-toggle', 'timepicker');
+        element.parent().addClass('bootstrap-timepicker');
+        element.timepicker($strapConfig.timepicker || {});
+        var timepicker = element.data('timepicker');
+        var component = element.siblings('[data-toggle="timepicker"]');
+        if (component.length) {
+          component.on('click', $.proxy(timepicker.showWidget, timepicker));
+        }
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsTooltip', [
+  '$parse',
+  '$compile',
+  function ($parse, $compile) {
+    return {
+      restrict: 'A',
+      scope: true,
+      link: function postLink(scope, element, attrs, ctrl) {
+        var getter = $parse(attrs.bsTooltip), setter = getter.assign, value = getter(scope);
+        scope.$watch(attrs.bsTooltip, function (newValue, oldValue) {
+          if (newValue !== oldValue) {
+            value = newValue;
+          }
+        });
+        if (!!attrs.unique) {
+          element.on('show', function (ev) {
+            $('.tooltip.in').each(function () {
+              var $this = $(this), tooltip = $this.data('tooltip');
+              if (tooltip && !tooltip.$element.is(element)) {
+                $this.tooltip('hide');
+              }
+            });
+          });
+        }
+        element.tooltip({
+          title: function () {
+            return angular.isFunction(value) ? value.apply(null, arguments) : value;
+          },
+          html: true
+        });
+        var tooltip = element.data('tooltip');
+        tooltip.show = function () {
+          var r = $.fn.tooltip.Constructor.prototype.show.apply(this, arguments);
+          this.tip().data('tooltip', this);
+          return r;
+        };
+        scope._tooltip = function (event) {
+          element.tooltip(event);
+        };
+        scope.hide = function () {
+          element.tooltip('hide');
+        };
+        scope.show = function () {
+          element.tooltip('show');
+        };
+        scope.dismiss = scope.hide;
+      }
+    };
+  }
+]);
+'use strict';
+angular.module('$strap.directives').directive('bsTypeahead', [
+  '$parse',
+  function ($parse) {
+    return {
+      restrict: 'A',
+      require: '?ngModel',
+      link: function postLink(scope, element, attrs, controller) {
+        var getter = $parse(attrs.bsTypeahead), setter = getter.assign, value = getter(scope);
+        scope.$watch(attrs.bsTypeahead, function (newValue, oldValue) {
+          if (newValue !== oldValue) {
+            value = newValue;
+          }
+        });
+        element.attr('data-provide', 'typeahead');
+        element.typeahead({
+          source: function (query) {
+            return angular.isFunction(value) ? value.apply(null, arguments) : value;
+          },
+          minLength: attrs.minLength || 1,
+          items: attrs.items,
+          updater: function (value) {
+            if (controller) {
+              scope.$apply(function () {
+                controller.$setViewValue(value);
+              });
+            }
+            scope.$emit('typeahead-updated', value);
+            return value;
+          }
+        });
+        var typeahead = element.data('typeahead');
+        typeahead.lookup = function (ev) {
+          var items;
+          this.query = this.$element.val() || '';
+          if (this.query.length < this.options.minLength) {
+            return this.shown ? this.hide() : this;
+          }
+          items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source;
+          return items ? this.process(items) : this;
+        };
+        if (!!attrs.matchAll) {
+          typeahead.matcher = function (item) {
+            return true;
+          };
+        }
+        if (attrs.minLength === '0') {
+          setTimeout(function () {
+            element.on('focus', function () {
+              element.val().length === 0 && setTimeout(element.typeahead.bind(element, 'lookup'), 200);
+            });
+          });
+        }
+      }
+    };
+  }
+]);
\ No newline at end of file