LH :: Refreshed js library files to angular version 1.3.11

- Added ngResource
This commit is contained in:
Lim Han 2015-02-03 22:37:09 +08:00
parent 756e74f7cc
commit b8f97b53b9
24 changed files with 10537 additions and 13822 deletions

View file

@ -38,12 +38,13 @@
<!-- build:js({app,.tmp}) scripts/main.js -->
<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-statehelper.js"></script>
<script src="js/lib/angular-animate.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/about/AboutController.js"></script>
<script src="components/veterinarians/VeterinarianController.js"></script>
<script src="components/pets/PetController.js"></script>
<script src="components/owners/OwnerController.js"></script>

View file

@ -1,4 +0,0 @@
var AboutController = function($scope) {
};
var AboutControllerDeclaration = ['$scope',AboutController];

View file

@ -1 +0,0 @@
<section data-ng-controller="AboutController">This is the about page</section>

View file

@ -14,7 +14,7 @@
<!-- Collect the nav links, forms, and other content for toggling -->
<div id="nav-menu" class="navbar-collapse navbar-left collapse">
<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('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>

View file

@ -1,4 +1,4 @@
var MainController = function($scope, $rootScope, $state) {
var MainController = ['$scope','$rootScope','$state',function($scope, $rootScope, $state) {
$scope.getSession = function() {
return $scope.session;
@ -37,7 +37,5 @@ var MainController = function($scope, $rootScope, $state) {
$scope.footerText = '© ' + new Date().getFullYear() + ' Pet Clinic, A Spring Framework Demonstration';
$rootScope.$state = $state;
};
var MainControllerDeclaration = ['$scope','$rootScope','$state',MainController];
}];

View file

@ -1,9 +1,7 @@
var OwnerController = function($scope) {
var OwnerController = ['$scope',function($scope) {
$scope.$on('$viewContentLoaded', function(event){
$('html, body').animate({
scrollTop: $("#owners").offset().top
}, 1000);
});
};
var OwnerControllerDeclaration = ['$scope',OwnerController];
}];

View file

@ -1,4 +1,4 @@
var PetController = function($scope) {
var PetController = ['$scope', function($scope) {
$scope.$on('$viewContentLoaded', function(event){
$('html, body').animate({
@ -6,6 +6,4 @@ var PetController = function($scope) {
}, 1000);
});
};
var PetControllerDeclaration = ['$scope',PetController];
}];

View file

@ -1,7 +1,4 @@
var VeterinarianController = function ($scope, $http) {
$http.get('api/vets').success(function(data) {
$scope.veterinarians = data;
});
var VeterinarianController = ['$scope','$http','Vet', function ($scope, $http, Vet) {
$scope.$on('$viewContentLoaded', function(event){
$('html, body').animate({
@ -9,6 +6,6 @@ var VeterinarianController = function ($scope, $http) {
}, 1000);
});
}
var VeterinarianControllerDeclaration = ['$scope','$http',VeterinarianController];
$scope.vets = Vet.query();
}];

View file

@ -5,7 +5,7 @@
<div class="col-md-8 col-md-offset-2">
<div class="text-center">
<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>

View file

@ -1,4 +1,3 @@
var VisitController = function($scope) {
};
var VisitController = ['$scope',function($scope) {
var VisitControllerDeclaration = ['$scope',VisitController];
}];

View file

@ -1,11 +1,4 @@
var app = angular.module('spring-petclinic', ['ui.router','ui.router.stateHelper','ngAnimate','ngCookies']);
app.controller('MainController', MainControllerDeclaration);
app.controller('VeterinarianController', VeterinarianControllerDeclaration);
app.controller('PetController', PetControllerDeclaration);
app.controller('OwnerController', OwnerControllerDeclaration);
app.controller('VisitController', VisitControllerDeclaration);
app.controller('AboutController', AboutControllerDeclaration);
var app = angular.module('spring-petclinic', ['ui.router','ui.router.stateHelper','ngAnimate','ngCookies','ngResource']);
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);

File diff suppressed because it is too large Load diff

View file

@ -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
* License: MIT
*/
@ -48,7 +48,7 @@ angular.module('ngCookies', ['ng']).
* }]);
* ```
*/
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
factory('$cookies', ['$rootScope', '$browser', function($rootScope, $browser) {
var cookies = {},
lastCookies = {},
lastBrowserCookies,
@ -95,7 +95,7 @@ angular.module('ngCookies', ['ng']).
}
//update all cookies updated in $cookies
for(name in cookies) {
for (name in cookies) {
value = cookies[name];
if (!angular.isString(value)) {
value = '' + value;
@ -108,7 +108,7 @@ angular.module('ngCookies', ['ng']).
}
//verify what was actually stored
if (updated){
if (updated) {
updated = false;
browserCookies = $browser.cookies();

View file

@ -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);
}
}
}
})();

View file

@ -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;
}
})();

View file

@ -1,5 +1,5 @@
/**
* @license AngularJS v1.3.0
* @license AngularJS v1.3.11
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
@ -74,7 +74,7 @@ angular.mock.$Browser = function() {
self.defer = function(fn, delay) {
delay = delay || 0;
self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
self.deferredFns.sort(function(a,b){ return a.time - b.time;});
self.deferredFns.sort(function(a, b) { return a.time - b.time;});
return self.deferredNextId++;
};
@ -117,7 +117,7 @@ angular.mock.$Browser = function() {
self.defer.now += delay;
} else {
if (self.deferredFns.length) {
self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
} else {
throw new Error('No deferred tasks to be flushed');
}
@ -142,7 +142,7 @@ angular.mock.$Browser.prototype = {
* run all fns in pollFns
*/
poll: function poll() {
angular.forEach(this.pollFns, function(pollFn){
angular.forEach(this.pollFns, function(pollFn) {
pollFn();
});
},
@ -250,31 +250,31 @@ angular.mock.$ExceptionHandlerProvider = function() {
*
* @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`
* mode stores an array of errors in `$exceptionHandler.errors`, to allow later
* assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
* {@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) {
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) {
if (arguments.length == 1) {
errors.push(e);
} else {
errors.push([].slice.call(arguments, 0));
}
if (mode === "rethrow") {
throw e;
}
};
handler.errors = errors;
break;
default:
@ -316,7 +316,7 @@ angular.mock.$LogProvider = function() {
}
};
this.$get = function () {
this.$get = function() {
var $log = {
log: function() { $log.log.logs.push(concat([], arguments, 0)); },
warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
@ -336,7 +336,7 @@ angular.mock.$LogProvider = function() {
* @description
* Reset all of the logging arrays to empty.
*/
$log.reset = function () {
$log.reset = function() {
/**
* @ngdoc property
* @name $log#log.logs
@ -421,14 +421,14 @@ angular.mock.$LogProvider = function() {
var errors = [];
angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
angular.forEach($log[logLevel].logs, function(log) {
angular.forEach(log, function (logItem) {
angular.forEach(log, function(logItem) {
errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
(logItem.stack || ''));
});
});
});
if (errors.length) {
errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " +
"an expected log message was not checked and removed:");
errors.push('');
throw new Error(errors.join('\n---------\n'));
@ -461,17 +461,17 @@ angular.mock.$LogProvider = function() {
* @returns {promise} A promise which will be notified on each iteration.
*/
angular.mock.$IntervalProvider = function() {
this.$get = ['$rootScope', '$q',
function($rootScope, $q) {
this.$get = ['$browser', '$rootScope', '$q', '$$q',
function($browser, $rootScope, $q, $$q) {
var repeatFns = [],
nextRepeatId = 0,
now = 0;
var $interval = function(fn, delay, count, invokeApply) {
var deferred = $q.defer(),
promise = deferred.promise,
iteration = 0,
skipApply = (angular.isDefined(invokeApply) && !invokeApply);
var iteration = 0,
skipApply = (angular.isDefined(invokeApply) && !invokeApply),
deferred = (skipApply ? $$q : $q).defer(),
promise = deferred.promise;
count = (angular.isDefined(count)) ? count : 0;
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({
@ -504,7 +508,7 @@ angular.mock.$IntervalProvider = function() {
id: nextRepeatId,
deferred: deferred
});
repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
nextRepeatId++;
return promise;
@ -520,7 +524,7 @@ angular.mock.$IntervalProvider = function() {
* @returns {boolean} Returns `true` if the task was successfully cancelled.
*/
$interval.cancel = function(promise) {
if(!promise) return false;
if (!promise) return false;
var fnIndex;
angular.forEach(repeatFns, function(fn, index) {
@ -553,7 +557,7 @@ angular.mock.$IntervalProvider = function() {
var task = repeatFns[0];
task.fn();
task.nextTime += task.delay;
repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
}
return millis;
};
@ -581,10 +585,10 @@ function jsonStringToDate(string) {
tzMin = int(match[9] + match[11]);
}
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
date.setUTCHours(int(match[4]||0) - tzHour,
int(match[5]||0) - tzMin,
int(match[6]||0),
int(match[7]||0));
date.setUTCHours(int(match[4] || 0) - tzHour,
int(match[5] || 0) - tzMin,
int(match[6] || 0),
int(match[7] || 0));
return date;
}
return string;
@ -601,7 +605,7 @@ function padNumber(num, digits, trim) {
num = -num;
}
num = '' + num;
while(num.length < digits) num = '0' + num;
while (num.length < digits) num = '0' + num;
if (trim)
num = num.substr(num.length - digits);
return neg + num;
@ -645,7 +649,7 @@ function padNumber(num, digits, trim) {
* ```
*
*/
angular.mock.TzDate = function (offset, timestamp) {
angular.mock.TzDate = function(offset, timestamp) {
var self = new Date(0);
if (angular.isString(timestamp)) {
var tsStr = timestamp;
@ -663,7 +667,7 @@ angular.mock.TzDate = function (offset, timestamp) {
}
var localOffset = new Date(timestamp).getTimezoneOffset();
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
self.date = new Date(timestamp + self.offsetDiff);
self.getTime = function() {
@ -788,20 +792,20 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
$provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser',
function($delegate, $$asyncCallback, $timeout, $browser) {
var animate = {
queue : [],
cancel : $delegate.cancel,
enabled : $delegate.enabled,
triggerCallbackEvents : function() {
queue: [],
cancel: $delegate.cancel,
enabled: $delegate.enabled,
triggerCallbackEvents: function() {
$$asyncCallback.flush();
},
triggerCallbackPromise : function() {
triggerCallbackPromise: function() {
$timeout.flush(0);
},
triggerCallbacks : function() {
triggerCallbacks: function() {
this.triggerCallbackEvents();
this.triggerCallbackPromise();
},
triggerReflow : function() {
triggerReflow: function() {
angular.forEach(reflowQueue, function(fn) {
fn();
});
@ -813,10 +817,10 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
animate[method] = function() {
animate.queue.push({
event : method,
element : arguments[0],
options : arguments[arguments.length-1],
args : arguments
event: method,
element: arguments[0],
options: arguments[arguments.length - 1],
args: arguments
});
return $delegate[method].apply($delegate, arguments);
};
@ -883,13 +887,13 @@ angular.mock.dump = function(object) {
function serializeScope(scope, offset) {
offset = offset || ' ';
var log = [offset + 'Scope(' + scope.$id + '): {'];
for ( var key in scope ) {
for (var key in scope) {
if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
log.push(' ' + key + ': ' + angular.toJson(scope[key]));
}
}
var child = scope.$$childHead;
while(child) {
while (child) {
log.push(serializeScope(child, offset + ' '));
child = child.$$nextSibling;
}
@ -1115,7 +1119,7 @@ angular.mock.dump = function(object) {
```
*/
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
* @return {Object} Instance of $httpBackend mock
*/
function createHttpBackendMock($rootScope, $delegate, $browser) {
function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
var definitions = [],
expectations = [],
responses = [],
@ -1145,7 +1149,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
return function() {
return angular.isNumber(status)
? [status, data, headers, statusText]
: [200, status, data];
: [200, status, data, headers];
};
}
@ -1162,7 +1166,9 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
}
function wrapResponse(wrapped) {
if (!$browser && timeout && timeout.then) timeout.then(handleTimeout);
if (!$browser && timeout) {
timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout);
}
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
* and returns true if the url match the current definition.
* @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
* 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
* and returns true if the url match the current definition.
* @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
* 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
* and returns true if the url match the current definition.
* @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
* 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
* data string and returns true if the data is as expected.
* @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
* 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
* data string and returns true if the data is as expected.
* @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
* 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
* 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
* order to change how a matched request is handled.
*/
@ -1371,7 +1377,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* is in JSON format.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
* 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
* order to change how a matched request is handled.
*
@ -1386,7 +1392,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
$httpBackend.expect = function(method, url, data, headers) {
var expectation = new MockHttpExpectation(method, url, data, headers),
chain = {
respond: function (status, data, headers, statusText) {
respond: function(status, data, headers, statusText) {
expectation.response = createResponse(status, data, headers, statusText);
return chain;
}
@ -1406,7 +1412,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* @param {string|RegExp|function(string)} url HTTP url or function that receives the url
* and returns true if the url match the current definition.
* @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
* 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
* and returns true if the url match the current definition.
* @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
* 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
* and returns true if the url match the current definition.
* @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
* 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
* is in JSON format.
* @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
* 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
* is in JSON format.
* @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
* 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
* is in JSON format.
* @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
* 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
* 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
* 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 (data && angular.isFunction(data.test)) return data.test(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;
};
@ -1709,7 +1717,7 @@ function MockXhr() {
* that adds a "flush" and "verifyNoPendingTasks" methods.
*/
angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function ($delegate, $browser) {
angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
/**
* @ngdoc method
@ -1763,12 +1771,12 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
rafFn.supported = $delegate.supported;
rafFn.flush = function() {
if(queue.length === 0) {
if (queue.length === 0) {
throw new Error('No rAF callbacks present');
}
var length = queue.length;
for(var i=0;i<length;i++) {
for (var i = 0; i < length; i++) {
queue[i]();
}
@ -1828,6 +1836,7 @@ angular.module('ngMock', ['ng']).provider({
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
$provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
}]);
/**
@ -2033,10 +2042,96 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
*/
angular.mock.e2e = {};
angular.mock.e2e.$httpBackendDecorator =
['$rootScope', '$delegate', '$browser', createHttpBackendMock];
['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
if(window.jasmine || window.mocha) {
/**
* @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) {
var currentSpec = null,
isSpecRunning = function() {
@ -2244,7 +2339,7 @@ if(window.jasmine || window.mocha) {
injector = currentSpec.$injector = angular.injector(modules, strictDi);
currentSpec.$injectorStrict = strictDi;
}
for(var i = 0, ii = blockFns.length; i < ii; i++) {
for (var i = 0, ii = blockFns.length; i < ii; i++) {
if (currentSpec.$injectorStrict) {
// If the injector is strict / strictDi, and the spec wants to inject using automatic
// annotation, then annotate the function here.

View 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);

View file

@ -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);

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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

View 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'});
}];