You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by ma...@apache.org on 2014/01/17 02:03:25 UTC

[2/6] Scheduler home page new using AngularJS and UI client

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/3rdparty/javascript/bower_components/angular/bower.json
----------------------------------------------------------------------
diff --git a/3rdparty/javascript/bower_components/angular/bower.json b/3rdparty/javascript/bower_components/angular/bower.json
new file mode 100644
index 0000000..9079df9
--- /dev/null
+++ b/3rdparty/javascript/bower_components/angular/bower.json
@@ -0,0 +1,7 @@
+{
+  "name": "angular",
+  "version": "1.2.6",
+  "main": "./angular.js",
+  "dependencies": {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/3rdparty/javascript/bower_components/smart-table/.bower.json
----------------------------------------------------------------------
diff --git a/3rdparty/javascript/bower_components/smart-table/.bower.json b/3rdparty/javascript/bower_components/smart-table/.bower.json
new file mode 100644
index 0000000..4dc3ebf
--- /dev/null
+++ b/3rdparty/javascript/bower_components/smart-table/.bower.json
@@ -0,0 +1,25 @@
+{
+  "author": "Laurent Renard",
+  "name": "smart-table",
+  "description": "A grid/table module for angular js",
+  "version": "0.1.5",
+  "homepage": "http://lorenzofox3.github.io/smart-table-website/",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/lorenzofox3/bower-smart-table.git"
+  },
+  "main": "./Smart-Table.min.js",
+  "dependencies": {
+    "angular": "1.0.7"
+  },
+  "_release": "0.1.5",
+  "_resolution": {
+    "type": "version",
+    "tag": "v0.1.5",
+    "commit": "36adf6a69f83f1c28c16d21416555a63c7f75be8"
+  },
+  "_source": "git://github.com/lorenzofox3/bower-smart-table.git",
+  "_target": "~0.1.5",
+  "_originalSource": "smart-table",
+  "_direct": true
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/3rdparty/javascript/bower_components/smart-table/Smart-Table.debug.js
----------------------------------------------------------------------
diff --git a/3rdparty/javascript/bower_components/smart-table/Smart-Table.debug.js b/3rdparty/javascript/bower_components/smart-table/Smart-Table.debug.js
new file mode 100644
index 0000000..b79c60e
--- /dev/null
+++ b/3rdparty/javascript/bower_components/smart-table/Smart-Table.debug.js
@@ -0,0 +1,943 @@
+/* Column module */
+
+(function (global, angular) {
+    "use strict";
+    var smartTableColumnModule = angular.module('smartTable.column', ['smartTable.templateUrlList']).constant('DefaultColumnConfiguration', {
+        isSortable: true,
+        isEditable: false,
+        type: 'text',
+
+
+        //it is useless to have that empty strings, but it reminds what is available
+        headerTemplateUrl: '',
+        map: '',
+        label: '',
+        sortPredicate: '',
+        formatFunction: '',
+        formatParameter: '',
+        filterPredicate: '',
+        cellTemplateUrl: '',
+        headerClass: '',
+        cellClass: ''
+    });
+
+    function ColumnProvider(DefaultColumnConfiguration, templateUrlList) {
+
+        function Column(config) {
+            if (!(this instanceof Column)) {
+                return new Column(config);
+            }
+            angular.extend(this, config);
+        }
+
+        this.setDefaultOption = function (option) {
+            angular.extend(Column.prototype, option);
+        };
+
+        DefaultColumnConfiguration.headerTemplateUrl = templateUrlList.defaultHeader;
+        this.setDefaultOption(DefaultColumnConfiguration);
+
+        this.$get = function () {
+            return Column;
+        };
+    }
+
+    ColumnProvider.$inject = ['DefaultColumnConfiguration', 'templateUrlList'];
+    smartTableColumnModule.provider('Column', ColumnProvider);
+
+    //make it global so it can be tested
+    global.ColumnProvider = ColumnProvider;
+})(window, angular);
+
+
+/* Directives */
+(function (angular) {
+    "use strict";
+    angular.module('smartTable.directives', ['smartTable.templateUrlList', 'smartTable.templates'])
+        .directive('smartTable', ['templateUrlList', 'DefaultTableConfiguration', function (templateList, defaultConfig) {
+            return {
+                restrict: 'EA',
+                scope: {
+                    columnCollection: '=columns',
+                    dataCollection: '=rows',
+                    config: '='
+                },
+                replace: 'true',
+                templateUrl: templateList.smartTable,
+                controller: 'TableCtrl',
+                link: function (scope, element, attr, ctrl) {
+
+                    var templateObject;
+
+                    scope.$watch('config', function (config) {
+                        var newConfig = angular.extend({}, defaultConfig, config),
+                            length = scope.columns !== undefined ? scope.columns.length : 0;
+
+                        ctrl.setGlobalConfig(newConfig);
+
+                        //remove the checkbox column if needed
+                        if (newConfig.selectionMode !== 'multiple' || newConfig.displaySelectionCheckbox !== true) {
+                            for (var i = length - 1; i >= 0; i--) {
+                                if (scope.columns[i].isSelectionColumn === true) {
+                                    ctrl.removeColumn(i);
+                                }
+                            }
+                        } else {
+                            //add selection box column if required
+                            ctrl.insertColumn({cellTemplateUrl: templateList.selectionCheckbox, headerTemplateUrl: templateList.selectAllCheckbox, isSelectionColumn: true}, 0);
+                        }
+                    }, true);
+
+                    //insert columns from column config
+                    scope.$watch('columnCollection', function (oldValue, newValue) {
+
+                        ctrl.clearColumns();
+
+                        if (scope.columnCollection) {
+                            for (var i = 0, l = scope.columnCollection.length; i < l; i++) {
+                                ctrl.insertColumn(scope.columnCollection[i]);
+                            }
+                        } else {
+                            //or guess data Structure
+                            if (scope.dataCollection && scope.dataCollection.length > 0) {
+                                templateObject = scope.dataCollection[0];
+                                angular.forEach(templateObject, function (value, key) {
+                                    if (key[0] != '$') {
+                                        ctrl.insertColumn({label: key, map: key});
+                                    }
+                                });
+                            }
+                        }
+                    }, true);
+
+                    //if item are added or removed into the data model from outside the grid
+                    scope.$watch('dataCollection.length', function (oldValue, newValue) {
+                        if (oldValue !== newValue) {
+                            ctrl.sortBy();//it will trigger the refresh... some hack ?
+                        }
+                    });
+                }
+            };
+        }])
+        //just to be able to select the row
+        .directive('smartTableDataRow', function () {
+
+            return {
+                require: '^smartTable',
+                restrict: 'C',
+                link: function (scope, element, attr, ctrl) {
+
+                    element.bind('click', function () {
+                        scope.$apply(function () {
+                            ctrl.toggleSelection(scope.dataRow);
+                        })
+                    });
+                }
+            };
+        })
+        //header cell with sorting functionality or put a checkbox if this column is a selection column
+        .directive('smartTableHeaderCell',function () {
+            return {
+                restrict: 'C',
+                require: '^smartTable',
+                link: function (scope, element, attr, ctrl) {
+                    element.bind('click', function () {
+                        scope.$apply(function () {
+                            ctrl.sortBy(scope.column);
+                        });
+                    })
+                }
+            };
+        }).directive('smartTableSelectAll', function () {
+            return {
+                restrict: 'C',
+                require: '^smartTable',
+                link: function (scope, element, attr, ctrl) {
+                    element.bind('click', function (event) {
+                        ctrl.toggleSelectionAll(element[0].checked === true);
+                    })
+                }
+            };
+        })
+        //credit to Valentyn shybanov : http://stackoverflow.com/questions/14544741/angularjs-directive-to-stoppropagation
+        .directive('stopEvent', function () {
+            return {
+                restrict: 'A',
+                link: function (scope, element, attr) {
+                    element.bind(attr.stopEvent, function (e) {
+                        e.stopPropagation();
+                    });
+                }
+            }
+        })
+        //the global filter
+        .directive('smartTableGlobalSearch', ['templateUrlList', function (templateList) {
+            return {
+                restrict: 'C',
+                require: '^smartTable',
+                scope: {
+                    columnSpan: '@'
+                },
+                templateUrl: templateList.smartTableGlobalSearch,
+                replace: false,
+                link: function (scope, element, attr, ctrl) {
+
+                    scope.searchValue = '';
+
+                    scope.$watch('searchValue', function (value) {
+                        //todo perf improvement only filter on blur ?
+                        ctrl.search(value);
+                    });
+                }
+            }
+        }])
+        //a customisable cell (see templateUrl) and editable
+        //TODO check with the ng-include strategy
+        .directive('smartTableDataCell', ['$filter', '$http', '$templateCache', '$compile', '$parse', function (filter, http, templateCache, compile, parse) {
+            return {
+                restrict: 'C',
+                link: function (scope, element) {
+                    var
+                        column = scope.column,
+                        isSimpleCell = !column.isEditable,
+                        row = scope.dataRow,
+                        format = filter('format'),
+                        getter = parse(column.map),
+                        childScope;
+
+                    //can be useful for child directives
+                    scope.$watch('dataRow', function (value) {
+                        scope.formatedValue = format(getter(row), column.formatFunction, column.formatParameter);
+                        if (isSimpleCell === true) {
+                            element.text(scope.formatedValue);
+                        }
+                    }, true);
+
+                    function defaultContent() {
+                        if (column.isEditable) {
+                            element.html('<div editable-cell="" row="dataRow" column="column" type="column.type"></div>');
+                            compile(element.contents())(scope);
+                        } else {
+                            element.text(scope.formatedValue);
+                        }
+                    }
+
+                    scope.$watch('column.cellTemplateUrl', function (value) {
+
+                        if (value) {
+                            //we have to load the template (and cache it) : a kind of ngInclude
+                            http.get(value, {cache: templateCache}).success(function (response) {
+
+                                isSimpleCell = false;
+
+                                //create a scope
+                                childScope = scope.$new();
+                                //compile the element with its new content and new scope
+                                element.html(response);
+                                compile(element.contents())(childScope);
+                            }).error(defaultContent);
+
+                        } else {
+                            defaultContent();
+                        }
+                    });
+                }
+            };
+        }])
+        //directive that allows type to be bound in input
+        .directive('inputType', function () {
+            return {
+                restrict: 'A',
+                priority: 1,
+                link: function (scope, ielement, iattr) {
+                    //force the type to be set before inputDirective is called
+                    var type = scope.$eval(iattr.type);
+                    iattr.$set('type', type);
+                }
+            };
+        })
+        //an editable content in the context of a cell (see row, column)
+        .directive('editableCell', ['templateUrlList', '$parse', function (templateList, parse) {
+            return {
+                restrict: 'EA',
+                require: '^smartTable',
+                templateUrl: templateList.editableCell,
+                scope: {
+                    row: '=',
+                    column: '=',
+                    type: '='
+                },
+                replace: true,
+                link: function (scope, element, attrs, ctrl) {
+                    var form = angular.element(element.children()[1]),
+                        input = angular.element(form.children()[0]),
+                        getter = parse(scope.column.map);
+
+                    //init values
+                    scope.isEditMode = false;
+                    scope.$watch('row', function () {
+                        scope.value = getter(scope.row);
+                    }, true);
+
+
+                    scope.submit = function () {
+                        //update model if valid
+                        if (scope.myForm.$valid === true) {
+                            ctrl.updateDataRow(scope.row, scope.column.map, scope.value);
+                            ctrl.sortBy();//it will trigger the refresh...  (ie it will sort, filter, etc with the new value)
+                        }
+                        scope.toggleEditMode();
+                    };
+
+                    scope.toggleEditMode = function () {
+                        scope.value = getter(scope.row);
+                        scope.isEditMode = scope.isEditMode !== true;
+                    };
+
+                    scope.$watch('isEditMode', function (newValue) {
+                        if (newValue === true) {
+                            input[0].select();
+                            input[0].focus();
+                        }
+                    });
+
+                    input.bind('blur', function () {
+                        scope.$apply(function () {
+                            scope.submit();
+                        });
+                    });
+                }
+            };
+        }]);
+})(angular);
+/* Filters */
+(function (angular) {
+    "use strict";
+    angular.module('smartTable.filters', []).
+        constant('DefaultFilters', ['currency', 'date', 'json', 'lowercase', 'number', 'uppercase']).
+        filter('format', ['$filter', 'DefaultFilters', function (filter, defaultfilters) {
+            return function (value, formatFunction, filterParameter) {
+
+                var returnFunction;
+
+                if (formatFunction && angular.isFunction(formatFunction)) {
+                    returnFunction = formatFunction;
+                } else {
+                    returnFunction = defaultfilters.indexOf(formatFunction) !== -1 ? filter(formatFunction) : function (value) {
+                        return value;
+                    };
+                }
+                return returnFunction(value, filterParameter);
+            };
+        }]);
+})(angular);
+
+
+/*table module */
+
+(function (angular) {
+    "use strict";
+    angular.module('smartTable.table', ['smartTable.column', 'smartTable.utilities', 'smartTable.directives', 'smartTable.filters', 'ui.bootstrap.pagination.smartTable'])
+        .constant('DefaultTableConfiguration', {
+            selectionMode: 'none',
+            isGlobalSearchActivated: false,
+            displaySelectionCheckbox: false,
+            isPaginationEnabled: true,
+            itemsByPage: 10,
+            maxSize: 5,
+
+            //just to remind available option
+            sortAlgorithm: '',
+            filterAlgorithm: ''
+        })
+        .controller('TableCtrl', ['$scope', 'Column', '$filter', '$parse', 'ArrayUtility', 'DefaultTableConfiguration', function (scope, Column, filter, parse, arrayUtility, defaultConfig) {
+
+            scope.columns = [];
+            scope.dataCollection = scope.dataCollection || [];
+            scope.displayedCollection = []; //init empty array so that if pagination is enabled, it does not spoil performances
+            scope.numberOfPages = calculateNumberOfPages(scope.dataCollection);
+            scope.currentPage = 1;
+            scope.holder = {isAllSelected: false};
+
+            var predicate = {},
+                lastColumnSort;
+
+            function isAllSelected() {
+                var i,
+                    l = scope.displayedCollection.length;
+                for (i = 0; i < l; i++) {
+                    if (scope.displayedCollection[i].isSelected !== true) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            function calculateNumberOfPages(array) {
+
+                if (!angular.isArray(array) || array.length === 0 || scope.itemsByPage < 1) {
+                    return 1;
+                }
+                return Math.ceil(array.length / scope.itemsByPage);
+            }
+
+            function sortDataRow(array, column) {
+                var sortAlgo = (scope.sortAlgorithm && angular.isFunction(scope.sortAlgorithm)) === true ? scope.sortAlgorithm : filter('orderBy');
+                if (column) {
+                    return arrayUtility.sort(array, sortAlgo, column.sortPredicate, column.reverse);
+                } else {
+                    return array;
+                }
+            }
+
+            function selectDataRow(array, selectionMode, index, select) {
+
+                var dataRow, oldValue;
+
+                if ((!angular.isArray(array)) || (selectionMode !== 'multiple' && selectionMode !== 'single')) {
+                    return;
+                }
+
+                if (index >= 0 && index < array.length) {
+                    dataRow = array[index];
+                    if (selectionMode === 'single') {
+                        //unselect all the others
+                        for (var i = 0, l = array.length; i < l; i++) {
+                            oldValue = array[i].isSelected;
+                            array[i].isSelected = false;
+                            if (oldValue === true) {
+                                scope.$emit('selectionChange', {item: array[i]});
+                            }
+                        }
+                    }
+                    dataRow.isSelected = select;
+                    scope.holder.isAllSelected = isAllSelected();
+                    scope.$emit('selectionChange', {item: dataRow});
+                }
+            }
+
+            /**
+             * set the config (config parameters will be available through scope
+             * @param config
+             */
+            this.setGlobalConfig = function (config) {
+                angular.extend(scope, defaultConfig, config);
+            };
+
+            /**
+             * change the current page displayed
+             * @param page
+             */
+            this.changePage = function (page) {
+                var oldPage = scope.currentPage;
+                if (angular.isNumber(page.page)) {
+                    scope.currentPage = page.page;
+                    scope.displayedCollection = this.pipe(scope.dataCollection);
+                    scope.holder.isAllSelected = isAllSelected();
+                    scope.$emit('changePage', {oldValue: oldPage, newValue: scope.currentPage});
+                }
+            };
+
+            /**
+             * set column as the column used to sort the data (if it is already the case, it will change the reverse value)
+             * @method sortBy
+             * @param column
+             */
+            this.sortBy = function (column) {
+                var index = scope.columns.indexOf(column);
+                if (index !== -1) {
+                    if (column.isSortable === true) {
+                        // reset the last column used
+                        if (lastColumnSort && lastColumnSort !== column) {
+                            lastColumnSort.reverse = 'none';
+                        }
+
+                        column.sortPredicate = column.sortPredicate || column.map;
+                        column.reverse = column.reverse !== true;
+                        lastColumnSort = column;
+                    }
+                }
+
+                scope.displayedCollection = this.pipe(scope.dataCollection);
+            };
+
+            /**
+             * set the filter predicate used for searching
+             * @param input
+             * @param column
+             */
+            this.search = function (input, column) {
+
+                var j, l = scope.columns.length;
+                //update column and global predicate
+                if (column && scope.columns.indexOf(column) !== -1) {
+                    predicate.$ = '';
+                    column.filterPredicate = input;
+                } else {
+                    for (j = 0; j < l; j++) {
+                        scope.columns[j].filterPredicate = '';
+                    }
+                    predicate.$ = input;
+                }
+
+                for (j = 0; j < l; j++) {
+                    predicate[scope.columns[j].map] = scope.columns[j].filterPredicate;
+                }
+                scope.displayedCollection = this.pipe(scope.dataCollection);
+
+            };
+
+            /**
+             * combine sort, search and limitTo operations on an array,
+             * @param array
+             * @returns Array, an array result of the operations on input array
+             */
+            this.pipe = function (array) {
+                var filterAlgo = (scope.filterAlgorithm && angular.isFunction(scope.filterAlgorithm)) === true ? scope.filterAlgorithm : filter('filter'),
+                    output;
+                //filter and sort are commutative
+                output = sortDataRow(arrayUtility.filter(array, filterAlgo, predicate), lastColumnSort);
+                scope.numberOfPages = calculateNumberOfPages(output);
+                return scope.isPaginationEnabled ? arrayUtility.fromTo(output, (scope.currentPage - 1) * scope.itemsByPage, scope.itemsByPage) : output;
+            };
+
+            /*////////////
+             Column API
+             ///////////*/
+
+
+            /**
+             * insert a new column in scope.collection at index or push at the end if no index
+             * @param columnConfig column configuration used to instantiate the new Column
+             * @param index where to insert the column (at the end if not specified)
+             */
+            this.insertColumn = function (columnConfig, index) {
+                var column = new Column(columnConfig);
+                arrayUtility.insertAt(scope.columns, index, column);
+            };
+
+            /**
+             * remove the column at columnIndex from scope.columns
+             * @param columnIndex index of the column to be removed
+             */
+            this.removeColumn = function (columnIndex) {
+                arrayUtility.removeAt(scope.columns, columnIndex);
+            };
+
+            /**
+             * move column located at oldIndex to the newIndex in scope.columns
+             * @param oldIndex index of the column before it is moved
+             * @param newIndex index of the column after the column is moved
+             */
+            this.moveColumn = function (oldIndex, newIndex) {
+                arrayUtility.moveAt(scope.columns, oldIndex, newIndex);
+            };
+
+            /**
+             * remove all columns
+             */
+            this.clearColumns = function () {
+                scope.columns.length = 0;
+            };
+
+            /*///////////
+             ROW API
+             */
+
+            /**
+             * select or unselect the item of the displayedCollection with the selection mode set in the scope
+             * @param dataRow
+             */
+            this.toggleSelection = function (dataRow) {
+                var index = scope.dataCollection.indexOf(dataRow);
+                if (index !== -1) {
+                    selectDataRow(scope.dataCollection, scope.selectionMode, index, dataRow.isSelected !== true);
+                }
+            };
+
+            /**
+             * select/unselect all the currently displayed rows
+             * @param value if true select, else unselect
+             */
+            this.toggleSelectionAll = function (value) {
+                var i = 0,
+                    l = scope.displayedCollection.length;
+
+                if (scope.selectionMode !== 'multiple') {
+                    return;
+                }
+                for (; i < l; i++) {
+                    selectDataRow(scope.displayedCollection, scope.selectionMode, i, value === true);
+                }
+            };
+
+            /**
+             * remove the item at index rowIndex from the displayed collection
+             * @param rowIndex
+             * @returns {*} item just removed or undefined
+             */
+            this.removeDataRow = function (rowIndex) {
+                var toRemove = arrayUtility.removeAt(scope.displayedCollection, rowIndex);
+                arrayUtility.removeAt(scope.dataCollection, scope.dataCollection.indexOf(toRemove));
+            };
+
+            /**
+             * move an item from oldIndex to newIndex in displayedCollection
+             * @param oldIndex
+             * @param newIndex
+             */
+            this.moveDataRow = function (oldIndex, newIndex) {
+                arrayUtility.moveAt(scope.displayedCollection, oldIndex, newIndex);
+            };
+
+            /**
+             * update the model, it can be a non existing yet property
+             * @param dataRow the dataRow to update
+             * @param propertyName the property on the dataRow ojbect to update
+             * @param newValue the value to set
+             */
+            this.updateDataRow = function (dataRow, propertyName, newValue) {
+                var index = scope.displayedCollection.indexOf(dataRow),
+                    getter = parse(propertyName),
+                    setter = getter.assign,
+                    oldValue;
+                if (index !== -1) {
+                    oldValue = getter(scope.displayedCollection[index]);
+                    if (oldValue !== newValue) {
+                        setter(scope.displayedCollection[index], newValue);
+                        scope.$emit('updateDataRow', {item: scope.displayedCollection[index]});
+                    }
+                }
+            };
+        }]);
+})(angular);
+
+
+angular.module('smartTable.templates', ['partials/defaultCell.html', 'partials/defaultHeader.html', 'partials/editableCell.html', 'partials/globalSearchCell.html', 'partials/pagination.html', 'partials/selectAllCheckbox.html', 'partials/selectionCheckbox.html', 'partials/smartTable.html']);
+
+angular.module("partials/defaultCell.html", []).run(["$templateCache", function ($templateCache) {
+    $templateCache.put("partials/defaultCell.html",
+        "{{formatedValue}}");
+}]);
+
+angular.module("partials/defaultHeader.html", []).run(["$templateCache", function ($templateCache) {
+    $templateCache.put("partials/defaultHeader.html",
+        "<span class=\"header-content\" ng-class=\"{'sort-ascent':column.reverse==true,'sort-descent':column.reverse==false}\">{{column.label}}</span>");
+}]);
+
+angular.module("partials/editableCell.html", []).run(["$templateCache", function ($templateCache) {
+    $templateCache.put("partials/editableCell.html",
+        "<div ng-dblclick=\"toggleEditMode($event)\">\n" +
+            "    <span ng-hide=\"isEditMode\">{{value | format:column.formatFunction:column.formatParameter}}</span>\n" +
+            "\n" +
+            "    <form ng-submit=\"submit()\" ng-show=\"isEditMode\" name=\"myForm\">\n" +
+            "        <input name=\"myInput\" ng-model=\"value\" type=\"type\" input-type/>\n" +
+            "    </form>\n" +
+            "</div>");
+}]);
+
+angular.module("partials/globalSearchCell.html", []).run(["$templateCache", function ($templateCache) {
+    $templateCache.put("partials/globalSearchCell.html",
+        "<label>Search :</label>\n" +
+            "<input type=\"text\" ng-model=\"searchValue\"/>");
+}]);
+
+angular.module("partials/pagination.html", []).run(["$templateCache", function ($templateCache) {
+    $templateCache.put("partials/pagination.html",
+        "<div class=\"pagination\">\n" +
+            "    <ul>\n" +
+            "        <li ng-repeat=\"page in pages\" ng-class=\"{active: page.active, disabled: page.disabled}\"><a\n" +
+            "                ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\n" +
+            "    </ul>\n" +
+            "</div> ");
+}]);
+
+angular.module("partials/selectAllCheckbox.html", []).run(["$templateCache", function ($templateCache) {
+    $templateCache.put("partials/selectAllCheckbox.html",
+        "<input class=\"smart-table-select-all\"  type=\"checkbox\" ng-model=\"holder.isAllSelected\"/>");
+}]);
+
+angular.module("partials/selectionCheckbox.html", []).run(["$templateCache", function ($templateCache) {
+    $templateCache.put("partials/selectionCheckbox.html",
+        "<input type=\"checkbox\" ng-model=\"dataRow.isSelected\" stop-event=\"click\"/>");
+}]);
+
+angular.module("partials/smartTable.html", []).run(["$templateCache", function ($templateCache) {
+    $templateCache.put("partials/smartTable.html",
+        "<table class=\"smart-table\">\n" +
+            "    <thead>\n" +
+            "    <tr class=\"smart-table-global-search-row\" ng-show=\"isGlobalSearchActivated\">\n" +
+            "        <td class=\"smart-table-global-search\" column-span=\"{{columns.length}}\" colspan=\"{{columnSpan}}\">\n" +
+            "        </td>\n" +
+            "    </tr>\n" +
+            "    <tr class=\"smart-table-header-row\">\n" +
+            "        <th ng-repeat=\"column in columns\" ng-include=\"column.headerTemplateUrl\"\n" +
+            "            class=\"smart-table-header-cell {{column.headerClass}}\" scope=\"col\">\n" +
+            "        </th>\n" +
+            "    </tr>\n" +
+            "    </thead>\n" +
+            "    <tbody>\n" +
+            "    <tr ng-repeat=\"dataRow in displayedCollection\" ng-class=\"{selected:dataRow.isSelected}\"\n" +
+            "        class=\"smart-table-data-row\">\n" +
+            "        <td ng-repeat=\"column in columns\" class=\"smart-table-data-cell {{column.cellClass}}\"></td>\n" +
+            "    </tr>\n" +
+            "    </tbody>\n" +
+            "    <tfoot ng-show=\"isPaginationEnabled\">\n" +
+            "    <tr class=\"smart-table-footer-row\">\n" +
+            "        <td colspan=\"{{columns.length}}\">\n" +
+            "            <div pagination-smart-table=\"\" num-pages=\"numberOfPages\" max-size=\"maxSize\" current-page=\"currentPage\"></div>\n" +
+            "        </td>\n" +
+            "    </tr>\n" +
+            "    </tfoot>\n" +
+            "</table>\n" +
+            "\n" +
+            "\n" +
+            "");
+}]);
+
+(function (angular) {
+    "use strict";
+    angular.module('smartTable.templateUrlList', [])
+        .constant('templateUrlList', {
+            smartTable: 'partials/smartTable.html',
+            smartTableGlobalSearch: 'partials/globalSearchCell.html',
+            editableCell: 'partials/editableCell.html',
+            selectionCheckbox: 'partials/selectionCheckbox.html',
+            selectAllCheckbox: 'partials/selectAllCheckbox.html',
+            defaultHeader: 'partials/defaultHeader.html',
+            pagination: 'partials/pagination.html'
+        });
+})(angular);
+
+
+(function (angular) {
+    "use strict";
+    angular.module('smartTable.utilities', [])
+
+        .factory('ArrayUtility', function () {
+
+            /**
+             * remove the item at index from arrayRef and return the removed item
+             * @param arrayRef
+             * @param index
+             * @returns {*}
+             */
+            var removeAt = function (arrayRef, index) {
+                    if (index >= 0 && index < arrayRef.length) {
+                        return arrayRef.splice(index, 1)[0];
+                    }
+                },
+
+                /**
+                 * insert item in arrayRef at index or a the end if index is wrong
+                 * @param arrayRef
+                 * @param index
+                 * @param item
+                 */
+                    insertAt = function (arrayRef, index, item) {
+                    if (index >= 0 && index < arrayRef.length) {
+                        arrayRef.splice(index, 0, item);
+                    } else {
+                        arrayRef.push(item);
+                    }
+                },
+
+                /**
+                 * move the item at oldIndex to newIndex in arrayRef
+                 * @param arrayRef
+                 * @param oldIndex
+                 * @param newIndex
+                 */
+                    moveAt = function (arrayRef, oldIndex, newIndex) {
+                    var elementToMove;
+                    if (oldIndex >= 0 && oldIndex < arrayRef.length && newIndex >= 0 && newIndex < arrayRef.length) {
+                        elementToMove = arrayRef.splice(oldIndex, 1)[0];
+                        arrayRef.splice(newIndex, 0, elementToMove);
+                    }
+                },
+
+                /**
+                 * sort arrayRef according to sortAlgorithm following predicate and reverse
+                 * @param arrayRef
+                 * @param sortAlgorithm
+                 * @param predicate
+                 * @param reverse
+                 * @returns {*}
+                 */
+                    sort = function (arrayRef, sortAlgorithm, predicate, reverse) {
+
+                    if (!sortAlgorithm || !angular.isFunction(sortAlgorithm)) {
+                        return arrayRef;
+                    } else {
+                        return sortAlgorithm(arrayRef, predicate, reverse === true);//excpet if reverse is true it will take it as false
+                    }
+                },
+
+                /**
+                 * filter arrayRef according with filterAlgorithm and predicate
+                 * @param arrayRef
+                 * @param filterAlgorithm
+                 * @param predicate
+                 * @returns {*}
+                 */
+                    filter = function (arrayRef, filterAlgorithm, predicate) {
+                    if (!filterAlgorithm || !angular.isFunction(filterAlgorithm)) {
+                        return arrayRef;
+                    } else {
+                        return filterAlgorithm(arrayRef, predicate);
+                    }
+                },
+
+                /**
+                 * return an array, part of array ref starting at min and the size of length
+                 * @param arrayRef
+                 * @param min
+                 * @param length
+                 * @returns {*}
+                 */
+                    fromTo = function (arrayRef, min, length) {
+
+                    var out = [],
+                        limit,
+                        start;
+
+                    if (!angular.isArray(arrayRef)) {
+                        return arrayRef;
+                    }
+
+                    start = Math.max(min, 0);
+                    start = Math.min(start, (arrayRef.length - 1) > 0 ? arrayRef.length - 1 : 0);
+
+                    length = Math.max(0, length);
+                    limit = Math.min(start + length, arrayRef.length);
+
+                    for (var i = start; i < limit; i++) {
+                        out.push(arrayRef[i]);
+                    }
+                    return out;
+                };
+
+
+            return {
+                removeAt: removeAt,
+                insertAt: insertAt,
+                moveAt: moveAt,
+                sort: sort,
+                filter: filter,
+                fromTo: fromTo
+            };
+        });
+})(angular);
+
+
+(function (angular) {
+    angular.module('ui.bootstrap.pagination.smartTable', ['smartTable.templateUrlList'])
+
+        .constant('paginationConfig', {
+            boundaryLinks: false,
+            directionLinks: true,
+            firstText: 'First',
+            previousText: '<',
+            nextText: '>',
+            lastText: 'Last'
+        })
+
+        .directive('paginationSmartTable', ['paginationConfig', 'templateUrlList', function (paginationConfig, templateUrlList) {
+            return {
+                restrict: 'EA',
+                require: '^smartTable',
+                scope: {
+                    numPages: '=',
+                    currentPage: '=',
+                    maxSize: '='
+                },
+                templateUrl: templateUrlList.pagination,
+                replace: true,
+                link: function (scope, element, attrs, ctrl) {
+
+                    // Setup configuration parameters
+                    var boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
+                    var directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
+                    var firstText = angular.isDefined(attrs.firstText) ? attrs.firstText : paginationConfig.firstText;
+                    var previousText = angular.isDefined(attrs.previousText) ? attrs.previousText : paginationConfig.previousText;
+                    var nextText = angular.isDefined(attrs.nextText) ? attrs.nextText : paginationConfig.nextText;
+                    var lastText = angular.isDefined(attrs.lastText) ? attrs.lastText : paginationConfig.lastText;
+
+                    // Create page object used in template
+                    function makePage(number, text, isActive, isDisabled) {
+                        return {
+                            number: number,
+                            text: text,
+                            active: isActive,
+                            disabled: isDisabled
+                        };
+                    }
+
+                    scope.$watch('numPages + currentPage + maxSize', function () {
+                        scope.pages = [];
+
+                        // Default page limits
+                        var startPage = 1, endPage = scope.numPages;
+
+                        // recompute if maxSize
+                        if (scope.maxSize && scope.maxSize < scope.numPages) {
+                            startPage = Math.max(scope.currentPage - Math.floor(scope.maxSize / 2), 1);
+                            endPage = startPage + scope.maxSize - 1;
+
+                            // Adjust if limit is exceeded
+                            if (endPage > scope.numPages) {
+                                endPage = scope.numPages;
+                                startPage = endPage - scope.maxSize + 1;
+                            }
+                        }
+
+                        // Add page number links
+                        for (var number = startPage; number <= endPage; number++) {
+                            var page = makePage(number, number, scope.isActive(number), false);
+                            scope.pages.push(page);
+                        }
+
+                        // Add previous & next links
+                        if (directionLinks) {
+                            var previousPage = makePage(scope.currentPage - 1, previousText, false, scope.noPrevious());
+                            scope.pages.unshift(previousPage);
+
+                            var nextPage = makePage(scope.currentPage + 1, nextText, false, scope.noNext());
+                            scope.pages.push(nextPage);
+                        }
+
+                        // Add first & last links
+                        if (boundaryLinks) {
+                            var firstPage = makePage(1, firstText, false, scope.noPrevious());
+                            scope.pages.unshift(firstPage);
+
+                            var lastPage = makePage(scope.numPages, lastText, false, scope.noNext());
+                            scope.pages.push(lastPage);
+                        }
+
+
+                        if (scope.currentPage > scope.numPages) {
+                            scope.selectPage(scope.numPages);
+                        }
+                    });
+                    scope.noPrevious = function () {
+                        return scope.currentPage === 1;
+                    };
+                    scope.noNext = function () {
+                        return scope.currentPage === scope.numPages;
+                    };
+                    scope.isActive = function (page) {
+                        return scope.currentPage === page;
+                    };
+
+                    scope.selectPage = function (page) {
+                        if (!scope.isActive(page) && page > 0 && page <= scope.numPages) {
+                            scope.currentPage = page;
+                            ctrl.changePage({ page: page });
+                        }
+                    };
+                }
+            };
+        }]);
+})(angular);
+

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/3rdparty/javascript/bower_components/smart-table/Smart-Table.min.js
----------------------------------------------------------------------
diff --git a/3rdparty/javascript/bower_components/smart-table/Smart-Table.min.js b/3rdparty/javascript/bower_components/smart-table/Smart-Table.min.js
new file mode 100644
index 0000000..49611b2
--- /dev/null
+++ b/3rdparty/javascript/bower_components/smart-table/Smart-Table.min.js
@@ -0,0 +1 @@
+!function(a,b){"use strict";function c(a,c){function d(a){return this instanceof d?(b.extend(this,a),void 0):new d(a)}this.setDefaultOption=function(a){b.extend(d.prototype,a)},a.headerTemplateUrl=c.defaultHeader,this.setDefaultOption(a),this.$get=function(){return d}}var d=b.module("smartTable.column",["smartTable.templateUrlList"]).constant("DefaultColumnConfiguration",{isSortable:!0,isEditable:!1,type:"text",headerTemplateUrl:"",map:"",label:"",sortPredicate:"",formatFunction:"",formatParameter:"",filterPredicate:"",cellTemplateUrl:"",headerClass:"",cellClass:""});c.$inject=["DefaultColumnConfiguration","templateUrlList"],d.provider("Column",c),a.ColumnProvider=c}(window,angular),function(a){"use strict";a.module("smartTable.directives",["smartTable.templateUrlList","smartTable.templates"]).directive("smartTable",["templateUrlList","DefaultTableConfiguration",function(b,c){return{restrict:"EA",scope:{columnCollection:"=columns",dataCollection:"=rows",config:"="},replace:"true",te
 mplateUrl:b.smartTable,controller:"TableCtrl",link:function(d,e,f,g){var h;d.$watch("config",function(e){var f=a.extend({},c,e),h=void 0!==d.columns?d.columns.length:0;if(g.setGlobalConfig(f),"multiple"!==f.selectionMode||f.displaySelectionCheckbox!==!0)for(var i=h-1;i>=0;i--)d.columns[i].isSelectionColumn===!0&&g.removeColumn(i);else g.insertColumn({cellTemplateUrl:b.selectionCheckbox,headerTemplateUrl:b.selectAllCheckbox,isSelectionColumn:!0},0)},!0),d.$watch("columnCollection",function(){if(g.clearColumns(),d.columnCollection)for(var b=0,c=d.columnCollection.length;c>b;b++)g.insertColumn(d.columnCollection[b]);else d.dataCollection&&d.dataCollection.length>0&&(h=d.dataCollection[0],a.forEach(h,function(a,b){"$"!=b[0]&&g.insertColumn({label:b,map:b})}))},!0),d.$watch("dataCollection.length",function(a,b){a!==b&&g.sortBy()})}}}]).directive("smartTableDataRow",function(){return{require:"^smartTable",restrict:"C",link:function(a,b,c,d){b.bind("click",function(){a.$apply(function(){d.
 toggleSelection(a.dataRow)})})}}}).directive("smartTableHeaderCell",function(){return{restrict:"C",require:"^smartTable",link:function(a,b,c,d){b.bind("click",function(){a.$apply(function(){d.sortBy(a.column)})})}}}).directive("smartTableSelectAll",function(){return{restrict:"C",require:"^smartTable",link:function(a,b,c,d){b.bind("click",function(){d.toggleSelectionAll(b[0].checked===!0)})}}}).directive("stopEvent",function(){return{restrict:"A",link:function(a,b,c){b.bind(c.stopEvent,function(a){a.stopPropagation()})}}}).directive("smartTableGlobalSearch",["templateUrlList",function(a){return{restrict:"C",require:"^smartTable",scope:{columnSpan:"@"},templateUrl:a.smartTableGlobalSearch,replace:!1,link:function(a,b,c,d){a.searchValue="",a.$watch("searchValue",function(a){d.search(a)})}}}]).directive("smartTableDataCell",["$filter","$http","$templateCache","$compile","$parse",function(a,b,c,d,e){return{restrict:"C",link:function(f,g){function h(){j.isEditable?(g.html('<div editable-c
 ell="" row="dataRow" column="column" type="column.type"></div>'),d(g.contents())(f)):g.text(f.formatedValue)}var i,j=f.column,k=!j.isEditable,l=f.dataRow,m=a("format"),n=e(j.map);f.$watch("dataRow",function(){f.formatedValue=m(n(l),j.formatFunction,j.formatParameter),k===!0&&g.text(f.formatedValue)},!0),f.$watch("column.cellTemplateUrl",function(a){a?b.get(a,{cache:c}).success(function(a){k=!1,i=f.$new(),g.html(a),d(g.contents())(i)}).error(h):h()})}}}]).directive("inputType",function(){return{restrict:"A",priority:1,link:function(a,b,c){var d=a.$eval(c.type);c.$set("type",d)}}}).directive("editableCell",["templateUrlList","$parse",function(b,c){return{restrict:"EA",require:"^smartTable",templateUrl:b.editableCell,scope:{row:"=",column:"=",type:"="},replace:!0,link:function(b,d,e,f){var g=a.element(d.children()[1]),h=a.element(g.children()[0]),i=c(b.column.map);b.isEditMode=!1,b.$watch("row",function(){b.value=i(b.row)},!0),b.submit=function(){b.myForm.$valid===!0&&(f.updateDataRow(
 b.row,b.column.map,b.value),f.sortBy()),b.toggleEditMode()},b.toggleEditMode=function(){b.value=i(b.row),b.isEditMode=b.isEditMode!==!0},b.$watch("isEditMode",function(a){a===!0&&(h[0].select(),h[0].focus())}),h.bind("blur",function(){b.$apply(function(){b.submit()})})}}}])}(angular),function(a){"use strict";a.module("smartTable.filters",[]).constant("DefaultFilters",["currency","date","json","lowercase","number","uppercase"]).filter("format",["$filter","DefaultFilters",function(b,c){return function(d,e,f){var g;return g=e&&a.isFunction(e)?e:-1!==c.indexOf(e)?b(e):function(a){return a},g(d,f)}}])}(angular),function(a){"use strict";a.module("smartTable.table",["smartTable.column","smartTable.utilities","smartTable.directives","smartTable.filters","ui.bootstrap.pagination.smartTable"]).constant("DefaultTableConfiguration",{selectionMode:"none",isGlobalSearchActivated:!1,displaySelectionCheckbox:!1,isPaginationEnabled:!0,itemsByPage:10,maxSize:5,sortAlgorithm:"",filterAlgorithm:""}).co
 ntroller("TableCtrl",["$scope","Column","$filter","$parse","ArrayUtility","DefaultTableConfiguration",function(b,c,d,e,f,g){function h(){var a,c=b.displayedCollection.length;for(a=0;c>a;a++)if(b.displayedCollection[a].isSelected!==!0)return!1;return!0}function i(c){return!a.isArray(c)||0===c.length||b.itemsByPage<1?1:Math.ceil(c.length/b.itemsByPage)}function j(c,e){var g=(b.sortAlgorithm&&a.isFunction(b.sortAlgorithm))===!0?b.sortAlgorithm:d("orderBy");return e?f.sort(c,g,e.sortPredicate,e.reverse):c}function k(c,d,e,f){var g,i;if(a.isArray(c)&&("multiple"===d||"single"===d)&&e>=0&&e<c.length){if(g=c[e],"single"===d)for(var j=0,k=c.length;k>j;j++)i=c[j].isSelected,c[j].isSelected=!1,i===!0&&b.$emit("selectionChange",{item:c[j]});g.isSelected=f,b.holder.isAllSelected=h(),b.$emit("selectionChange",{item:g})}}b.columns=[],b.dataCollection=b.dataCollection||[],b.displayedCollection=[],b.numberOfPages=i(b.dataCollection),b.currentPage=1,b.holder={isAllSelected:!1};var l,m={};this.setGlo
 balConfig=function(c){a.extend(b,g,c)},this.changePage=function(c){var d=b.currentPage;a.isNumber(c.page)&&(b.currentPage=c.page,b.displayedCollection=this.pipe(b.dataCollection),b.holder.isAllSelected=h(),b.$emit("changePage",{oldValue:d,newValue:b.currentPage}))},this.sortBy=function(a){var c=b.columns.indexOf(a);-1!==c&&a.isSortable===!0&&(l&&l!==a&&(l.reverse="none"),a.sortPredicate=a.sortPredicate||a.map,a.reverse=a.reverse!==!0,l=a),b.displayedCollection=this.pipe(b.dataCollection)},this.search=function(a,c){var d,e=b.columns.length;if(c&&-1!==b.columns.indexOf(c))m.$="",c.filterPredicate=a;else{for(d=0;e>d;d++)b.columns[d].filterPredicate="";m.$=a}for(d=0;e>d;d++)m[b.columns[d].map]=b.columns[d].filterPredicate;b.displayedCollection=this.pipe(b.dataCollection)},this.pipe=function(c){var e,g=(b.filterAlgorithm&&a.isFunction(b.filterAlgorithm))===!0?b.filterAlgorithm:d("filter");return e=j(f.filter(c,g,m),l),b.numberOfPages=i(e),b.isPaginationEnabled?f.fromTo(e,(b.currentPage-1
 )*b.itemsByPage,b.itemsByPage):e},this.insertColumn=function(a,d){var e=new c(a);f.insertAt(b.columns,d,e)},this.removeColumn=function(a){f.removeAt(b.columns,a)},this.moveColumn=function(a,c){f.moveAt(b.columns,a,c)},this.clearColumns=function(){b.columns.length=0},this.toggleSelection=function(a){var c=b.dataCollection.indexOf(a);-1!==c&&k(b.dataCollection,b.selectionMode,c,a.isSelected!==!0)},this.toggleSelectionAll=function(a){var c=0,d=b.displayedCollection.length;if("multiple"===b.selectionMode)for(;d>c;c++)k(b.displayedCollection,b.selectionMode,c,a===!0)},this.removeDataRow=function(a){var c=f.removeAt(b.displayedCollection,a);f.removeAt(b.dataCollection,b.dataCollection.indexOf(c))},this.moveDataRow=function(a,c){f.moveAt(b.displayedCollection,a,c)},this.updateDataRow=function(a,c,d){var f,g=b.displayedCollection.indexOf(a),h=e(c),i=h.assign;-1!==g&&(f=h(b.displayedCollection[g]),f!==d&&(i(b.displayedCollection[g],d),b.$emit("updateDataRow",{item:b.displayedCollection[g]}))
 )}}])}(angular),angular.module("smartTable.templates",["partials/defaultCell.html","partials/defaultHeader.html","partials/editableCell.html","partials/globalSearchCell.html","partials/pagination.html","partials/selectAllCheckbox.html","partials/selectionCheckbox.html","partials/smartTable.html"]),angular.module("partials/defaultCell.html",[]).run(["$templateCache",function(a){a.put("partials/defaultCell.html","{{formatedValue}}")}]),angular.module("partials/defaultHeader.html",[]).run(["$templateCache",function(a){a.put("partials/defaultHeader.html","<span class=\"header-content\" ng-class=\"{'sort-ascent':column.reverse==true,'sort-descent':column.reverse==false}\">{{column.label}}</span>")}]),angular.module("partials/editableCell.html",[]).run(["$templateCache",function(a){a.put("partials/editableCell.html",'<div ng-dblclick="toggleEditMode($event)">\n <span ng-hide="isEditMode">{{value | format:column.formatFunction:column.formatParameter}}</span>\n\n <form ng-submit="submit()" 
 ng-show="isEditMode" name="myForm">\n <input name="myInput" ng-model="value" type="type" input-type/>\n </form>\n</div>')}]),angular.module("partials/globalSearchCell.html",[]).run(["$templateCache",function(a){a.put("partials/globalSearchCell.html",'<label>Search :</label>\n<input type="text" ng-model="searchValue"/>')}]),angular.module("partials/pagination.html",[]).run(["$templateCache",function(a){a.put("partials/pagination.html",'<div class="pagination">\n <ul>\n <li ng-repeat="page in pages" ng-class="{active: page.active, disabled: page.disabled}"><a\n ng-click="selectPage(page.number)">{{page.text}}</a></li>\n </ul>\n</div> ')}]),angular.module("partials/selectAllCheckbox.html",[]).run(["$templateCache",function(a){a.put("partials/selectAllCheckbox.html",'<input class="smart-table-select-all" type="checkbox" ng-model="holder.isAllSelected"/>')}]),angular.module("partials/selectionCheckbox.html",[]).run(["$templateCache",function(a){a.put("partials/selectionCheckbox.html",'<i
 nput type="checkbox" ng-model="dataRow.isSelected" stop-event="click"/>')}]),angular.module("partials/smartTable.html",[]).run(["$templateCache",function(a){a.put("partials/smartTable.html",'<table class="smart-table">\n <thead>\n <tr class="smart-table-global-search-row" ng-show="isGlobalSearchActivated">\n <td class="smart-table-global-search" column-span="{{columns.length}}" colspan="{{columnSpan}}">\n </td>\n </tr>\n <tr class="smart-table-header-row">\n <th ng-repeat="column in columns" ng-include="column.headerTemplateUrl"\n class="smart-table-header-cell {{column.headerClass}}" scope="col">\n </th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="dataRow in displayedCollection" ng-class="{selected:dataRow.isSelected}"\n class="smart-table-data-row">\n <td ng-repeat="column in columns" class="smart-table-data-cell {{column.cellClass}}"></td>\n </tr>\n </tbody>\n <tfoot ng-show="isPaginationEnabled">\n <tr class="smart-table-footer-row">\n <td colspan="{{columns.length}}">\n <div 
 pagination-smart-table="" num-pages="numberOfPages" max-size="maxSize" current-page="currentPage"></div>\n </td>\n </tr>\n </tfoot>\n</table>\n\n\n')}]),function(a){"use strict";a.module("smartTable.templateUrlList",[]).constant("templateUrlList",{smartTable:"partials/smartTable.html",smartTableGlobalSearch:"partials/globalSearchCell.html",editableCell:"partials/editableCell.html",selectionCheckbox:"partials/selectionCheckbox.html",selectAllCheckbox:"partials/selectAllCheckbox.html",defaultHeader:"partials/defaultHeader.html",pagination:"partials/pagination.html"})}(angular),function(a){"use strict";a.module("smartTable.utilities",[]).factory("ArrayUtility",function(){var b=function(a,b){return b>=0&&b<a.length?a.splice(b,1)[0]:void 0},c=function(a,b,c){b>=0&&b<a.length?a.splice(b,0,c):a.push(c)},d=function(a,b,c){var d;b>=0&&b<a.length&&c>=0&&c<a.length&&(d=a.splice(b,1)[0],a.splice(c,0,d))},e=function(b,c,d,e){return c&&a.isFunction(c)?c(b,d,e===!0):b},f=function(b,c,d){return c&&
 a.isFunction(c)?c(b,d):b},g=function(b,c,d){var e,f,g=[];if(!a.isArray(b))return b;f=Math.max(c,0),f=Math.min(f,b.length-1>0?b.length-1:0),d=Math.max(0,d),e=Math.min(f+d,b.length);for(var h=f;e>h;h++)g.push(b[h]);return g};return{removeAt:b,insertAt:c,moveAt:d,sort:e,filter:f,fromTo:g}})}(angular),function(a){a.module("ui.bootstrap.pagination.smartTable",["smartTable.templateUrlList"]).constant("paginationConfig",{boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"<",nextText:">",lastText:"Last"}).directive("paginationSmartTable",["paginationConfig","templateUrlList",function(b,c){return{restrict:"EA",require:"^smartTable",scope:{numPages:"=",currentPage:"=",maxSize:"="},templateUrl:c.pagination,replace:!0,link:function(c,d,e,f){function g(a,b,c,d){return{number:a,text:b,active:c,disabled:d}}var h=a.isDefined(e.boundaryLinks)?c.$eval(e.boundaryLinks):b.boundaryLinks,i=a.isDefined(e.directionLinks)?c.$eval(e.directionLinks):b.directionLinks,j=a.isDefined(e.firstText)?
 e.firstText:b.firstText,k=a.isDefined(e.previousText)?e.previousText:b.previousText,l=a.isDefined(e.nextText)?e.nextText:b.nextText,m=a.isDefined(e.lastText)?e.lastText:b.lastText;c.$watch("numPages + currentPage + maxSize",function(){c.pages=[];var a=1,b=c.numPages;c.maxSize&&c.maxSize<c.numPages&&(a=Math.max(c.currentPage-Math.floor(c.maxSize/2),1),b=a+c.maxSize-1,b>c.numPages&&(b=c.numPages,a=b-c.maxSize+1));for(var d=a;b>=d;d++){var e=g(d,d,c.isActive(d),!1);c.pages.push(e)}if(i){var f=g(c.currentPage-1,k,!1,c.noPrevious());c.pages.unshift(f);var n=g(c.currentPage+1,l,!1,c.noNext());c.pages.push(n)}if(h){var o=g(1,j,!1,c.noPrevious());c.pages.unshift(o);var p=g(c.numPages,m,!1,c.noNext());c.pages.push(p)}c.currentPage>c.numPages&&c.selectPage(c.numPages)}),c.noPrevious=function(){return 1===c.currentPage},c.noNext=function(){return c.currentPage===c.numPages},c.isActive=function(a){return c.currentPage===a},c.selectPage=function(a){!c.isActive(a)&&a>0&&a<=c.numPages&&(c.currentP
 age=a,f.changePage({page:a}))}}}}])}(angular);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/3rdparty/javascript/bower_components/smart-table/bower.json
----------------------------------------------------------------------
diff --git a/3rdparty/javascript/bower_components/smart-table/bower.json b/3rdparty/javascript/bower_components/smart-table/bower.json
new file mode 100644
index 0000000..68709ef
--- /dev/null
+++ b/3rdparty/javascript/bower_components/smart-table/bower.json
@@ -0,0 +1,15 @@
+{
+    "author": "Laurent Renard",
+    "name": "smart-table",
+    "description": "A grid/table module for angular js",
+    "version": "0.1.5",
+    "homepage": "http://lorenzofox3.github.io/smart-table-website/",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/lorenzofox3/bower-smart-table.git"
+    },
+    "main": "./Smart-Table.min.js",
+    "dependencies": {
+        "angular": "1.0.7"
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/3rdparty/javascript/thrift.js
----------------------------------------------------------------------
diff --git a/3rdparty/javascript/thrift.js b/3rdparty/javascript/thrift.js
new file mode 100644
index 0000000..5dcfec1
--- /dev/null
+++ b/3rdparty/javascript/thrift.js
@@ -0,0 +1,779 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var Thrift = {
+    Version: '0.9.1',
+/*
+    Description: 'JavaScript bindings for the Apache Thrift RPC system',
+    License: 'http://www.apache.org/licenses/LICENSE-2.0',
+    Homepage: 'http://thrift.apache.org',
+    BugReports: 'https://issues.apache.org/jira/browse/THRIFT',
+    Maintainer: 'dev@thrift.apache.org',
+*/
+
+    Type: {
+        'STOP' : 0,
+        'VOID' : 1,
+        'BOOL' : 2,
+        'BYTE' : 3,
+        'I08' : 3,
+        'DOUBLE' : 4,
+        'I16' : 6,
+        'I32' : 8,
+        'I64' : 10,
+        'STRING' : 11,
+        'UTF7' : 11,
+        'STRUCT' : 12,
+        'MAP' : 13,
+        'SET' : 14,
+        'LIST' : 15,
+        'UTF8' : 16,
+        'UTF16' : 17
+    },
+
+    MessageType: {
+        'CALL' : 1,
+        'REPLY' : 2,
+        'EXCEPTION' : 3
+    },
+
+    objectLength: function(obj) {
+        var length = 0;
+        for (var k in obj) {
+            if (obj.hasOwnProperty(k)) {
+                length++;
+            }
+        }
+
+        return length;
+    },
+
+    inherits: function(constructor, superConstructor) {
+      //Prototypal Inheritance http://javascript.crockford.com/prototypal.html
+      function F() {}
+      F.prototype = superConstructor.prototype;
+      constructor.prototype = new F();
+    }
+};
+
+
+
+Thrift.TException = function(message) {
+    this.message = message;
+};
+Thrift.inherits(Thrift.TException, Error);
+Thrift.TException.prototype.name = 'TException';
+
+Thrift.TApplicationExceptionType = {
+    'UNKNOWN' : 0,
+    'UNKNOWN_METHOD' : 1,
+    'INVALID_MESSAGE_TYPE' : 2,
+    'WRONG_METHOD_NAME' : 3,
+    'BAD_SEQUENCE_ID' : 4,
+    'MISSING_RESULT' : 5,
+    'INTERNAL_ERROR' : 6,
+    'PROTOCOL_ERROR' : 7,
+    'INVALID_TRANSFORM' : 8,
+    'INVALID_PROTOCOL' : 9,
+    'UNSUPPORTED_CLIENT_TYPE' : 10
+};
+
+Thrift.TApplicationException = function(message, code) {
+    this.message = message;
+    this.code = (code === null) ? 0 : code;
+};
+Thrift.inherits(Thrift.TApplicationException, Thrift.TException);
+Thrift.TApplicationException.prototype.name = 'TApplicationException';
+
+Thrift.TApplicationException.prototype.read = function(input) {
+    while (1) {
+        var ret = input.readFieldBegin();
+
+        if (ret.ftype == Thrift.Type.STOP) {
+            break;
+        }
+
+        var fid = ret.fid;
+
+        switch (fid) {
+            case 1:
+                if (ret.ftype == Thrift.Type.STRING) {
+                    ret = input.readString();
+                    this.message = ret.value;
+                } else {
+                    ret = input.skip(ret.ftype);
+                }
+                break;
+            case 2:
+                if (ret.ftype == Thrift.Type.I32) {
+                    ret = input.readI32();
+                    this.code = ret.value;
+                } else {
+                    ret = input.skip(ret.ftype);
+                }
+                break;
+           default:
+                ret = input.skip(ret.ftype);
+                break;
+        }
+
+        input.readFieldEnd();
+    }
+
+    input.readStructEnd();
+};
+
+Thrift.TApplicationException.prototype.write = function(output) {
+    var xfer = 0;
+
+    output.writeStructBegin('TApplicationException');
+
+    if (this.message) {
+        output.writeFieldBegin('message', Thrift.Type.STRING, 1);
+        output.writeString(this.getMessage());
+        output.writeFieldEnd();
+    }
+
+    if (this.code) {
+        output.writeFieldBegin('type', Thrift.Type.I32, 2);
+        output.writeI32(this.code);
+        output.writeFieldEnd();
+    }
+
+    output.writeFieldStop();
+    output.writeStructEnd();
+};
+
+Thrift.TApplicationException.prototype.getCode = function() {
+    return this.code;
+};
+
+Thrift.TApplicationException.prototype.getMessage = function() {
+    return this.message;
+};
+
+/**
+ *If you do not specify a url then you must handle ajax on your own.
+ *This is how to use js bindings in a async fashion.
+ */
+Thrift.Transport = function(url) {
+    this.url = url;
+    this.wpos = 0;
+    this.rpos = 0;
+
+    this.send_buf = '';
+    this.recv_buf = '';
+};
+
+Thrift.Transport.prototype = {
+
+    //Gets the browser specific XmlHttpRequest Object
+    getXmlHttpRequestObject: function() {
+        try { return new XMLHttpRequest(); } catch (e1) { }
+        try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { }
+        try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { }
+
+        throw "Your browser doesn't support the XmlHttpRequest object.";
+    },
+
+    flush: function(async) {
+        //async mode
+        if (async || this.url === undefined || this.url === '') {
+            return this.send_buf;
+        }
+
+        var xreq = this.getXmlHttpRequestObject();
+
+        if (xreq.overrideMimeType) {
+            xreq.overrideMimeType('application/json');
+        }
+
+        xreq.open('POST', this.url, false);
+        xreq.send(this.send_buf);
+
+        if (xreq.readyState != 4) {
+            throw 'encountered an unknown ajax ready state: ' + xreq.readyState;
+        }
+
+        if (xreq.status != 200) {
+            throw 'encountered a unknown request status: ' + xreq.status;
+        }
+
+        this.recv_buf = xreq.responseText;
+        this.recv_buf_sz = this.recv_buf.length;
+        this.wpos = this.recv_buf.length;
+        this.rpos = 0;
+    },
+
+    jqRequest: function(client, postData, args, recv_method) {
+        if (typeof jQuery === 'undefined' ||
+            typeof jQuery.Deferred === 'undefined') {
+            throw 'Thrift.js requires jQuery 1.5+ to use asynchronous requests';
+        }
+
+        var thriftTransport = this;
+
+        var jqXHR = jQuery.ajax({
+            url: this.url,
+            data: postData,
+            type: 'POST',
+            cache: false,
+            contentType: 'application/json',
+            dataType: 'text thrift',
+            converters: {
+                'text thrift' : function(responseData) {
+                    thriftTransport.setRecvBuffer(responseData);
+                    var value = recv_method.call(client);
+                    return value;
+                }
+            },
+            context: client,
+            success: jQuery.makeArray(args).pop()
+        });
+
+        return jqXHR;
+    },
+
+    setRecvBuffer: function(buf) {
+        this.recv_buf = buf;
+        this.recv_buf_sz = this.recv_buf.length;
+        this.wpos = this.recv_buf.length;
+        this.rpos = 0;
+    },
+
+    isOpen: function() {
+        return true;
+    },
+
+    open: function() {},
+
+    close: function() {},
+
+    read: function(len) {
+        var avail = this.wpos - this.rpos;
+
+        if (avail === 0) {
+            return '';
+        }
+
+        var give = len;
+
+        if (avail < len) {
+            give = avail;
+        }
+
+        var ret = this.read_buf.substr(this.rpos, give);
+        this.rpos += give;
+
+        //clear buf when complete?
+        return ret;
+    },
+
+    readAll: function() {
+        return this.recv_buf;
+    },
+
+    write: function(buf) {
+        this.send_buf = buf;
+    },
+
+    getSendBuffer: function() {
+        return this.send_buf;
+    }
+
+};
+
+
+
+Thrift.Protocol = function(transport) {
+    this.transport = transport;
+};
+
+Thrift.Protocol.Type = {};
+Thrift.Protocol.Type[Thrift.Type.BOOL] = '"tf"';
+Thrift.Protocol.Type[Thrift.Type.BYTE] = '"i8"';
+Thrift.Protocol.Type[Thrift.Type.I16] = '"i16"';
+Thrift.Protocol.Type[Thrift.Type.I32] = '"i32"';
+Thrift.Protocol.Type[Thrift.Type.I64] = '"i64"';
+Thrift.Protocol.Type[Thrift.Type.DOUBLE] = '"dbl"';
+Thrift.Protocol.Type[Thrift.Type.STRUCT] = '"rec"';
+Thrift.Protocol.Type[Thrift.Type.STRING] = '"str"';
+Thrift.Protocol.Type[Thrift.Type.MAP] = '"map"';
+Thrift.Protocol.Type[Thrift.Type.LIST] = '"lst"';
+Thrift.Protocol.Type[Thrift.Type.SET] = '"set"';
+
+
+Thrift.Protocol.RType = {};
+Thrift.Protocol.RType.tf = Thrift.Type.BOOL;
+Thrift.Protocol.RType.i8 = Thrift.Type.BYTE;
+Thrift.Protocol.RType.i16 = Thrift.Type.I16;
+Thrift.Protocol.RType.i32 = Thrift.Type.I32;
+Thrift.Protocol.RType.i64 = Thrift.Type.I64;
+Thrift.Protocol.RType.dbl = Thrift.Type.DOUBLE;
+Thrift.Protocol.RType.rec = Thrift.Type.STRUCT;
+Thrift.Protocol.RType.str = Thrift.Type.STRING;
+Thrift.Protocol.RType.map = Thrift.Type.MAP;
+Thrift.Protocol.RType.lst = Thrift.Type.LIST;
+Thrift.Protocol.RType.set = Thrift.Type.SET;
+
+Thrift.Protocol.Version = 1;
+
+Thrift.Protocol.prototype = {
+
+    getTransport: function() {
+        return this.transport;
+    },
+
+    //Write functions
+    writeMessageBegin: function(name, messageType, seqid) {
+        this.tstack = [];
+        this.tpos = [];
+
+        this.tstack.push([Thrift.Protocol.Version, '"' +
+            name + '"', messageType, seqid]);
+    },
+
+    writeMessageEnd: function() {
+        var obj = this.tstack.pop();
+
+        this.wobj = this.tstack.pop();
+        this.wobj.push(obj);
+
+        this.wbuf = '[' + this.wobj.join(',') + ']';
+
+        this.transport.write(this.wbuf);
+     },
+
+
+    writeStructBegin: function(name) {
+        this.tpos.push(this.tstack.length);
+        this.tstack.push({});
+    },
+
+    writeStructEnd: function() {
+
+        var p = this.tpos.pop();
+        var struct = this.tstack[p];
+        var str = '{';
+        var first = true;
+        for (var key in struct) {
+            if (first) {
+                first = false;
+            } else {
+                str += ',';
+            }
+
+            str += key + ':' + struct[key];
+        }
+
+        str += '}';
+        this.tstack[p] = str;
+    },
+
+    writeFieldBegin: function(name, fieldType, fieldId) {
+        this.tpos.push(this.tstack.length);
+        this.tstack.push({ 'fieldId': '"' +
+            fieldId + '"', 'fieldType': Thrift.Protocol.Type[fieldType]
+        });
+
+    },
+
+    writeFieldEnd: function() {
+        var value = this.tstack.pop();
+        var fieldInfo = this.tstack.pop();
+
+        this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
+            fieldInfo.fieldType + ':' + value + '}';
+        this.tpos.pop();
+    },
+
+    writeFieldStop: function() {
+        //na
+    },
+
+    writeMapBegin: function(keyType, valType, size) {
+        //size is invalid, we'll set it on end.
+        this.tpos.push(this.tstack.length);
+        this.tstack.push([Thrift.Protocol.Type[keyType],
+            Thrift.Protocol.Type[valType], 0]);
+    },
+
+    writeMapEnd: function() {
+        var p = this.tpos.pop();
+
+        if (p == this.tstack.length) {
+            return;
+        }
+
+        if ((this.tstack.length - p - 1) % 2 !== 0) {
+            this.tstack.push('');
+        }
+
+        var size = (this.tstack.length - p - 1) / 2;
+
+        this.tstack[p][this.tstack[p].length - 1] = size;
+
+        var map = '}';
+        var first = true;
+        while (this.tstack.length > p + 1) {
+            var v = this.tstack.pop();
+            var k = this.tstack.pop();
+            if (first) {
+                first = false;
+            } else {
+                map = ',' + map;
+            }
+
+            if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings
+            map = k + ':' + v + map;
+        }
+        map = '{' + map;
+
+        this.tstack[p].push(map);
+        this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+    },
+
+    writeListBegin: function(elemType, size) {
+        this.tpos.push(this.tstack.length);
+        this.tstack.push([Thrift.Protocol.Type[elemType], size]);
+    },
+
+    writeListEnd: function() {
+        var p = this.tpos.pop();
+
+        while (this.tstack.length > p + 1) {
+            var tmpVal = this.tstack[p + 1];
+            this.tstack.splice(p + 1, 1);
+            this.tstack[p].push(tmpVal);
+        }
+
+        this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+    },
+
+    writeSetBegin: function(elemType, size) {
+        this.tpos.push(this.tstack.length);
+        this.tstack.push([Thrift.Protocol.Type[elemType], size]);
+    },
+
+    writeSetEnd: function() {
+        var p = this.tpos.pop();
+
+        while (this.tstack.length > p + 1) {
+            var tmpVal = this.tstack[p + 1];
+            this.tstack.splice(p + 1, 1);
+            this.tstack[p].push(tmpVal);
+        }
+
+        this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
+    },
+
+    writeBool: function(value) {
+        this.tstack.push(value ? 1 : 0);
+    },
+
+    writeByte: function(i8) {
+        this.tstack.push(i8);
+    },
+
+    writeI16: function(i16) {
+        this.tstack.push(i16);
+    },
+
+    writeI32: function(i32) {
+        this.tstack.push(i32);
+    },
+
+    writeI64: function(i64) {
+        this.tstack.push(i64);
+    },
+
+    writeDouble: function(dbl) {
+        this.tstack.push(dbl);
+    },
+
+    writeString: function(str) {
+        // We do not encode uri components for wire transfer:
+        if (str === null) {
+            this.tstack.push(null);
+        } else {
+            // concat may be slower than building a byte buffer
+            var escapedString = '';
+            for (var i = 0; i < str.length; i++) {
+                var ch = str.charAt(i);      // a single double quote: "
+                if (ch === '\"') {
+                    escapedString += '\\\"'; // write out as: \"
+                } else if (ch === '\\') {    // a single backslash: \
+                    escapedString += '\\\\'; // write out as: \\
+                /* Currently escaped forward slashes break TJSONProtocol.
+                 * As it stands, we can simply pass forward slashes into
+                 * our strings across the wire without being escaped.
+                 * I think this is the protocol's bug, not thrift.js
+                 * } else if(ch === '/') {   // a single forward slash: /
+                 *  escapedString += '\\/';  // write out as \/
+                 * }
+                 */
+                } else if (ch === '\b') {    // a single backspace: invisible
+                    escapedString += '\\b';  // write out as: \b"
+                } else if (ch === '\f') {    // a single formfeed: invisible
+                    escapedString += '\\f';  // write out as: \f"
+                } else if (ch === '\n') {    // a single newline: invisible
+                    escapedString += '\\n';  // write out as: \n"
+                } else if (ch === '\r') {    // a single return: invisible
+                    escapedString += '\\r';  // write out as: \r"
+                } else if (ch === '\t') {    // a single tab: invisible
+                    escapedString += '\\t';  // write out as: \t"
+                } else {
+                    escapedString += ch;     // Else it need not be escaped
+                }
+            }
+            this.tstack.push('"' + escapedString + '"');
+        }
+    },
+
+    writeBinary: function(str) {
+        this.writeString(str);
+    },
+
+
+
+    // Reading functions
+    readMessageBegin: function(name, messageType, seqid) {
+        this.rstack = [];
+        this.rpos = [];
+
+        if (typeof jQuery !== 'undefined') {
+            this.robj = jQuery.parseJSON(this.transport.readAll());
+        } else {
+            this.robj = eval(this.transport.readAll());
+        }
+
+        var r = {};
+        var version = this.robj.shift();
+
+        if (version != Thrift.Protocol.Version) {
+            throw 'Wrong thrift protocol version: ' + version;
+        }
+
+        r.fname = this.robj.shift();
+        r.mtype = this.robj.shift();
+        r.rseqid = this.robj.shift();
+
+
+        //get to the main obj
+        this.rstack.push(this.robj.shift());
+
+        return r;
+    },
+
+    readMessageEnd: function() {
+    },
+
+    readStructBegin: function(name) {
+        var r = {};
+        r.fname = '';
+
+        //incase this is an array of structs
+        if (this.rstack[this.rstack.length - 1] instanceof Array) {
+            this.rstack.push(this.rstack[this.rstack.length - 1].shift());
+        }
+
+        return r;
+    },
+
+    readStructEnd: function() {
+        if (this.rstack[this.rstack.length - 2] instanceof Array) {
+            this.rstack.pop();
+        }
+    },
+
+    readFieldBegin: function() {
+        var r = {};
+
+        var fid = -1;
+        var ftype = Thrift.Type.STOP;
+
+        //get a fieldId
+        for (var f in (this.rstack[this.rstack.length - 1])) {
+            if (f === null) {
+              continue;
+            }
+
+            fid = parseInt(f, 10);
+            this.rpos.push(this.rstack.length);
+
+            var field = this.rstack[this.rstack.length - 1][fid];
+
+            //remove so we don't see it again
+            delete this.rstack[this.rstack.length - 1][fid];
+
+            this.rstack.push(field);
+
+            break;
+        }
+
+        if (fid != -1) {
+
+            //should only be 1 of these but this is the only
+            //way to match a key
+            for (var i in (this.rstack[this.rstack.length - 1])) {
+                if (Thrift.Protocol.RType[i] === null) {
+                    continue;
+                }
+
+                ftype = Thrift.Protocol.RType[i];
+                this.rstack[this.rstack.length - 1] =
+                    this.rstack[this.rstack.length - 1][i];
+            }
+        }
+
+        r.fname = '';
+        r.ftype = ftype;
+        r.fid = fid;
+
+        return r;
+    },
+
+    readFieldEnd: function() {
+        var pos = this.rpos.pop();
+
+        //get back to the right place in the stack
+        while (this.rstack.length > pos) {
+            this.rstack.pop();
+        }
+
+    },
+
+    readMapBegin: function(keyType, valType, size) {
+        var map = this.rstack.pop();
+
+        var r = {};
+        r.ktype = Thrift.Protocol.RType[map.shift()];
+        r.vtype = Thrift.Protocol.RType[map.shift()];
+        r.size = map.shift();
+
+
+        this.rpos.push(this.rstack.length);
+        this.rstack.push(map.shift());
+
+        return r;
+    },
+
+    readMapEnd: function() {
+        this.readFieldEnd();
+    },
+
+    readListBegin: function(elemType, size) {
+        var list = this.rstack[this.rstack.length - 1];
+
+        var r = {};
+        r.etype = Thrift.Protocol.RType[list.shift()];
+        r.size = list.shift();
+
+        this.rpos.push(this.rstack.length);
+        this.rstack.push(list);
+
+        return r;
+    },
+
+    readListEnd: function() {
+        this.readFieldEnd();
+    },
+
+    readSetBegin: function(elemType, size) {
+        return this.readListBegin(elemType, size);
+    },
+
+    readSetEnd: function() {
+        return this.readListEnd();
+    },
+
+    readBool: function() {
+        var r = this.readI32();
+
+        if (r !== null && r.value == '1') {
+            r.value = true;
+        } else {
+            r.value = false;
+        }
+
+        return r;
+    },
+
+    readByte: function() {
+        return this.readI32();
+    },
+
+    readI16: function() {
+        return this.readI32();
+    },
+
+    readI32: function(f) {
+        if (f === undefined) {
+            f = this.rstack[this.rstack.length - 1];
+        }
+
+        var r = {};
+
+        if (f instanceof Array) {
+            if (f.length === 0) {
+                r.value = undefined;
+            } else {
+                r.value = f.shift();
+            }
+        } else if (f instanceof Object) {
+           for (var i in f) {
+                if (i === null) {
+                  continue;
+                }
+                this.rstack.push(f[i]);
+                delete f[i];
+
+                r.value = i;
+                break;
+           }
+        } else {
+            r.value = f;
+            this.rstack.pop();
+        }
+
+        return r;
+    },
+
+    readI64: function() {
+        return this.readI32();
+    },
+
+    readDouble: function() {
+        return this.readI32();
+    },
+
+    readString: function() {
+        var r = this.readI32();
+        return r;
+    },
+
+    readBinary: function() {
+        return this.readString();
+    },
+
+
+    //Method to arbitrarily skip over data.
+    skip: function(type) {
+        throw 'skip not supported yet';
+    }
+};

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
index b4b055c..4a0372f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,6 +20,7 @@ buildscript {
 buildDir = 'dist'
 def generatedDir = "$buildDir/generated-src"
 def generatedJavaDir = "$generatedDir/gen-java"
+def generatedJSDir = "$generatedDir/gen-js"
 
 compileJava {
   sourceCompatibility = 1.7
@@ -87,6 +88,7 @@ sourceSets {
     compileClasspath += sourceSets.generated.output
     resources {
       srcDir '3rdparty/javascript'
+      srcDir generatedJSDir
     }
   }
   test {
@@ -205,7 +207,7 @@ task generateSources(dependsOn: 'bootstrapThrift') {
     inputFiles.each { File file ->
       exec {
         executable = thriftBinary
-        args = ['--gen', 'java:hashcode', '-o', outputDir, file]
+        args = ['--gen', 'java:hashcode', '--gen', 'js', '-o', outputDir, file]
       }
     }
     // These are the 'root' structs, the tool recursively generates all composed structs.

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/src/main/java/org/apache/aurora/scheduler/http/DisplayUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/DisplayUtils.java b/src/main/java/org/apache/aurora/scheduler/http/DisplayUtils.java
index d775ede..6870fd8 100644
--- a/src/main/java/org/apache/aurora/scheduler/http/DisplayUtils.java
+++ b/src/main/java/org/apache/aurora/scheduler/http/DisplayUtils.java
@@ -21,7 +21,6 @@ import com.twitter.common.args.Arg;
 import com.twitter.common.args.CmdLine;
 
 import org.apache.aurora.scheduler.MesosTaskFactory.MesosTaskFactoryImpl;
-import org.apache.aurora.scheduler.http.SchedulerzHome.Role;
 import org.apache.aurora.scheduler.http.SchedulerzRole.Job;
 import org.apache.aurora.scheduler.storage.entities.IJobKey;
 
@@ -37,13 +36,6 @@ public final class DisplayUtils {
     // Utility class.
   }
 
-  static final Ordering<Role> ROLE_ORDERING = Ordering.natural().onResultOf(
-      new Function<Role, String>() {
-        @Override public String apply(Role role) {
-          return role.getRole();
-        }
-      });
-
   static final Ordering<Job> JOB_ORDERING = Ordering.natural().onResultOf(
       new Function<Job, String>() {
         @Override public String apply(Job job) {

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/35fcc545/src/main/java/org/apache/aurora/scheduler/http/SchedulerzHome.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/SchedulerzHome.java b/src/main/java/org/apache/aurora/scheduler/http/SchedulerzHome.java
deleted file mode 100644
index 263591a..0000000
--- a/src/main/java/org/apache/aurora/scheduler/http/SchedulerzHome.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * Copyright 2013 Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.aurora.scheduler.http;
-
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import com.google.common.base.Function;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import com.twitter.common.base.Closure;
-
-import org.antlr.stringtemplate.StringTemplate;
-import org.apache.aurora.scheduler.base.Query;
-import org.apache.aurora.scheduler.base.Tasks;
-import org.apache.aurora.scheduler.state.CronJobManager;
-import org.apache.aurora.scheduler.storage.Storage;
-import org.apache.aurora.scheduler.storage.entities.IJobConfiguration;
-import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
-import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.twitter.common.base.MorePreconditions.checkNotBlank;
-
-/**
- * HTTP interface to serve as a HUD for the aurora scheduler.
- */
-@Path("/scheduler")
-public class SchedulerzHome extends JerseyTemplateServlet {
-
-  private static final Function<String, Role> CREATE_ROLE = new Function<String, Role>() {
-    @Override public Role apply(String ownerRole) {
-      Role role = new Role();
-      role.role = ownerRole;
-      return role;
-    }
-  };
-
-  private final Storage storage;
-  private final CronJobManager cronScheduler;
-  private final String clusterName;
-
-  /**
-   * Creates a new scheduler home servlet.
-   *
-   * @param storage Backing store to fetch tasks from.
-   * @param cronScheduler Cron scheduler.
-   * @param clusterName Name of the serving cluster.
-   */
-  @Inject
-  public SchedulerzHome(
-      Storage storage,
-      CronJobManager cronScheduler,
-      @ClusterName String clusterName) {
-
-    super("schedulerzhome");
-    this.storage = checkNotNull(storage);
-    this.cronScheduler = checkNotNull(cronScheduler);
-    this.clusterName = checkNotBlank(clusterName);
-  }
-
-  /**
-   * Fetches the scheduler landing page.
-   *
-   * @return HTTP response.
-   */
-  @GET
-  @Produces(MediaType.TEXT_HTML)
-  public Response get() {
-    return fillTemplate(new Closure<StringTemplate>() {
-      @Override public void execute(StringTemplate template) {
-        template.setAttribute("cluster_name", clusterName);
-
-        LoadingCache<String, Role> owners =
-            CacheBuilder.newBuilder().build(CacheLoader.from(CREATE_ROLE));
-
-        // TODO(William Farner): Render this page without an expensive query.
-        Set<IScheduledTask> tasks =
-            Storage.Util.weaklyConsistentFetchTasks(storage, Query.unscoped());
-        for (ITaskConfig task : Iterables.transform(tasks, Tasks.SCHEDULED_TO_INFO)) {
-          owners.getUnchecked(task.getOwner().getRole()).accumulate(task);
-        }
-
-        // Add cron job counts for each role.
-        for (IJobConfiguration job : cronScheduler.getJobs()) {
-          owners.getUnchecked(job.getOwner().getRole()).accumulate(job);
-        }
-
-        template.setAttribute(
-            "owners",
-            DisplayUtils.ROLE_ORDERING.sortedCopy(owners.asMap().values()));
-      }
-    });
-  }
-
-  /**
-   * Template object to represent a role.
-   */
-  static class Role {
-    private String role;
-    private Set<String> jobs = Sets.newHashSet();
-    private Set<String> cronJobs = Sets.newHashSet();
-
-    private void accumulate(ITaskConfig task) {
-      jobs.add(task.getJobName());
-    }
-
-    private void accumulate(IJobConfiguration job) {
-      cronJobs.add(job.getKey().getName());
-    }
-
-    public String getRole() {
-      return role;
-    }
-
-    public int getJobCount() {
-      return jobs.size();
-    }
-
-    public int getCronJobCount() {
-      return cronJobs.size();
-    }
-  }
-}