Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initialer Commit für finaler Version zum Kapitel 9.

  • Loading branch information...
commit caf9e310eaa5a8fb8587292e3f3218f6afdf64c2 1 parent 91bc4fd
@stefanscheidt stefanscheidt authored
Showing with 3,461 additions and 763 deletions.
  1. +1 −0  .gitignore
  2. +2 −11 jstd-unit.conf
  3. +151 −8 pom.xml
  4. +29 −0 src/main/webapp/app/AuthenticationController.js
  5. +76 −0 src/main/webapp/app/RentalController.js
  6. +32 −0 src/main/webapp/app/RentalHistoryController.js
  7. +2 −2 src/main/webapp/app/application.js
  8. +167 −0 src/main/webapp/app/backendService.js
  9. +7 −148 src/main/webapp/app/controllers.js
  10. +3 −2 src/main/webapp/app/markup.js
  11. +17 −0 src/main/webapp/app/rentalService.js
  12. +3 −284 src/main/webapp/app/services.js
  13. +121 −0 src/main/webapp/app/utilsService.js
  14. +23 −212 src/main/webapp/index.html
  15. +277 −0 src/main/webapp/lib/almond.js
  16. +189 −0 src/main/webapp/lib/order.js
  17. +2,053 −0 src/main/webapp/lib/require.js
  18. +2 −0  src/main/webapp/{rylc.css → main.css}
  19. +15 −0 src/main/webapp/main.js
  20. +33 −0 src/main/webapp/profilePage.html
  21. +37 −0 src/main/webapp/rental1Page.html
  22. +24 −0 src/main/webapp/rental2Page.html
  23. +37 −0 src/main/webapp/rental3Page.html
  24. +23 −0 src/main/webapp/rentalHistoryPage.html
  25. +14 −0 src/main/webapp/welcomePage.html
  26. +21 −30 src/test/webapp/UnitSpecRunner.html
  27. +42 −41 src/test/webapp/lib/testutils.js
  28. +1 −1  src/test/webapp/ui/loginUiSpec.js
  29. +1 −1  src/test/webapp/ui/profileUiSpec.js
  30. +4 −4 src/test/webapp/ui/rental1UiSpec.js
  31. +3 −3 src/test/webapp/ui/rental2UiSpec.js
  32. +3 −3 src/test/webapp/ui/rental3UiSpec.js
  33. +1 −1  src/test/webapp/ui/rentalHistoryUiSpec.js
  34. +7 −7 src/test/webapp/ui/welcomeUiSpec.js
  35. +1 −1  src/test/webapp/unit/AuthenticationControllerSpec.js
  36. +5 −0 src/test/webapp/unit/JSONParseDateSpec.js
  37. +25 −4 src/test/webapp/unit/backendServiceSpec.js
  38. +9 −0 src/test/webapp/unit/utilsServiceSpec.js
