From 422b1caeaa9f7d069a9208ecb0d0249485b1a05e Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Wed, 26 Dec 2012 21:45:38 +0100 Subject: o Adding details page for persons. o Better front page. --- .../webapp/WEB-INF/tags/common/head-element.tagx | 10 +- src/main/webapp/WEB-INF/urlrewrite.xml | 6 + src/main/webapp/apps/app.css | 41 + src/main/webapp/apps/frontPageApp/frontPage.html | 29 +- src/main/webapp/apps/frontPageApp/frontPageApp.js | 64 +- src/main/webapp/apps/personApp/person.html | 17 + src/main/webapp/apps/personApp/personApp.js | 12 + .../ng-grid-1.5.0/ng-grid-1.5.0.debug.js | 2334 ++++++++++++++++++++ .../angular-ui/ng-grid-1.5.0/ng-grid-1.5.0.js | 7 + .../external/angular-ui/ng-grid-1.5.0/ng-grid.css | 456 ++++ src/main/webapp/login.jspx | 4 +- src/main/webapp/person/person.jspx | 22 + 12 files changed, 2973 insertions(+), 29 deletions(-) create mode 100644 src/main/webapp/apps/app.css create mode 100644 src/main/webapp/apps/personApp/person.html create mode 100644 src/main/webapp/apps/personApp/personApp.js create mode 100644 src/main/webapp/external/angular-ui/ng-grid-1.5.0/ng-grid-1.5.0.debug.js create mode 100644 src/main/webapp/external/angular-ui/ng-grid-1.5.0/ng-grid-1.5.0.js create mode 100644 src/main/webapp/external/angular-ui/ng-grid-1.5.0/ng-grid.css create mode 100644 src/main/webapp/person/person.jspx (limited to 'src/main/webapp') diff --git a/src/main/webapp/WEB-INF/tags/common/head-element.tagx b/src/main/webapp/WEB-INF/tags/common/head-element.tagx index e552b7b..bb8804c 100644 --- a/src/main/webapp/WEB-INF/tags/common/head-element.tagx +++ b/src/main/webapp/WEB-INF/tags/common/head-element.tagx @@ -1,7 +1,6 @@ - + @@ -11,13 +10,16 @@ <c:if test="${not empty title }">${title } - </c:if>Yeah + + diff --git a/src/main/webapp/WEB-INF/urlrewrite.xml b/src/main/webapp/WEB-INF/urlrewrite.xml index ff4bf10..e5f3338 100644 --- a/src/main/webapp/WEB-INF/urlrewrite.xml +++ b/src/main/webapp/WEB-INF/urlrewrite.xml @@ -15,4 +15,10 @@ /index.jspx + + ^/person/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$ + $1 + /person/person.jspx + + diff --git a/src/main/webapp/apps/app.css b/src/main/webapp/apps/app.css new file mode 100644 index 0000000..761b765 --- /dev/null +++ b/src/main/webapp/apps/app.css @@ -0,0 +1,41 @@ +/* + bronze = #8c7853 + bronze ii = #a67d3d + */ + +.badge-level-1 { background-color: #a67d3d; } +.badge-level-2 { background-color: silver; } +.badge-level-3 { background-color: #ffd700; } + +#content { + background-color: #ffffff; + padding-bottom: 60px; +} + +#footer { + background-color: #f5f5f5; + border-top: 1px solid #ccc; + color: #000000; +} + +#footer .container { + padding: 60px 0; +} + +#footer abbr[title] { + border-bottom: 1px dotted #000; +} + +#footer p { + margin-bottom: 0; + color: #777; +} + +#footer-links { + margin: 10px 0; +} + +#footer-links li { + display: inline; + margin-right: 10px; +} diff --git a/src/main/webapp/apps/frontPageApp/frontPage.html b/src/main/webapp/apps/frontPageApp/frontPage.html index bfa2477..4523e6f 100644 --- a/src/main/webapp/apps/frontPageApp/frontPage.html +++ b/src/main/webapp/apps/frontPageApp/frontPage.html @@ -1,24 +1,15 @@
- + + + -

- - - - - - - - - - - - - -
NameLevelCountProgressGoal
{{person.name}}{{person.badges.length}}
-

+
diff --git a/src/main/webapp/apps/frontPageApp/frontPageApp.js b/src/main/webapp/apps/frontPageApp/frontPageApp.js index 4c5df8b..d92a163 100644 --- a/src/main/webapp/apps/frontPageApp/frontPageApp.js +++ b/src/main/webapp/apps/frontPageApp/frontPageApp.js @@ -1,12 +1,68 @@ 'use strict'; -var frontPageApp = angular.module('frontPageApp', ['personService']).config(function ($routeProvider, $locationProvider) { +var frontPageApp = angular.module('frontPageApp', ['ngGrid', 'personService']).config(function ($routeProvider, $locationProvider) { $routeProvider. when('/', {controller: FrontPageCtrl, templateUrl: '/apps/frontPageApp/frontPage.html?noCache=' + noCache}); }); -function FrontPageCtrl($scope, $location, PersonService) { - PersonService.query(function (persons) { - $scope.persons = persons; +function FrontPageCtrl($scope, $http, PersonService) { + $scope.persons = []; + + $scope.pagingOptions = { + pageSizes: [10], + pageSize: 10, + totalServerItems: 0, + currentPage: 1 + }; + + $scope.personsGridOptions = { + data: 'persons', + displayFooter: true, + enablePaging: true, + showFilter: false, + showColumnMenu: false, + canSelectRows: false, + displaySelectionCheckbox: false, + pagingOptions: $scope.pagingOptions, + columnDefs: [ + { + field: 'name', + displayName: 'Name', + cellTemplate: '{{row.getProperty(col.field)}}' + }, + { + field: 'badges', + displayName: 'Badges', + cellTemplate: '
{{row.getProperty(col.field).length}}
' + } + ] + }; + + $scope.setPagingData = function(data, page, pageSize){ +// $scope.persons = data.slice((page - 1) * pageSize, page * pageSize); + $scope.persons = data; + $scope.personsGridOptions.totalServerItems = data.length; + if (!$scope.$$phase) { + $scope.$apply(); + } + }; + + $scope.getPagedDataAsync = function (pageSize, page/*, searchText*/) { + setTimeout(function () { + + PersonService.query({startIndex: page * pageSize, count: pageSize}, function (persons) { + $scope.setPagingData(persons, page, pageSize); + }); + }, 100); + }; + + $scope.$watch('pagingOptions', function () { + $scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage); + }, true); + + $http.get('/resource/core/person-count').success(function(count) { + $scope.pagingOptions.totalServerItems = count; + + $scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage); }); } diff --git a/src/main/webapp/apps/personApp/person.html b/src/main/webapp/apps/personApp/person.html new file mode 100644 index 0000000..4189f20 --- /dev/null +++ b/src/main/webapp/apps/personApp/person.html @@ -0,0 +1,17 @@ +
+ + + +

Badges

+

+ {{badge.name}} x {{badge.count}} +

+ +

Badges in progress

+

+ {{badge.name}} progress: {{badge.progress}} of {{badge.goal}} +

+ +
diff --git a/src/main/webapp/apps/personApp/personApp.js b/src/main/webapp/apps/personApp/personApp.js new file mode 100644 index 0000000..59f5a7d --- /dev/null +++ b/src/main/webapp/apps/personApp/personApp.js @@ -0,0 +1,12 @@ +'use strict'; + +var personApp = angular.module('personApp', ['personService']).config(function ($routeProvider, $locationProvider) { + $routeProvider. + when('/', {controller: PersonCtrl, templateUrl: '/apps/personApp/person.html?noCache=' + noCache}); +}); + +function PersonCtrl($scope, $location, PersonService) { + PersonService.get({uuid: uuid}, function (person) { + $scope.person = person; + }); +} diff --git a/src/main/webapp/external/angular-ui/ng-grid-1.5.0/ng-grid-1.5.0.debug.js b/src/main/webapp/external/angular-ui/ng-grid-1.5.0/ng-grid-1.5.0.debug.js new file mode 100644 index 0000000..4ceaa00 --- /dev/null +++ b/src/main/webapp/external/angular-ui/ng-grid-1.5.0/ng-grid-1.5.0.debug.js @@ -0,0 +1,2334 @@ +/*********************************************** + * ng-grid JavaScript Library + * Authors: https://github.com/angular-ui/ng-grid/blob/master/README.md + * License: MIT (http://www.opensource.org/licenses/mit-license.php) + * Compiled At: 12/21/2012 15:55:15 + ***********************************************/ + +(function(window) { + 'use strict'; + + /*********************************************** + * FILE: ..\src\namespace.js + ***********************************************/ + window.ng = {}; + window.ng.$http = undefined; + var ngGridServices = angular.module('ngGrid.services', []); + var ngGridDirectives = angular.module('ngGrid.directives', []); + var ngGridFilters = angular.module('ngGrid.filters', []); +// Declare app level module which depends on filters, and services + + /*********************************************** + * FILE: ..\src\constants.js + ***********************************************/ + var SELECTED_PROP = '__ng_selected__'; + var GRID_KEY = '__koGrid__'; +// the # of rows we want to add to the top and bottom of the rendered grid rows + var EXCESS_ROWS = 8; + var SCROLL_THRESHOLD = 6; + var ASC = "asc"; +// constant for sorting direction + var DESC = "desc"; +// constant for sorting direction + var NG_FIELD = '_ng_field_'; + var NG_DEPTH = '_ng_depth_'; + var NG_HIDDEN = '_ng_hidden_'; + var NG_COLUMN = '_ng_column_'; + var CUSTOM_FILTERS = /CUSTOM_FILTERS/g; + var TEMPLATE_REGEXP = /<.+>/; + + /*********************************************** + * FILE: ..\src\navigation.js + ***********************************************/ +//set event binding on the grid so we can select using the up/down keys + ng.moveSelectionHandler = function($scope, grid, evt) { + // null checks + if (grid === null || grid === undefined) { + return true; + } + if (grid.config.selectedItems === undefined) { + return true; + } + var charCode = evt.which || evt.keyCode; + // detect which direction for arrow keys to navigate the grid + var offset = (charCode == 38 ? -1 : (charCode == 40 ? 1 : null)); + if (!offset) { + return true; + } + var items = $scope.renderedRows; + var index = items.indexOf(grid.selectionService.lastClickedRow) + offset; + if (index < 0 || index >= items.length) { + return true; + } + grid.selectionService.ChangeSelection(items[index], evt); + if (index > items.length - EXCESS_ROWS) { + grid.$viewport.scrollTop(grid.$viewport.scrollTop() + (grid.config.rowHeight * 2)); + } else if (index < EXCESS_ROWS) { + grid.$viewport.scrollTop(grid.$viewport.scrollTop() - (grid.config.rowHeight * 2)); + } + if (!$scope.$$phase) { + $scope.$parent.$digest(); + } + return false; + }; + + /*********************************************** + * FILE: ..\src\utils.js + ***********************************************/ + if (!String.prototype.trim) { + String.prototype.trim = function() { + return this.replace(/^\s+|\s+$/g, ''); + }; + } + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(elt /*, from*/) { + var len = this.length >>> 0; + var from = Number(arguments[1]) || 0; + from = (from < 0) ? Math.ceil(from) : Math.floor(from); + if (from < 0) { + from += len; + } + for (; from < len; from++) { + if (from in this && this[from] === elt) { + return from; + } + } + return -1; + }; + } + if (!Array.prototype.filter) { + Array.prototype.filter = function(fun /*, thisp */) { + "use strict"; + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== "function") { + throw new TypeError(); + } + var res = []; + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in t) { + var val = t[i]; // in case fun mutates this + if (fun.call(thisp, val, i, t)) { + res.push(val); + } + } + } + return res; + }; + } + ng.utils = { + visualLength: function(node) { + var elem = document.getElementById('testDataLength'); + if (!elem) { + elem = document.createElement('SPAN'); + elem.id = "testDataLength"; + elem.style.visibility = "hidden"; + document.body.appendChild(elem); + } + $(elem).css('font', $(node).css('font')); + elem.innerHTML = $(node).text(); + return elem.offsetWidth; + }, + forIn: function(obj, action) { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + action(obj[prop], prop); + } + } + }, + evalProperty: function(entity, path) { + var propPath = path.split('.'), i = 0; + var tempProp = entity[propPath[i]], links = propPath.length; + i++; + while (tempProp && i < links) { + tempProp = tempProp[propPath[i]]; + i++; + } + return tempProp; + }, + endsWith: function(str, suffix) { + if (!str || !suffix || typeof str != "string") { + return false; + } + return str.indexOf(suffix, str.length - suffix.length) !== -1; + }, + isNullOrUndefined: function(obj) { + if (obj === undefined || obj === null) { + return true; + } + return false; + }, + getElementsByClassName: function(cl) { + var retnode = []; + var myclass = new RegExp('\\b' + cl + '\\b'); + var elem = document.getElementsByTagName('*'); + for (var i = 0; i < elem.length; i++) { + var classes = elem[i].className; + if (myclass.test(classes)) { + retnode.push(elem[i]); + } + } + return retnode; + }, + newId: (function() { + var seedId = new Date().getTime(); + return function() { + return seedId += 1; + }; + })(), + + // we copy KO's ie detection here bc it isn't exported in the min versions of KO + // Detect IE versions for workarounds (uses IE conditionals, not UA string, for robustness) + ieVersion: (function() { + var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i'); + // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment + while (div.innerHTML = '', + iElems[0]); + return version > 4 ? version : undefined; + })() + }; + + $.extend(ng.utils, { + isIe6: (function() { + return ng.utils.ieVersion === 6; + })(), + isIe7: (function() { + return ng.utils.ieVersion === 7; + })(), + isIe: (function() { + return ng.utils.ieVersion !== undefined; + })() + }); + + /*********************************************** + * FILE: ..\src\filters\ngColumns.js + ***********************************************/ + ngGridFilters.filter('ngColumns', function() { + return function(input) { + return input.filter(function(col) { + return !col.isAggCol; + }); + }; + }); + + /*********************************************** + * FILE: ..\src\filters\checkmark.js + ***********************************************/ + ngGridFilters.filter('checkmark', function() { + return function(input) { + return input ? '\u2714' : '\u2718'; + }; + }); + + /*********************************************** + * FILE: ..\src\services\SortService.js + ***********************************************/ + ngGridServices.factory('SortService', function() { + var sortService = {}; + sortService.colSortFnCache = {}; // cache of sorting functions. Once we create them, we don't want to keep re-doing it + sortService.dateRE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; // nasty regex for date parsing + // this takes an piece of data from the cell and tries to determine its type and what sorting + // function to use for it + // @item - the cell data + sortService.guessSortFn = function(item) { + var sortFn, // sorting function that is guessed + itemType, // the typeof item + dateParts, // for date parsing + month, // for date parsing + day; // for date parsing + + if (item === undefined || item === null || item === '') { + return null; + } + itemType = typeof(item); + //check for numbers and booleans + switch (itemType) { + case "number": + sortFn = sortService.sortNumber; + break; + case "boolean": + sortFn = sortService.sortBool; + break; + default: + sortFn = undefined; + break; + } + //if we found one, return it + if (sortFn) { + return sortFn; + } + //check if the item is a valid Date + if (Object.prototype.toString.call(item) === '[object Date]') { + return sortService.sortDate; + } + // if we aren't left with a string, return a basic sorting function... + if (itemType !== "string") { + return sortService.basicSort; + } + // now lets string check.. + //check if the item data is a valid number + if (item.match(/^-?[�$�]?[\d,.]+%?$/)) { + return sortService.sortNumberStr; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + dateParts = item.match(sortService.dateRE); + if (dateParts) { + // looks like a date + month = parseInt(dateParts[1], 10); + day = parseInt(dateParts[2], 10); + if (month > 12) { + // definitely dd/mm + return sortService.sortDDMMStr; + } else if (day > 12) { + return sortService.sortMMDDStr; + } else { + // looks like a date, but we can't tell which, so assume that it's MM/DD + return sortService.sortMMDDStr; + } + } + //finally just sort the normal string... + return sortService.sortAlpha; + }; + //#region Sorting Functions + sortService.basicSort = function(a, b) { + if (a == b) { + return 0; + } + if (a < b) { + return -1; + } + return 1; + }; + sortService.sortNumber = function(a, b) { + return a - b; + }; + sortService.sortNumberStr = function(a, b) { + var numA, numB, badA = false, badB = false; + numA = parseFloat(a.replace(/[^0-9.-]/g, '')); + if (isNaN(numA)) { + badA = true; + } + numB = parseFloat(b.replace(/[^0-9.-]/g, '')); + if (isNaN(numB)) { + badB = true; + } + // we want bad ones to get pushed to the bottom... which effectively is "greater than" + if (badA && badB) { + return 0; + } + if (badA) { + return 1; + } + if (badB) { + return -1; + } + return numA - numB; + }; + sortService.sortAlpha = function(a, b) { + var strA = a.toLowerCase(), + strB = b.toLowerCase(); + return strA == strB ? 0 : (strA < strB ? -1 : 1); + }; + sortService.sortDate = function(a, b) { + var timeA = a.getTime(), + timeB = b.getTime(); + return timeA == timeB ? 0 : (timeA < timeB ? -1 : 1); + }; + sortService.sortBool = function(a, b) { + if (a && b) { + return 0; + } + if (!a && !b) { + return 0; + } else { + return a ? 1 : -1; + } + }; + sortService.sortDDMMStr = function(a, b) { + var dateA, dateB, mtch, m, d, y; + mtch = a.match(sortService.dateRE); + y = mtch[3]; + m = mtch[2]; + d = mtch[1]; + if (m.length == 1) { + m = '0' + m; + } + if (d.length == 1) { + d = '0' + d; + } + dateA = y + m + d; + mtch = b.match(sortService.dateRE); + y = mtch[3]; + m = mtch[2]; + d = mtch[1]; + if (m.length == 1) { + m = '0' + m; + } + if (d.length == 1) { + d = '0' + d; + } + dateB = y + m + d; + if (dateA == dateB) { + return 0; + } + if (dateA < dateB) { + return -1; + } + return 1; + }; + sortService.sortMMDDStr = function(a, b) { + var dateA, dateB, mtch, m, d, y; + mtch = a.match(sortService.dateRE); + y = mtch[3]; + d = mtch[2]; + m = mtch[1]; + if (m.length == 1) { + m = '0' + m; + } + if (d.length == 1) { + d = '0' + d; + } + dateA = y + m + d; + mtch = b.match(sortService.dateRE); + y = mtch[3]; + d = mtch[2]; + m = mtch[1]; + if (m.length == 1) { + m = '0' + m; + } + if (d.length == 1) { + d = '0' + d; + } + dateB = y + m + d; + if (dateA == dateB) { + return 0; + } + if (dateA < dateB) { + return -1; + } + return 1; + }; + //#endregion + // the core sorting logic trigger + sortService.sortData = function(data /*datasource*/, sortInfo) { + // first make sure we are even supposed to do work + if (!data || !sortInfo) { + return; + } + // grab the metadata for the rest of the logic + var col = sortInfo.column, + direction = sortInfo.direction, + sortFn, + item; + + //see if we already figured out what to use to sort the column + if (sortService.colSortFnCache[col.field]) { + sortFn = sortService.colSortFnCache[col.field]; + } else if (col.sortingAlgorithm != undefined) { + sortFn = col.sortingAlgorithm; + sortService.colSortFnCache[col.field] = col.sortingAlgorithm; + } else { // try and guess what sort function to use + item = data[0]; + if (!item) { + return; + } + sortFn = sortService.guessSortFn(item[col.field]); + //cache it + if (sortFn) { + sortService.colSortFnCache[col.field] = sortFn; + } else { + // we assign the alpha sort because anything that is null/undefined will never get passed to + // the actual sorting function. It will get caught in our null check and returned to be sorted + // down to the bottom + sortFn = sortService.sortAlpha; + } + } + //now actually sort the data + data.sort(function(itemA, itemB) { + var propA = ng.utils.evalProperty(itemA, col.field); + var propB = ng.utils.evalProperty(itemB, col.field); + // we want to force nulls and such to the bottom when we sort... which effectively is "greater than" + if (!propB && !propA) { + return 0; + } else if (!propA) { + return 1; + } else if (!propB) { + return -1; + } + //made it this far, we don't have to worry about null & undefined + if (direction === ASC) { + return sortFn(propA, propB); + } else { + return 0 - sortFn(propA, propB); + } + }); + return; + }; + sortService.Sort = function(sortInfo, data) { + if (sortService.isSorting) { + return; + } + sortService.isSorting = true; + sortService.sortData(data, sortInfo); + sortService.isSorting = false; + }; + return sortService; + }); + + /*********************************************** + * FILE: ..\src\services\DomUtilityService.js + ***********************************************/ + ngGridServices.factory('DomUtilityService', function() { + var domUtilityService = {}; + var getWidths = function() { + var $testContainer = $('
'); + $testContainer.appendTo('body'); + // 1. Run all the following measurements on startup! + //measure Scroll Bars + $testContainer.height(100).width(100).css("position", "absolute").css("overflow", "scroll"); + $testContainer.append('
'); + domUtilityService.ScrollH = ($testContainer.height() - $testContainer[0].clientHeight); + domUtilityService.ScrollW = ($testContainer.width() - $testContainer[0].clientWidth); + $testContainer.empty(); + //clear styles + $testContainer.attr('style', ''); + //measure letter sizes using a pretty typical font size and fat font-family + $testContainer.append('M'); + domUtilityService.LetterW = $testContainer.children().first().width(); + $testContainer.remove(); + }; + domUtilityService.eventStorage = {}; + domUtilityService.AssignGridContainers = function(rootEl, grid) { + grid.$root = $(rootEl); + //Headers + grid.$topPanel = grid.$root.find(".ngTopPanel"); + grid.$groupPanel = grid.$root.find(".ngGroupPanel"); + grid.$headerContainer = grid.$topPanel.find(".ngHeaderContainer"); + grid.$headerScroller = grid.$topPanel.find(".ngHeaderScroller"); + grid.$headers = grid.$headerScroller.children(); + //Viewport + grid.$viewport = grid.$root.find(".ngViewport"); + //Canvas + grid.$canvas = grid.$viewport.find(".ngCanvas"); + //Footers + grid.$footerPanel = grid.$root.find(".ngFooterPanel"); + domUtilityService.UpdateGridLayout(grid); + }; + domUtilityService.UpdateGridLayout = function(grid) { + //catch this so we can return the viewer to their original scroll after the resize! + var scrollTop = grid.$viewport.scrollTop(); + grid.elementDims.rootMaxW = grid.$root.width(); + grid.elementDims.rootMaxH = grid.$root.height(); + //check to see if anything has changed + grid.refreshDomSizes(); + grid.adjustScrollTop(scrollTop, true); //ensure that the user stays scrolled where they were + }; + domUtilityService.numberOfGrids = 0; + domUtilityService.BuildStyles = function($scope, grid, digest) { + var rowHeight = grid.config.rowHeight, + $style = grid.$styleSheet, + gridId = grid.gridId, + css, + cols = $scope.visibleColumns(), + sumWidth = 0; + + if (!$style) { + $style = $('#' + gridId); + if (!$style[0]) { + $style = $("