mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-17 21:35:50 +00:00
LH :: Refreshed js library files to angular version 1.3.11
- Added ngResource
This commit is contained in:
parent
756e74f7cc
commit
b8f97b53b9
24 changed files with 10537 additions and 13822 deletions
|
@ -38,12 +38,13 @@
|
||||||
|
|
||||||
<!-- build:js({app,.tmp}) scripts/main.js -->
|
<!-- build:js({app,.tmp}) scripts/main.js -->
|
||||||
<script src="js/lib/angular.js"></script>
|
<script src="js/lib/angular.js"></script>
|
||||||
|
<script src="js/lib/angular-resource.js"></script>
|
||||||
<script src="js/lib/angular-ui-router.js"></script>
|
<script src="js/lib/angular-ui-router.js"></script>
|
||||||
<script src="js/lib/angular-ui-router-statehelper.js"></script>
|
<script src="js/lib/angular-ui-router-statehelper.js"></script>
|
||||||
<script src="js/lib/angular-animate.js"></script>
|
<script src="js/lib/angular-animate.js"></script>
|
||||||
<script src="js/lib/angular-cookies.js"></script>
|
<script src="js/lib/angular-cookies.js"></script>
|
||||||
|
<script src="services/services.js"></script>
|
||||||
<script src="components/main/MainController.js"></script>
|
<script src="components/main/MainController.js"></script>
|
||||||
<script src="components/about/AboutController.js"></script>
|
|
||||||
<script src="components/veterinarians/VeterinarianController.js"></script>
|
<script src="components/veterinarians/VeterinarianController.js"></script>
|
||||||
<script src="components/pets/PetController.js"></script>
|
<script src="components/pets/PetController.js"></script>
|
||||||
<script src="components/owners/OwnerController.js"></script>
|
<script src="components/owners/OwnerController.js"></script>
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
var AboutController = function($scope) {
|
|
||||||
};
|
|
||||||
|
|
||||||
var AboutControllerDeclaration = ['$scope',AboutController];
|
|
|
@ -1 +0,0 @@
|
||||||
<section data-ng-controller="AboutController">This is the about page</section>
|
|
|
@ -14,7 +14,7 @@
|
||||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||||
<div id="nav-menu" class="navbar-collapse navbar-left collapse">
|
<div id="nav-menu" class="navbar-collapse navbar-left collapse">
|
||||||
<ul class="nav navbar-nav navbar-menu" data-ng-if="getSession() != null">
|
<ul class="nav navbar-nav navbar-menu" data-ng-if="getSession() != null">
|
||||||
<li data-ng-class="$state.is('landing') ? 'active' : ''"><a data-ui-sref="about">Home</a></li>
|
<li data-ng-class="$state.is('landing') ? 'active' : ''"><a data-ui-sref="landing">Home</a></li>
|
||||||
<li data-ng-class="$state.is('pets') ? 'active' : ''"><a data-ui-sref="pets">Pets</a></li>
|
<li data-ng-class="$state.is('pets') ? 'active' : ''"><a data-ui-sref="pets">Pets</a></li>
|
||||||
<li data-ng-class="$state.is('owners') ? 'active' : ''"><a data-ui-sref="owners">Owners</a></li>
|
<li data-ng-class="$state.is('owners') ? 'active' : ''"><a data-ui-sref="owners">Owners</a></li>
|
||||||
<li data-ng-class="$state.is('vets') ? 'active' : ''"><a data-ui-sref="vets">Vets</a></li>
|
<li data-ng-class="$state.is('vets') ? 'active' : ''"><a data-ui-sref="vets">Vets</a></li>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var MainController = function($scope, $rootScope, $state) {
|
var MainController = ['$scope','$rootScope','$state',function($scope, $rootScope, $state) {
|
||||||
|
|
||||||
$scope.getSession = function() {
|
$scope.getSession = function() {
|
||||||
return $scope.session;
|
return $scope.session;
|
||||||
|
@ -37,7 +37,5 @@ var MainController = function($scope, $rootScope, $state) {
|
||||||
$scope.footerText = '© ' + new Date().getFullYear() + ' Pet Clinic, A Spring Framework Demonstration';
|
$scope.footerText = '© ' + new Date().getFullYear() + ' Pet Clinic, A Spring Framework Demonstration';
|
||||||
|
|
||||||
$rootScope.$state = $state;
|
$rootScope.$state = $state;
|
||||||
};
|
}];
|
||||||
|
|
||||||
var MainControllerDeclaration = ['$scope','$rootScope','$state',MainController];
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
var OwnerController = function($scope) {
|
var OwnerController = ['$scope',function($scope) {
|
||||||
$scope.$on('$viewContentLoaded', function(event){
|
$scope.$on('$viewContentLoaded', function(event){
|
||||||
$('html, body').animate({
|
$('html, body').animate({
|
||||||
scrollTop: $("#owners").offset().top
|
scrollTop: $("#owners").offset().top
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
};
|
}];
|
||||||
|
|
||||||
var OwnerControllerDeclaration = ['$scope',OwnerController];
|
|
|
@ -1,4 +1,4 @@
|
||||||
var PetController = function($scope) {
|
var PetController = ['$scope', function($scope) {
|
||||||
|
|
||||||
$scope.$on('$viewContentLoaded', function(event){
|
$scope.$on('$viewContentLoaded', function(event){
|
||||||
$('html, body').animate({
|
$('html, body').animate({
|
||||||
|
@ -6,6 +6,4 @@ var PetController = function($scope) {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
}];
|
||||||
|
|
||||||
var PetControllerDeclaration = ['$scope',PetController];
|
|
|
@ -1,7 +1,4 @@
|
||||||
var VeterinarianController = function ($scope, $http) {
|
var VeterinarianController = ['$scope','$http','Vet', function ($scope, $http, Vet) {
|
||||||
$http.get('api/vets').success(function(data) {
|
|
||||||
$scope.veterinarians = data;
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.$on('$viewContentLoaded', function(event){
|
$scope.$on('$viewContentLoaded', function(event){
|
||||||
$('html, body').animate({
|
$('html, body').animate({
|
||||||
|
@ -9,6 +6,6 @@ var VeterinarianController = function ($scope, $http) {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
$scope.vets = Vet.query();
|
||||||
|
|
||||||
var VeterinarianControllerDeclaration = ['$scope','$http',VeterinarianController];
|
}];
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="col-md-8 col-md-offset-2">
|
<div class="col-md-8 col-md-offset-2">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h3 class="section-heading">Our Veterinarians</h3>
|
<h3 class="section-heading">Our Veterinarians</h3>
|
||||||
<p class="section-desc">A small river named Duden flows by their place and supplies it with the necessary regelialia. It is a paradisematic country, in which roasted parts of sentences fly into your mouth.</p>
|
<p class="section-desc">We provide the best medical expertise and care for your fur kids!</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
var VisitController = function($scope) {
|
var VisitController = ['$scope',function($scope) {
|
||||||
};
|
|
||||||
|
|
||||||
var VisitControllerDeclaration = ['$scope',VisitController];
|
}];
|
|
@ -1,11 +1,4 @@
|
||||||
var app = angular.module('spring-petclinic', ['ui.router','ui.router.stateHelper','ngAnimate','ngCookies']);
|
var app = angular.module('spring-petclinic', ['ui.router','ui.router.stateHelper','ngAnimate','ngCookies','ngResource']);
|
||||||
|
|
||||||
app.controller('MainController', MainControllerDeclaration);
|
|
||||||
app.controller('VeterinarianController', VeterinarianControllerDeclaration);
|
|
||||||
app.controller('PetController', PetControllerDeclaration);
|
|
||||||
app.controller('OwnerController', OwnerControllerDeclaration);
|
|
||||||
app.controller('VisitController', VisitControllerDeclaration);
|
|
||||||
app.controller('AboutController', AboutControllerDeclaration);
|
|
||||||
|
|
||||||
app.config(['stateHelperProvider','$urlRouterProvider','$urlMatcherFactoryProvider',function(stateHelperProvider,$urlRouterProvider,$urlMatcherFactoryProvider) {
|
app.config(['stateHelperProvider','$urlRouterProvider','$urlMatcherFactoryProvider',function(stateHelperProvider,$urlRouterProvider,$urlMatcherFactoryProvider) {
|
||||||
|
|
||||||
|
@ -36,3 +29,16 @@ app.config(['stateHelperProvider','$urlRouterProvider','$urlMatcherFactoryProvid
|
||||||
});
|
});
|
||||||
|
|
||||||
} ]);
|
} ]);
|
||||||
|
|
||||||
|
/** Controllers **/
|
||||||
|
app.controller('MainController', MainController);
|
||||||
|
app.controller('VeterinarianController', VeterinarianController);
|
||||||
|
app.controller('PetController', PetController);
|
||||||
|
app.controller('OwnerController', OwnerController);
|
||||||
|
app.controller('VisitController', VisitController);
|
||||||
|
|
||||||
|
/** Services **/
|
||||||
|
app.factory('Owner', Owner);
|
||||||
|
app.factory('Pet', Pet);
|
||||||
|
app.factory('Vet', Vet);
|
||||||
|
app.factory('Visit', Visit);
|
1229
src/main/webapp/js/lib/angular-animate.js
vendored
1229
src/main/webapp/js/lib/angular-animate.js
vendored
File diff suppressed because it is too large
Load diff
2
src/main/webapp/js/lib/angular-cookies.js
vendored
2
src/main/webapp/js/lib/angular-cookies.js
vendored
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* @license AngularJS v1.3.0-build.3242+sha.6a96a82
|
* @license AngularJS v1.3.11
|
||||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
* License: MIT
|
* License: MIT
|
||||||
*/
|
*/
|
||||||
|
|
366
src/main/webapp/js/lib/angular-file-upload-shim.js
vendored
366
src/main/webapp/js/lib/angular-file-upload-shim.js
vendored
|
@ -1,366 +0,0 @@
|
||||||
/**!
|
|
||||||
* AngularJS file upload/drop directive with progress and abort
|
|
||||||
* FileAPI Flash shim for old browsers not supporting FormData
|
|
||||||
* @author Danial <danial.farid@gmail.com>
|
|
||||||
* @version <%= pkg.version %>
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var hasFlash = function() {
|
|
||||||
try {
|
|
||||||
var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
|
|
||||||
if (fo) return true;
|
|
||||||
} catch(e) {
|
|
||||||
if (navigator.mimeTypes['application/x-shockwave-flash'] != undefined) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchXHR(fnName, newFn) {
|
|
||||||
window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((window.XMLHttpRequest && !window.FormData) || (window.FileAPI && FileAPI.forceLoad)) {
|
|
||||||
var initializeUploadListener = function(xhr) {
|
|
||||||
if (!xhr.__listeners) {
|
|
||||||
if (!xhr.upload) xhr.upload = {};
|
|
||||||
xhr.__listeners = [];
|
|
||||||
var origAddEventListener = xhr.upload.addEventListener;
|
|
||||||
xhr.upload.addEventListener = function(t, fn, b) {
|
|
||||||
xhr.__listeners[t] = fn;
|
|
||||||
origAddEventListener && origAddEventListener.apply(this, arguments);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patchXHR('open', function(orig) {
|
|
||||||
return function(m, url, b) {
|
|
||||||
initializeUploadListener(this);
|
|
||||||
this.__url = url;
|
|
||||||
try {
|
|
||||||
orig.apply(this, [m, url, b]);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message.indexOf('Access is denied') > -1) {
|
|
||||||
orig.apply(this, [m, '_fix_for_ie_crossdomain__', b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
patchXHR('getResponseHeader', function(orig) {
|
|
||||||
return function(h) {
|
|
||||||
return this.__fileApiXHR && this.__fileApiXHR.getResponseHeader ? this.__fileApiXHR.getResponseHeader(h) : (orig == null ? null : orig.apply(this, [h]));
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
patchXHR('getAllResponseHeaders', function(orig) {
|
|
||||||
return function() {
|
|
||||||
return this.__fileApiXHR && this.__fileApiXHR.getAllResponseHeaders ? this.__fileApiXHR.getAllResponseHeaders() : (orig == null ? null : orig.apply(this));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
patchXHR('abort', function(orig) {
|
|
||||||
return function() {
|
|
||||||
return this.__fileApiXHR && this.__fileApiXHR.abort ? this.__fileApiXHR.abort() : (orig == null ? null : orig.apply(this));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
patchXHR('setRequestHeader', function(orig) {
|
|
||||||
return function(header, value) {
|
|
||||||
if (header === '__setXHR_') {
|
|
||||||
initializeUploadListener(this);
|
|
||||||
var val = value(this);
|
|
||||||
// fix for angular < 1.2.0
|
|
||||||
if (val instanceof Function) {
|
|
||||||
val(this);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.__requestHeaders = this.__requestHeaders || {};
|
|
||||||
this.__requestHeaders[header] = value;
|
|
||||||
orig.apply(this, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function redefineProp(xhr, prop, fn) {
|
|
||||||
try {
|
|
||||||
Object.defineProperty(xhr, prop, {get: fn});
|
|
||||||
} catch (e) {/*ignore*/}
|
|
||||||
}
|
|
||||||
|
|
||||||
patchXHR('send', function(orig) {
|
|
||||||
return function() {
|
|
||||||
var xhr = this;
|
|
||||||
if (arguments[0] && arguments[0].__isFileAPIShim) {
|
|
||||||
var formData = arguments[0];
|
|
||||||
var config = {
|
|
||||||
url: xhr.__url,
|
|
||||||
jsonp: false, //removes the callback form param
|
|
||||||
cache: true, //removes the ?fileapiXXX in the url
|
|
||||||
complete: function(err, fileApiXHR) {
|
|
||||||
xhr.__completed = true;
|
|
||||||
if (!err && xhr.__listeners['load'])
|
|
||||||
xhr.__listeners['load']({type: 'load', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
|
|
||||||
if (!err && xhr.__listeners['loadend'])
|
|
||||||
xhr.__listeners['loadend']({type: 'loadend', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
|
|
||||||
if (err === 'abort' && xhr.__listeners['abort'])
|
|
||||||
xhr.__listeners['abort']({type: 'abort', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
|
|
||||||
if (fileApiXHR.status !== undefined) redefineProp(xhr, 'status', function() {return (fileApiXHR.status == 0 && err && err !== 'abort') ? 500 : fileApiXHR.status});
|
|
||||||
if (fileApiXHR.statusText !== undefined) redefineProp(xhr, 'statusText', function() {return fileApiXHR.statusText});
|
|
||||||
redefineProp(xhr, 'readyState', function() {return 4});
|
|
||||||
if (fileApiXHR.response !== undefined) redefineProp(xhr, 'response', function() {return fileApiXHR.response});
|
|
||||||
var resp = fileApiXHR.responseText || (err && fileApiXHR.status == 0 && err !== 'abort' ? err : undefined);
|
|
||||||
redefineProp(xhr, 'responseText', function() {return resp});
|
|
||||||
redefineProp(xhr, 'response', function() {return resp});
|
|
||||||
if (err) redefineProp(xhr, 'err', function() {return err});
|
|
||||||
xhr.__fileApiXHR = fileApiXHR;
|
|
||||||
if (xhr.onreadystatechange) xhr.onreadystatechange();
|
|
||||||
if (xhr.onload) xhr.onload();
|
|
||||||
},
|
|
||||||
fileprogress: function(e) {
|
|
||||||
e.target = xhr;
|
|
||||||
xhr.__listeners['progress'] && xhr.__listeners['progress'](e);
|
|
||||||
xhr.__total = e.total;
|
|
||||||
xhr.__loaded = e.loaded;
|
|
||||||
if (e.total === e.loaded) {
|
|
||||||
// fix flash issue that doesn't call complete if there is no response text from the server
|
|
||||||
var _this = this
|
|
||||||
setTimeout(function() {
|
|
||||||
if (!xhr.__completed) {
|
|
||||||
xhr.getAllResponseHeaders = function(){};
|
|
||||||
_this.complete(null, {status: 204, statusText: 'No Content'});
|
|
||||||
}
|
|
||||||
}, 10000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
headers: xhr.__requestHeaders
|
|
||||||
}
|
|
||||||
config.data = {};
|
|
||||||
config.files = {}
|
|
||||||
for (var i = 0; i < formData.data.length; i++) {
|
|
||||||
var item = formData.data[i];
|
|
||||||
if (item.val != null && item.val.name != null && item.val.size != null && item.val.type != null) {
|
|
||||||
config.files[item.key] = item.val;
|
|
||||||
} else {
|
|
||||||
config.data[item.key] = item.val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
if (!hasFlash()) {
|
|
||||||
throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';
|
|
||||||
}
|
|
||||||
xhr.__fileApiXHR = FileAPI.upload(config);
|
|
||||||
}, 1);
|
|
||||||
} else {
|
|
||||||
orig.apply(xhr, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.XMLHttpRequest.__isFileAPIShim = true;
|
|
||||||
|
|
||||||
var addFlash = function(elem) {
|
|
||||||
if (!hasFlash()) {
|
|
||||||
throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';
|
|
||||||
}
|
|
||||||
var el = angular.element(elem);
|
|
||||||
if (!el.attr('disabled')) {
|
|
||||||
if (!el.hasClass('js-fileapi-wrapper') && (el.attr('ng-file-select') != null || el.attr('data-ng-file-select') != null ||
|
|
||||||
(el.attr('ng-file-generated-elem') &&
|
|
||||||
(el.parent().attr('ng-file-select') != null || el.parent().attr('data-ng-file-select') != null)))) {
|
|
||||||
if (FileAPI.wrapInsideDiv) {
|
|
||||||
var wrap = document.createElement('div');
|
|
||||||
wrap.innerHTML = '<div class="js-fileapi-wrapper" style="position:relative; overflow:hidden"></div>';
|
|
||||||
wrap = wrap.firstChild;
|
|
||||||
var parent = elem.parentNode;
|
|
||||||
parent.insertBefore(wrap, elem);
|
|
||||||
parent.removeChild(elem);
|
|
||||||
wrap.appendChild(elem);
|
|
||||||
} else {
|
|
||||||
el.addClass('js-fileapi-wrapper');
|
|
||||||
if (el.attr('ng-file-generated-elem')) {
|
|
||||||
if (el.parent().css('position') === '' || el.parent().css('position') === 'static') {
|
|
||||||
el.parent().css('position', 'relative');
|
|
||||||
}
|
|
||||||
el.css('top', 0).css('bottom', 0).css('left', 0).css('right', 0).css('width', '100%').css('height', '100%').
|
|
||||||
css('padding', 0).css('margin', 0);
|
|
||||||
el.parent().unbind('click', el.parent().__afu_fileClickDelegate__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var changeFnWrapper = function(fn) {
|
|
||||||
return function(evt) {
|
|
||||||
var files = FileAPI.getFiles(evt);
|
|
||||||
//just a double check for #233
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
|
||||||
if (files[i].size === undefined) files[i].size = 0;
|
|
||||||
if (files[i].name === undefined) files[i].name = 'file';
|
|
||||||
if (files[i].type === undefined) files[i].type = 'undefined';
|
|
||||||
}
|
|
||||||
if (!evt.target) {
|
|
||||||
evt.target = {};
|
|
||||||
}
|
|
||||||
evt.target.files = files;
|
|
||||||
// if evt.target.files is not writable use helper field
|
|
||||||
if (evt.target.files != files) {
|
|
||||||
evt.__files_ = files;
|
|
||||||
}
|
|
||||||
(evt.__files_ || evt.target.files).item = function(i) {
|
|
||||||
return (evt.__files_ || evt.target.files)[i] || null;
|
|
||||||
}
|
|
||||||
if (fn) fn.apply(this, [evt]);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var isFileChange = function(elem, e) {
|
|
||||||
return (e.toLowerCase() === 'change' || e.toLowerCase() === 'onchange') && elem.getAttribute('type') == 'file';
|
|
||||||
}
|
|
||||||
if (HTMLInputElement.prototype.addEventListener) {
|
|
||||||
HTMLInputElement.prototype.addEventListener = (function(origAddEventListener) {
|
|
||||||
return function(e, fn, b, d) {
|
|
||||||
if (isFileChange(this, e)) {
|
|
||||||
addFlash(this);
|
|
||||||
origAddEventListener.apply(this, [e, changeFnWrapper(fn), b, d]);
|
|
||||||
} else {
|
|
||||||
origAddEventListener.apply(this, [e, fn, b, d]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})(HTMLInputElement.prototype.addEventListener);
|
|
||||||
}
|
|
||||||
if (HTMLInputElement.prototype.attachEvent) {
|
|
||||||
HTMLInputElement.prototype.attachEvent = (function(origAttachEvent) {
|
|
||||||
return function(e, fn) {
|
|
||||||
if (isFileChange(this, e)) {
|
|
||||||
addFlash(this);
|
|
||||||
if (window.jQuery) {
|
|
||||||
// fix for #281 jQuery on IE8
|
|
||||||
angular.element(this).bind('change', changeFnWrapper(null));
|
|
||||||
} else {
|
|
||||||
origAttachEvent.apply(this, [e, changeFnWrapper(fn)]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
origAttachEvent.apply(this, [e, fn]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})(HTMLInputElement.prototype.attachEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.FormData = FormData = function() {
|
|
||||||
return {
|
|
||||||
append: function(key, val, name) {
|
|
||||||
this.data.push({
|
|
||||||
key: key,
|
|
||||||
val: val,
|
|
||||||
name: name
|
|
||||||
});
|
|
||||||
},
|
|
||||||
data: [],
|
|
||||||
__isFileAPIShim: true
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
//load FileAPI
|
|
||||||
if (!window.FileAPI) {
|
|
||||||
window.FileAPI = {};
|
|
||||||
}
|
|
||||||
if (FileAPI.forceLoad) {
|
|
||||||
FileAPI.html5 = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FileAPI.upload) {
|
|
||||||
var jsUrl, basePath, script = document.createElement('script'), allScripts = document.getElementsByTagName('script'), i, index, src;
|
|
||||||
if (window.FileAPI.jsUrl) {
|
|
||||||
jsUrl = window.FileAPI.jsUrl;
|
|
||||||
} else if (window.FileAPI.jsPath) {
|
|
||||||
basePath = window.FileAPI.jsPath;
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < allScripts.length; i++) {
|
|
||||||
src = allScripts[i].src;
|
|
||||||
index = src.search(/\/angular\-file\-upload[\-a-zA-z0-9\.]*\.js/)
|
|
||||||
if (index > -1) {
|
|
||||||
basePath = src.substring(0, index + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FileAPI.staticPath == null) FileAPI.staticPath = basePath;
|
|
||||||
script.setAttribute('src', jsUrl || basePath + 'FileAPI.min.js');
|
|
||||||
document.getElementsByTagName('head')[0].appendChild(script);
|
|
||||||
FileAPI.hasFlash = hasFlash();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
FileAPI.disableFileInput = function(elem, disable) {
|
|
||||||
if (disable) {
|
|
||||||
elem.removeClass('js-fileapi-wrapper')
|
|
||||||
} else {
|
|
||||||
elem.addClass('js-fileapi-wrapper');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!window.FileReader) {
|
|
||||||
window.FileReader = function() {
|
|
||||||
var _this = this, loadStarted = false;
|
|
||||||
this.listeners = {};
|
|
||||||
this.addEventListener = function(type, fn) {
|
|
||||||
_this.listeners[type] = _this.listeners[type] || [];
|
|
||||||
_this.listeners[type].push(fn);
|
|
||||||
};
|
|
||||||
this.removeEventListener = function(type, fn) {
|
|
||||||
_this.listeners[type] && _this.listeners[type].splice(_this.listeners[type].indexOf(fn), 1);
|
|
||||||
};
|
|
||||||
this.dispatchEvent = function(evt) {
|
|
||||||
var list = _this.listeners[evt.type];
|
|
||||||
if (list) {
|
|
||||||
for (var i = 0; i < list.length; i++) {
|
|
||||||
list[i].call(_this, evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.onabort = this.onerror = this.onload = this.onloadstart = this.onloadend = this.onprogress = null;
|
|
||||||
|
|
||||||
var constructEvent = function(type, evt) {
|
|
||||||
var e = {type: type, target: _this, loaded: evt.loaded, total: evt.total, error: evt.error};
|
|
||||||
if (evt.result != null) e.target.result = evt.result;
|
|
||||||
return e;
|
|
||||||
};
|
|
||||||
var listener = function(evt) {
|
|
||||||
if (!loadStarted) {
|
|
||||||
loadStarted = true;
|
|
||||||
_this.onloadstart && this.onloadstart(constructEvent('loadstart', evt));
|
|
||||||
}
|
|
||||||
if (evt.type === 'load') {
|
|
||||||
_this.onloadend && _this.onloadend(constructEvent('loadend', evt));
|
|
||||||
var e = constructEvent('load', evt);
|
|
||||||
_this.onload && _this.onload(e);
|
|
||||||
_this.dispatchEvent(e);
|
|
||||||
} else if (evt.type === 'progress') {
|
|
||||||
var e = constructEvent('progress', evt);
|
|
||||||
_this.onprogress && _this.onprogress(e);
|
|
||||||
_this.dispatchEvent(e);
|
|
||||||
} else {
|
|
||||||
var e = constructEvent('error', evt);
|
|
||||||
_this.onerror && _this.onerror(e);
|
|
||||||
_this.dispatchEvent(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.readAsArrayBuffer = function(file) {
|
|
||||||
FileAPI.readAsBinaryString(file, listener);
|
|
||||||
}
|
|
||||||
this.readAsBinaryString = function(file) {
|
|
||||||
FileAPI.readAsBinaryString(file, listener);
|
|
||||||
}
|
|
||||||
this.readAsDataURL = function(file) {
|
|
||||||
FileAPI.readAsDataURL(file, listener);
|
|
||||||
}
|
|
||||||
this.readAsText = function(file) {
|
|
||||||
FileAPI.readAsText(file, listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
483
src/main/webapp/js/lib/angular-file-upload.js
vendored
483
src/main/webapp/js/lib/angular-file-upload.js
vendored
|
@ -1,483 +0,0 @@
|
||||||
/**!
|
|
||||||
* AngularJS file upload/drop directive with progress and abort
|
|
||||||
* @author Danial <danial.farid@gmail.com>
|
|
||||||
* @version <%= pkg.version %>
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
function patchXHR(fnName, newFn) {
|
|
||||||
window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.XMLHttpRequest && !window.XMLHttpRequest.__isFileAPIShim) {
|
|
||||||
patchXHR('setRequestHeader', function(orig) {
|
|
||||||
return function(header, value) {
|
|
||||||
if (header === '__setXHR_') {
|
|
||||||
var val = value(this);
|
|
||||||
// fix for angular < 1.2.0
|
|
||||||
if (val instanceof Function) {
|
|
||||||
val(this);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
orig.apply(this, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var angularFileUpload = angular.module('angularFileUpload', []);
|
|
||||||
angularFileUpload.version = '<%= pkg.version %>';
|
|
||||||
angularFileUpload.service('$upload', ['$http', '$q', '$timeout', function($http, $q, $timeout) {
|
|
||||||
function sendHttp(config) {
|
|
||||||
config.method = config.method || 'POST';
|
|
||||||
config.headers = config.headers || {};
|
|
||||||
config.transformRequest = config.transformRequest || function(data, headersGetter) {
|
|
||||||
if (window.ArrayBuffer && data instanceof window.ArrayBuffer) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return $http.defaults.transformRequest[0](data, headersGetter);
|
|
||||||
};
|
|
||||||
var deferred = $q.defer();
|
|
||||||
var promise = deferred.promise;
|
|
||||||
|
|
||||||
config.headers['__setXHR_'] = function() {
|
|
||||||
return function(xhr) {
|
|
||||||
if (!xhr) return;
|
|
||||||
config.__XHR = xhr;
|
|
||||||
config.xhrFn && config.xhrFn(xhr);
|
|
||||||
xhr.upload.addEventListener('progress', function(e) {
|
|
||||||
e.config = config;
|
|
||||||
deferred.notify ? deferred.notify(e) : promise.progress_fn && $timeout(function(){promise.progress_fn(e)});
|
|
||||||
}, false);
|
|
||||||
//fix for firefox not firing upload progress end, also IE8-9
|
|
||||||
xhr.upload.addEventListener('load', function(e) {
|
|
||||||
if (e.lengthComputable) {
|
|
||||||
e.config = config;
|
|
||||||
deferred.notify ? deferred.notify(e) : promise.progress_fn && $timeout(function(){promise.progress_fn(e)});
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
$http(config).then(function(r){deferred.resolve(r)}, function(e){deferred.reject(e)}, function(n){deferred.notify(n)});
|
|
||||||
|
|
||||||
promise.success = function(fn) {
|
|
||||||
promise.then(function(response) {
|
|
||||||
fn(response.data, response.status, response.headers, config);
|
|
||||||
});
|
|
||||||
return promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
promise.error = function(fn) {
|
|
||||||
promise.then(null, function(response) {
|
|
||||||
fn(response.data, response.status, response.headers, config);
|
|
||||||
});
|
|
||||||
return promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
promise.progress = function(fn) {
|
|
||||||
promise.progress_fn = fn;
|
|
||||||
promise.then(null, null, function(update) {
|
|
||||||
fn(update);
|
|
||||||
});
|
|
||||||
return promise;
|
|
||||||
};
|
|
||||||
promise.abort = function() {
|
|
||||||
if (config.__XHR) {
|
|
||||||
$timeout(function() {
|
|
||||||
config.__XHR.abort();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return promise;
|
|
||||||
};
|
|
||||||
promise.xhr = function(fn) {
|
|
||||||
config.xhrFn = (function(origXhrFn) {
|
|
||||||
return function() {
|
|
||||||
origXhrFn && origXhrFn.apply(promise, arguments);
|
|
||||||
fn.apply(promise, arguments);
|
|
||||||
}
|
|
||||||
})(config.xhrFn);
|
|
||||||
return promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.upload = function(config) {
|
|
||||||
config.headers = config.headers || {};
|
|
||||||
config.headers['Content-Type'] = undefined;
|
|
||||||
config.transformRequest = config.transformRequest || $http.defaults.transformRequest;
|
|
||||||
var formData = new FormData();
|
|
||||||
var origTransformRequest = config.transformRequest;
|
|
||||||
var origData = config.data;
|
|
||||||
config.transformRequest = function(formData, headerGetter) {
|
|
||||||
if (origData) {
|
|
||||||
if (config.formDataAppender) {
|
|
||||||
for (var key in origData) {
|
|
||||||
var val = origData[key];
|
|
||||||
config.formDataAppender(formData, key, val);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (var key in origData) {
|
|
||||||
var val = origData[key];
|
|
||||||
if (typeof origTransformRequest == 'function') {
|
|
||||||
val = origTransformRequest(val, headerGetter);
|
|
||||||
} else {
|
|
||||||
for (var i = 0; i < origTransformRequest.length; i++) {
|
|
||||||
var transformFn = origTransformRequest[i];
|
|
||||||
if (typeof transformFn == 'function') {
|
|
||||||
val = transformFn(val, headerGetter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (val != undefined) formData.append(key, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.file != null) {
|
|
||||||
var fileFormName = config.fileFormDataName || 'file';
|
|
||||||
|
|
||||||
if (Object.prototype.toString.call(config.file) === '[object Array]') {
|
|
||||||
var isFileFormNameString = Object.prototype.toString.call(fileFormName) === '[object String]';
|
|
||||||
for (var i = 0; i < config.file.length; i++) {
|
|
||||||
formData.append(isFileFormNameString ? fileFormName : fileFormName[i], config.file[i],
|
|
||||||
(config.fileName && config.fileName[i]) || config.file[i].name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
formData.append(fileFormName, config.file, config.fileName || config.file.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return formData;
|
|
||||||
};
|
|
||||||
|
|
||||||
config.data = formData;
|
|
||||||
|
|
||||||
return sendHttp(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http = function(config) {
|
|
||||||
return sendHttp(config);
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
|
|
||||||
angularFileUpload.directive('ngFileSelect', [ '$parse', '$timeout', function($parse, $timeout) { return {
|
|
||||||
restrict: 'AEC',
|
|
||||||
require:'?ngModel',
|
|
||||||
scope: {
|
|
||||||
fileModel: '=ngModel',
|
|
||||||
change: '&ngFileChange',
|
|
||||||
select : '&ngFileSelect',
|
|
||||||
resetOnClick: '&resetOnClick',
|
|
||||||
multiple: '&ngMultiple',
|
|
||||||
accept: '&ngAccept'
|
|
||||||
},
|
|
||||||
link: function(scope, elem, attr, ngModel) {
|
|
||||||
handleFileSelect(scope, elem, attr, ngModel, $parse, $timeout);
|
|
||||||
}
|
|
||||||
}}]);
|
|
||||||
|
|
||||||
function handleFileSelect(scope, elem, attr, ngModel, $parse, $timeout) {
|
|
||||||
if (scope.multiple()) {
|
|
||||||
elem.attr('multiple', 'true');
|
|
||||||
attr['multiple'] = 'true';
|
|
||||||
}
|
|
||||||
var accept = scope.accept();
|
|
||||||
if (accept) {
|
|
||||||
elem.attr('accept', accept);
|
|
||||||
attr['accept'] = accept;
|
|
||||||
}
|
|
||||||
if (elem[0].tagName.toLowerCase() !== 'input' || (elem.attr('type') && elem.attr('type').toLowerCase()) !== 'file') {
|
|
||||||
var fileElem = angular.element('<input type="file">')
|
|
||||||
if (attr['multiple']) fileElem.attr('multiple', attr['multiple']);
|
|
||||||
if (attr['accept']) fileElem.attr('accept', attr['accept']);
|
|
||||||
fileElem.css('width', '1px').css('height', '1px').css('opacity', 0).css('position', 'absolute').css('filter', 'alpha(opacity=0)')
|
|
||||||
.css('padding', 0).css('margin', 0).css('overflow', 'hidden').attr('tabindex', '-1').attr('ng-file-generated-elem', true);
|
|
||||||
elem.append(fileElem);
|
|
||||||
elem.__afu_fileClickDelegate__ = function() {
|
|
||||||
fileElem[0].click();
|
|
||||||
};
|
|
||||||
elem.bind('click', elem.__afu_fileClickDelegate__);
|
|
||||||
elem.css('overflow', 'hidden');
|
|
||||||
elem = fileElem;
|
|
||||||
}
|
|
||||||
if (scope.resetOnClick() != false) {
|
|
||||||
elem.bind('click', function(evt) {
|
|
||||||
if (elem[0].value) {
|
|
||||||
updateModel([], attr, ngModel, scope, evt);
|
|
||||||
}
|
|
||||||
elem[0].value = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (ngModel) {
|
|
||||||
scope.$parent.$watch(attr['ngModel'], function(val) {
|
|
||||||
if (val == null) {
|
|
||||||
elem[0].value = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (attr['ngFileSelect'] != '') {
|
|
||||||
scope.change = scope.select;
|
|
||||||
}
|
|
||||||
elem.bind('change', function(evt) {
|
|
||||||
var files = [], fileList, i;
|
|
||||||
fileList = evt.__files_ || evt.target.files;
|
|
||||||
updateModel(fileList, attr, ngModel, scope, evt);
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateModel(fileList, attr, ngModel, scope, evt) {
|
|
||||||
$timeout(function() {
|
|
||||||
var files = [];
|
|
||||||
for (var i = 0; i < fileList.length; i++) {
|
|
||||||
files.push(fileList.item(i));
|
|
||||||
}
|
|
||||||
if (ngModel) {
|
|
||||||
scope.fileModel = files;
|
|
||||||
ngModel && ngModel.$setViewValue(files != null && files.length == 0 ? '' : files);
|
|
||||||
}
|
|
||||||
$timeout(function() {
|
|
||||||
scope.change({
|
|
||||||
$files : files,
|
|
||||||
$event : evt
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
angularFileUpload.directive('ngFileDrop', [ '$parse', '$timeout', '$location', function($parse, $timeout, $location) { return {
|
|
||||||
restrict: 'AEC',
|
|
||||||
require:'?ngModel',
|
|
||||||
scope: {
|
|
||||||
fileModel: '=ngModel',
|
|
||||||
fileRejectedModel: '=ngFileRejectedModel',
|
|
||||||
change: '&ngFileChange',
|
|
||||||
drop: '&ngFileDrop',
|
|
||||||
allowDir: '&allowDir',
|
|
||||||
dragOverClass: '&dragOverClass',
|
|
||||||
dropAvailable: '=dropAvailable',
|
|
||||||
stopPropagation: '&stopPropagation',
|
|
||||||
hideOnDropNotAvailable: '&hideOnDropNotAvailable',
|
|
||||||
multiple: '&ngMultiple',
|
|
||||||
accept: '&ngAccept'
|
|
||||||
},
|
|
||||||
link: function(scope, elem, attr, ngModel) {
|
|
||||||
handleDrop(scope, elem, attr, ngModel, $parse, $timeout, $location);
|
|
||||||
}
|
|
||||||
}}]);
|
|
||||||
|
|
||||||
angularFileUpload.directive('ngNoFileDrop', function() {
|
|
||||||
return function(scope, elem, attr) {
|
|
||||||
if (dropAvailable()) elem.css('display', 'none')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//for backward compatibility
|
|
||||||
angularFileUpload.directive('ngFileDropAvailable', [ '$parse', '$timeout', function($parse, $timeout) {
|
|
||||||
return function(scope, elem, attr) {
|
|
||||||
if (dropAvailable()) {
|
|
||||||
var fn = $parse(attr['ngFileDropAvailable']);
|
|
||||||
$timeout(function() {
|
|
||||||
fn(scope);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
|
|
||||||
function handleDrop(scope, elem, attr, ngModel, $parse, $timeout, $location) {
|
|
||||||
var available = dropAvailable();
|
|
||||||
if (attr['dropAvailable']) {
|
|
||||||
$timeout(function() {
|
|
||||||
scope.dropAvailable = available;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!available) {
|
|
||||||
if (scope.hideOnDropNotAvailable() != false) {
|
|
||||||
elem.css('display', 'none');
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var leaveTimeout = null;
|
|
||||||
var stopPropagation = scope.stopPropagation();
|
|
||||||
var dragOverDelay = 1;
|
|
||||||
var accept = scope.accept() || attr['accept'] || attr['ngAccept'];
|
|
||||||
var regexp = accept ? new RegExp(globStringToRegex(accept)) : null;
|
|
||||||
elem[0].addEventListener('dragover', function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
if (stopPropagation) evt.stopPropagation();
|
|
||||||
$timeout.cancel(leaveTimeout);
|
|
||||||
if (!scope.actualDragOverClass) {
|
|
||||||
scope.actualDragOverClass = calculateDragOverClass(scope, attr, evt);
|
|
||||||
}
|
|
||||||
elem.addClass(scope.actualDragOverClass);
|
|
||||||
}, false);
|
|
||||||
elem[0].addEventListener('dragenter', function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
if (stopPropagation) evt.stopPropagation();
|
|
||||||
}, false);
|
|
||||||
elem[0].addEventListener('dragleave', function(evt) {
|
|
||||||
leaveTimeout = $timeout(function() {
|
|
||||||
elem.removeClass(scope.actualDragOverClass);
|
|
||||||
scope.actualDragOverClass = null;
|
|
||||||
}, dragOverDelay || 1);
|
|
||||||
}, false);
|
|
||||||
if (attr['ngFileDrop'] != '') {
|
|
||||||
scope.change = scope.drop;
|
|
||||||
}
|
|
||||||
elem[0].addEventListener('drop', function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
if (stopPropagation) evt.stopPropagation();
|
|
||||||
elem.removeClass(scope.actualDragOverClass);
|
|
||||||
scope.actualDragOverClass = null;
|
|
||||||
extractFiles(evt, function(files, rejFiles) {
|
|
||||||
if (ngModel) {
|
|
||||||
scope.fileModel = files;
|
|
||||||
ngModel && ngModel.$setViewValue(files != null && files.length == 0 ? '' : files);
|
|
||||||
}
|
|
||||||
if (attr['ngFileRejectedModel']) scope.fileRejectedModel = rejFiles;
|
|
||||||
$timeout(function(){
|
|
||||||
scope.change({
|
|
||||||
$files : files,
|
|
||||||
$rejectedFiles: rejFiles,
|
|
||||||
$event : evt
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, scope.allowDir() != false, attr['multiple'] || scope.multiple() || attr['ngMultiple'] == 'true');
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
function calculateDragOverClass(scope, attr, evt) {
|
|
||||||
var valid = true;
|
|
||||||
if (regexp) {
|
|
||||||
var items = evt.dataTransfer.items;
|
|
||||||
if (items != null) {
|
|
||||||
for (var i = 0 ; i < items.length && valid; i++) {
|
|
||||||
valid = valid && (items[i].kind == 'file' || items[i].kind == '') &&
|
|
||||||
(items[i].type.match(regexp) != null || (items[i].name != null && items[i].name.match(regexp) != null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var clazz = scope.dragOverClass({$event : evt});
|
|
||||||
if (clazz) {
|
|
||||||
if (clazz.delay) dragOverDelay = clazz.delay;
|
|
||||||
if (clazz.accept) clazz = valid ? clazz.accept : clazz.reject;
|
|
||||||
}
|
|
||||||
return clazz || attr['dragOverClass'] || 'dragover';
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractFiles(evt, callback, allowDir, multiple) {
|
|
||||||
var files = [], rejFiles = [], items = evt.dataTransfer.items;
|
|
||||||
|
|
||||||
function addFile(file) {
|
|
||||||
if (!regexp || file.type.match(regexp) || (file.name != null && file.name.match(regexp))) {
|
|
||||||
files.push(file);
|
|
||||||
} else {
|
|
||||||
rejFiles.push(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items && items.length > 0 && $location.protocol() != 'file') {
|
|
||||||
for (var i = 0; i < items.length; i++) {
|
|
||||||
if (items[i].webkitGetAsEntry && items[i].webkitGetAsEntry() && items[i].webkitGetAsEntry().isDirectory) {
|
|
||||||
var entry = items[i].webkitGetAsEntry();
|
|
||||||
if (entry.isDirectory && !allowDir) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (entry != null) {
|
|
||||||
//fix for chrome bug https://code.google.com/p/chromium/issues/detail?id=149735
|
|
||||||
if (isASCII(entry.name)) {
|
|
||||||
traverseFileTree(files, entry);
|
|
||||||
} else if (!items[i].webkitGetAsEntry().isDirectory) {
|
|
||||||
addFile(items[i].getAsFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var f = items[i].getAsFile();
|
|
||||||
if (f != null) addFile(f);
|
|
||||||
}
|
|
||||||
if (!multiple && files.length > 0) break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var fileList = evt.dataTransfer.files;
|
|
||||||
if (fileList != null) {
|
|
||||||
for (var i = 0; i < fileList.length; i++) {
|
|
||||||
addFile(fileList.item(i));
|
|
||||||
if (!multiple && files.length > 0) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var delays = 0;
|
|
||||||
(function waitForProcess(delay) {
|
|
||||||
$timeout(function() {
|
|
||||||
if (!processing) {
|
|
||||||
if (!multiple && files.length > 1) {
|
|
||||||
var i = 0;
|
|
||||||
while (files[i].type == 'directory') i++;
|
|
||||||
files = [files[i]];
|
|
||||||
}
|
|
||||||
callback(files, rejFiles);
|
|
||||||
} else {
|
|
||||||
if (delays++ * 10 < 20 * 1000) {
|
|
||||||
waitForProcess(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, delay || 0)
|
|
||||||
})();
|
|
||||||
|
|
||||||
var processing = 0;
|
|
||||||
function traverseFileTree(files, entry, path) {
|
|
||||||
if (entry != null) {
|
|
||||||
if (entry.isDirectory) {
|
|
||||||
addFile({name: entry.name, type: 'directory', path: (path ? path : '') + entry.name});
|
|
||||||
var dirReader = entry.createReader();
|
|
||||||
processing++;
|
|
||||||
dirReader.readEntries(function(entries) {
|
|
||||||
try {
|
|
||||||
for (var i = 0; i < entries.length; i++) {
|
|
||||||
traverseFileTree(files, entries[i], (path ? path : '') + entry.name + '/');
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
processing--;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
processing++;
|
|
||||||
entry.file(function(file) {
|
|
||||||
processing--;
|
|
||||||
file.path = (path ? path : '') + file.name;
|
|
||||||
addFile(file);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dropAvailable() {
|
|
||||||
var div = document.createElement('div');
|
|
||||||
return ('draggable' in div) && ('ondrop' in div);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isASCII(str) {
|
|
||||||
return /^[\000-\177]*$/.test(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function globStringToRegex(str) {
|
|
||||||
if (str.length > 2 && str[0] === '/' && str[str.length -1] === '/') {
|
|
||||||
return str.substring(1, str.length - 1);
|
|
||||||
}
|
|
||||||
var split = str.split(','), result = '';
|
|
||||||
if (split.length > 1) {
|
|
||||||
for (var i = 0; i < split.length; i++) {
|
|
||||||
result += '(' + globStringToRegex(split[i]) + ')';
|
|
||||||
if (i < split.length - 1) {
|
|
||||||
result += '|'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + '-]', 'g'), '\\$&') + '$';
|
|
||||||
result = result.replace(/\\\*/g, '.*').replace(/\\\?/g, '.');
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
173
src/main/webapp/js/lib/angular-mocks.js
vendored
173
src/main/webapp/js/lib/angular-mocks.js
vendored
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* @license AngularJS v1.3.0
|
* @license AngularJS v1.3.11
|
||||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
* License: MIT
|
* License: MIT
|
||||||
*/
|
*/
|
||||||
|
@ -250,31 +250,31 @@ angular.mock.$ExceptionHandlerProvider = function() {
|
||||||
*
|
*
|
||||||
* @param {string} mode Mode of operation, defaults to `rethrow`.
|
* @param {string} mode Mode of operation, defaults to `rethrow`.
|
||||||
*
|
*
|
||||||
* - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
|
|
||||||
* is a bug in the application or test, so this mock will make these tests fail.
|
|
||||||
* - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
|
* - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
|
||||||
* mode stores an array of errors in `$exceptionHandler.errors`, to allow later
|
* mode stores an array of errors in `$exceptionHandler.errors`, to allow later
|
||||||
* assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
|
* assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
|
||||||
* {@link ngMock.$log#reset reset()}
|
* {@link ngMock.$log#reset reset()}
|
||||||
|
* - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
|
||||||
|
* is a bug in the application or test, so this mock will make these tests fail.
|
||||||
|
* For any implementations that expect exceptions to be thrown, the `rethrow` mode
|
||||||
|
* will also maintain a log of thrown errors.
|
||||||
*/
|
*/
|
||||||
this.mode = function(mode) {
|
this.mode = function(mode) {
|
||||||
switch(mode) {
|
|
||||||
case 'rethrow':
|
|
||||||
handler = function(e) {
|
|
||||||
throw e;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'log':
|
|
||||||
var errors = [];
|
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case 'log':
|
||||||
|
case 'rethrow':
|
||||||
|
var errors = [];
|
||||||
handler = function(e) {
|
handler = function(e) {
|
||||||
if (arguments.length == 1) {
|
if (arguments.length == 1) {
|
||||||
errors.push(e);
|
errors.push(e);
|
||||||
} else {
|
} else {
|
||||||
errors.push([].slice.call(arguments, 0));
|
errors.push([].slice.call(arguments, 0));
|
||||||
}
|
}
|
||||||
|
if (mode === "rethrow") {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.errors = errors;
|
handler.errors = errors;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -461,17 +461,17 @@ angular.mock.$LogProvider = function() {
|
||||||
* @returns {promise} A promise which will be notified on each iteration.
|
* @returns {promise} A promise which will be notified on each iteration.
|
||||||
*/
|
*/
|
||||||
angular.mock.$IntervalProvider = function() {
|
angular.mock.$IntervalProvider = function() {
|
||||||
this.$get = ['$rootScope', '$q',
|
this.$get = ['$browser', '$rootScope', '$q', '$$q',
|
||||||
function($rootScope, $q) {
|
function($browser, $rootScope, $q, $$q) {
|
||||||
var repeatFns = [],
|
var repeatFns = [],
|
||||||
nextRepeatId = 0,
|
nextRepeatId = 0,
|
||||||
now = 0;
|
now = 0;
|
||||||
|
|
||||||
var $interval = function(fn, delay, count, invokeApply) {
|
var $interval = function(fn, delay, count, invokeApply) {
|
||||||
var deferred = $q.defer(),
|
var iteration = 0,
|
||||||
promise = deferred.promise,
|
skipApply = (angular.isDefined(invokeApply) && !invokeApply),
|
||||||
iteration = 0,
|
deferred = (skipApply ? $$q : $q).defer(),
|
||||||
skipApply = (angular.isDefined(invokeApply) && !invokeApply);
|
promise = deferred.promise;
|
||||||
|
|
||||||
count = (angular.isDefined(count)) ? count : 0;
|
count = (angular.isDefined(count)) ? count : 0;
|
||||||
promise.then(null, null, fn);
|
promise.then(null, null, fn);
|
||||||
|
@ -494,7 +494,11 @@ angular.mock.$IntervalProvider = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipApply) $rootScope.$apply();
|
if (skipApply) {
|
||||||
|
$browser.defer.flush();
|
||||||
|
} else {
|
||||||
|
$rootScope.$apply();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repeatFns.push({
|
repeatFns.push({
|
||||||
|
@ -1115,7 +1119,7 @@ angular.mock.dump = function(object) {
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
angular.mock.$HttpBackendProvider = function() {
|
angular.mock.$HttpBackendProvider = function() {
|
||||||
this.$get = ['$rootScope', createHttpBackendMock];
|
this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1132,7 +1136,7 @@ angular.mock.$HttpBackendProvider = function() {
|
||||||
* @param {Object=} $browser Auto-flushing enabled if specified
|
* @param {Object=} $browser Auto-flushing enabled if specified
|
||||||
* @return {Object} Instance of $httpBackend mock
|
* @return {Object} Instance of $httpBackend mock
|
||||||
*/
|
*/
|
||||||
function createHttpBackendMock($rootScope, $delegate, $browser) {
|
function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||||
var definitions = [],
|
var definitions = [],
|
||||||
expectations = [],
|
expectations = [],
|
||||||
responses = [],
|
responses = [],
|
||||||
|
@ -1145,7 +1149,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
return function() {
|
return function() {
|
||||||
return angular.isNumber(status)
|
return angular.isNumber(status)
|
||||||
? [status, data, headers, statusText]
|
? [status, data, headers, statusText]
|
||||||
: [200, status, data];
|
: [200, status, data, headers];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1162,7 +1166,9 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapResponse(wrapped) {
|
function wrapResponse(wrapped) {
|
||||||
if (!$browser && timeout && timeout.then) timeout.then(handleTimeout);
|
if (!$browser && timeout) {
|
||||||
|
timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
return handleResponse;
|
return handleResponse;
|
||||||
|
|
||||||
|
@ -1277,7 +1283,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
||||||
* and returns true if the url match the current definition.
|
* and returns true if the url match the current definition.
|
||||||
* @param {(Object|function(Object))=} headers HTTP headers.
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1291,7 +1297,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
||||||
* and returns true if the url match the current definition.
|
* and returns true if the url match the current definition.
|
||||||
* @param {(Object|function(Object))=} headers HTTP headers.
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1305,7 +1311,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
||||||
* and returns true if the url match the current definition.
|
* and returns true if the url match the current definition.
|
||||||
* @param {(Object|function(Object))=} headers HTTP headers.
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1321,7 +1327,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
||||||
* data string and returns true if the data is as expected.
|
* data string and returns true if the data is as expected.
|
||||||
* @param {(Object|function(Object))=} headers HTTP headers.
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1337,7 +1343,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
||||||
* data string and returns true if the data is as expected.
|
* data string and returns true if the data is as expected.
|
||||||
* @param {(Object|function(Object))=} headers HTTP headers.
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1350,7 +1356,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
*
|
*
|
||||||
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
||||||
* and returns true if the url match the current definition.
|
* and returns true if the url match the current definition.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1371,7 +1377,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* is in JSON format.
|
* is in JSON format.
|
||||||
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
||||||
* object and returns true if the headers match the current expectation.
|
* object and returns true if the headers match the current expectation.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*
|
*
|
||||||
|
@ -1406,7 +1412,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
||||||
* and returns true if the url match the current definition.
|
* and returns true if the url match the current definition.
|
||||||
* @param {Object=} headers HTTP headers.
|
* @param {Object=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled. See #expect for more info.
|
* order to change how a matched request is handled. See #expect for more info.
|
||||||
*/
|
*/
|
||||||
|
@ -1420,7 +1426,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
||||||
* and returns true if the url match the current definition.
|
* and returns true if the url match the current definition.
|
||||||
* @param {Object=} headers HTTP headers.
|
* @param {Object=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1434,7 +1440,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
||||||
* and returns true if the url match the current definition.
|
* and returns true if the url match the current definition.
|
||||||
* @param {Object=} headers HTTP headers.
|
* @param {Object=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1451,7 +1457,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* receives data string and returns true if the data is as expected, or Object if request body
|
* receives data string and returns true if the data is as expected, or Object if request body
|
||||||
* is in JSON format.
|
* is in JSON format.
|
||||||
* @param {Object=} headers HTTP headers.
|
* @param {Object=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1468,7 +1474,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* receives data string and returns true if the data is as expected, or Object if request body
|
* receives data string and returns true if the data is as expected, or Object if request body
|
||||||
* is in JSON format.
|
* is in JSON format.
|
||||||
* @param {Object=} headers HTTP headers.
|
* @param {Object=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1485,7 +1491,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
* receives data string and returns true if the data is as expected, or Object if request body
|
* receives data string and returns true if the data is as expected, or Object if request body
|
||||||
* is in JSON format.
|
* is in JSON format.
|
||||||
* @param {Object=} headers HTTP headers.
|
* @param {Object=} headers HTTP headers.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1498,7 +1504,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
*
|
*
|
||||||
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
|
||||||
* and returns true if the url match the current definition.
|
* and returns true if the url match the current definition.
|
||||||
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
||||||
* request is handled. You can save this object for later use and invoke `respond` again in
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
||||||
* order to change how a matched request is handled.
|
* order to change how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
|
@ -1636,7 +1642,9 @@ function MockHttpExpectation(method, url, data, headers) {
|
||||||
if (angular.isUndefined(data)) return true;
|
if (angular.isUndefined(data)) return true;
|
||||||
if (data && angular.isFunction(data.test)) return data.test(d);
|
if (data && angular.isFunction(data.test)) return data.test(d);
|
||||||
if (data && angular.isFunction(data)) return data(d);
|
if (data && angular.isFunction(data)) return data(d);
|
||||||
if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
|
if (data && !angular.isString(data)) {
|
||||||
|
return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
|
||||||
|
}
|
||||||
return data == d;
|
return data == d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1828,6 +1836,7 @@ angular.module('ngMock', ['ng']).provider({
|
||||||
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
|
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
|
||||||
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
|
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
|
||||||
$provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
|
$provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
|
||||||
|
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2033,7 +2042,93 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||||
*/
|
*/
|
||||||
angular.mock.e2e = {};
|
angular.mock.e2e = {};
|
||||||
angular.mock.e2e.$httpBackendDecorator =
|
angular.mock.e2e.$httpBackendDecorator =
|
||||||
['$rootScope', '$delegate', '$browser', createHttpBackendMock];
|
['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc type
|
||||||
|
* @name $rootScope.Scope
|
||||||
|
* @module ngMock
|
||||||
|
* @description
|
||||||
|
* {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
|
||||||
|
* methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
|
||||||
|
* `ngMock` module is loaded.
|
||||||
|
*
|
||||||
|
* In addition to all the regular `Scope` methods, the following helper methods are available:
|
||||||
|
*/
|
||||||
|
angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
||||||
|
|
||||||
|
var $rootScopePrototype = Object.getPrototypeOf($delegate);
|
||||||
|
|
||||||
|
$rootScopePrototype.$countChildScopes = countChildScopes;
|
||||||
|
$rootScopePrototype.$countWatchers = countWatchers;
|
||||||
|
|
||||||
|
return $delegate;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name $rootScope.Scope#$countChildScopes
|
||||||
|
* @module ngMock
|
||||||
|
* @description
|
||||||
|
* Counts all the direct and indirect child scopes of the current scope.
|
||||||
|
*
|
||||||
|
* The current scope is excluded from the count. The count includes all isolate child scopes.
|
||||||
|
*
|
||||||
|
* @returns {number} Total number of child scopes.
|
||||||
|
*/
|
||||||
|
function countChildScopes() {
|
||||||
|
// jshint validthis: true
|
||||||
|
var count = 0; // exclude the current scope
|
||||||
|
var pendingChildHeads = [this.$$childHead];
|
||||||
|
var currentScope;
|
||||||
|
|
||||||
|
while (pendingChildHeads.length) {
|
||||||
|
currentScope = pendingChildHeads.shift();
|
||||||
|
|
||||||
|
while (currentScope) {
|
||||||
|
count += 1;
|
||||||
|
pendingChildHeads.push(currentScope.$$childHead);
|
||||||
|
currentScope = currentScope.$$nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name $rootScope.Scope#$countWatchers
|
||||||
|
* @module ngMock
|
||||||
|
* @description
|
||||||
|
* Counts all the watchers of direct and indirect child scopes of the current scope.
|
||||||
|
*
|
||||||
|
* The watchers of the current scope are included in the count and so are all the watchers of
|
||||||
|
* isolate child scopes.
|
||||||
|
*
|
||||||
|
* @returns {number} Total number of watchers.
|
||||||
|
*/
|
||||||
|
function countWatchers() {
|
||||||
|
// jshint validthis: true
|
||||||
|
var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
|
||||||
|
var pendingChildHeads = [this.$$childHead];
|
||||||
|
var currentScope;
|
||||||
|
|
||||||
|
while (pendingChildHeads.length) {
|
||||||
|
currentScope = pendingChildHeads.shift();
|
||||||
|
|
||||||
|
while (currentScope) {
|
||||||
|
count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
|
||||||
|
pendingChildHeads.push(currentScope.$$childHead);
|
||||||
|
currentScope = currentScope.$$nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
|
||||||
if (window.jasmine || window.mocha) {
|
if (window.jasmine || window.mocha) {
|
||||||
|
|
667
src/main/webapp/js/lib/angular-resource.js
vendored
Normal file
667
src/main/webapp/js/lib/angular-resource.js
vendored
Normal file
|
@ -0,0 +1,667 @@
|
||||||
|
/**
|
||||||
|
* @license AngularJS v1.3.11
|
||||||
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
|
* License: MIT
|
||||||
|
*/
|
||||||
|
(function(window, angular, undefined) {'use strict';
|
||||||
|
|
||||||
|
var $resourceMinErr = angular.$$minErr('$resource');
|
||||||
|
|
||||||
|
// Helper functions and regex to lookup a dotted path on an object
|
||||||
|
// stopping at undefined/null. The path must be composed of ASCII
|
||||||
|
// identifiers (just like $parse)
|
||||||
|
var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;
|
||||||
|
|
||||||
|
function isValidDottedPath(path) {
|
||||||
|
return (path != null && path !== '' && path !== 'hasOwnProperty' &&
|
||||||
|
MEMBER_NAME_REGEX.test('.' + path));
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookupDottedPath(obj, path) {
|
||||||
|
if (!isValidDottedPath(path)) {
|
||||||
|
throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
|
||||||
|
}
|
||||||
|
var keys = path.split('.');
|
||||||
|
for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) {
|
||||||
|
var key = keys[i];
|
||||||
|
obj = (obj !== null) ? obj[key] : undefined;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a shallow copy of an object and clear other fields from the destination
|
||||||
|
*/
|
||||||
|
function shallowClearAndCopy(src, dst) {
|
||||||
|
dst = dst || {};
|
||||||
|
|
||||||
|
angular.forEach(dst, function(value, key) {
|
||||||
|
delete dst[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var key in src) {
|
||||||
|
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||||
|
dst[key] = src[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc module
|
||||||
|
* @name ngResource
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* # ngResource
|
||||||
|
*
|
||||||
|
* The `ngResource` module provides interaction support with RESTful services
|
||||||
|
* via the $resource service.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <div doc-module-components="ngResource"></div>
|
||||||
|
*
|
||||||
|
* See {@link ngResource.$resource `$resource`} for usage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc service
|
||||||
|
* @name $resource
|
||||||
|
* @requires $http
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* A factory which creates a resource object that lets you interact with
|
||||||
|
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
|
||||||
|
*
|
||||||
|
* The returned resource object has action methods which provide high-level behaviors without
|
||||||
|
* the need to interact with the low level {@link ng.$http $http} service.
|
||||||
|
*
|
||||||
|
* Requires the {@link ngResource `ngResource`} module to be installed.
|
||||||
|
*
|
||||||
|
* By default, trailing slashes will be stripped from the calculated URLs,
|
||||||
|
* which can pose problems with server backends that do not expect that
|
||||||
|
* behavior. This can be disabled by configuring the `$resourceProvider` like
|
||||||
|
* this:
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
app.config(['$resourceProvider', function($resourceProvider) {
|
||||||
|
// Don't strip trailing slashes from calculated URLs
|
||||||
|
$resourceProvider.defaults.stripTrailingSlashes = false;
|
||||||
|
}]);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {string} url A parametrized URL template with parameters prefixed by `:` as in
|
||||||
|
* `/user/:username`. If you are using a URL with a port number (e.g.
|
||||||
|
* `http://example.com:8080/api`), it will be respected.
|
||||||
|
*
|
||||||
|
* If you are using a url with a suffix, just add the suffix, like this:
|
||||||
|
* `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
|
||||||
|
* or even `$resource('http://example.com/resource/:resource_id.:format')`
|
||||||
|
* If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
|
||||||
|
* collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
|
||||||
|
* can escape it with `/\.`.
|
||||||
|
*
|
||||||
|
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
|
||||||
|
* `actions` methods. If any of the parameter value is a function, it will be executed every time
|
||||||
|
* when a param value needs to be obtained for a request (unless the param was overridden).
|
||||||
|
*
|
||||||
|
* Each key value in the parameter object is first bound to url template if present and then any
|
||||||
|
* excess keys are appended to the url search query after the `?`.
|
||||||
|
*
|
||||||
|
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||||
|
* URL `/path/greet?salutation=Hello`.
|
||||||
|
*
|
||||||
|
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
|
||||||
|
* from the corresponding property on the `data` object (provided when calling an action method). For
|
||||||
|
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
|
||||||
|
* will be `data.someProp`.
|
||||||
|
*
|
||||||
|
* @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
|
||||||
|
* the default set of resource actions. The declaration should be created in the format of {@link
|
||||||
|
* ng.$http#usage $http.config}:
|
||||||
|
*
|
||||||
|
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
|
||||||
|
* action2: {method:?, params:?, isArray:?, headers:?, ...},
|
||||||
|
* ...}
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
*
|
||||||
|
* - **`action`** – {string} – The name of action. This name becomes the name of the method on
|
||||||
|
* your resource object.
|
||||||
|
* - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
|
||||||
|
* `DELETE`, `JSONP`, etc).
|
||||||
|
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
|
||||||
|
* the parameter value is a function, it will be executed every time when a param value needs to
|
||||||
|
* be obtained for a request (unless the param was overridden).
|
||||||
|
* - **`url`** – {string} – action specific `url` override. The url templating is supported just
|
||||||
|
* like for the resource-level urls.
|
||||||
|
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
|
||||||
|
* see `returns` section.
|
||||||
|
* - **`transformRequest`** –
|
||||||
|
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||||
|
* transform function or an array of such functions. The transform function takes the http
|
||||||
|
* request body and headers and returns its transformed (typically serialized) version.
|
||||||
|
* By default, transformRequest will contain one function that checks if the request data is
|
||||||
|
* an object and serializes to using `angular.toJson`. To prevent this behavior, set
|
||||||
|
* `transformRequest` to an empty array: `transformRequest: []`
|
||||||
|
* - **`transformResponse`** –
|
||||||
|
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||||
|
* transform function or an array of such functions. The transform function takes the http
|
||||||
|
* response body and headers and returns its transformed (typically deserialized) version.
|
||||||
|
* By default, transformResponse will contain one function that checks if the response looks like
|
||||||
|
* a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
|
||||||
|
* `transformResponse` to an empty array: `transformResponse: []`
|
||||||
|
* - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||||
|
* GET request, otherwise if a cache instance built with
|
||||||
|
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
|
||||||
|
* caching.
|
||||||
|
* - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
|
||||||
|
* should abort the request when resolved.
|
||||||
|
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
|
||||||
|
* XHR object. See
|
||||||
|
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
|
||||||
|
* for more information.
|
||||||
|
* - **`responseType`** - `{string}` - see
|
||||||
|
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
|
||||||
|
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
|
||||||
|
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
|
||||||
|
* with `http response` object. See {@link ng.$http $http interceptors}.
|
||||||
|
*
|
||||||
|
* @param {Object} options Hash with custom settings that should extend the
|
||||||
|
* default `$resourceProvider` behavior. The only supported option is
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
*
|
||||||
|
* - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
|
||||||
|
* slashes from any calculated URL will be stripped. (Defaults to true.)
|
||||||
|
*
|
||||||
|
* @returns {Object} A resource "class" object with methods for the default set of resource actions
|
||||||
|
* optionally extended with custom `actions`. The default set contains these actions:
|
||||||
|
* ```js
|
||||||
|
* { 'get': {method:'GET'},
|
||||||
|
* 'save': {method:'POST'},
|
||||||
|
* 'query': {method:'GET', isArray:true},
|
||||||
|
* 'remove': {method:'DELETE'},
|
||||||
|
* 'delete': {method:'DELETE'} };
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Calling these methods invoke an {@link ng.$http} with the specified http method,
|
||||||
|
* destination and parameters. When the data is returned from the server then the object is an
|
||||||
|
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
|
||||||
|
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
|
||||||
|
* read, update, delete) on server-side data like this:
|
||||||
|
* ```js
|
||||||
|
* var User = $resource('/user/:userId', {userId:'@id'});
|
||||||
|
* var user = User.get({userId:123}, function() {
|
||||||
|
* user.abc = true;
|
||||||
|
* user.$save();
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* It is important to realize that invoking a $resource object method immediately returns an
|
||||||
|
* empty reference (object or array depending on `isArray`). Once the data is returned from the
|
||||||
|
* server the existing reference is populated with the actual data. This is a useful trick since
|
||||||
|
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
|
||||||
|
* object results in no rendering, once the data arrives from the server then the object is
|
||||||
|
* populated with the data and the view automatically re-renders itself showing the new data. This
|
||||||
|
* means that in most cases one never has to write a callback function for the action methods.
|
||||||
|
*
|
||||||
|
* The action methods on the class object or instance object can be invoked with the following
|
||||||
|
* parameters:
|
||||||
|
*
|
||||||
|
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
|
||||||
|
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
|
||||||
|
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||||
|
*
|
||||||
|
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
|
||||||
|
* with (httpResponse) argument.
|
||||||
|
*
|
||||||
|
* Class actions return empty instance (with additional properties below).
|
||||||
|
* Instance actions return promise of the action.
|
||||||
|
*
|
||||||
|
* The Resource instances and collection have these additional properties:
|
||||||
|
*
|
||||||
|
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
|
||||||
|
* instance or collection.
|
||||||
|
*
|
||||||
|
* On success, the promise is resolved with the same resource instance or collection object,
|
||||||
|
* updated with data from server. This makes it easy to use in
|
||||||
|
* {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
|
||||||
|
* rendering until the resource(s) are loaded.
|
||||||
|
*
|
||||||
|
* On failure, the promise is resolved with the {@link ng.$http http response} object, without
|
||||||
|
* the `resource` property.
|
||||||
|
*
|
||||||
|
* If an interceptor object was provided, the promise will instead be resolved with the value
|
||||||
|
* returned by the interceptor.
|
||||||
|
*
|
||||||
|
* - `$resolved`: `true` after first server interaction is completed (either with success or
|
||||||
|
* rejection), `false` before that. Knowing if the Resource has been resolved is useful in
|
||||||
|
* data-binding.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* # Credit card resource
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
// Define CreditCard class
|
||||||
|
var CreditCard = $resource('/user/:userId/card/:cardId',
|
||||||
|
{userId:123, cardId:'@id'}, {
|
||||||
|
charge: {method:'POST', params:{charge:true}}
|
||||||
|
});
|
||||||
|
|
||||||
|
// We can retrieve a collection from the server
|
||||||
|
var cards = CreditCard.query(function() {
|
||||||
|
// GET: /user/123/card
|
||||||
|
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
|
||||||
|
|
||||||
|
var card = cards[0];
|
||||||
|
// each item is an instance of CreditCard
|
||||||
|
expect(card instanceof CreditCard).toEqual(true);
|
||||||
|
card.name = "J. Smith";
|
||||||
|
// non GET methods are mapped onto the instances
|
||||||
|
card.$save();
|
||||||
|
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
|
||||||
|
// server returns: {id:456, number:'1234', name: 'J. Smith'};
|
||||||
|
|
||||||
|
// our custom method is mapped as well.
|
||||||
|
card.$charge({amount:9.99});
|
||||||
|
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
|
||||||
|
});
|
||||||
|
|
||||||
|
// we can create an instance as well
|
||||||
|
var newCard = new CreditCard({number:'0123'});
|
||||||
|
newCard.name = "Mike Smith";
|
||||||
|
newCard.$save();
|
||||||
|
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
|
||||||
|
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
|
||||||
|
expect(newCard.id).toEqual(789);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The object returned from this function execution is a resource "class" which has "static" method
|
||||||
|
* for each action in the definition.
|
||||||
|
*
|
||||||
|
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
|
||||||
|
* `headers`.
|
||||||
|
* When the data is returned from the server then the object is an instance of the resource type and
|
||||||
|
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
|
||||||
|
* operations (create, read, update, delete) on server-side data.
|
||||||
|
|
||||||
|
```js
|
||||||
|
var User = $resource('/user/:userId', {userId:'@id'});
|
||||||
|
User.get({userId:123}, function(user) {
|
||||||
|
user.abc = true;
|
||||||
|
user.$save();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
*
|
||||||
|
* It's worth noting that the success callback for `get`, `query` and other methods gets passed
|
||||||
|
* in the response that came from the server as well as $http header getter function, so one
|
||||||
|
* could rewrite the above example and get access to http headers as:
|
||||||
|
*
|
||||||
|
```js
|
||||||
|
var User = $resource('/user/:userId', {userId:'@id'});
|
||||||
|
User.get({userId:123}, function(u, getResponseHeaders){
|
||||||
|
u.abc = true;
|
||||||
|
u.$save(function(u, putResponseHeaders) {
|
||||||
|
//u => saved user object
|
||||||
|
//putResponseHeaders => $http header getter
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
*
|
||||||
|
* You can also access the raw `$http` promise via the `$promise` property on the object returned
|
||||||
|
*
|
||||||
|
```
|
||||||
|
var User = $resource('/user/:userId', {userId:'@id'});
|
||||||
|
User.get({userId:123})
|
||||||
|
.$promise.then(function(user) {
|
||||||
|
$scope.user = user;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
* # Creating a custom 'PUT' request
|
||||||
|
* In this example we create a custom method on our resource to make a PUT request
|
||||||
|
* ```js
|
||||||
|
* var app = angular.module('app', ['ngResource', 'ngRoute']);
|
||||||
|
*
|
||||||
|
* // Some APIs expect a PUT request in the format URL/object/ID
|
||||||
|
* // Here we are creating an 'update' method
|
||||||
|
* app.factory('Notes', ['$resource', function($resource) {
|
||||||
|
* return $resource('/notes/:id', null,
|
||||||
|
* {
|
||||||
|
* 'update': { method:'PUT' }
|
||||||
|
* });
|
||||||
|
* }]);
|
||||||
|
*
|
||||||
|
* // In our controller we get the ID from the URL using ngRoute and $routeParams
|
||||||
|
* // We pass in $routeParams and our Notes factory along with $scope
|
||||||
|
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
|
||||||
|
function($scope, $routeParams, Notes) {
|
||||||
|
* // First get a note object from the factory
|
||||||
|
* var note = Notes.get({ id:$routeParams.id });
|
||||||
|
* $id = note.id;
|
||||||
|
*
|
||||||
|
* // Now call update passing in the ID first then the object you are updating
|
||||||
|
* Notes.update({ id:$id }, note);
|
||||||
|
*
|
||||||
|
* // This will PUT /notes/ID with the note object in the request payload
|
||||||
|
* }]);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
angular.module('ngResource', ['ng']).
|
||||||
|
provider('$resource', function() {
|
||||||
|
var provider = this;
|
||||||
|
|
||||||
|
this.defaults = {
|
||||||
|
// Strip slashes by default
|
||||||
|
stripTrailingSlashes: true,
|
||||||
|
|
||||||
|
// Default actions configuration
|
||||||
|
actions: {
|
||||||
|
'get': {method: 'GET'},
|
||||||
|
'save': {method: 'POST'},
|
||||||
|
'query': {method: 'GET', isArray: true},
|
||||||
|
'remove': {method: 'DELETE'},
|
||||||
|
'delete': {method: 'DELETE'}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$get = ['$http', '$q', function($http, $q) {
|
||||||
|
|
||||||
|
var noop = angular.noop,
|
||||||
|
forEach = angular.forEach,
|
||||||
|
extend = angular.extend,
|
||||||
|
copy = angular.copy,
|
||||||
|
isFunction = angular.isFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
|
||||||
|
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
|
||||||
|
* (pchar) allowed in path segments:
|
||||||
|
* segment = *pchar
|
||||||
|
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||||
|
* pct-encoded = "%" HEXDIG HEXDIG
|
||||||
|
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||||
|
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||||
|
* / "*" / "+" / "," / ";" / "="
|
||||||
|
*/
|
||||||
|
function encodeUriSegment(val) {
|
||||||
|
return encodeUriQuery(val, true).
|
||||||
|
replace(/%26/gi, '&').
|
||||||
|
replace(/%3D/gi, '=').
|
||||||
|
replace(/%2B/gi, '+');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is intended for encoding *key* or *value* parts of query component. We need a
|
||||||
|
* custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
|
||||||
|
* have to be encoded per http://tools.ietf.org/html/rfc3986:
|
||||||
|
* query = *( pchar / "/" / "?" )
|
||||||
|
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||||
|
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||||
|
* pct-encoded = "%" HEXDIG HEXDIG
|
||||||
|
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||||
|
* / "*" / "+" / "," / ";" / "="
|
||||||
|
*/
|
||||||
|
function encodeUriQuery(val, pctEncodeSpaces) {
|
||||||
|
return encodeURIComponent(val).
|
||||||
|
replace(/%40/gi, '@').
|
||||||
|
replace(/%3A/gi, ':').
|
||||||
|
replace(/%24/g, '$').
|
||||||
|
replace(/%2C/gi, ',').
|
||||||
|
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Route(template, defaults) {
|
||||||
|
this.template = template;
|
||||||
|
this.defaults = extend({}, provider.defaults, defaults);
|
||||||
|
this.urlParams = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Route.prototype = {
|
||||||
|
setUrlParams: function(config, params, actionUrl) {
|
||||||
|
var self = this,
|
||||||
|
url = actionUrl || self.template,
|
||||||
|
val,
|
||||||
|
encodedVal;
|
||||||
|
|
||||||
|
var urlParams = self.urlParams = {};
|
||||||
|
forEach(url.split(/\W/), function(param) {
|
||||||
|
if (param === 'hasOwnProperty') {
|
||||||
|
throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
|
||||||
|
}
|
||||||
|
if (!(new RegExp("^\\d+$").test(param)) && param &&
|
||||||
|
(new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
|
||||||
|
urlParams[param] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
url = url.replace(/\\:/g, ':');
|
||||||
|
|
||||||
|
params = params || {};
|
||||||
|
forEach(self.urlParams, function(_, urlParam) {
|
||||||
|
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
|
||||||
|
if (angular.isDefined(val) && val !== null) {
|
||||||
|
encodedVal = encodeUriSegment(val);
|
||||||
|
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
|
||||||
|
return encodedVal + p1;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
|
||||||
|
leadingSlashes, tail) {
|
||||||
|
if (tail.charAt(0) == '/') {
|
||||||
|
return tail;
|
||||||
|
} else {
|
||||||
|
return leadingSlashes + tail;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// strip trailing slashes and set the url (unless this behavior is specifically disabled)
|
||||||
|
if (self.defaults.stripTrailingSlashes) {
|
||||||
|
url = url.replace(/\/+$/, '') || '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
// then replace collapse `/.` if found in the last URL path segment before the query
|
||||||
|
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
|
||||||
|
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
|
||||||
|
// replace escaped `/\.` with `/.`
|
||||||
|
config.url = url.replace(/\/\\\./, '/.');
|
||||||
|
|
||||||
|
|
||||||
|
// set params - delegate param encoding to $http
|
||||||
|
forEach(params, function(value, key) {
|
||||||
|
if (!self.urlParams[key]) {
|
||||||
|
config.params = config.params || {};
|
||||||
|
config.params[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function resourceFactory(url, paramDefaults, actions, options) {
|
||||||
|
var route = new Route(url, options);
|
||||||
|
|
||||||
|
actions = extend({}, provider.defaults.actions, actions);
|
||||||
|
|
||||||
|
function extractParams(data, actionParams) {
|
||||||
|
var ids = {};
|
||||||
|
actionParams = extend({}, paramDefaults, actionParams);
|
||||||
|
forEach(actionParams, function(value, key) {
|
||||||
|
if (isFunction(value)) { value = value(); }
|
||||||
|
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
|
||||||
|
lookupDottedPath(data, value.substr(1)) : value;
|
||||||
|
});
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultResponseInterceptor(response) {
|
||||||
|
return response.resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Resource(value) {
|
||||||
|
shallowClearAndCopy(value || {}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource.prototype.toJSON = function() {
|
||||||
|
var data = extend({}, this);
|
||||||
|
delete data.$promise;
|
||||||
|
delete data.$resolved;
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
forEach(actions, function(action, name) {
|
||||||
|
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
|
||||||
|
|
||||||
|
Resource[name] = function(a1, a2, a3, a4) {
|
||||||
|
var params = {}, data, success, error;
|
||||||
|
|
||||||
|
/* jshint -W086 */ /* (purposefully fall through case statements) */
|
||||||
|
switch (arguments.length) {
|
||||||
|
case 4:
|
||||||
|
error = a4;
|
||||||
|
success = a3;
|
||||||
|
//fallthrough
|
||||||
|
case 3:
|
||||||
|
case 2:
|
||||||
|
if (isFunction(a2)) {
|
||||||
|
if (isFunction(a1)) {
|
||||||
|
success = a1;
|
||||||
|
error = a2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = a2;
|
||||||
|
error = a3;
|
||||||
|
//fallthrough
|
||||||
|
} else {
|
||||||
|
params = a1;
|
||||||
|
data = a2;
|
||||||
|
success = a3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if (isFunction(a1)) success = a1;
|
||||||
|
else if (hasBody) data = a1;
|
||||||
|
else params = a1;
|
||||||
|
break;
|
||||||
|
case 0: break;
|
||||||
|
default:
|
||||||
|
throw $resourceMinErr('badargs',
|
||||||
|
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
|
||||||
|
arguments.length);
|
||||||
|
}
|
||||||
|
/* jshint +W086 */ /* (purposefully fall through case statements) */
|
||||||
|
|
||||||
|
var isInstanceCall = this instanceof Resource;
|
||||||
|
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
|
||||||
|
var httpConfig = {};
|
||||||
|
var responseInterceptor = action.interceptor && action.interceptor.response ||
|
||||||
|
defaultResponseInterceptor;
|
||||||
|
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
|
||||||
|
undefined;
|
||||||
|
|
||||||
|
forEach(action, function(value, key) {
|
||||||
|
if (key != 'params' && key != 'isArray' && key != 'interceptor') {
|
||||||
|
httpConfig[key] = copy(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasBody) httpConfig.data = data;
|
||||||
|
route.setUrlParams(httpConfig,
|
||||||
|
extend({}, extractParams(data, action.params || {}), params),
|
||||||
|
action.url);
|
||||||
|
|
||||||
|
var promise = $http(httpConfig).then(function(response) {
|
||||||
|
var data = response.data,
|
||||||
|
promise = value.$promise;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
// Need to convert action.isArray to boolean in case it is undefined
|
||||||
|
// jshint -W018
|
||||||
|
if (angular.isArray(data) !== (!!action.isArray)) {
|
||||||
|
throw $resourceMinErr('badcfg',
|
||||||
|
'Error in resource configuration for action `{0}`. Expected response to ' +
|
||||||
|
'contain an {1} but got an {2}', name, action.isArray ? 'array' : 'object',
|
||||||
|
angular.isArray(data) ? 'array' : 'object');
|
||||||
|
}
|
||||||
|
// jshint +W018
|
||||||
|
if (action.isArray) {
|
||||||
|
value.length = 0;
|
||||||
|
forEach(data, function(item) {
|
||||||
|
if (typeof item === "object") {
|
||||||
|
value.push(new Resource(item));
|
||||||
|
} else {
|
||||||
|
// Valid JSON values may be string literals, and these should not be converted
|
||||||
|
// into objects. These items will not have access to the Resource prototype
|
||||||
|
// methods, but unfortunately there
|
||||||
|
value.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
shallowClearAndCopy(data, value);
|
||||||
|
value.$promise = promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value.$resolved = true;
|
||||||
|
|
||||||
|
response.resource = value;
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}, function(response) {
|
||||||
|
value.$resolved = true;
|
||||||
|
|
||||||
|
(error || noop)(response);
|
||||||
|
|
||||||
|
return $q.reject(response);
|
||||||
|
});
|
||||||
|
|
||||||
|
promise = promise.then(
|
||||||
|
function(response) {
|
||||||
|
var value = responseInterceptor(response);
|
||||||
|
(success || noop)(value, response.headers);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
responseErrorInterceptor);
|
||||||
|
|
||||||
|
if (!isInstanceCall) {
|
||||||
|
// we are creating instance / collection
|
||||||
|
// - set the initial promise
|
||||||
|
// - return the instance / collection
|
||||||
|
value.$promise = promise;
|
||||||
|
value.$resolved = false;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// instance call
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Resource.prototype['$' + name] = function(params, success, error) {
|
||||||
|
if (isFunction(params)) {
|
||||||
|
error = success; success = params; params = {};
|
||||||
|
}
|
||||||
|
var result = Resource[name].call(this, params, this, success, error);
|
||||||
|
return result.$promise || result;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
Resource.bind = function(additionalParamDefaults) {
|
||||||
|
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
|
||||||
|
};
|
||||||
|
|
||||||
|
return Resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceFactory;
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
})(window, window.angular);
|
925
src/main/webapp/js/lib/angular-route.js
vendored
925
src/main/webapp/js/lib/angular-route.js
vendored
|
@ -1,925 +0,0 @@
|
||||||
/**
|
|
||||||
* @license AngularJS v1.2.24-build.423+sha.7e02fa0
|
|
||||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
|
||||||
* License: MIT
|
|
||||||
*/
|
|
||||||
(function(window, angular, undefined) {'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc module
|
|
||||||
* @name ngRoute
|
|
||||||
* @description
|
|
||||||
*
|
|
||||||
* # ngRoute
|
|
||||||
*
|
|
||||||
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
|
|
||||||
*
|
|
||||||
* ## Example
|
|
||||||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* <div doc-module-components="ngRoute"></div>
|
|
||||||
*/
|
|
||||||
/* global -ngRouteModule */
|
|
||||||
var ngRouteModule = angular.module('ngRoute', ['ng']).
|
|
||||||
provider('$route', $RouteProvider);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc provider
|
|
||||||
* @name $routeProvider
|
|
||||||
* @kind function
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
*
|
|
||||||
* Used for configuring routes.
|
|
||||||
*
|
|
||||||
* ## Example
|
|
||||||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
|
|
||||||
*
|
|
||||||
* ## Dependencies
|
|
||||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
|
||||||
*/
|
|
||||||
function $RouteProvider(){
|
|
||||||
function inherit(parent, extra) {
|
|
||||||
return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
var routes = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc method
|
|
||||||
* @name $routeProvider#when
|
|
||||||
*
|
|
||||||
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
|
|
||||||
* contains redundant trailing slash or is missing one, the route will still match and the
|
|
||||||
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
|
|
||||||
* route definition.
|
|
||||||
*
|
|
||||||
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
|
|
||||||
* to the next slash are matched and stored in `$routeParams` under the given `name`
|
|
||||||
* when the route matches.
|
|
||||||
* * `path` can contain named groups starting with a colon and ending with a star:
|
|
||||||
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
|
|
||||||
* when the route matches.
|
|
||||||
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
|
|
||||||
*
|
|
||||||
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
|
|
||||||
* `/color/brown/largecode/code/with/slashes/edit` and extract:
|
|
||||||
*
|
|
||||||
* * `color: brown`
|
|
||||||
* * `largecode: code/with/slashes`.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
|
||||||
* match.
|
|
||||||
*
|
|
||||||
* Object properties:
|
|
||||||
*
|
|
||||||
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
|
|
||||||
* newly created scope or the name of a {@link angular.Module#controller registered
|
|
||||||
* controller} if passed as a string.
|
|
||||||
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
|
|
||||||
* published to scope under the `controllerAs` name.
|
|
||||||
* - `template` – `{string=|function()=}` – html template as a string or a function that
|
|
||||||
* returns an html template as a string which should be used by {@link
|
|
||||||
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
|
|
||||||
* This property takes precedence over `templateUrl`.
|
|
||||||
*
|
|
||||||
* If `template` is a function, it will be called with the following parameters:
|
|
||||||
*
|
|
||||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
|
||||||
* `$location.path()` by applying the current route
|
|
||||||
*
|
|
||||||
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
|
|
||||||
* template that should be used by {@link ngRoute.directive:ngView ngView}.
|
|
||||||
*
|
|
||||||
* If `templateUrl` is a function, it will be called with the following parameters:
|
|
||||||
*
|
|
||||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
|
||||||
* `$location.path()` by applying the current route
|
|
||||||
*
|
|
||||||
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
|
|
||||||
* be injected into the controller. If any of these dependencies are promises, the router
|
|
||||||
* will wait for them all to be resolved or one to be rejected before the controller is
|
|
||||||
* instantiated.
|
|
||||||
* If all the promises are resolved successfully, the values of the resolved promises are
|
|
||||||
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
|
|
||||||
* fired. If any of the promises are rejected the
|
|
||||||
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
|
|
||||||
* is:
|
|
||||||
*
|
|
||||||
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
|
|
||||||
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
|
|
||||||
* Otherwise if function, then it is {@link auto.$injector#invoke injected}
|
|
||||||
* and the return value is treated as the dependency. If the result is a promise, it is
|
|
||||||
* resolved before its value is injected into the controller. Be aware that
|
|
||||||
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
|
|
||||||
* functions. Use `$route.current.params` to access the new route parameters, instead.
|
|
||||||
*
|
|
||||||
* - `redirectTo` – {(string|function())=} – value to update
|
|
||||||
* {@link ng.$location $location} path with and trigger route redirection.
|
|
||||||
*
|
|
||||||
* If `redirectTo` is a function, it will be called with the following parameters:
|
|
||||||
*
|
|
||||||
* - `{Object.<string>}` - route parameters extracted from the current
|
|
||||||
* `$location.path()` by applying the current route templateUrl.
|
|
||||||
* - `{string}` - current `$location.path()`
|
|
||||||
* - `{Object}` - current `$location.search()`
|
|
||||||
*
|
|
||||||
* The custom `redirectTo` function is expected to return a string which will be used
|
|
||||||
* to update `$location.path()` and `$location.search()`.
|
|
||||||
*
|
|
||||||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
|
|
||||||
* or `$location.hash()` changes.
|
|
||||||
*
|
|
||||||
* If the option is set to `false` and url in the browser changes, then
|
|
||||||
* `$routeUpdate` event is broadcasted on the root scope.
|
|
||||||
*
|
|
||||||
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
|
|
||||||
*
|
|
||||||
* If the option is set to `true`, then the particular route can be matched without being
|
|
||||||
* case sensitive
|
|
||||||
*
|
|
||||||
* @returns {Object} self
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Adds a new route definition to the `$route` service.
|
|
||||||
*/
|
|
||||||
this.when = function(path, route) {
|
|
||||||
routes[path] = angular.extend(
|
|
||||||
{reloadOnSearch: true},
|
|
||||||
route,
|
|
||||||
path && pathRegExp(path, route)
|
|
||||||
);
|
|
||||||
|
|
||||||
// create redirection for trailing slashes
|
|
||||||
if (path) {
|
|
||||||
var redirectPath = (path[path.length-1] == '/')
|
|
||||||
? path.substr(0, path.length-1)
|
|
||||||
: path +'/';
|
|
||||||
|
|
||||||
routes[redirectPath] = angular.extend(
|
|
||||||
{redirectTo: path},
|
|
||||||
pathRegExp(redirectPath, route)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param path {string} path
|
|
||||||
* @param opts {Object} options
|
|
||||||
* @return {?Object}
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Normalizes the given path, returning a regular expression
|
|
||||||
* and the original path.
|
|
||||||
*
|
|
||||||
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
|
|
||||||
*/
|
|
||||||
function pathRegExp(path, opts) {
|
|
||||||
var insensitive = opts.caseInsensitiveMatch,
|
|
||||||
ret = {
|
|
||||||
originalPath: path,
|
|
||||||
regexp: path
|
|
||||||
},
|
|
||||||
keys = ret.keys = [];
|
|
||||||
|
|
||||||
path = path
|
|
||||||
.replace(/([().])/g, '\\$1')
|
|
||||||
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){
|
|
||||||
var optional = option === '?' ? option : null;
|
|
||||||
var star = option === '*' ? option : null;
|
|
||||||
keys.push({ name: key, optional: !!optional });
|
|
||||||
slash = slash || '';
|
|
||||||
return ''
|
|
||||||
+ (optional ? '' : slash)
|
|
||||||
+ '(?:'
|
|
||||||
+ (optional ? slash : '')
|
|
||||||
+ (star && '(.+?)' || '([^/]+)')
|
|
||||||
+ (optional || '')
|
|
||||||
+ ')'
|
|
||||||
+ (optional || '');
|
|
||||||
})
|
|
||||||
.replace(/([\/$\*])/g, '\\$1');
|
|
||||||
|
|
||||||
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc method
|
|
||||||
* @name $routeProvider#otherwise
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Sets route definition that will be used on route change when no other route definition
|
|
||||||
* is matched.
|
|
||||||
*
|
|
||||||
* @param {Object} params Mapping information to be assigned to `$route.current`.
|
|
||||||
* @returns {Object} self
|
|
||||||
*/
|
|
||||||
this.otherwise = function(params) {
|
|
||||||
this.when(null, params);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
this.$get = ['$rootScope',
|
|
||||||
'$location',
|
|
||||||
'$routeParams',
|
|
||||||
'$q',
|
|
||||||
'$injector',
|
|
||||||
'$http',
|
|
||||||
'$templateCache',
|
|
||||||
'$sce',
|
|
||||||
function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc service
|
|
||||||
* @name $route
|
|
||||||
* @requires $location
|
|
||||||
* @requires $routeParams
|
|
||||||
*
|
|
||||||
* @property {Object} current Reference to the current route definition.
|
|
||||||
* The route definition contains:
|
|
||||||
*
|
|
||||||
* - `controller`: The controller constructor as define in route definition.
|
|
||||||
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
|
|
||||||
* controller instantiation. The `locals` contain
|
|
||||||
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
|
|
||||||
*
|
|
||||||
* - `$scope` - The current route scope.
|
|
||||||
* - `$template` - The current route template HTML.
|
|
||||||
*
|
|
||||||
* @property {Object} routes Object with all route configuration Objects as its properties.
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
|
|
||||||
* It watches `$location.url()` and tries to map the path to an existing route definition.
|
|
||||||
*
|
|
||||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
|
||||||
*
|
|
||||||
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
|
|
||||||
*
|
|
||||||
* The `$route` service is typically used in conjunction with the
|
|
||||||
* {@link ngRoute.directive:ngView `ngView`} directive and the
|
|
||||||
* {@link ngRoute.$routeParams `$routeParams`} service.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* This example shows how changing the URL hash causes the `$route` to match a route against the
|
|
||||||
* URL, and the `ngView` pulls in the partial.
|
|
||||||
*
|
|
||||||
* Note that this example is using {@link ng.directive:script inlined templates}
|
|
||||||
* to get it working on jsfiddle as well.
|
|
||||||
*
|
|
||||||
* <example name="$route-service" module="ngRouteExample"
|
|
||||||
* deps="angular-route.js" fixBase="true">
|
|
||||||
* <file name="index.html">
|
|
||||||
* <div ng-controller="MainController">
|
|
||||||
* Choose:
|
|
||||||
* <a href="Book/Moby">Moby</a> |
|
|
||||||
* <a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
|
||||||
* <a href="Book/Gatsby">Gatsby</a> |
|
|
||||||
* <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
|
||||||
* <a href="Book/Scarlet">Scarlet Letter</a><br/>
|
|
||||||
*
|
|
||||||
* <div ng-view></div>
|
|
||||||
*
|
|
||||||
* <hr />
|
|
||||||
*
|
|
||||||
* <pre>$location.path() = {{$location.path()}}</pre>
|
|
||||||
* <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
|
|
||||||
* <pre>$route.current.params = {{$route.current.params}}</pre>
|
|
||||||
* <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
|
|
||||||
* <pre>$routeParams = {{$routeParams}}</pre>
|
|
||||||
* </div>
|
|
||||||
* </file>
|
|
||||||
*
|
|
||||||
* <file name="book.html">
|
|
||||||
* controller: {{name}}<br />
|
|
||||||
* Book Id: {{params.bookId}}<br />
|
|
||||||
* </file>
|
|
||||||
*
|
|
||||||
* <file name="chapter.html">
|
|
||||||
* controller: {{name}}<br />
|
|
||||||
* Book Id: {{params.bookId}}<br />
|
|
||||||
* Chapter Id: {{params.chapterId}}
|
|
||||||
* </file>
|
|
||||||
*
|
|
||||||
* <file name="script.js">
|
|
||||||
* angular.module('ngRouteExample', ['ngRoute'])
|
|
||||||
*
|
|
||||||
* .controller('MainController', function($scope, $route, $routeParams, $location) {
|
|
||||||
* $scope.$route = $route;
|
|
||||||
* $scope.$location = $location;
|
|
||||||
* $scope.$routeParams = $routeParams;
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* .controller('BookController', function($scope, $routeParams) {
|
|
||||||
* $scope.name = "BookController";
|
|
||||||
* $scope.params = $routeParams;
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* .controller('ChapterController', function($scope, $routeParams) {
|
|
||||||
* $scope.name = "ChapterController";
|
|
||||||
* $scope.params = $routeParams;
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* .config(function($routeProvider, $locationProvider) {
|
|
||||||
* $routeProvider
|
|
||||||
* .when('/Book/:bookId', {
|
|
||||||
* templateUrl: 'book.html',
|
|
||||||
* controller: 'BookController',
|
|
||||||
* resolve: {
|
|
||||||
* // I will cause a 1 second delay
|
|
||||||
* delay: function($q, $timeout) {
|
|
||||||
* var delay = $q.defer();
|
|
||||||
* $timeout(delay.resolve, 1000);
|
|
||||||
* return delay.promise;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* })
|
|
||||||
* .when('/Book/:bookId/ch/:chapterId', {
|
|
||||||
* templateUrl: 'chapter.html',
|
|
||||||
* controller: 'ChapterController'
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* // configure html5 to get links working on jsfiddle
|
|
||||||
* $locationProvider.html5Mode(true);
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* </file>
|
|
||||||
*
|
|
||||||
* <file name="protractor.js" type="protractor">
|
|
||||||
* it('should load and compile correct template', function() {
|
|
||||||
* element(by.linkText('Moby: Ch1')).click();
|
|
||||||
* var content = element(by.css('[ng-view]')).getText();
|
|
||||||
* expect(content).toMatch(/controller\: ChapterController/);
|
|
||||||
* expect(content).toMatch(/Book Id\: Moby/);
|
|
||||||
* expect(content).toMatch(/Chapter Id\: 1/);
|
|
||||||
*
|
|
||||||
* element(by.partialLinkText('Scarlet')).click();
|
|
||||||
*
|
|
||||||
* content = element(by.css('[ng-view]')).getText();
|
|
||||||
* expect(content).toMatch(/controller\: BookController/);
|
|
||||||
* expect(content).toMatch(/Book Id\: Scarlet/);
|
|
||||||
* });
|
|
||||||
* </file>
|
|
||||||
* </example>
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc event
|
|
||||||
* @name $route#$routeChangeStart
|
|
||||||
* @eventType broadcast on root scope
|
|
||||||
* @description
|
|
||||||
* Broadcasted before a route change. At this point the route services starts
|
|
||||||
* resolving all of the dependencies needed for the route change to occur.
|
|
||||||
* Typically this involves fetching the view template as well as any dependencies
|
|
||||||
* defined in `resolve` route property. Once all of the dependencies are resolved
|
|
||||||
* `$routeChangeSuccess` is fired.
|
|
||||||
*
|
|
||||||
* @param {Object} angularEvent Synthetic event object.
|
|
||||||
* @param {Route} next Future route information.
|
|
||||||
* @param {Route} current Current route information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc event
|
|
||||||
* @name $route#$routeChangeSuccess
|
|
||||||
* @eventType broadcast on root scope
|
|
||||||
* @description
|
|
||||||
* Broadcasted after a route dependencies are resolved.
|
|
||||||
* {@link ngRoute.directive:ngView ngView} listens for the directive
|
|
||||||
* to instantiate the controller and render the view.
|
|
||||||
*
|
|
||||||
* @param {Object} angularEvent Synthetic event object.
|
|
||||||
* @param {Route} current Current route information.
|
|
||||||
* @param {Route|Undefined} previous Previous route information, or undefined if current is
|
|
||||||
* first route entered.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc event
|
|
||||||
* @name $route#$routeChangeError
|
|
||||||
* @eventType broadcast on root scope
|
|
||||||
* @description
|
|
||||||
* Broadcasted if any of the resolve promises are rejected.
|
|
||||||
*
|
|
||||||
* @param {Object} angularEvent Synthetic event object
|
|
||||||
* @param {Route} current Current route information.
|
|
||||||
* @param {Route} previous Previous route information.
|
|
||||||
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc event
|
|
||||||
* @name $route#$routeUpdate
|
|
||||||
* @eventType broadcast on root scope
|
|
||||||
* @description
|
|
||||||
*
|
|
||||||
* The `reloadOnSearch` property has been set to false, and we are reusing the same
|
|
||||||
* instance of the Controller.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var forceReload = false,
|
|
||||||
$route = {
|
|
||||||
routes: routes,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc method
|
|
||||||
* @name $route#reload
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Causes `$route` service to reload the current route even if
|
|
||||||
* {@link ng.$location $location} hasn't changed.
|
|
||||||
*
|
|
||||||
* As a result of that, {@link ngRoute.directive:ngView ngView}
|
|
||||||
* creates new scope, reinstantiates the controller.
|
|
||||||
*/
|
|
||||||
reload: function() {
|
|
||||||
forceReload = true;
|
|
||||||
$rootScope.$evalAsync(updateRoute);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$rootScope.$on('$locationChangeSuccess', updateRoute);
|
|
||||||
|
|
||||||
return $route;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param on {string} current url
|
|
||||||
* @param route {Object} route regexp to match the url against
|
|
||||||
* @return {?Object}
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Check if the route matches the current url.
|
|
||||||
*
|
|
||||||
* Inspired by match in
|
|
||||||
* visionmedia/express/lib/router/router.js.
|
|
||||||
*/
|
|
||||||
function switchRouteMatcher(on, route) {
|
|
||||||
var keys = route.keys,
|
|
||||||
params = {};
|
|
||||||
|
|
||||||
if (!route.regexp) return null;
|
|
||||||
|
|
||||||
var m = route.regexp.exec(on);
|
|
||||||
if (!m) return null;
|
|
||||||
|
|
||||||
for (var i = 1, len = m.length; i < len; ++i) {
|
|
||||||
var key = keys[i - 1];
|
|
||||||
|
|
||||||
var val = m[i];
|
|
||||||
|
|
||||||
if (key && val) {
|
|
||||||
params[key.name] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateRoute() {
|
|
||||||
var next = parseRoute(),
|
|
||||||
last = $route.current;
|
|
||||||
|
|
||||||
if (next && last && next.$$route === last.$$route
|
|
||||||
&& angular.equals(next.pathParams, last.pathParams)
|
|
||||||
&& !next.reloadOnSearch && !forceReload) {
|
|
||||||
last.params = next.params;
|
|
||||||
angular.copy(last.params, $routeParams);
|
|
||||||
$rootScope.$broadcast('$routeUpdate', last);
|
|
||||||
} else if (next || last) {
|
|
||||||
forceReload = false;
|
|
||||||
$rootScope.$broadcast('$routeChangeStart', next, last);
|
|
||||||
$route.current = next;
|
|
||||||
if (next) {
|
|
||||||
if (next.redirectTo) {
|
|
||||||
if (angular.isString(next.redirectTo)) {
|
|
||||||
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
|
|
||||||
.replace();
|
|
||||||
} else {
|
|
||||||
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
|
|
||||||
.replace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$q.when(next).
|
|
||||||
then(function() {
|
|
||||||
if (next) {
|
|
||||||
var locals = angular.extend({}, next.resolve),
|
|
||||||
template, templateUrl;
|
|
||||||
|
|
||||||
angular.forEach(locals, function(value, key) {
|
|
||||||
locals[key] = angular.isString(value) ?
|
|
||||||
$injector.get(value) : $injector.invoke(value);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (angular.isDefined(template = next.template)) {
|
|
||||||
if (angular.isFunction(template)) {
|
|
||||||
template = template(next.params);
|
|
||||||
}
|
|
||||||
} else if (angular.isDefined(templateUrl = next.templateUrl)) {
|
|
||||||
if (angular.isFunction(templateUrl)) {
|
|
||||||
templateUrl = templateUrl(next.params);
|
|
||||||
}
|
|
||||||
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
|
|
||||||
if (angular.isDefined(templateUrl)) {
|
|
||||||
next.loadedTemplateUrl = templateUrl;
|
|
||||||
template = $http.get(templateUrl, {cache: $templateCache}).
|
|
||||||
then(function(response) { return response.data; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (angular.isDefined(template)) {
|
|
||||||
locals['$template'] = template;
|
|
||||||
}
|
|
||||||
return $q.all(locals);
|
|
||||||
}
|
|
||||||
}).
|
|
||||||
// after route change
|
|
||||||
then(function(locals) {
|
|
||||||
if (next == $route.current) {
|
|
||||||
if (next) {
|
|
||||||
next.locals = locals;
|
|
||||||
angular.copy(next.params, $routeParams);
|
|
||||||
}
|
|
||||||
$rootScope.$broadcast('$routeChangeSuccess', next, last);
|
|
||||||
}
|
|
||||||
}, function(error) {
|
|
||||||
if (next == $route.current) {
|
|
||||||
$rootScope.$broadcast('$routeChangeError', next, last, error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Object} the current active route, by matching it against the URL
|
|
||||||
*/
|
|
||||||
function parseRoute() {
|
|
||||||
// Match a route
|
|
||||||
var params, match;
|
|
||||||
angular.forEach(routes, function(route, path) {
|
|
||||||
if (!match && (params = switchRouteMatcher($location.path(), route))) {
|
|
||||||
match = inherit(route, {
|
|
||||||
params: angular.extend({}, $location.search(), params),
|
|
||||||
pathParams: params});
|
|
||||||
match.$$route = route;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// No route matched; fallback to "otherwise" route
|
|
||||||
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string} interpolation of the redirect path with the parameters
|
|
||||||
*/
|
|
||||||
function interpolate(string, params) {
|
|
||||||
var result = [];
|
|
||||||
angular.forEach((string||'').split(':'), function(segment, i) {
|
|
||||||
if (i === 0) {
|
|
||||||
result.push(segment);
|
|
||||||
} else {
|
|
||||||
var segmentMatch = segment.match(/(\w+)(.*)/);
|
|
||||||
var key = segmentMatch[1];
|
|
||||||
result.push(params[key]);
|
|
||||||
result.push(segmentMatch[2] || '');
|
|
||||||
delete params[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result.join('');
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
ngRouteModule.provider('$routeParams', $RouteParamsProvider);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc service
|
|
||||||
* @name $routeParams
|
|
||||||
* @requires $route
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* The `$routeParams` service allows you to retrieve the current set of route parameters.
|
|
||||||
*
|
|
||||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
|
||||||
*
|
|
||||||
* The route parameters are a combination of {@link ng.$location `$location`}'s
|
|
||||||
* {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
|
|
||||||
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
|
|
||||||
*
|
|
||||||
* In case of parameter name collision, `path` params take precedence over `search` params.
|
|
||||||
*
|
|
||||||
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
|
|
||||||
* (but its properties will likely change) even when a route change occurs.
|
|
||||||
*
|
|
||||||
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
|
|
||||||
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
|
|
||||||
* Instead you can use `$route.current.params` to access the new route's parameters.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* // Given:
|
|
||||||
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
|
|
||||||
* // Route: /Chapter/:chapterId/Section/:sectionId
|
|
||||||
* //
|
|
||||||
* // Then
|
|
||||||
* $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
function $RouteParamsProvider() {
|
|
||||||
this.$get = function() { return {}; };
|
|
||||||
}
|
|
||||||
|
|
||||||
ngRouteModule.directive('ngView', ngViewFactory);
|
|
||||||
ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc directive
|
|
||||||
* @name ngView
|
|
||||||
* @restrict ECA
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* # Overview
|
|
||||||
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
|
|
||||||
* including the rendered template of the current route into the main layout (`index.html`) file.
|
|
||||||
* Every time the current route changes, the included view changes with it according to the
|
|
||||||
* configuration of the `$route` service.
|
|
||||||
*
|
|
||||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
|
||||||
*
|
|
||||||
* @animations
|
|
||||||
* enter - animation is used to bring new content into the browser.
|
|
||||||
* leave - animation is used to animate existing content away.
|
|
||||||
*
|
|
||||||
* The enter and leave animation occur concurrently.
|
|
||||||
*
|
|
||||||
* @scope
|
|
||||||
* @priority 400
|
|
||||||
* @param {string=} onload Expression to evaluate whenever the view updates.
|
|
||||||
*
|
|
||||||
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
|
|
||||||
* $anchorScroll} to scroll the viewport after the view is updated.
|
|
||||||
*
|
|
||||||
* - If the attribute is not set, disable scrolling.
|
|
||||||
* - If the attribute is set without value, enable scrolling.
|
|
||||||
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
|
|
||||||
* as an expression yields a truthy value.
|
|
||||||
* @example
|
|
||||||
<example name="ngView-directive" module="ngViewExample"
|
|
||||||
deps="angular-route.js;angular-animate.js"
|
|
||||||
animations="true" fixBase="true">
|
|
||||||
<file name="index.html">
|
|
||||||
<div ng-controller="MainCtrl as main">
|
|
||||||
Choose:
|
|
||||||
<a href="Book/Moby">Moby</a> |
|
|
||||||
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
|
||||||
<a href="Book/Gatsby">Gatsby</a> |
|
|
||||||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
|
||||||
<a href="Book/Scarlet">Scarlet Letter</a><br/>
|
|
||||||
|
|
||||||
<div class="view-animate-container">
|
|
||||||
<div ng-view class="view-animate"></div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<pre>$location.path() = {{main.$location.path()}}</pre>
|
|
||||||
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
|
|
||||||
<pre>$route.current.params = {{main.$route.current.params}}</pre>
|
|
||||||
<pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
|
|
||||||
<pre>$routeParams = {{main.$routeParams}}</pre>
|
|
||||||
</div>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file name="book.html">
|
|
||||||
<div>
|
|
||||||
controller: {{book.name}}<br />
|
|
||||||
Book Id: {{book.params.bookId}}<br />
|
|
||||||
</div>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file name="chapter.html">
|
|
||||||
<div>
|
|
||||||
controller: {{chapter.name}}<br />
|
|
||||||
Book Id: {{chapter.params.bookId}}<br />
|
|
||||||
Chapter Id: {{chapter.params.chapterId}}
|
|
||||||
</div>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file name="animations.css">
|
|
||||||
.view-animate-container {
|
|
||||||
position:relative;
|
|
||||||
height:100px!important;
|
|
||||||
position:relative;
|
|
||||||
background:white;
|
|
||||||
border:1px solid black;
|
|
||||||
height:40px;
|
|
||||||
overflow:hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-animate {
|
|
||||||
padding:10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-animate.ng-enter, .view-animate.ng-leave {
|
|
||||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
|
||||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
|
||||||
|
|
||||||
display:block;
|
|
||||||
width:100%;
|
|
||||||
border-left:1px solid black;
|
|
||||||
|
|
||||||
position:absolute;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
right:0;
|
|
||||||
bottom:0;
|
|
||||||
padding:10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-animate.ng-enter {
|
|
||||||
left:100%;
|
|
||||||
}
|
|
||||||
.view-animate.ng-enter.ng-enter-active {
|
|
||||||
left:0;
|
|
||||||
}
|
|
||||||
.view-animate.ng-leave.ng-leave-active {
|
|
||||||
left:-100%;
|
|
||||||
}
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file name="script.js">
|
|
||||||
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
|
|
||||||
.config(['$routeProvider', '$locationProvider',
|
|
||||||
function($routeProvider, $locationProvider) {
|
|
||||||
$routeProvider
|
|
||||||
.when('/Book/:bookId', {
|
|
||||||
templateUrl: 'book.html',
|
|
||||||
controller: 'BookCtrl',
|
|
||||||
controllerAs: 'book'
|
|
||||||
})
|
|
||||||
.when('/Book/:bookId/ch/:chapterId', {
|
|
||||||
templateUrl: 'chapter.html',
|
|
||||||
controller: 'ChapterCtrl',
|
|
||||||
controllerAs: 'chapter'
|
|
||||||
});
|
|
||||||
|
|
||||||
// configure html5 to get links working on jsfiddle
|
|
||||||
$locationProvider.html5Mode(true);
|
|
||||||
}])
|
|
||||||
.controller('MainCtrl', ['$route', '$routeParams', '$location',
|
|
||||||
function($route, $routeParams, $location) {
|
|
||||||
this.$route = $route;
|
|
||||||
this.$location = $location;
|
|
||||||
this.$routeParams = $routeParams;
|
|
||||||
}])
|
|
||||||
.controller('BookCtrl', ['$routeParams', function($routeParams) {
|
|
||||||
this.name = "BookCtrl";
|
|
||||||
this.params = $routeParams;
|
|
||||||
}])
|
|
||||||
.controller('ChapterCtrl', ['$routeParams', function($routeParams) {
|
|
||||||
this.name = "ChapterCtrl";
|
|
||||||
this.params = $routeParams;
|
|
||||||
}]);
|
|
||||||
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file name="protractor.js" type="protractor">
|
|
||||||
it('should load and compile correct template', function() {
|
|
||||||
element(by.linkText('Moby: Ch1')).click();
|
|
||||||
var content = element(by.css('[ng-view]')).getText();
|
|
||||||
expect(content).toMatch(/controller\: ChapterCtrl/);
|
|
||||||
expect(content).toMatch(/Book Id\: Moby/);
|
|
||||||
expect(content).toMatch(/Chapter Id\: 1/);
|
|
||||||
|
|
||||||
element(by.partialLinkText('Scarlet')).click();
|
|
||||||
|
|
||||||
content = element(by.css('[ng-view]')).getText();
|
|
||||||
expect(content).toMatch(/controller\: BookCtrl/);
|
|
||||||
expect(content).toMatch(/Book Id\: Scarlet/);
|
|
||||||
});
|
|
||||||
</file>
|
|
||||||
</example>
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc event
|
|
||||||
* @name ngView#$viewContentLoaded
|
|
||||||
* @eventType emit on the current ngView scope
|
|
||||||
* @description
|
|
||||||
* Emitted every time the ngView content is reloaded.
|
|
||||||
*/
|
|
||||||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
|
|
||||||
function ngViewFactory( $route, $anchorScroll, $animate) {
|
|
||||||
return {
|
|
||||||
restrict: 'ECA',
|
|
||||||
terminal: true,
|
|
||||||
priority: 400,
|
|
||||||
transclude: 'element',
|
|
||||||
link: function(scope, $element, attr, ctrl, $transclude) {
|
|
||||||
var currentScope,
|
|
||||||
currentElement,
|
|
||||||
previousElement,
|
|
||||||
autoScrollExp = attr.autoscroll,
|
|
||||||
onloadExp = attr.onload || '';
|
|
||||||
|
|
||||||
scope.$on('$routeChangeSuccess', update);
|
|
||||||
update();
|
|
||||||
|
|
||||||
function cleanupLastView() {
|
|
||||||
if(previousElement) {
|
|
||||||
previousElement.remove();
|
|
||||||
previousElement = null;
|
|
||||||
}
|
|
||||||
if(currentScope) {
|
|
||||||
currentScope.$destroy();
|
|
||||||
currentScope = null;
|
|
||||||
}
|
|
||||||
if(currentElement) {
|
|
||||||
$animate.leave(currentElement, function() {
|
|
||||||
previousElement = null;
|
|
||||||
});
|
|
||||||
previousElement = currentElement;
|
|
||||||
currentElement = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
var locals = $route.current && $route.current.locals,
|
|
||||||
template = locals && locals.$template;
|
|
||||||
|
|
||||||
if (angular.isDefined(template)) {
|
|
||||||
var newScope = scope.$new();
|
|
||||||
var current = $route.current;
|
|
||||||
|
|
||||||
// Note: This will also link all children of ng-view that were contained in the original
|
|
||||||
// html. If that content contains controllers, ... they could pollute/change the scope.
|
|
||||||
// However, using ng-view on an element with additional content does not make sense...
|
|
||||||
// Note: We can't remove them in the cloneAttchFn of $transclude as that
|
|
||||||
// function is called before linking the content, which would apply child
|
|
||||||
// directives to non existing elements.
|
|
||||||
var clone = $transclude(newScope, function(clone) {
|
|
||||||
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
|
|
||||||
if (angular.isDefined(autoScrollExp)
|
|
||||||
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
|
||||||
$anchorScroll();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cleanupLastView();
|
|
||||||
});
|
|
||||||
|
|
||||||
currentElement = clone;
|
|
||||||
currentScope = current.scope = newScope;
|
|
||||||
currentScope.$emit('$viewContentLoaded');
|
|
||||||
currentScope.$eval(onloadExp);
|
|
||||||
} else {
|
|
||||||
cleanupLastView();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This directive is called during the $transclude call of the first `ngView` directive.
|
|
||||||
// It will replace and compile the content of the element with the loaded template.
|
|
||||||
// We need this directive so that the element content is already filled when
|
|
||||||
// the link function of another directive on the same element as ngView
|
|
||||||
// is called.
|
|
||||||
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
|
|
||||||
function ngViewFillContentFactory($compile, $controller, $route) {
|
|
||||||
return {
|
|
||||||
restrict: 'ECA',
|
|
||||||
priority: -400,
|
|
||||||
link: function(scope, $element) {
|
|
||||||
var current = $route.current,
|
|
||||||
locals = current.locals;
|
|
||||||
|
|
||||||
$element.html(locals.$template);
|
|
||||||
|
|
||||||
var link = $compile($element.contents());
|
|
||||||
|
|
||||||
if (current.controller) {
|
|
||||||
locals.$scope = scope;
|
|
||||||
var controller = $controller(current.controller, locals);
|
|
||||||
if (current.controllerAs) {
|
|
||||||
scope[current.controllerAs] = controller;
|
|
||||||
}
|
|
||||||
$element.data('$ngControllerController', controller);
|
|
||||||
$element.children().data('$ngControllerController', controller);
|
|
||||||
}
|
|
||||||
|
|
||||||
link(scope);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})(window, window.angular);
|
|
174
src/main/webapp/js/lib/angular-toaster.js
vendored
174
src/main/webapp/js/lib/angular-toaster.js
vendored
|
@ -1,174 +0,0 @@
|
||||||
(function(window, angular, undefined) {'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* AngularJS Toaster
|
|
||||||
* Version: 0.4.4
|
|
||||||
*
|
|
||||||
* Copyright 2013 Jiri Kavulak.
|
|
||||||
* All Rights Reserved.
|
|
||||||
* Use, reproduction, distribution, and modification of this code is subject to the terms and
|
|
||||||
* conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
|
|
||||||
*
|
|
||||||
* Author: Jiri Kavulak
|
|
||||||
* Related to project of John Papa and Hans Fjällemark
|
|
||||||
*/
|
|
||||||
|
|
||||||
angular.module('toaster', ['ngAnimate'])
|
|
||||||
.service('toaster', ['$rootScope', function ($rootScope) {
|
|
||||||
this.pop = function (type, title, body, timeout, bodyOutputType) {
|
|
||||||
this.toast = {
|
|
||||||
type: type,
|
|
||||||
title: title,
|
|
||||||
body: body,
|
|
||||||
timeout: timeout,
|
|
||||||
bodyOutputType: bodyOutputType
|
|
||||||
};
|
|
||||||
$rootScope.$broadcast('toaster-newToast');
|
|
||||||
};
|
|
||||||
|
|
||||||
this.clear = function () {
|
|
||||||
$rootScope.$broadcast('toaster-clearToasts');
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
.constant('toasterConfig', {
|
|
||||||
'limit': 0, // limits max number of toasts
|
|
||||||
'tap-to-dismiss': true,
|
|
||||||
'newest-on-top': true,
|
|
||||||
//'fade-in': 1000, // done in css
|
|
||||||
//'on-fade-in': undefined, // not implemented
|
|
||||||
//'fade-out': 1000, // done in css
|
|
||||||
// 'on-fade-out': undefined, // not implemented
|
|
||||||
//'extended-time-out': 1000, // not implemented
|
|
||||||
'time-out': 5000, // Set timeOut and extendedTimeout to 0 to make it sticky
|
|
||||||
'icon-classes': {
|
|
||||||
error: 'toast-error',
|
|
||||||
info: 'toast-info',
|
|
||||||
success: 'toast-success',
|
|
||||||
warning: 'toast-warning'
|
|
||||||
},
|
|
||||||
'body-output-type': '', // Options: '', 'trustedHtml', 'template'
|
|
||||||
'body-template': 'toasterBodyTmpl.html',
|
|
||||||
'icon-class': 'toast-info',
|
|
||||||
'position-class': 'toast-top-right',
|
|
||||||
'title-class': 'toast-title',
|
|
||||||
'message-class': 'toast-message'
|
|
||||||
})
|
|
||||||
.directive('toasterContainer', ['$compile', '$timeout', '$sce', 'toasterConfig', 'toaster',
|
|
||||||
function ($compile, $timeout, $sce, toasterConfig, toaster) {
|
|
||||||
return {
|
|
||||||
replace: true,
|
|
||||||
restrict: 'EA',
|
|
||||||
link: function (scope, elm, attrs) {
|
|
||||||
|
|
||||||
var id = 0;
|
|
||||||
|
|
||||||
var mergedConfig = toasterConfig;
|
|
||||||
if (attrs.toasterOptions) {
|
|
||||||
angular.extend(mergedConfig, scope.$eval(attrs.toasterOptions));
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.config = {
|
|
||||||
position: mergedConfig['position-class'],
|
|
||||||
title: mergedConfig['title-class'],
|
|
||||||
message: mergedConfig['message-class'],
|
|
||||||
tap: mergedConfig['tap-to-dismiss']
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.configureTimer = function configureTimer(toast) {
|
|
||||||
var timeout = typeof (toast.timeout) == "number" ? toast.timeout : mergedConfig['time-out'];
|
|
||||||
if (timeout > 0)
|
|
||||||
setTimeout(toast, timeout);
|
|
||||||
};
|
|
||||||
|
|
||||||
function addToast(toast) {
|
|
||||||
toast.type = mergedConfig['icon-classes'][toast.type];
|
|
||||||
if (!toast.type)
|
|
||||||
toast.type = mergedConfig['icon-class'];
|
|
||||||
|
|
||||||
id++;
|
|
||||||
angular.extend(toast, { id: id });
|
|
||||||
|
|
||||||
// Set the toast.bodyOutputType to the default if it isn't set
|
|
||||||
toast.bodyOutputType = toast.bodyOutputType || mergedConfig['body-output-type']
|
|
||||||
switch (toast.bodyOutputType) {
|
|
||||||
case 'trustedHtml':
|
|
||||||
toast.html = $sce.trustAsHtml(toast.body);
|
|
||||||
break;
|
|
||||||
case 'template':
|
|
||||||
toast.bodyTemplate = toast.body || mergedConfig['body-template'];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.configureTimer(toast);
|
|
||||||
|
|
||||||
if (mergedConfig['newest-on-top'] === true) {
|
|
||||||
scope.toasters.unshift(toast);
|
|
||||||
if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
|
|
||||||
scope.toasters.pop();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scope.toasters.push(toast);
|
|
||||||
if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
|
|
||||||
scope.toasters.shift();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTimeout(toast, time) {
|
|
||||||
toast.timeout = $timeout(function () {
|
|
||||||
scope.removeToast(toast.id);
|
|
||||||
}, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.toasters = [];
|
|
||||||
scope.$on('toaster-newToast', function () {
|
|
||||||
addToast(toaster.toast);
|
|
||||||
});
|
|
||||||
|
|
||||||
scope.$on('toaster-clearToasts', function () {
|
|
||||||
scope.toasters.splice(0, scope.toasters.length);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
|
|
||||||
|
|
||||||
$scope.stopTimer = function (toast) {
|
|
||||||
if (toast.timeout) {
|
|
||||||
$timeout.cancel(toast.timeout);
|
|
||||||
toast.timeout = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.restartTimer = function (toast) {
|
|
||||||
if (!toast.timeout)
|
|
||||||
$scope.configureTimer(toast);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.removeToast = function (id) {
|
|
||||||
var i = 0;
|
|
||||||
for (i; i < $scope.toasters.length; i++) {
|
|
||||||
if ($scope.toasters[i].id === id)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$scope.toasters.splice(i, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.remove = function (id) {
|
|
||||||
if ($scope.config.tap === true) {
|
|
||||||
$scope.removeToast(id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}],
|
|
||||||
template:
|
|
||||||
'<div id="toast-container" ng-class="config.position">' +
|
|
||||||
'<div ng-repeat="toaster in toasters" class="toast" ng-class="toaster.type" ng-click="remove(toaster.id)" ng-mouseover="stopTimer(toaster)" ng-mouseout="restartTimer(toaster)">' +
|
|
||||||
'<div ng-class="config.title">{{toaster.title}}</div>' +
|
|
||||||
'<div ng-class="config.message" ng-switch on="toaster.bodyOutputType">' +
|
|
||||||
'<div ng-switch-when="trustedHtml" ng-bind-html="toaster.html"></div>' +
|
|
||||||
'<div ng-switch-when="template"><div ng-include="toaster.bodyTemplate"></div></div>' +
|
|
||||||
'<div ng-switch-default >{{toaster.body}}</div>' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>'
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
})(window, window.angular);
|
|
11876
src/main/webapp/js/lib/angular.js
vendored
11876
src/main/webapp/js/lib/angular.js
vendored
File diff suppressed because it is too large
Load diff
502
src/main/webapp/js/lib/angularAMD.js
vendored
502
src/main/webapp/js/lib/angularAMD.js
vendored
|
@ -1,502 +0,0 @@
|
||||||
/*!
|
|
||||||
angularAMD v0.2.1
|
|
||||||
(c) 2013-2014 Marcos Lin https://github.com/marcoslin/
|
|
||||||
License: MIT
|
|
||||||
*/
|
|
||||||
define(function () {
|
|
||||||
'use strict';
|
|
||||||
var bootstrapped = false,
|
|
||||||
|
|
||||||
// Used in .bootstrap
|
|
||||||
app_name,
|
|
||||||
orig_app,
|
|
||||||
alt_app,
|
|
||||||
run_injector,
|
|
||||||
config_injector,
|
|
||||||
app_cached_providers = {},
|
|
||||||
|
|
||||||
// Used in setAlternateAngular(), alt_angular is set to become angular.module
|
|
||||||
orig_angular,
|
|
||||||
alt_angular,
|
|
||||||
|
|
||||||
// Object that wrap the provider methods that enables lazy loading
|
|
||||||
onDemandLoader = {},
|
|
||||||
preBootstrapLoaderQueue = [],
|
|
||||||
|
|
||||||
// Used in setAlternateAngular() and .processQueue
|
|
||||||
alternate_modules = {},
|
|
||||||
alternate_modules_tracker = {},
|
|
||||||
alternate_queue = [];
|
|
||||||
|
|
||||||
// Private method to check if angularAMD has been initialized
|
|
||||||
function checkBootstrapped() {
|
|
||||||
if ( !bootstrapped ) {
|
|
||||||
throw new Error('angularAMD not initialized. Need to call angularAMD.bootstrap(app) first.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an alternate angular so that subsequent call to angular.module will queue up
|
|
||||||
* the module created for later processing via the .processQueue method.
|
|
||||||
*
|
|
||||||
* This delaying processing is needed as angular does not recognize any newly created
|
|
||||||
* module after angular.bootstrap has ran. The only way to add new objects to angular
|
|
||||||
* post bootstrap is using cached provider.
|
|
||||||
*
|
|
||||||
* Once the modules has been queued, processQueue would then use each module's _invokeQueue
|
|
||||||
* and _runBlock to recreate object using cached $provider. In essence, creating a duplicate
|
|
||||||
* object into the current ng-app. As result, if there are subsequent call to retrieve the
|
|
||||||
* module post processQueue, it would retrieve a module that is not integrated into the ng-app.
|
|
||||||
*
|
|
||||||
* Therefore, any subsequent to call to angular.module after processQueue should return undefined
|
|
||||||
* to prevent obtaining a duplicated object. However, it is critical that angular.module return
|
|
||||||
* appropriate object *during* processQueue.
|
|
||||||
*/
|
|
||||||
function setAlternateAngular() {
|
|
||||||
// This method cannot be called more than once
|
|
||||||
if (alt_angular) {
|
|
||||||
throw new Error('setAlternateAngular can only be called once.');
|
|
||||||
} else {
|
|
||||||
alt_angular = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that bootstrap has been called
|
|
||||||
checkBootstrapped();
|
|
||||||
|
|
||||||
// Create a a copy of orig_angular with on demand loading capability
|
|
||||||
orig_angular.extend(alt_angular, orig_angular);
|
|
||||||
|
|
||||||
// Custom version of angular.module used as cache
|
|
||||||
alt_angular.module = function (name, requires) {
|
|
||||||
if (typeof requires === 'undefined') {
|
|
||||||
// Return module from alternate_modules if it was created using the alt_angular
|
|
||||||
if (alternate_modules_tracker.hasOwnProperty(name)) {
|
|
||||||
return alternate_modules[name];
|
|
||||||
} else {
|
|
||||||
return orig_angular.module(name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var orig_mod = orig_angular.module.apply(null, arguments),
|
|
||||||
item = { name: name, module: orig_mod};
|
|
||||||
alternate_queue.push(item);
|
|
||||||
orig_angular.extend(orig_mod, onDemandLoader);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Use `alternate_modules_tracker` to track which module has been created by alt_angular
|
|
||||||
but use `alternate_modules` to cache the module created. This is to simplify the
|
|
||||||
removal of cached modules after .processQueue.
|
|
||||||
*/
|
|
||||||
alternate_modules_tracker[name] = true;
|
|
||||||
alternate_modules[name] = orig_mod;
|
|
||||||
|
|
||||||
// Return created module
|
|
||||||
return orig_mod;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.angular = alt_angular;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
function AngularAMD() {}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to generate angular's $routeProvider.route. 'config' input param must be an object.
|
|
||||||
*
|
|
||||||
* Populate the resolve attribute using either 'controllerUrl' or 'controller'. If 'controllerUrl'
|
|
||||||
* is passed, it will attempt to load the Url using requirejs and remove the attribute from the config
|
|
||||||
* object. Otherwise, it will attempt to populate resolve by loading what's been passed in 'controller'.
|
|
||||||
* If neither is passed, resolve is not populated.
|
|
||||||
*
|
|
||||||
* This function works as a pass-through, meaning what ever is passed in as 'config' will be returned,
|
|
||||||
* except for 'controllerUrl' attribute.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
AngularAMD.prototype.route = function (config) {
|
|
||||||
// Initialization not necessary to call this method.
|
|
||||||
var load_controller;
|
|
||||||
|
|
||||||
/*
|
|
||||||
If `controllerUrl` is provided, load the provided Url using requirejs. If `controller` is not provided
|
|
||||||
but `controllerUrl` is, assume that module to be loaded will return a function to act as controller.
|
|
||||||
|
|
||||||
Otherwise, attempt to load the controller using the controller name. In the later case, controller name
|
|
||||||
is expected to be defined as one of 'paths' in main.js.
|
|
||||||
*/
|
|
||||||
if ( config.hasOwnProperty('controllerUrl') ) {
|
|
||||||
load_controller = config.controllerUrl;
|
|
||||||
delete config.controllerUrl;
|
|
||||||
if (typeof config.controller === 'undefined') {
|
|
||||||
// Only controllerUrl is defined. Attempt to set the controller to return value of package loaded.
|
|
||||||
config.controller = [
|
|
||||||
'$scope', '__AAMDCtrl', '$injector',
|
|
||||||
function ($scope, __AAMDCtrl, $injector) {
|
|
||||||
if (typeof __AAMDCtrl !== 'undefined' ) {
|
|
||||||
$injector.invoke(__AAMDCtrl, this, { '$scope': $scope });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} else if (typeof config.controller === 'string') {
|
|
||||||
load_controller = config.controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If controller needs to be loaded, append to the resolve property
|
|
||||||
if (load_controller) {
|
|
||||||
var resolve = config.resolve || {};
|
|
||||||
resolve['__AAMDCtrl'] = ['$q', '$rootScope', function ($q, $rootScope) { // jshint ignore:line
|
|
||||||
var defer = $q.defer();
|
|
||||||
require([load_controller], function (ctrl) {
|
|
||||||
defer.resolve(ctrl);
|
|
||||||
$rootScope.$apply();
|
|
||||||
});
|
|
||||||
return defer.promise;
|
|
||||||
}];
|
|
||||||
config.resolve = resolve;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expose name of the app that has been bootstrapped
|
|
||||||
*/
|
|
||||||
AngularAMD.prototype.appname = function () {
|
|
||||||
checkBootstrapped();
|
|
||||||
return app_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recreate the modules created by alternate angular in ng-app using cached $provider.
|
|
||||||
* As AMD loader does not guarantee the order of dependency in a require([...],...)
|
|
||||||
* clause, user must make sure that dependecies are clearly setup in shim in order
|
|
||||||
* for this to work.
|
|
||||||
*
|
|
||||||
* HACK ALERT:
|
|
||||||
* This method relay on inner working of angular.module code, and access _invokeQueue
|
|
||||||
* and _runBlock private variable. Must test carefully with each release of angular.
|
|
||||||
*
|
|
||||||
* As of AngularJS 1.3.x, there is new _configBlocks that get populated with configuration
|
|
||||||
* blocks, thus replacing the need for "provider === '$injector' && method === 'invoke'"
|
|
||||||
* logic.
|
|
||||||
*/
|
|
||||||
AngularAMD.prototype.processQueue = function () {
|
|
||||||
checkBootstrapped();
|
|
||||||
|
|
||||||
if (typeof alt_angular === 'undefined') {
|
|
||||||
throw new Error('Alternate angular not set. Make sure that `enable_ngload` option has been set when calling angularAMD.bootstrap');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process alternate queue in FIFO fashion
|
|
||||||
function processRunBlock(block) {
|
|
||||||
//console.info('"' + item.name + '": executing run block: ', run_block);
|
|
||||||
run_injector.invoke(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (alternate_queue.length) {
|
|
||||||
var item = alternate_queue.shift(),
|
|
||||||
invokeQueue = item.module._invokeQueue,
|
|
||||||
y;
|
|
||||||
|
|
||||||
// Setup the providers define in the module
|
|
||||||
// console.info('invokeQueue: ', invokeQueue);
|
|
||||||
for (y = 0; y < invokeQueue.length; y += 1) {
|
|
||||||
var q = invokeQueue[y],
|
|
||||||
provider = q[0],
|
|
||||||
method = q[1],
|
|
||||||
args = q[2];
|
|
||||||
|
|
||||||
// Make sure that provider exists.
|
|
||||||
if (app_cached_providers.hasOwnProperty(provider)) {
|
|
||||||
var cachedProvider;
|
|
||||||
if (provider === '$injector' && method === 'invoke') {
|
|
||||||
cachedProvider = config_injector;
|
|
||||||
} else {
|
|
||||||
cachedProvider = app_cached_providers[provider];
|
|
||||||
}
|
|
||||||
// console.info('"' + item.name + '": applying ' + provider + '.' + method + ' for args: ', args);
|
|
||||||
cachedProvider[method].apply(null, args);
|
|
||||||
} else {
|
|
||||||
// Make sure that console exists before calling it
|
|
||||||
if ( window.console ) {
|
|
||||||
window.console.error('"' + provider + '" not found!!!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
As of AngularJS 1.3.x, the config block are now stored in a new _configBlocks private
|
|
||||||
variable. Loop through the list and invoke the config block with config_injector
|
|
||||||
*/
|
|
||||||
if (item.module._configBlocks) {
|
|
||||||
var configBlocks = item.module._configBlocks;
|
|
||||||
|
|
||||||
// console.info('configBlock: ', configBlocks);
|
|
||||||
for (y = 0; y < configBlocks.length; y += 1) {
|
|
||||||
var cf = configBlocks[y],
|
|
||||||
cf_method = cf[1],
|
|
||||||
cf_args = cf[2];
|
|
||||||
|
|
||||||
config_injector[cf_method].apply(null, cf_args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Execute the run block of the module
|
|
||||||
if (item.module._runBlocks) {
|
|
||||||
angular.forEach(item.module._runBlocks, processRunBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Clear the cached modules created by alt_angular so that subsequent call to
|
|
||||||
angular.module will return undefined.
|
|
||||||
*/
|
|
||||||
alternate_modules = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return cached app provider
|
|
||||||
*/
|
|
||||||
AngularAMD.prototype.getCachedProvider = function (provider_name) {
|
|
||||||
checkBootstrapped();
|
|
||||||
// Hack used for unit testing that orig_angular has been captured
|
|
||||||
var cachedProvider;
|
|
||||||
|
|
||||||
switch(provider_name) {
|
|
||||||
case '__orig_angular':
|
|
||||||
cachedProvider = orig_angular;
|
|
||||||
break;
|
|
||||||
case '__alt_angular':
|
|
||||||
cachedProvider = alt_angular;
|
|
||||||
break;
|
|
||||||
case '__orig_app':
|
|
||||||
cachedProvider = orig_app;
|
|
||||||
break;
|
|
||||||
case '__alt_app':
|
|
||||||
cachedProvider = alt_app;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
cachedProvider = app_cached_providers[provider_name];
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedProvider;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create inject function that uses cached $injector.
|
|
||||||
* Designed primarly to be used during unit testing.
|
|
||||||
*/
|
|
||||||
AngularAMD.prototype.inject = function () {
|
|
||||||
checkBootstrapped();
|
|
||||||
return run_injector.invoke.apply(null, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create config function that uses cached config_injector.
|
|
||||||
* Designed to simulate app.config.
|
|
||||||
*/
|
|
||||||
AngularAMD.prototype.config = function () {
|
|
||||||
checkBootstrapped();
|
|
||||||
return config_injector.invoke.apply(null, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset angularAMD for resuse
|
|
||||||
*/
|
|
||||||
AngularAMD.prototype.reset = function () {
|
|
||||||
if (typeof orig_angular === 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore original angular instance
|
|
||||||
window.angular = orig_angular;
|
|
||||||
|
|
||||||
// Clear stored app
|
|
||||||
orig_app = undefined;
|
|
||||||
alt_app = undefined;
|
|
||||||
|
|
||||||
// Clear original angular
|
|
||||||
alt_angular = undefined;
|
|
||||||
orig_angular = undefined;
|
|
||||||
onDemandLoader = {};
|
|
||||||
preBootstrapLoaderQueue = [];
|
|
||||||
|
|
||||||
// Clear private variables
|
|
||||||
alternate_queue = [];
|
|
||||||
app_name = undefined;
|
|
||||||
run_injector = undefined;
|
|
||||||
config_injector = undefined;
|
|
||||||
app_cached_providers = {};
|
|
||||||
|
|
||||||
// Clear bootstrap flag but there is no way to un-bootstrap AngularJS
|
|
||||||
bootstrapped = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialization of angularAMD that bootstraps AngularJS. The objective is to cache the
|
|
||||||
* $provider and $injector from the app to be used later.
|
|
||||||
*
|
|
||||||
* enable_ngload:
|
|
||||||
*/
|
|
||||||
AngularAMD.prototype.bootstrap = function (app, enable_ngload, elem) {
|
|
||||||
// Prevent bootstrap from being called multiple times
|
|
||||||
if (bootstrapped) {
|
|
||||||
throw Error('bootstrap can only be called once.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof enable_ngload === 'undefined') {
|
|
||||||
enable_ngload = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store reference to original angular and app
|
|
||||||
orig_angular = angular;
|
|
||||||
|
|
||||||
// Create new version of app
|
|
||||||
orig_app = app;
|
|
||||||
alt_app = {};
|
|
||||||
orig_angular.extend(alt_app, orig_app);
|
|
||||||
|
|
||||||
// Determine element to bootstrap angular
|
|
||||||
elem = elem || document.documentElement;
|
|
||||||
|
|
||||||
// Cache provider needed
|
|
||||||
app.config(
|
|
||||||
['$controllerProvider', '$compileProvider', '$filterProvider', '$animateProvider', '$provide', '$injector', function (controllerProvider, compileProvider, filterProvider, animateProvider, provide, injector) {
|
|
||||||
// Cache Providers
|
|
||||||
config_injector = injector;
|
|
||||||
app_cached_providers = {
|
|
||||||
$controllerProvider: controllerProvider,
|
|
||||||
$compileProvider: compileProvider,
|
|
||||||
$filterProvider: filterProvider,
|
|
||||||
$animateProvider: animateProvider,
|
|
||||||
$provide: provide
|
|
||||||
};
|
|
||||||
|
|
||||||
// Substitue provider methods from app call the cached provider
|
|
||||||
angular.extend(onDemandLoader, {
|
|
||||||
provider : function(name, constructor) {
|
|
||||||
provide.provider(name, constructor);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
controller : function(name, constructor) {
|
|
||||||
controllerProvider.register(name, constructor);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
directive : function(name, constructor) {
|
|
||||||
compileProvider.directive(name, constructor);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
filter : function(name, constructor) {
|
|
||||||
filterProvider.register(name, constructor);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
factory : function(name, constructor) {
|
|
||||||
// console.log('onDemandLoader.factory called for ' + name);
|
|
||||||
provide.factory(name, constructor);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
service : function(name, constructor) {
|
|
||||||
provide.service(name, constructor);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
constant : function(name, constructor) {
|
|
||||||
provide.constant(name, constructor);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
value : function(name, constructor) {
|
|
||||||
provide.value(name, constructor);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
animation: angular.bind(animateProvider, animateProvider.register)
|
|
||||||
});
|
|
||||||
angular.extend(alt_app, onDemandLoader);
|
|
||||||
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get the injector for the app
|
|
||||||
app.run(['$injector', function ($injector) {
|
|
||||||
// $injector must be obtained in .run instead of .config
|
|
||||||
run_injector = $injector;
|
|
||||||
app_cached_providers.$injector = run_injector;
|
|
||||||
}]);
|
|
||||||
|
|
||||||
// Store the app name needed by .bootstrap function.
|
|
||||||
app_name = app.name;
|
|
||||||
|
|
||||||
// If there are angular provider recipe queued up, process it
|
|
||||||
if (preBootstrapLoaderQueue.length > 0) {
|
|
||||||
for (var iq = 0; iq < preBootstrapLoaderQueue.length; iq += 1) {
|
|
||||||
var item = preBootstrapLoaderQueue[iq];
|
|
||||||
orig_app[item.recipe](item.name, item.constructor);
|
|
||||||
}
|
|
||||||
preBootstrapLoaderQueue = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a app.register object to keep backward compatibility
|
|
||||||
orig_app.register = onDemandLoader;
|
|
||||||
|
|
||||||
// Bootstrap Angular
|
|
||||||
orig_angular.element(document).ready(function () {
|
|
||||||
orig_angular.bootstrap(elem, [app_name]);
|
|
||||||
// Indicate bootstrap completed
|
|
||||||
bootstrapped = true;
|
|
||||||
|
|
||||||
// Replace angular.module
|
|
||||||
if (enable_ngload) {
|
|
||||||
//console.info('Setting alternate angular');
|
|
||||||
setAlternateAngular();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return app
|
|
||||||
return alt_app;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define provider
|
|
||||||
function executeProvider(providerRecipe) {
|
|
||||||
return function (name, constructor) {
|
|
||||||
if (bootstrapped) {
|
|
||||||
onDemandLoader[providerRecipe](name, constructor);
|
|
||||||
} else {
|
|
||||||
// Queue up the request to be used during .bootstrap
|
|
||||||
preBootstrapLoaderQueue.push({
|
|
||||||
'recipe': providerRecipe,
|
|
||||||
'name': name,
|
|
||||||
'constructor': constructor
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// .provider
|
|
||||||
AngularAMD.prototype.provider = executeProvider('provider');
|
|
||||||
// .controller
|
|
||||||
AngularAMD.prototype.controller = executeProvider('controller');
|
|
||||||
// .directive
|
|
||||||
AngularAMD.prototype.directive = executeProvider('directive');
|
|
||||||
// .filter
|
|
||||||
AngularAMD.prototype.filter = executeProvider('filter');
|
|
||||||
// .factory
|
|
||||||
AngularAMD.prototype.factory = executeProvider('factory');
|
|
||||||
// .service
|
|
||||||
AngularAMD.prototype.service = executeProvider('service');
|
|
||||||
// .constant
|
|
||||||
AngularAMD.prototype.constant = executeProvider('constant');
|
|
||||||
// .value
|
|
||||||
AngularAMD.prototype.value = executeProvider('value');
|
|
||||||
// .animation
|
|
||||||
AngularAMD.prototype.animation = executeProvider('animation');
|
|
||||||
|
|
||||||
// Create a new instance and return
|
|
||||||
return new AngularAMD();
|
|
||||||
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
16
src/main/webapp/services/services.js
Normal file
16
src/main/webapp/services/services.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
var Owner = ['$resource', function($resource) {
|
||||||
|
return $resource('/petclinic/api/owners/:id');
|
||||||
|
}];
|
||||||
|
|
||||||
|
var Pet = ['$resource', function($resource) {
|
||||||
|
return $resource('/petclinic/api/owners/:ownerId/pets', {ownerId : '@ownerId'});
|
||||||
|
}];
|
||||||
|
|
||||||
|
var Vet = ['$resource', function($resource) {
|
||||||
|
return $resource('/petclinic/api/vets/:vetId');
|
||||||
|
}];
|
||||||
|
|
||||||
|
var Visit = ['$resource', function($resource) {
|
||||||
|
return $resource('/petclinic/api/pets/:petId/visits', {petId : '@id'});
|
||||||
|
}];
|
||||||
|
|
Loading…
Reference in a new issue