View
1  .gitignore
@@ -3,4 +3,5 @@
*.iml
# Maven
+build
target
View
13 jstd-unit.conf
@@ -1,23 +1,14 @@
server: http://localhost:9876
load:
- # Libraries
- - src/main/webapp/lib/JSONParseDate.js
- - src/main/webapp/lib/Base64.js
- - src/main/webapp/lib/jquery.js
- - src/main/webapp/lib/angular.js
+ # Libraries and Application
+ - target/html5/main.js
# Test-Libraries
- src/test/webapp/lib/jasmine.js
- src/test/webapp/lib/angular-mocks.js
- src/test/webapp/lib/JasmineAdapter.js
- # Application
- - src/main/webapp/app/application.js
- - src/main/webapp/app/controllers.js
- - src/main/webapp/app/markup.js
- - src/main/webapp/app/services.js
-
test:
# Tests
- src/test/webapp/unit/*.js
View
159 pom.xml
@@ -42,13 +42,109 @@
<webAppConfig>
<contextPath>${jetty.contextPath}</contextPath>
<defaultsDescriptor>${basedir}/misc/jetty-webapp/webdefault.xml</defaultsDescriptor>
- <resourceBases>
- <resourceBase>${basedir}/src/main/webapp</resourceBase>
- <resourceBase>${basedir}/src/test/webapp</resourceBase>
- </resourceBases>
</webAppConfig>
</configuration>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <id>package</id>
+ <phase>prepare-package</phase>
+ <configuration>
+ <target>
+ <property name="sourceDir" value="${project.basedir}/src/main/webapp"/>
+ <property name="outputDir" value="${project.build.directory}/html5"/>
+ <mkdir dir="${outputDir}"/>
+
+ <echo>-------- Building HTML ---------</echo>
+ <!-- Concat all html files into one file -->
+ <copy file="${sourceDir}/index.html" todir="${outputDir}" overwrite="true"/>
+ <loadresource property="pages">
+ <concat>
+ <fileset dir="${sourceDir}" includes="*.html" excludes="index.html"/>
+ <footer><![CDATA[</body>]]></footer>
+ </concat>
+ </loadresource>
+ <replace file="${outputDir}/index.html" value="${pages}">
+ <replacetoken><![CDATA[</body>]]></replacetoken>
+ </replace>
+ <!-- Minify html -->
+ <java classpathref="maven.compile.classpath" classname="com.googlecode.htmlcompressor.CmdLineCompressor">
+ <arg value="${outputDir}/index.html"/>
+ <arg value="-o"/>
+ <arg value="${outputDir}/index.html"/>
+ </java>
+
+ <echo>-------- Building JavaScript ---------</echo>
+ <!-- Concat all javascript files into one file -->
+ <java classpathref="maven.compile.classpath" classname="org.mozilla.javascript.tools.shell.Main">
+ <arg value="${project.basedir}/build/r.js"/>
+ <arg value="-o"/>
+ <arg value="baseUrl=src/main/webapp"/>
+ <arg value="name=lib/almond"/>
+ <arg value="include=main.js"/>
+ <arg value="out=${outputDir}/main.js"/>
+ <arg value="optimize=none"/>
+ </java>
+ <!-- Minify javascript -->
+ <java classpathref="maven.compile.classpath" classname="com.google.javascript.jscomp.CommandLineRunner">
+ <arg value="--js"/>
+ <arg value="${outputDir}/main.js"/>
+ <arg value="--compilation_level"/>
+ <arg value="SIMPLE_OPTIMIZATIONS"/>
+ <arg value="--language_in"/>
+ <arg value="ECMASCRIPT5"/>
+ <arg value="--js_output_file"/>
+ <arg value="${outputDir}/main.min.js"/>
+ </java>
+ <move file="${outputDir}/main.min.js" tofile="${outputDir}/main.js" overwrite="true"/>
+ <!-- include in index.html -->
+ <replace file="${outputDir}/index.html">
+ <replacetoken><![CDATA[src="lib/require.js"]]></replacetoken>
+ <replacevalue><![CDATA[src="main.js"]]></replacevalue>
+ </replace>
+
+ <echo>-------- Building CSS ---------</echo>
+ <!-- Concat all css includes into one file -->
+ <java classpathref="maven.compile.classpath" classname="org.mozilla.javascript.tools.shell.Main">
+ <arg value="${project.basedir}/build/r.js"/>
+ <arg value="-o"/>
+ <arg value="cssIn=src/main/webapp/main.css"/>
+ <arg value="out=${outputDir}/main.css"/>
+ </java>
+ <!-- Minify css -->
+ <java classpathref="maven.compile.classpath" classname="com.yahoo.platform.yui.compressor.YUICompressor">
+ <arg value="${outputDir}/main.css"/>
+ <arg value="-o"/>
+ <arg value="${outputDir}/main.css"/>
+ </java>
+
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>2.1.1</version>
+ <configuration>
+ <warSourceIncludes>**/*.xml, **/*.gif, **/*.png</warSourceIncludes>
+ <webResources>
+ <resource>
+ <directory>target/html5</directory>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
</plugins>
</build>
@@ -59,11 +155,35 @@
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
- <groupId>com.googlecode.jstd-maven-plugin</groupId>
- <artifactId>jstd-maven-plugin</artifactId>
- <version>1.3.2.5</version>
+ <groupId>com.google.jstestdriver</groupId>
+ <artifactId>jstestdriver</artifactId>
+ <version>1.3.2</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>rhino</groupId>
+ <artifactId>js</artifactId>
+ <version>1.7R2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.googlecode.htmlcompressor</groupId>
+ <artifactId>htmlcompressor</artifactId>
+ <version>1.5.2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.javascript</groupId>
+ <artifactId>closure-compiler</artifactId>
+ <version>r1810</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.platform.yui</groupId>
+ <artifactId>yuicompressor</artifactId>
+ <version>2.4.6</version>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<repositories>
@@ -83,6 +203,29 @@
<profiles>
<profile>
+ <id>development</id>
+ <activation>
+ <activeByDefault/>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <configuration>
+ <webAppConfig>
+ <resourceBases>
+ <resourceBase>${basedir}/src/main/webapp</resourceBase>
+ <resourceBase>${basedir}/src/test/webapp</resourceBase>
+ </resourceBases>
+ </webAppConfig>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
<id>integration</id>
<build>
<plugins>
@@ -129,7 +272,7 @@
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
- <goal>run</goal>
+ <goal>run-war</goal>
</goals>
<configuration>
<scanIntervalSeconds>0</scanIntervalSeconds>
View
29 src/main/webapp/app/AuthenticationController.js
@@ -0,0 +1,29 @@
+define(function () {
+ function AuthenticationController($scope, backendService, $navigate) {
+
+ $scope.login = function () {
+ return backendService.login($scope.username, $scope.password).then(function () {
+ $navigate('welcomePage.html');
+ }, function (errorMessage) {
+ $scope.errorMessage = errorMessage;
+ });
+ };
+
+ $scope.loginPossible = function () {
+ return $scope.username && $scope.password;
+ };
+
+ $scope.logout = function () {
+ backendService.logout();
+ };
+
+ $scope.customer = function () {
+ return backendService.authenticatedCustomer();
+ };
+
+ }
+
+ AuthenticationController.$inject = ['$scope', 'backendService', '$navigate'];
+
+ return AuthenticationController;
+});
View
76 src/main/webapp/app/RentalController.js
@@ -0,0 +1,76 @@
+define(function () {
+ function RentalController($scope, rentalService, backendService, $navigate) {
+ var SUCCESS_MESSAGE = "Bestellung erfolgreich entgegengenommen.";
+
+ $scope.clearMessages = function () {
+ $scope.successMessage = null;
+ $scope.errorMessage = null;
+ };
+
+ $scope.prefetchMasterData = function () {
+ backendService.carTypesBackground();
+ backendService.citiesBackground();
+ };
+
+ $scope.initRental = function () {
+ $scope.errorMessage = null;
+ $scope.successMessage = null;
+ $scope.startDate = new Date();
+ $scope.endDate = new Date();
+ $scope.maxPrice = 100;
+ $scope.endDate.setDate($scope.startDate.getDate() + 1);
+ backendService.cities().then(function (cities) {
+ $scope.cities = cities;
+ $scope.city = cities[0];
+ }, function (errorMessage) {
+ $scope.errorMessage = errorMessage;
+ });
+ backendService.carTypes().then(function (carTypes) {
+ $scope.carTypes = carTypes;
+ $scope.carType = carTypes[0];
+ }, function (errorMessage) {
+ $scope.errorMessage = errorMessage;
+ });
+ $navigate('rental1Page.html');
+ };
+
+ $scope.searchAvailableCars = function () {
+ return backendService.availableCars($scope.city.id, $scope.startDate, $scope.endDate, $scope.maxPrice).then(
+ function (cars) {
+ $scope.availableCars = cars;
+ $navigate("rental2Page.html");
+ }, function (errorMessage) {
+ $scope.errorMessage = errorMessage;
+ });
+ };
+
+ $scope.totalPrice = function () {
+ if ($scope.car) {
+ return rentalService.totalPrice($scope.car.price, $scope.startDate, $scope.endDate);
+ } else {
+ return 0;
+ }
+ };
+
+ $scope.selectCar = function (car) {
+ $scope.car = car;
+ $navigate('rental3Page.html');
+ };
+
+ $scope.rentCar = function () {
+ return backendService.rentCar($scope.car.id, $scope.startDate, $scope.endDate).then(
+ function (rental) {
+ $scope.rental = rental;
+ $scope.successMessage = SUCCESS_MESSAGE;
+ $navigate('back:welcomePage.html');
+ }, function (errorMessage) {
+ $scope.errorMessage = errorMessage;
+ $navigate('back:welcomePage.html');
+ });
+ };
+ }
+
+ RentalController.$inject = ['$scope', 'rentalService', 'backendService', '$navigate'];
+
+ return RentalController;
+});
View
32 src/main/webapp/app/RentalHistoryController.js
@@ -0,0 +1,32 @@
+define(function () {
+ function RentalHistoryController($scope, rentalService, backendService) {
+
+ $scope.searchRentals = function () {
+ var res = backendService.rentalsByCustomerId(backendService.authenticatedCustomer().id);
+ res.then(function (data) {
+ $scope.rentals = data;
+ }, function (errorMessage) {
+ $scope.errorMessage = errorMessage;
+ });
+ return res;
+ };
+
+ $scope.totalPrice = function (rental) {
+ return rentalService.totalPrice(
+ rental.car.price, rental.hireStartDate, rental.hireEndDate);
+ };
+
+ $scope.infoMessage = function () {
+ if ($scope.rentals && $scope.rentals.length === 0) {
+ return "Keine Daten gefunden.";
+ } else {
+ return null;
+ }
+ };
+
+ }
+
+ RentalHistoryController.$inject = ['$scope', 'rentalService', 'backendService'];
+
+ return RentalHistoryController;
+});
View
4 src/main/webapp/app/application.js
@@ -1,5 +1,5 @@
-(function (angular) {
+define(function () {
angular.module("rylc", ["rylc-controllers", "rylc-markup", "rylc-services"]);
-})(angular);
+});
View
167 src/main/webapp/app/backendService.js
@@ -0,0 +1,167 @@
+define(function () {
+ function backendServiceFactory($http, $q, $waitDialog) {
+ var defaultErrorCodeMessageMapping = {
+ 401:'Zugriff verweigert.',
+ 0:'Ein unbekannter Fehler ist aufgetreten.'
+ };
+
+ var backendBaseUri = '/rylc-html5/api';
+
+ var carTypesPromise, citiesPromise, authorizationHeader, _authenticationCustomer;
+
+ function carTypesBackground() {
+ if (!carTypesPromise) {
+ carTypesPromise = unpackPromiseAndHandleErrorCodes($http({
+ url:backendBaseUri + '/cartypes',
+ method:'GET',
+ headers:{
+ 'Authorization':authorizationHeader
+ }
+ }));
+ }
+ return carTypesPromise;
+ }
+
+ function carTypes() {
+ return showWaitDialogWhile(carTypesBackground());
+ }
+
+ function citiesBackground() {
+ if (!citiesPromise) {
+ citiesPromise = unpackPromiseAndHandleErrorCodes($http({
+ url:backendBaseUri + '/cities',
+ method:'GET',
+ headers:{
+ 'Authorization':authorizationHeader
+ }
+ }));
+ }
+ return citiesPromise;
+ }
+
+ function cities() {
+ return showWaitDialogWhile(citiesBackground());
+ }
+
+ function rentalsByCustomerId(customerId) {
+ return showWaitDialogWhile(unpackPromiseAndHandleErrorCodes($http({
+ url:backendBaseUri + '/rentals?customerId=' + customerId,
+ method:'GET',
+ headers:{
+ 'Authorization':authorizationHeader
+ }
+ })));
+ }
+
+ function customerByUsername(username) {
+ return showWaitDialogWhile(unpackPromiseAndHandleErrorCodes($http({
+ url:backendBaseUri + '/customers?username=' + username,
+ method:'GET',
+ headers:{
+ 'Authorization':authorizationHeader
+ }
+ })));
+ }
+
+ function availableCars(cityId, startDate, endDate, maxPrice) {
+ return showWaitDialogWhile(unpackPromiseAndHandleErrorCodes($http({
+ url:backendBaseUri + '/availableCars?cityId=' + cityId + '&startDate=' + startDate.toISOString() + '&endDate=' + endDate.toISOString() + '&maxPrice=' + maxPrice,
+ method:'GET',
+ headers:{
+ 'Authorization':authorizationHeader
+ }
+ })));
+ }
+
+ function rentCar(carId, startDate, endDate) {
+ var errorCodeMapping = {
+ 409:"Fahrzeug leider nicht mehr verf\u00FCgbar."
+ };
+ return showWaitDialogWhile(unpackPromiseAndHandleErrorCodes($http({
+ url:backendBaseUri + '/rental',
+ method:'POST',
+ data:{
+ carId:carId,
+ startDate:startDate,
+ endDate:endDate
+ },
+ headers:{
+ 'Authorization':authorizationHeader
+ }
+ }), errorCodeMapping));
+ }
+
+ function setCredentials(username, password) {
+ authorizationHeader = "Basic " + Base64.encode(username + ':' + password);
+ }
+
+ function login(username, password) {
+ setCredentials(username, password);
+ return customerByUsername(username).then(function (data) {
+ return _authenticationCustomer = data;
+ }, function (data) {
+ return $q.reject(data);
+ });
+ }
+
+ function authenticatedCustomer() {
+ return _authenticationCustomer;
+ }
+
+ function logout() {
+ var url = location.href;
+ var lastSlash = url.lastIndexOf('/');
+ location.href = url.substring(0, lastSlash) + '/index.html';
+ }
+
+ function showWaitDialogWhile(promise) {
+ $waitDialog.show();
+ return promise.then(function (response) {
+ $waitDialog.hide();
+ return response;
+ }, function (response) {
+ $waitDialog.hide();
+ return $q.reject(response);
+ });
+ }
+
+ function unpackPromiseAndHandleErrorCodes(promise, errorCodeMessageMapping) {
+ return promise.then(function (response) {
+ return response.data;
+ }, function (response) {
+ var errorCode = response.status;
+ var errorMessage;
+ if (errorCodeMessageMapping) {
+ errorMessage = errorCodeMessageMapping[errorCode];
+ }
+ if (!errorMessage) {
+ errorMessage = defaultErrorCodeMessageMapping[errorCode];
+ }
+ if (!errorMessage) {
+ errorMessage = defaultErrorCodeMessageMapping[0];
+ }
+
+ return $q.reject(errorMessage);
+ });
+ }
+
+ return {
+ carTypes:carTypes,
+ carTypesBackground:carTypesBackground,
+ cities:cities,
+ citiesBackground:citiesBackground,
+ rentalsByCustomerId:rentalsByCustomerId,
+ customerByUsername:customerByUsername,
+ availableCars:availableCars,
+ rentCar:rentCar,
+ login:login,
+ setCredentials:setCredentials,
+ authenticatedCustomer:authenticatedCustomer,
+ logout:logout
+ }
+ }
+
+ backendServiceFactory.$inject = ["$http", "$q", "$waitDialog"];
+
+ return backendServiceFactory;
+});
View
155 src/main/webapp/app/controllers.js
@@ -1,150 +1,9 @@
-(function (window, angular) {
+define(['app/AuthenticationController', 'app/RentalHistoryController', 'app/RentalController'],
+ function (AuthenticationController, RentalHistoryController, RentalController) {
+ var module = angular.module("rylc-controllers", ["rylc-services"]);
- /*
- * AuthenticationController
- */
+ module.controller("rylc.AuthenticationController", AuthenticationController);
+ module.controller("rylc.RentalHistoryController", RentalHistoryController);
+ module.controller("rylc.RentalController", RentalController);
- function AuthenticationController($scope, backendService, $navigate) {
-
- $scope.login = function () {
- return backendService.login($scope.username, $scope.password).then(function () {
- $navigate("#welcomePage");
- }, function (errorMessage) {
- $scope.errorMessage = errorMessage;
- });
- };
-
- $scope.loginPossible = function () {
- return $scope.username && $scope.password;
- };
-
- $scope.logout = function () {
- backendService.logout();
- };
-
- $scope.customer = function () {
- return backendService.authenticatedCustomer();
- };
-
- }
- AuthenticationController.$inject = ['$scope', 'backendService', '$navigate'];
-
- /*
- * RentalHistoryController
- */
-
- function RentalHistoryController($scope, rentalService, backendService) {
-
- $scope.searchRentals = function () {
- var res = backendService.rentalsByCustomerId(backendService.authenticatedCustomer().id);
- res.then(function (data) {
- $scope.rentals = data;
- }, function (errorMessage) {
- $scope.errorMessage = errorMessage;
- });
- return res;
- };
-
- $scope.totalPrice = function (rental) {
- return rentalService.totalPrice(
- rental.car.price, rental.hireStartDate, rental.hireEndDate);
- };
-
- $scope.infoMessage = function () {
- if ($scope.rentals && $scope.rentals.length === 0) {
- return "Keine Daten gefunden.";
- } else {
- return null;
- }
- };
-
- }
- RentalHistoryController.$inject = ['$scope', 'rentalService', 'backendService'];
-
- /*
- * RentalController
- */
-
- function RentalController($scope, rentalService, backendService, $navigate) {
- var SUCCESS_MESSAGE = "Bestellung erfolgreich entgegengenommen.";
-
- $scope.clearMessages = function () {
- $scope.successMessage = null;
- $scope.errorMessage = null;
- };
-
- $scope.prefetchMasterData = function () {
- backendService.carTypesBackground();
- backendService.citiesBackground();
- };
-
- $scope.initRental = function () {
- $scope.errorMessage = null;
- $scope.successMessage = null;
- $scope.startDate = new Date();
- $scope.endDate = new Date();
- $scope.maxPrice = 100;
- $scope.endDate.setDate($scope.startDate.getDate() + 1);
- backendService.cities().then(function (cities) {
- $scope.cities = cities;
- $scope.city = cities[0];
- }, function (errorMessage) {
- $scope.errorMessage = errorMessage;
- });
- backendService.carTypes().then(function (carTypes) {
- $scope.carTypes = carTypes;
- $scope.carType = carTypes[0];
- }, function (errorMessage) {
- $scope.errorMessage = errorMessage;
- });
- $navigate("#rental1Page");
- };
-
- $scope.searchAvailableCars = function () {
- return backendService.availableCars($scope.city.id, $scope.startDate, $scope.endDate, $scope.maxPrice).then(
- function (cars) {
- $scope.availableCars = cars;
- $navigate("#rental2Page");
- }, function (errorMessage) {
- $scope.errorMessage = errorMessage;
- });
- };
-
- $scope.totalPrice = function () {
- if ($scope.car) {
- return rentalService.totalPrice($scope.car.price, $scope.startDate, $scope.endDate);
- } else {
- return 0;
- }
- };
-
- $scope.selectCar = function (car) {
- $scope.car = car;
- $navigate("#rental3Page");
- };
-
- $scope.rentCar = function () {
- return backendService.rentCar($scope.car.id, $scope.startDate, $scope.endDate).then(
- function (rental) {
- $scope.rental = rental;
- $scope.successMessage = SUCCESS_MESSAGE;
- $navigate("back:#welcomePage");
- }, function (errorMessage) {
- $scope.errorMessage = errorMessage;
- $navigate("back:#welcomePage");
- });
- };
- }
- RentalController.$inject = ['$scope', 'rentalService', 'backendService', '$navigate'];
-
- /*
- * define angular module
- */
-
- var module = angular.module("rylc-controllers", ["rylc-services"]);
-
- module.controller("rylc.AuthenticationController", AuthenticationController);
- module.controller("rylc.RentalHistoryController", RentalHistoryController);
- module.controller("rylc.RentalController", RentalController);
-
-})(window, angular);
+ });
View
5 src/main/webapp/app/markup.js
@@ -1,4 +1,4 @@
-(function (angular) {
+define(function () {
function simpleDateFilterFactory(utilsService) {
return utilsService.formatSimpleDate;
@@ -28,10 +28,11 @@
}
}
}
+
dateInputDirectiveFactory.$inject = ["utilsService"];
var module = angular.module("rylc-markup", ["rylc-services"]);
module.filter("simpleDate", simpleDateFilterFactory);
module.directive("input", dateInputDirectiveFactory);
-})(angular);
+});
View
17 src/main/webapp/app/rentalService.js
@@ -0,0 +1,17 @@
+define(function() {
+ function rentalServiceFactory(utilsService) {
+
+ function totalPrice(pricePerDay, startDate, endDate) {
+ return pricePerDay * utilsService.dayCount(startDate, endDate);
+ }
+
+ return {
+ totalPrice:totalPrice
+ }
+ }
+
+ rentalServiceFactory.$inject = ["utilsService"];
+
+ return rentalServiceFactory;
+
+});
View
287 src/main/webapp/app/services.js
@@ -1,296 +1,15 @@
-(function (angular, Base64) {
+define(["app/utilsService", "app/backendService", "app/rentalService"],
+ function (utilsServiceFactory, backendServiceFactory, rentalServiceFactory) {
function configureHttp($httpProvider) {
$httpProvider.defaults.headers.post = {'Content-Type':'application/json'};
}
configureHttp.$inject = ["$httpProvider"];
- /*
- * utilsService
- */
-
- function utilsServiceFactory() {
-
- function parseSimpleDate(dateAsString) {
- if (!dateAsString) {
- return undefined;
- }
- var parts = dateAsString.split('.');
- return new Date(parts[2], parts[1] - 1, parts[0]);
- }
-
- function formatSimpleDate(date) {
- if (!date) {
- return undefined;
- }
- var month = String(date.getMonth() + 1);
- if (month.length == 1) {
- month = "0" + month;
- }
- var day = String(date.getDate());
- if (day.length == 1) {
- day = "0" + day;
- }
- return day + "." + month + "." + date.getFullYear();
- }
-
- function validateSimpleDate(dateAsString) {
- var simpleDateRegex = /^\d\d?\.\d\d?\.\d\d(\d\d)?$/;
- return dateAsString && dateAsString.match(simpleDateRegex);
- }
-
- function parseHtml5Date(dateAsString) {
- if (!dateAsString) {
- return undefined;
- }
- var parts = dateAsString.split('-');
- return new Date(parts[0], parts[1] - 1, parts[2]);
- }
-
- function formatHtml5Date(date) {
- if (!date) {
- return undefined;
- }
- var month = String(date.getMonth() + 1);
- if (month.length == 1) {
- month = "0" + month;
- }
- var day = String(date.getDate());
- if (day.length == 1) {
- day = "0" + day;
- }
- return date.getFullYear() + '-' + month + "-" + day;
- }
-
- function validateHtml5Date(dateAsString) {
- var dateRegex = /^\d\d\d\d-\d\d-\d\d$/;
- return dateAsString && dateAsString.match(dateRegex);
- }
-
- var _html5DateSupport;
-
- function supportsHtml5Date() {
- if (_html5DateSupport === undefined) {
- var d = document.createElement('input');
- d.setAttribute('type', 'date');
- _html5DateSupport = d.type === 'date';
- }
- return _html5DateSupport;
- }
-
- function dayCount(startDate, endDate) {
- return 1 + (endDate - startDate) / 1000 / 60 / 60 / 24;
- }
-
- var parseDate = supportsHtml5Date() ? parseHtml5Date : parseSimpleDate;
- var formatDate = supportsHtml5Date() ? formatHtml5Date : formatSimpleDate;
- var validateDate = supportsHtml5Date() ? validateHtml5Date : validateSimpleDate;
-
- return {
- parseSimpleDate:parseSimpleDate,
- formatSimpleDate:formatSimpleDate,
- validateSimpleDate:validateSimpleDate,
- parseHtml5Date:parseHtml5Date,
- formatHtml5Date:formatHtml5Date,
- validateHtml5Date:validateHtml5Date,
- parseDate:parseDate,
- formatDate:formatDate,
- validateDate:validateDate,
- dayCount:dayCount
- };
- }
-
- /*
- * backendService
- */
-
- function backendServiceFactory($http, $q, $waitDialog) {
- var backendBaseUri = '/rylc-html5/api';
- var defaultErrorCodeMessageMapping = {
- 401:'Zugriff verweigert.',
- 0:'Ein unbekannter Fehler ist aufgetreten.'
- };
-
- var carTypesPromise, citiesPromise, authorizationHeader, _authenticationCustomer;
-
- function carTypesBackground() {
- if (!carTypesPromise) {
- carTypesPromise = unpackPromiseAndHandleErrorCodes($http({
- url:backendBaseUri + '/cartypes',
- method:'GET',
- headers:{
- 'Authorization':authorizationHeader
- }
- }));
- }
- return carTypesPromise;
- }
-
- function carTypes() {
- return showWaitDialogWhile(carTypesBackground());
- }
-
- function citiesBackground() {
- if (!citiesPromise) {
- citiesPromise = unpackPromiseAndHandleErrorCodes($http({
- url:backendBaseUri + '/cities',
- method:'GET',
- headers:{
- 'Authorization':authorizationHeader
- }
- }));
- }
- return citiesPromise;
- }
-
- function cities() {
- return showWaitDialogWhile(citiesBackground());
- }
-
- function rentalsByCustomerId(customerId) {
- return showWaitDialogWhile(unpackPromiseAndHandleErrorCodes($http({
- url:backendBaseUri + '/rentals?customerId=' + customerId,
- method:'GET',
- headers:{
- 'Authorization':authorizationHeader
- }
- })));
- }
-
- function customerByUsername(username) {
- return showWaitDialogWhile(unpackPromiseAndHandleErrorCodes($http({
- url:backendBaseUri + '/customers?username=' + username,
- method:'GET',
- headers:{
- 'Authorization':authorizationHeader
- }
- })));
- }
-
- function availableCars(cityId, startDate, endDate, maxPrice) {
- return showWaitDialogWhile(unpackPromiseAndHandleErrorCodes($http({
- url:backendBaseUri + '/availableCars?cityId=' + cityId + '&startDate=' + startDate.toISOString() + '&endDate=' + endDate.toISOString() + '&maxPrice=' + maxPrice,
- method:'GET',
- headers:{
- 'Authorization':authorizationHeader
- }
- })));
- }
-
- function rentCar(carId, startDate, endDate) {
- var errorCodeMapping = {
- 409:"Fahrzeug leider nicht mehr verf\u00FCgbar."
- };
- return showWaitDialogWhile(unpackPromiseAndHandleErrorCodes($http({
- url:backendBaseUri + '/rental',
- method:'POST',
- data:{
- carId:carId,
- startDate:startDate,
- endDate:endDate
- },
- headers:{
- 'Authorization':authorizationHeader
- }
- }), errorCodeMapping));
- }
-
- function setCredentials(username, password) {
- authorizationHeader = "Basic " + Base64.encode(username + ':' + password);
- }
-
- function login(username, password) {
- setCredentials(username, password);
- return customerByUsername(username).then(function (data) {
- return _authenticationCustomer = data;
- }, function (data) {
- return $q.reject(data);
- });
- }
-
- function authenticatedCustomer() {
- return _authenticationCustomer;
- }
-
- function logout() {
- var url = location.href;
- var lastSlash = url.lastIndexOf('/');
- location.href = url.substring(0, lastSlash) + '/index.html';
- }
-
- function showWaitDialogWhile(promise) {
- $waitDialog.show();
- return promise.then(function (response) {
- $waitDialog.hide();
- return response;
- }, function (response) {
- $waitDialog.hide();
- return $q.reject(response);
- });
- }
-
- function unpackPromiseAndHandleErrorCodes(promise, errorCodeMessageMapping) {
- return promise.then(function (response) {
- return response.data;
- }, function (response) {
- var errorCode = response.status;
- var errorMessage;
- if (errorCodeMessageMapping) {
- errorMessage = errorCodeMessageMapping[errorCode];
- }
- if (!errorMessage) {
- errorMessage = defaultErrorCodeMessageMapping[errorCode];
- }
- if (!errorMessage) {
- errorMessage = defaultErrorCodeMessageMapping[0];
- }
- return $q.reject(errorMessage);
- });
- }
-
- return {
- carTypes:carTypes,
- carTypesBackground:carTypesBackground,
- cities:cities,
- citiesBackground:citiesBackground,
- rentalsByCustomerId:rentalsByCustomerId,
- customerByUsername:customerByUsername,
- availableCars:availableCars,
- rentCar:rentCar,
- login:login,
- setCredentials:setCredentials,
- authenticatedCustomer:authenticatedCustomer,
- logout:logout
- }
- }
-
- backendServiceFactory.$inject = ["$http", "$q", "$waitDialog"];
-
- /*
- * rentalService
- */
-
- function rentalServiceFactory(utilsService) {
-
- function totalPrice(pricePerDay, startDate, endDate) {
- return pricePerDay * utilsService.dayCount(startDate, endDate);
- }
-
- return {
- totalPrice:totalPrice
- }
- }
-
- rentalServiceFactory.$inject = ["utilsService"];
-
- /*
- * define angular module
- */
-
var module = angular.module("rylc-services", []);
module.config(configureHttp);
module.factory("utilsService", utilsServiceFactory);
module.factory("backendService", backendServiceFactory);
module.factory("rentalService", rentalServiceFactory);
-})(angular, Base64);
+});
View
121 src/main/webapp/app/utilsService.js
@@ -0,0 +1,121 @@
+define(function() {
+ function utilsServiceFactory() {
+
+ function parseSimpleDate(dateAsString) {
+ if (!dateAsString) {
+ return undefined;
+ }
+ var parts = dateAsString.split('.');
+ return new Date(parts[2], parts[1] - 1, parts[0]);
+ }
+
+ function formatSimpleDate(date) {
+ if (!date) {
+ return undefined;
+ }
+ var month = String(date.getMonth() + 1);
+ if (month.length == 1) {
+ month = "0" + month;
+ }
+ var day = String(date.getDate());
+ if (day.length == 1) {
+ day = "0" + day;
+ }
+ return day + "." + month + "." + date.getFullYear();
+ }
+
+ function validateSimpleDate(dateAsString) {
+ var simpleDateRegex = /^\d\d?\.\d\d?\.\d\d(\d\d)?$/;
+ return dateAsString && dateAsString.match(simpleDateRegex);
+ }
+
+ function parseHtml5Date(dateAsString) {
+ if (!dateAsString) {
+ return undefined;
+ }
+ var parts = dateAsString.split('-');
+ return new Date(parts[0], parts[1] - 1, parts[2]);
+ }
+
+ function formatHtml5Date(date) {
+ if (!date) {
+ return undefined;
+ }
+ var month = String(date.getMonth() + 1);
+ if (month.length == 1) {
+ month = "0" + month;
+ }
+ var day = String(date.getDate());
+ if (day.length == 1) {
+ day = "0" + day;
+ }
+ return date.getFullYear() + '-' + month + "-" + day;
+ }
+
+ function validateHtml5Date(dateAsString) {
+ var dateRegex = /^\d\d\d\d-\d\d-\d\d$/;
+ return dateAsString && dateAsString.match(dateRegex);
+ }
+
+ var _html5DateSupport;
+
+ function supportsHtml5Date() {
+ if (_html5DateSupport === undefined) {
+ var d = document.createElement('input');
+ d.setAttribute('type', 'date');
+ _html5DateSupport = d.type === 'date';
+ }
+ return _html5DateSupport;
+ }
+
+ function dayCount(startDate, endDate) {
+ return 1 + (endDate - startDate) / 1000 / 60 / 60 / 24;
+ }
+
+ function builder(props, callback) {
+ var res;
+
+ function setterFor(key) {
+ return function (value) {
+ if (value === undefined) {
+ value = true;
+ }
+ props[key] = value;
+ return res;
+ }
+ }
+
+ res = function () {
+ return callback.call(this, props, arguments);
+ };
+
+ for (var key in props) {
+ res[key] = setterFor(key);
+ }
+ res.clone = function () {
+ return builder(angular.copy(props), callback);
+ };
+ return res;
+ }
+
+ var parseDate = supportsHtml5Date() ? parseHtml5Date : parseSimpleDate;
+ var formatDate = supportsHtml5Date() ? formatHtml5Date : formatSimpleDate;
+ var validateDate = supportsHtml5Date() ? validateHtml5Date : validateSimpleDate;
+
+ return {
+ parseSimpleDate: parseSimpleDate,
+ formatSimpleDate: formatSimpleDate,
+ validateSimpleDate: validateSimpleDate,
+ parseHtml5Date: parseHtml5Date,
+ formatHtml5Date: formatHtml5Date,
+ validateHtml5Date: validateHtml5Date,
+ parseDate: parseDate,
+ formatDate: formatDate,
+ validateDate: validateDate,
+ dayCount: dayCount,
+ builder: builder
+ };
+ }
+
+ return utilsServiceFactory;
+});
View
235 src/main/webapp/index.html
@@ -1,228 +1,39 @@
<!DOCTYPE html>
<html ng-app="rylc">
<head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
- <link rel="stylesheet" href="lib/jquery.mobile.css">
- <link rel="stylesheet" href="rylc.css">
+ <!-- make page testable for jasmine ui -->
+ <script type="text/javascript">eval(sessionStorage.jasmineui);</script>
- <!-- make page testable for jasmine ui -->
- <script type="text/javascript">eval(sessionStorage.jasmineui);</script>
+ <link rel="stylesheet" href="main.css">
+ <script src="lib/require.js" data-main="main"></script>
- <!-- Libraries -->
- <script src="lib/JSONParseDate.js"></script>
- <script src="lib/Base64.js"></script>
- <script src="lib/jquery.js"></script>
- <script src="lib/jquery.mobile.js"></script>
- <script src="lib/angular.js"></script>
- <script src="lib/jquery-mobile-angular-adapter.js"></script>
-
- <!-- Application -->
- <script src="app/services.js"></script>
- <script src="app/controllers.js"></script>
- <script src="app/markup.js"></script>
- <script src="app/application.js"></script>
-
- <title>RYLC</title>
+ <title>RYLC</title>
</head>
<body>
<div id="loginPage" data-role="page" ngm-shared-controller="auth:rylc.AuthenticationController">
- <div data-role="header">
- <h1>RYLC - Login</h1>
- </div>
- <div data-role="content">
- <div class="error" ngm-if="auth.errorMessage">{{auth.errorMessage}}</div>
- <form data-ajax="false" ng-submit="auth.login()" name="loginForm">
- <div data-role="fieldcontain">
- <label for="loginPage_username">Benutzername</label>
- <input type="text" ng-model="auth.username" id="loginPage_username">
- </div>
- <div data-role="fieldcontain">
- <label for="loginPage_password">Passwort</label>
- <input type="password" ng-model="auth.password" id="loginPage_password">
- </div>
- <input type="submit" class="login" value="Login" ng-disabled="!auth.loginPossible()">
- </form>
- </div>
-</div>
-
-<div id="welcomePage" data-role="page"
- ngm-shared-controller="rental:rylc.RentalController,auth:rylc.AuthenticationController"
- ngm-pagebeforeshow="rental.prefetchMasterData()" ngm-pagebeforehide="rental.clearMessages()">
- <div data-role="header">
- <h1 class="greeting">Hallo {{auth.customer().name}}</h1>
- <a href="" ngm-click="auth.logout()" class="logout ui-btn-right">Logout</a>
- </div>
- <div data-role="content">
- <div class="error" ngm-if="rental.errorMessage">{{rental.errorMessage}}</div>
- <div class="success" ngm-if="rental.successMessage">{{rental.successMessage}}</div>
- <a href="" data-role="button" class="newRental" ngm-click="rental.initRental()">Neue Buchung</a>
- <a href="#rentalHistoryPage" data-role="button" class="rentalHistory">Buchungen</a>
- <a href="#profilePage" data-role="button" class="profile">Benutzerprofil</a>
- </div>
-</div>
-
-<div id="profilePage" data-role="page" ngm-shared-controller="auth:rylc.AuthenticationController">
- <div data-role="header">
- <h1>Profil von {{auth.customer().name}}</h1>
- <a href="" data-rel="back" class="back">Zur&uuml;ck</a>
- <a href="" ngm-click="auth.logout()" class="logout ui-btn-right">Logout</a>
- </div>
- <div data-role="content">
- <div data-role="fieldcontain">
- <label for="profilePage_username">Benutzername</label>
- <input type="text" ng-model="auth.username" id="profilePage_username" disabled="disabled">
- </div>
- <div data-role="fieldcontain">
- <label for="profilePage_name">Name</label>
- <input type="text" ng-model="auth.customer().name" id="profilePage_name" disabled="disabled">
+ <div data-role="header">
+ <h1>RYLC - Login</h1>
+ </div>
+ <div data-role="content">
+ <div class="error" ngm-if="auth.errorMessage">{{auth.errorMessage}}</div>
+ <form data-ajax="false" ng-submit="auth.login()" name="loginForm">
+ <div data-role="fieldcontain">
+ <label for="loginPage_username">Benutzername</label>
+ <input type="text" ng-model="auth.username" id="loginPage_username">
+ </div>
+ <div data-role="fieldcontain">
+ <label for="loginPage_password">Passwort</label>
+ <input type="password" ng-model="auth.password" id="loginPage_password">
+ </div>
+ <input type="submit" class="login" value="Login" ng-disabled="!auth.loginPossible()">
+ </form>
</div>
- <div data-role="fieldcontain">
- <label for="profilePage_street">Stra&szlig;e</label>
- <input type="text" ng-model="auth.customer().street" id="profilePage_street" disabled="disabled">
- </div>
- <div data-role="fieldcontain">
- <label for="profilePage_zip">PLZ</label>
- <input type="text" ng-model="auth.customer().zip" id="profilePage_zip" disabled="disabled">
- </div>
- <div data-role="fieldcontain">
- <label for="profilePage_city">Ort</label>
- <input type="text" ng-model="auth.customer().city" id="profilePage_city" disabled="disabled">
- </div>
- <div data-role="fieldcontain">
- <label for="profilePage_email">E-Mail</label>
- <input type="text" ng-model="auth.customer().email" id="profilePage_email" disabled="disabled">
- </div>
- </div>
</div>
-<div id="rentalHistoryPage" data-role="page" ng-controller="rylc.RentalHistoryController"
- ngm-shared-controller="auth:rylc.AuthenticationController"
- ngm-pagebeforeshow="searchRentals()">
- <div data-role="header">
- <h1>Buchungen von {{auth.customer().name}}</h1>
- <a href="" data-rel="back" class="back">Zur&uuml;ck</a>
- <a href="" ngm-click="auth.logout()" class="logout ui-btn-right">Logout</a>
- </div>
- <div data-role="content">
- <div ngm-if="errorMessage" class="error">{{errorMessage}}</div>
- <div ngm-if="infoMessage()" class="info">{{infoMessage()}}</div>
- <ul data-role="listview" id="rentalHistoryPage_list">
- <li ng-repeat="rental in rentals | paged:{pageSize:5}" class="rental">
- <h4><span class="manufacturer">{{rental.car.manufacturer}}</span> <span class="description">{{rental.car.description}}</span></h4>
- <p><span class="startDate">{{rental.hireStartDate | simpleDate}}</span> - <span class="endDate">{{rental.hireEndDate | simpleDate}}</span></p>
- <p class="ui-li-aside">Total: <span class="totalPrice">{{totalPrice(rental) | currency:"&euro;"}}</span></p>
- </li>
- <li ngm-if="rentals | paged:'hasMore'">
- <a href="#" ngm-click="rentals | paged:'loadMore'">Mehr...</a>
- </li>
- </ul>
- </div>
-</div>
-
-<div id="rental1Page" data-role="page"
- ngm-shared-controller="rental:rylc.RentalController,auth:rylc.AuthenticationController"
- ngm-pagebeforehide="rental.clearMessages()">
- <div data-role="header">
- <h1>PKW suchen</h1>
- <a href="" data-rel="back" class="back">Zur&uuml;ck</a>
- <a href="" ngm-click="auth.logout()" class="logout ui-btn-right">Logout</a>
- </div>
- <div data-role="content" ng-form name="rentalForm">
- <div class="error" ngm-if="rental.errorMessage">{{rental.errorMessage}}</div>
- <div data-role="fieldcontain">
- <label for="rental1Page_city">Ort</label>
- <select id="rental1Page_city" ng-model="rental.city" ng-options="c.name for c in rental.cities"
- data-native-menu="true"
- data-theme="b" class="selectedCity">
- </select>
- </div>
- <div data-role="fieldcontain">
- <label for="rental1Page_startDate">von</label>
- <input type="date" id="rental1Page_startDate" class="startDate" ng-model="rental.startDate" ng-required="true">
- </div>
- <div data-role="fieldcontain">
- <label for="rental1Page_endDate">bis</label>
- <input type="date" id="rental1Page_endDate" class="endDate" ng-model="rental.endDate" ng-required="true">
- </div>
- <div data-role="fieldcontain">
- <label for="rental1Page_maxPrice">max. Preis</label>
- <input type="number" id="rental1Page_maxPrice" class="maxPrice" ng-model="rental.maxPrice" ng-required="true">
- </div>
- <button class="search" ng-disabled="!rentalForm.$valid"
- ngm-click="rental.searchAvailableCars()"
- data-role="button">Verf&uuml;gbare Autos ermitteln
- </button>
- </div>
-</div>
-
-<div id="rental2Page" data-role="page"
- ngm-shared-controller="rental:rylc.RentalController,auth:rylc.AuthenticationController"
- ngm-pagebeforehide="rental.clearMessages()">
- <div data-role="header">
- <h1>Autotyp w&auml;hlen</h1>
- <a href="" data-rel="back" class="back">Zur&uuml;ck</a>
- <a href="" ngm-click="auth.logout()" class="logout ui-btn-right">Logout</a>
- </div>
- <div data-role="content">
- <div class="error" ngm-if="rental.errorMessage">{{rental.errorMessage}}</div>
- <select id="rental2Page_carTypes" ng-model="rental.carType" ng-options="carType for carType in rental.carTypes"
- data-native-menu="true" data-theme="b" class="selectedCarType">
- </select>
- <ul data-role="listview" data-inset="true" id="rental2Page_list">
- <li ng-repeat="car in rental.availableCars | filter:rental.carType" class="car">
- <a href="#" ngm-click="rental.selectCar(car)"
- class="selectCar">
- <span class="carManufacturer">{{car.manufacturer}}</span> <span
- class="carDescription">{{car.description}}</span>
- - <span class="carPrice">{{car.price | currency:"&euro;"}}</span>
- </a>
- </li>
- </ul>
- </div>
-</div>
-
-<div id="rental3Page" data-role="page"
- ngm-shared-controller="rental:rylc.RentalController,auth:rylc.AuthenticationController">
- <div data-role="header">
- <h1>Best&auml;tigen</h1>
- <a href="" data-rel="back" class="back">Zur&uuml;ck</a>
- <a href="" ngm-click="auth.logout()" class="logout ui-btn-right">Logout</a>
- </div>
- <div data-role="content">
-
- <h1><span class="carManufacturer">{{rental.car.manufacturer}}</span> <span class="carDescription">{{rental.car.description}}</span>
- </h1>
-
- <table>
- <tr>
- <td>Art:</td>
- <td class="carType">{{rental.car.carType}}</td>
- </tr>
- <tr>
- <td>Ort:</td>
- <td class="city">{{rental.city.name}}</td>
- </tr>
- <tr>
- <td>Preis:</td>
- <td class="carPrice">{{rental.car.price | currency:"&euro;"}}</td>
- </tr>
- </table>
-
- <h3><span class="startDate">{{rental.startDate | simpleDate}}</span> - <span class="endDate">{{rental.endDate | simpleDate}}</span>
- </h3>
-
- <h3>Total: <span class="totalPrice">{{rental.totalPrice() | currency:"&euro;"}}</span></h3>
-
- <p>
- <a href="#" class="rentCar"
- ngm-click="rental.rentCar()"
- data-role="button">Ausleihen</a>
- </p>
- </div>
-</div>
</body>
</html>
View
277 src/main/webapp/lib/almond.js
@@ -0,0 +1,277 @@
+/**
+ * almond 0.0.3 Copyright (c) 2011, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/almond for details
+ */
+/*jslint strict: false, plusplus: false */
+/*global setTimeout: false */
+
+var requirejs, require, define;
+(function (undef) {
+
+ var defined = {},
+ waiting = {},
+ aps = [].slice,
+ main, req;
+
+ if (typeof define === "function") {
+ //If a define is already in play via another AMD loader,
+ //do not overwrite.
+ return;
+ }
+
+ /**
+ * Given a relative module name, like ./something, normalize it to
+ * a real name that can be mapped to a path.
+ * @param {String} name the relative name
+ * @param {String} baseName a real name that the name arg is relative
+ * to.
+ * @returns {String} normalized name
+ */
+ function normalize(name, baseName) {
+ //Adjust any relative paths.
+ if (name && name.charAt(0) === ".") {
+ //If have a base name, try to normalize against it,
+ //otherwise, assume it is a top-level require that will
+ //be relative to baseUrl in the end.
+ if (baseName) {
+ //Convert baseName to array, and lop off the last part,
+ //so that . matches that "directory" and not name of the baseName's
+ //module. For instance, baseName of "one/two/three", maps to
+ //"one/two/three.js", but we want the directory, "one/two" for
+ //this normalization.
+ baseName = baseName.split("/");
+ baseName = baseName.slice(0, baseName.length - 1);
+
+ name = baseName.concat(name.split("/"));
+
+ //start trimDots
+ var i, part;
+ for (i = 0; (part = name[i]); i++) {
+ if (part === ".") {
+ name.splice(i, 1);
+ i -= 1;
+ } else if (part === "..") {
+ if (i === 1 && (name[2] === '..' || name[0] === '..')) {
+ //End of the line. Keep at least one non-dot
+ //path segment at the front so it can be mapped
+ //correctly to disk. Otherwise, there is likely
+ //no path mapping for a path starting with '..'.
+ //This can still fail, but catches the most reasonable
+ //uses of ..
+ break;
+ } else if (i > 0) {
+ name.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+ //end trimDots
+
+ name = name.join("/");
+ }
+ }
+ return name;
+ }
+
+ function makeRequire(relName, forceSync) {
+ return function () {
+ //A version of a require function that passes a moduleName
+ //value for items that may need to
+ //look up paths relative to the moduleName
+ return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
+ };
+ }
+
+ function makeNormalize(relName) {
+ return function (name) {
+ return normalize(name, relName);
+ };
+ }
+
+ function makeLoad(depName) {
+ return function (value) {
+ defined[depName] = value;
+ };
+ }
+
+ function callDep(name) {
+ if (waiting.hasOwnProperty(name)) {
+ var args = waiting[name];
+ delete waiting[name];
+ main.apply(undef, args);
+ }
+ return defined[name];
+ }
+
+ /**
+ * Makes a name map, normalizing the name, and using a plugin
+ * for normalization if necessary. Grabs a ref to plugin
+ * too, as an optimization.
+ */
+ function makeMap(name, relName) {
+ var prefix, plugin,
+ index = name.indexOf('!');
+
+ if (index !== -1) {
+ prefix = normalize(name.slice(0, index), relName);
+ name = name.slice(index + 1);
+ plugin = callDep(prefix);
+
+ //Normalize according
+ if (plugin && plugin.normalize) {
+ name = plugin.normalize(name, makeNormalize(relName));
+ } else {
+ name = normalize(name, relName);
+ }
+ } else {
+ name = normalize(name, relName);
+ }
+
+ //Using ridiculous property names for space reasons
+ return {
+ f: prefix ? prefix + '!' + name : name, //fullName
+ n: name,
+ p: plugin
+ };
+ }
+
+ main = function (name, deps, callback, relName) {
+ var args = [],
+ usingExports,
+ cjsModule, depName, i, ret, map;
+
+ //Use name if no relName
+ if (!relName) {
+ relName = name;
+ }
+
+ //Call the callback to define the module, if necessary.
+ if (typeof callback === 'function') {
+
+ //Default to require, exports, module if no deps if
+ //the factory arg has any arguments specified.
+ if (!deps.length && callback.length) {
+ deps = ['require', 'exports', 'module'];
+ }
+
+ //Pull out the defined dependencies and pass the ordered
+ //values to the callback.
+ for (i = 0; i < deps.length; i++) {
+ map = makeMap(deps[i], relName);
+ depName = map.f;
+
+ //Fast path CommonJS standard dependencies.
+ if (depName === "require") {
+ args[i] = makeRequire(name);
+ } else if (depName === "exports") {
+ //CommonJS module spec 1.1
+ args[i] = defined[name] = {};
+ usingExports = true;
+ } else if (depName === "module") {
+ //CommonJS module spec 1.1
+ cjsModule = args[i] = {
+ id: name,
+ uri: '',
+ exports: defined[name]
+ };
+ } else if (defined.hasOwnProperty(depName) || waiting.hasOwnProperty(depName)) {
+ args[i] = callDep(depName);
+ } else if (map.p) {
+ map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
+ args[i] = defined[depName];
+ } else {
+ throw name + ' missing ' + depName;
+ }
+ }
+
+ ret = callback.apply(defined[name], args);
+
+ if (name) {
+ //If setting exports via "module" is in play,
+ //favor that over return value and exports. After that,
+ //favor a non-undefined return value over exports use.
+ if (cjsModule && cjsModule.exports !== undef) {
+ defined[name] = cjsModule.exports;
+ } else if (!usingExports) {
+ //Use the return value from the function.
+ defined[name] = ret;
+ }
+ }
+ } else if (name) {
+ //May just be an object definition for the module. Only
+ //worry about defining if have a module name.
+ defined[name] = callback;
+ }
+ };
+
+ requirejs = req = function (deps, callback, relName, forceSync) {
+ if (typeof deps === "string") {
+
+ //Just return the module wanted. In this scenario, the
+ //deps arg is the module name, and second arg (if passed)
+ //is just the relName.
+ //Normalize module name, if it contains . or ..
+ return callDep(makeMap(deps, callback).f);
+ } else if (!deps.splice) {
+ //deps is a config object, not an array.
+ //Drop the config stuff on the ground.
+ if (callback.splice) {
+ //callback is an array, which means it is a dependency list.
+ //Adjust args if there are dependencies
+ deps = callback;
+ callback = arguments[2];
+ } else {
+ deps = [];
+ }
+ }
+
+ //Simulate async callback;
+ if (forceSync) {
+ main(undef, deps, callback, relName);
+ } else {
+ setTimeout(function () {
+ main(undef, deps, callback, relName);
+ }, 15);
+ }
+
+ return req;
+ };
+
+ /**
+ * Just drops the config on the floor, but returns req in case
+ * the config return value is used.
+ */
+ req.config = function () {
+ return req;
+ };
+
+ /**
+ * Export require as a global, but only if it does not already exist.
+ */
+ if (!require) {
+ require = req;
+ }
+
+ define = function (name, deps, callback) {
+
+ //This module may not have dependencies
+ if (!deps.splice) {
+ //deps is not an array, so probably means
+ //an object literal or factory function for
+ //the value. Adjust args.
+ callback = deps;
+ deps = [];
+ }
+
+ if (define.unordered) {
+ waiting[name] = [name, deps, callback];
+ } else {
+ main(name, deps, callback);
+ }
+ };
+
+ define.amd = {
+ jQuery: true
+ };
+}());
View
189 src/main/webapp/lib/order.js
@@ -0,0 +1,189 @@
+/**
+ * @license RequireJS order 1.0.5 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/requirejs for details
+ */
+/*jslint nomen: false, plusplus: false, strict: false */
+/*global require: false, define: false, window: false, document: false,
+ setTimeout: false */
+
+//Specify that requirejs optimizer should wrap this code in a closure that
+//maps the namespaced requirejs API to non-namespaced local variables.
+/*requirejs namespace: true */
+
+(function () {
+
+ //Sadly necessary browser inference due to differences in the way
+ //that browsers load and execute dynamically inserted javascript
+ //and whether the script/cache method works when ordered execution is
+ //desired. Currently, Gecko and Opera do not load/fire onload for scripts with
+ //type="script/cache" but they execute injected scripts in order
+ //unless the 'async' flag is present.
+ //However, this is all changing in latest browsers implementing HTML5
+ //spec. With compliant browsers .async true by default, and
+ //if false, then it will execute in order. Favor that test first for forward
+ //compatibility.
+ var testScript = typeof document !== "undefined" &&
+ typeof window !== "undefined" &&
+ document.createElement("script"),
+
+ supportsInOrderExecution = testScript && (testScript.async ||
+ ((window.opera &&
+ Object.prototype.toString.call(window.opera) === "[object Opera]") ||
+ //If Firefox 2 does not have to be supported, then
+ //a better check may be:
+ //('mozIsLocallyAvailable' in window.navigator)
+ ("MozAppearance" in document.documentElement.style))),
+
+ //This test is true for IE browsers, which will load scripts but only
+ //execute them once the script is added to the DOM.
+ supportsLoadSeparateFromExecute = testScript &&
+ testScript.readyState === 'uninitialized',
+
+ readyRegExp = /^(complete|loaded)$/,
+ cacheWaiting = [],
+ cached = {},
+ scriptNodes = {},
+ scriptWaiting = [];
+
+ //Done with the test script.
+ testScript = null;
+
+ //Callback used by the type="script/cache" callback that indicates a script
+ //has finished downloading.
+ function scriptCacheCallback(evt) {
+ var node = evt.currentTarget || evt.srcElement, i,
+ moduleName, resource;
+
+ if (evt.type === "load" || readyRegExp.test(node.readyState)) {
+ //Pull out the name of the module and the context.
+ moduleName = node.getAttribute("data-requiremodule");
+
+ //Mark this cache request as loaded
+ cached[moduleName] = true;
+
+ //Find out how many ordered modules have loaded
+ for (i = 0; (resource = cacheWaiting[i]); i++) {
+ if (cached[resource.name]) {
+ resource.req([resource.name], resource.onLoad);
+ } else {
+ //Something in the ordered list is not loaded,
+ //so wait.
+ break;
+ }
+ }
+
+ //If just loaded some items, remove them from cacheWaiting.
+ if (i > 0) {
+ cacheWaiting.splice(0, i);
+ }
+
+ //Remove this script tag from the DOM
+ //Use a setTimeout for cleanup because some older IE versions vomit
+ //if removing a script node while it is being evaluated.
+ setTimeout(function () {
+ node.parentNode.removeChild(node);
+ }, 15);
+ }
+ }
+
+ /**
+ * Used for the IE case, where fetching is done by creating script element
+ * but not attaching it to the DOM. This function will be called when that
+ * happens so it can be determined when the node can be attached to the
+ * DOM to trigger its execution.
+ */
+ function onFetchOnly(node) {
+ var i, loadedNode, resourceName;
+
+ //Mark this script as loaded.
+ node.setAttribute('data-orderloaded', 'loaded');
+
+ //Cycle through waiting scripts. If the matching node for them
+ //is loaded, and is in the right order, add it to the DOM
+ //to execute the script.
+ for (i = 0; (resourceName = scriptWaiting[i]); i++) {
+ loadedNode = scriptNodes[resourceName];
+ if (loadedNode &&
+ loadedNode.getAttribute('data-orderloaded') === 'loaded') {
+ delete scriptNodes[resourceName];
+ require.addScriptToDom(loadedNode);
+ } else {
+ break;
+ }
+ }
+
+ //If just loaded some items, remove them from waiting.
+ if (i > 0) {
+ scriptWaiting.splice(0, i);
+ }
+ }
+
+ define({
+ version: '1.0.5',
+
+ load: function (name, req, onLoad, config) {
+ var hasToUrl = !!req.nameToUrl,
+ url, node, context;
+
+ //If no nameToUrl, then probably a build with a loader that
+ //does not support it, and all modules are inlined.
+ if (!hasToUrl) {
+ req([name], onLoad);
+ return;
+ }
+
+ url = req.nameToUrl(name, null);
+
+ //Make sure the async attribute is not set for any pathway involving
+ //this script.
+ require.s.skipAsync[url] = true;
+ if (supportsInOrderExecution || config.isBuild) {
+ //Just a normal script tag append, but without async attribute
+ //on the script.
+ req([name], onLoad);
+ } else if (supportsLoadSeparateFromExecute) {
+ //Just fetch the URL, but do not execute it yet. The
+ //non-standards IE case. Really not so nice because it is
+ //assuming and touching requrejs internals. OK though since
+ //ordered execution should go away after a long while.
+ context = require.s.contexts._;
+
+ if (!context.urlFetched[url] && !context.loaded[name]) {
+ //Indicate the script is being fetched.
+ context.urlFetched[url] = true;
+
+ //Stuff from require.load
+ require.resourcesReady(false);
+ context.scriptCount += 1;
+
+ //Fetch the script now, remember it.
+ node = require.attach(url, context, name, null, null, onFetchOnly);
+ scriptNodes[name] = node;
+ scriptWaiting.push(name);
+ }
+
+ //Do a normal require for it, once it loads, use it as return
+ //value.
+ req([name], onLoad);
+ } else {
+ //Credit to LABjs author Kyle Simpson for finding that scripts
+ //with type="script/cache" allow scripts to be downloaded into
+ //browser cache but not executed. Use that
+ //so that subsequent addition of a real type="text/javascript"
+ //tag will cause the scripts to be executed immediately in the
+ //correct order.
+ if (req.specified(name)) {
+ req([name], onLoad);
+ } else {
+ cacheWaiting.push({
+ name: name,
+ req: req,
+ onLoad: onLoad
+ });
+ require.attach(url, null, name, scriptCacheCallback, "script/cache");
+ }
+ }
+ }
+ });
+}());
View
2,053 src/main/webapp/lib/require.js
@@ -0,0 +1,2053 @@
+/** vim: et:ts=4:sw=4:sts=4
+ * @license RequireJS 1.0.7 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/requirejs for details
+ */
+/*jslint strict: false, plusplus: false, sub: true */
+/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */
+
+var requirejs, require, define;
+(function () {
+ //Change this version number for each release.
+ var version = "1.0.7",
+ commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
+ cjsRequireRegExp = /require\(\s*["']([^'"\s]+)["']\s*\)/g,
+ currDirRegExp = /^\.\//,
+ jsSuffixRegExp = /\.js$/,
+ ostring = Object.prototype.toString,
+ ap = Array.prototype,
+ aps = ap.slice,
+ apsp = ap.splice,
+ isBrowser = !!(typeof window !== "undefined" && navigator && document),
+ isWebWorker = !isBrowser && typeof importScripts !== "undefined",
+ //PS3 indicates loaded and complete, but need to wait for complete
+ //specifically. Sequence is "loading", "loaded", execution,
+ // then "complete". The UA check is unfortunate, but not sure how
+ //to feature test w/o causing perf issues.
+ readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
+ /^complete$/ : /^(complete|loaded)$/,
+ defContextName = "_",
+ //Oh the tragedy, detecting opera. See the usage of isOpera for reason.
+ isOpera = typeof opera !== "undefined" && opera.toString() === "[object Opera]",
+ empty = {},
+ contexts = {},
+ globalDefQueue = [],
+ interactiveScript = null,
+ checkLoadedDepth = 0,
+ useInteractive = false,
+ reservedDependencies = {
+ require: true,
+ module: true,
+ exports: true
+ },
+ req, cfg = {}, currentlyAddingScript, s, head, baseElement, scripts, script,
+ src, subPath, mainScript, dataMain, globalI, ctx, jQueryCheck, checkLoadedTimeoutId;
+
+ function isFunction(it) {
+ return ostring.call(it) === "[object Function]";
+ }
+
+ function isArray(it) {
+ return ostring.call(it) === "[object Array]";
+ }
+
+ /**
+ * Simple function to mix in properties from source into target,
+ * but only if target does not already have a property of the same name.
+ * This is not robust in IE for transferring methods that match
+ * Object.prototype names, but the uses of mixin here seem unlikely to
+ * trigger a problem related to that.
+ */
+ function mixin(target, source, force) {
+ for (var prop in source) {
+ if (!(prop in empty) && (!(prop in target) || force)) {
+ target[prop] = source[prop];
+ }
+ }
+ return req;
+ }
+
+ /**
+ * Constructs an error with a pointer to an URL with more information.
+ * @param {String} id the error ID that maps to an ID on a web page.
+ * @param {String} message human readable error.
+ * @param {Error} [err] the original error, if there is one.
+ *
+ * @returns {Error}
+ */
+ function makeError(id, msg, err) {
+ var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
+ if (err) {
+ e.originalError = err;
+ }
+ return e;
+ }
+
+ /**
+ * Used to set up package paths from a packagePaths or packages config object.
+ * @param {Object} pkgs the object to store the new package config
+ * @param {Array} currentPackages an array of packages to configure
+ * @param {String} [dir] a prefix dir to use.
+ */
+ function configurePackageDir(pkgs, currentPackages, dir) {
+ var i, location, pkgObj;
+
+ for (i = 0; (pkgObj = currentPackages[i]); i++) {
+ pkgObj = typeof pkgObj === "string" ? { name: pkgObj } : pkgObj;
+ location = pkgObj.location;
+
+ //Add dir to the path, but avoid paths that start with a slash
+ //or have a colon (indicates a protocol)
+ if (dir && (!location || (location.indexOf("/") !== 0 && location.indexOf(":") === -1))) {
+ location = dir + "/" + (location || pkgObj.name);
+ }
+
+ //Create a brand new object on pkgs, since currentPackages can
+ //be passed in again, and config.pkgs is the internal transformed
+ //state for all package configs.
+ pkgs[pkgObj.name] = {
+ name: pkgObj.name,
+ location: location || pkgObj.name,
+ //Remove leading dot in main, so main paths are normalized,
+ //and remove any trailing .js, since different package
+ //envs have different conventions: some use a module name,
+ //some use a file name.
+ main: (pkgObj.main || "main")
+ .replace(currDirRegExp, '')
+ .replace(jsSuffixRegExp, '')
+ };
+ }
+ }
+
+ /**
+ * jQuery 1.4.3-1.5.x use a readyWait/ready() pairing to hold DOM
+ * ready callbacks, but jQuery 1.6 supports a holdReady() API instead.
+ * At some point remove the readyWait/ready() support and just stick
+ * with using holdReady.
+ */
+ function jQueryHoldReady($, shouldHold) {
+ if ($.holdReady) {
+ $.holdReady(shouldHold);
+ } else if (shouldHold) {
+ $.readyWait += 1;
+ } else {
+ $.ready(true);
+ }
+ }
+
+ if (typeof define !== "undefined") {
+ //If a define is already in play via another AMD loader,
+ //do not overwrite.
+ return;
+ }
+
+ if (typeof requirejs !== "undefined") {
+ if (isFunction(requirejs)) {
+ //Do not overwrite and existing requirejs instance.
+ return;
+ } else {
+ cfg = requirejs;
+ requirejs = undefined;
+ }
+ }
+
+ //Allow for a require config object
+ if (typeof require !== "undefined" && !isFunction(require)) {
+ //assume it is a config object.
+ cfg = require;
+ require = undefined;
+ }
+
+ /**
+ * Creates a new context for use in require and define calls.
+ * Handle most of the heavy lifting. Do not want to use an object
+ * with prototype here to avoid using "this" in require, in case it
+ * needs to be used in more super secure envs that do not want this.
+ * Also there should not be that many contexts in the page. Usually just
+ * one for the default context, but could be extra for multiversion cases
+ * or if a package needs a special context for a dependency that conflicts
+ * with the standard context.
+ */
+ function newContext(contextName) {
+ var context, resume,
+ config = {
+ waitSeconds: 7,
+ baseUrl: "./",
+ paths: {},
+ pkgs: {},
+ catchError: {}
+ },
+ defQueue = [],
+ specified = {
+ "require": true,
+ "exports": true,
+ "module": true
+ },
+ urlMap = {},
+ defined = {},
+ loaded = {},
+ waiting = {},
+ waitAry = [],
+ urlFetched = {},
+ managerCounter = 0,
+ managerCallbacks = {},
+ plugins = {},
+ //Used to indicate which modules in a build scenario
+ //need to be full executed.
+ needFullExec = {},
+ fullExec = {},
+ resumeDepth = 0;
+
+ /**
+ * Trims the . and .. from an array of path segments.
+ * It will keep a leading path segment if a .. will become
+ * the first path segment, to help with module name lookups,
+ * which act like paths, but can be remapped. But the end result,
+ * all paths that use this function should look normalized.
+ * NOTE: this method MODIFIES the input array.
+ * @param {Array} ary the array of path segments.
+ */
+ function trimDots(ary) {
+ var i, part;
+ for (i = 0; (part = ary[i]); i++) {
+ if (part === ".") {
+ ary.splice(i, 1);
+ i -= 1;
+ } else if (part === "..") {
+ if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
+ //End of the line. Keep at least one non-dot
+ //path segment at the front so it can be mapped
+ //correctly to disk. Otherwise, there is likely
+ //no path mapping for a path starting with '..'.
+ //This can still fail, but catches the most reasonable
+ //uses of ..
+ break;
+ } else if (i > 0) {
+ ary.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Given a relative module name, like ./something, normalize it to
+ * a real name that can be mapped to a path.
+ * @param {String} name the relative name
+ * @param {String} baseName a real name that the name arg is relative
+ * to.
+ * @returns {String} normalized name
+ */
+ function normalize(name, baseName) {
+ var pkgName, pkgConfig;
+
+ //Adjust any relative paths.
+ if (name && name.charAt(0) === ".") {
+ //If have a base name, try to normalize against it,
+ //otherwise, assume it is a top-level require that will
+ //be relative to baseUrl in the end.
+ if (baseName) {
+ if (config.pkgs[baseName]) {
+ //If the baseName is a package name, then just treat it as one
+ //name to concat the name with.
+ baseName = [baseName];
+ } else {
+ //Convert baseName to array, and lop off the last part,
+ //so that . matches that "directory" and not name of the baseName's
+ //module. For instance, baseName of "one/two/three", maps to
+ //"one/two/three.js", but we want the directory, "one/two" for
+ //this normalization.
+ baseName = baseName.split("/");
+ baseName = baseName.slice(0, baseName.length - 1);
+ }
+
+ name = baseName.concat(name.split("/"));
+ trimDots(name);
+
+ //Some use of packages may use a . path to reference the
+ //"main" module name, so normalize for that.
+ pkgConfig = config.pkgs[(pkgName = name[0])];
+ name = name.join("/");
+ if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
+ name = pkgName;
+ }
+ } else if (name.indexOf("./") === 0) {
+ // No baseName, so this is ID is resolved relative
+ // to baseUrl, pull off the leading dot.
+ name = name.substring(2);
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Creates a module mapping that includes plugin prefix, module
+ * name, and path. If parentModuleMap is provided it will
+ * also normalize the name via require.normalize()
+ *
+ * @param {String} name the module name
+ * @param {String} [parentModuleMap] parent module map
+ * for the module name, used to resolve relative names.
+ *
+ * @returns {Object}
+ */
+ function makeModuleMap(name, parentModuleMap) {
+ var index = name ? name.indexOf("!") : -1,
+ prefix = null,
+ parentName = parentModuleMap ? parentModuleMap.name : null,
+ originalName = name,
+ normalizedName, url, pluginModule;
+
+ if (index !== -1) {
+ prefix = name.substring(0, index);
+ name = name.substring(index + 1, name.length);
+ }
+
+ if (prefix) {
+ prefix = normalize(prefix, parentName);
+ }
+
+ //Account for relative paths if there is a base name.
+ if (name) {
+ if (prefix) {
+ pluginModule = defined[prefix];
+ if (pluginModule && pluginModule.normalize) {
+ //Plugin is loaded, use its normalize method.
+ normalizedName = pluginModule.normalize(name, function (name) {
+ return normalize(name, parentName);
+ });
+ } else {
+ normalizedName = normalize(name, parentName);
+ }
+ } else {
+ //A regular module.
+ normalizedName = normalize(name, parentName);
+
+ url = urlMap[normalizedName];
+ if (!url) {
+ //Calculate url for the module, if it has a name.
+ //Use name here since nameToUrl also calls normalize,
+ //and for relative names that are outside the baseUrl
+ //this causes havoc. Was thinking of just removing
+ //parentModuleMap to avoid extra normalization, but
+ //normalize() still does a dot removal because of
+ //issue #142, so just pass in name here and redo
+ //the normalization. Paths outside baseUrl are just
+ //messy to support.
+ url = context.nameToUrl(name, null, parentModuleMap);
+
+ //Store the URL mapping for later.
+ urlMap[normalizedName] = url;
+ }
+ }
+ }
+
+ return {
+ prefix: prefix,
+ name: normalizedName,
+ parentMap: parentModuleMap,
+ url: url,
+ originalName: originalName,
+ fullName: prefix ? prefix + "!" + (normalizedName || '') : normalizedName
+ };
+ }
+
+ /**
+ * Determine if priority loading is done. If so clear the priorityWait
+ */
+ function isPriorityDone() {
+ var priorityDone = true,
+ priorityWait = config.priorityWait,
+ priorityName, i;
+ if (priorityWait) {
+ for (i = 0; (priorityName = priorityWait[i]); i++) {
+ if (!loaded[priorityName]) {
+ priorityDone = false;
+ break;
+ }
+ }
+ if (priorityDone) {
+ delete config.priorityWait;
+ }
+ }
+ return priorityDone;
+ }