├── .brackets.json ├── .gitattributes ├── .gitignore ├── .vscode └── launch.json ├── Content ├── Site.css ├── bootstrap.css └── bootstrap.min.css ├── Scripts ├── _references.js ├── angular-base64.min.js ├── angular-mocks.js ├── angular-ui-router.js ├── angular-ui-router.min.js ├── angular.js ├── angular.min.js ├── angular.min.js.map ├── bootstrap.js ├── bootstrap.min.js ├── jquery-1.10.2.intellisense.js ├── jquery-1.10.2.js ├── jquery-1.10.2.min.js ├── jquery-1.10.2.min.map ├── jquery.validate-vsdoc.js ├── jquery.validate.js ├── jquery.validate.min.js ├── jquery.validate.unobtrusive.js ├── jquery.validate.unobtrusive.min.js ├── modernizr-2.6.2.js ├── respond.js ├── respond.min.js └── typings │ ├── angularjs │ ├── angular-animate.d.ts │ ├── angular-cookies.d.ts │ ├── angular-mocks.d.ts │ ├── angular-resource.d.ts │ ├── angular-route.d.ts │ ├── angular-sanitize.d.ts │ ├── angular-scenario.d.ts │ └── angular.d.ts │ └── jquery │ └── jquery.d.ts ├── app ├── components │ └── oauth │ │ ├── oauth-directives.js │ │ ├── oauth-service.js │ │ └── oauth.js └── demo │ ├── app.js │ ├── app.js.bak │ ├── home.html │ ├── login.html │ ├── logout.html │ └── voucher.html ├── bower.json ├── bower_components ├── angular-base64 │ ├── .bower.json │ ├── LICENSE │ ├── README.md │ ├── angular-base64.js │ ├── angular-base64.min.js │ └── bower.json └── angular │ ├── .bower.json │ ├── README.md │ ├── angular-csp.css │ ├── angular.js │ ├── angular.min.js │ ├── angular.min.js.gzip │ ├── angular.min.js.map │ ├── bower.json │ ├── index.js │ └── package.json ├── callback.html ├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.svg ├── glyphicons-halflings-regular.ttf └── glyphicons-halflings-regular.woff ├── index.html ├── node_modules └── sha256 │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ ├── nodecrypto.js │ └── sha256.js │ ├── node_modules │ ├── convert-hex │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bower.json │ │ ├── component.json │ │ ├── convert-hex.js │ │ └── package.json │ └── convert-string │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bower.json │ │ ├── component.json │ │ ├── convert-string.js │ │ └── package.json │ └── package.json └── readme.md /.brackets.json: -------------------------------------------------------------------------------- 1 | { 2 | "sbruchmann.staticpreview.basepath": "C:/Users/Manfred/Dropbox/Docs/beratung/ASP.NET Web API/src/AnguarJS-with-OAuth2/" 3 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | // List of configurations. Add new configurations or edit existing ones. 4 | // ONLY "node" and "mono" are supported, change "type" to switch. 5 | "configurations": [ 6 | { 7 | // Name of configuration; appears in the launch configuration drop down menu. 8 | "name": "Launch app.js", 9 | // Type of configuration. Possible values: "node", "mono". 10 | "type": "node", 11 | // Workspace relative or absolute path to the program. 12 | "program": "app.js", 13 | // Automatically stop program after launch. 14 | "stopOnEntry": false, 15 | // Command line arguments passed to the program. 16 | "args": [], 17 | // Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace. 18 | "cwd": ".", 19 | // Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH. 20 | "runtimeExecutable": null, 21 | // Optional arguments passed to the runtime executable. 22 | "runtimeArgs": ["--nolazy"], 23 | // Environment variables passed to the program. 24 | "env": { }, 25 | // Use JavaScript source maps (if they exist). 26 | "sourceMaps": false, 27 | // If JavaScript source maps are enabled, the generated code is expected in this directory. 28 | "outDir": null 29 | }, 30 | { 31 | "name": "Attach", 32 | "type": "node", 33 | // TCP/IP address. Default is "localhost". 34 | "address": "localhost", 35 | // Port to attach to. 36 | "port": 5858, 37 | "sourceMaps": false 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Set width on the form input elements since they're 100% wide by default */ 13 | input, 14 | select, 15 | textarea { 16 | max-width: 280px; 17 | } 18 | -------------------------------------------------------------------------------- /Scripts/_references.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular-oauth-oidc/fc04c14d615ea97e5c3f96341df9bc085f852a29/Scripts/_references.js -------------------------------------------------------------------------------- /Scripts/angular-base64.min.js: -------------------------------------------------------------------------------- 1 | !function () { "use strict"; angular.module("base64", []).constant("$base64", function () { function a(a, b) { var c = f.indexOf(a.charAt(b)); if (-1 == c) throw "Cannot decode base64"; return c } function b(b) { b = "" + b; var c, d, f, g = b.length; if (0 == g) return b; if (0 != g % 4) throw "Cannot decode base64"; c = 0, b.charAt(g - 1) == e && (c = 1, b.charAt(g - 2) == e && (c = 2), g -= 4); var h = []; for (d = 0; g > d; d += 4) f = a(b, d) << 18 | a(b, d + 1) << 12 | a(b, d + 2) << 6 | a(b, d + 3), h.push(String.fromCharCode(f >> 16, 255 & f >> 8, 255 & f)); switch (c) { case 1: f = a(b, d) << 18 | a(b, d + 1) << 12 | a(b, d + 2) << 6, h.push(String.fromCharCode(f >> 16, 255 & f >> 8)); break; case 2: f = a(b, d) << 18 | a(b, d + 1) << 12, h.push(String.fromCharCode(f >> 16)) } return h.join("") } function c(a, b) { var c = a.charCodeAt(b); if (c > 255) throw "INVALID_CHARACTER_ERR: DOM Exception 5"; return c } function d(a) { if (1 != arguments.length) throw "SyntaxError: Not enough arguments"; var b, d, g = []; a = "" + a; var h = a.length - a.length % 3; if (0 == a.length) return a; for (b = 0; h > b; b += 3) d = c(a, b) << 16 | c(a, b + 1) << 8 | c(a, b + 2), g.push(f.charAt(d >> 18)), g.push(f.charAt(63 & d >> 12)), g.push(f.charAt(63 & d >> 6)), g.push(f.charAt(63 & d)); switch (a.length - h) { case 1: d = c(a, b) << 16, g.push(f.charAt(d >> 18) + f.charAt(63 & d >> 12) + e + e); break; case 2: d = c(a, b) << 16 | c(a, b + 1) << 8, g.push(f.charAt(d >> 18) + f.charAt(63 & d >> 12) + f.charAt(63 & d >> 6) + e) } return g.join("") } var e = "=", f = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; return { encode: d, decode: b } }()) }(); -------------------------------------------------------------------------------- /Scripts/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | 16 | /** 17 | * bootstrap.js v3.0.0 by @fat and @mdo 18 | * Copyright 2013 Twitter Inc. 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | */ 21 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); -------------------------------------------------------------------------------- /Scripts/jquery.validate.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! jQuery Validation Plugin - v1.11.1 - 3/22/2013\n* https://github.com/jzaefferer/jquery-validation 16 | * Copyright (c) 2013 Jörn Zaefferer; Licensed MIT */(function(t){t.extend(t.fn,{validate:function(e){if(!this.length)return e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."),void 0;var i=t.data(this[0],"validator");return i?i:(this.attr("novalidate","novalidate"),i=new t.validator(e,this[0]),t.data(this[0],"validator",i),i.settings.onsubmit&&(this.validateDelegate(":submit","click",function(e){i.settings.submitHandler&&(i.submitButton=e.target),t(e.target).hasClass("cancel")&&(i.cancelSubmit=!0),void 0!==t(e.target).attr("formnovalidate")&&(i.cancelSubmit=!0)}),this.submit(function(e){function s(){var s;return i.settings.submitHandler?(i.submitButton&&(s=t("").attr("name",i.submitButton.name).val(t(i.submitButton).val()).appendTo(i.currentForm)),i.settings.submitHandler.call(i,i.currentForm,e),i.submitButton&&s.remove(),!1):!0}return i.settings.debug&&e.preventDefault(),i.cancelSubmit?(i.cancelSubmit=!1,s()):i.form()?i.pendingRequest?(i.formSubmitted=!0,!1):s():(i.focusInvalid(),!1)})),i)},valid:function(){if(t(this[0]).is("form"))return this.validate().form();var e=!0,i=t(this[0].form).validate();return this.each(function(){e=e&&i.element(this)}),e},removeAttrs:function(e){var i={},s=this;return t.each(e.split(/\s/),function(t,e){i[e]=s.attr(e),s.removeAttr(e)}),i},rules:function(e,i){var s=this[0];if(e){var r=t.data(s.form,"validator").settings,n=r.rules,a=t.validator.staticRules(s);switch(e){case"add":t.extend(a,t.validator.normalizeRule(i)),delete a.messages,n[s.name]=a,i.messages&&(r.messages[s.name]=t.extend(r.messages[s.name],i.messages));break;case"remove":if(!i)return delete n[s.name],a;var u={};return t.each(i.split(/\s/),function(t,e){u[e]=a[e],delete a[e]}),u}}var o=t.validator.normalizeRules(t.extend({},t.validator.classRules(s),t.validator.attributeRules(s),t.validator.dataRules(s),t.validator.staticRules(s)),s);if(o.required){var l=o.required;delete o.required,o=t.extend({required:l},o)}return o}}),t.extend(t.expr[":"],{blank:function(e){return!t.trim(""+t(e).val())},filled:function(e){return!!t.trim(""+t(e).val())},unchecked:function(e){return!t(e).prop("checked")}}),t.validator=function(e,i){this.settings=t.extend(!0,{},t.validator.defaults,e),this.currentForm=i,this.init()},t.validator.format=function(e,i){return 1===arguments.length?function(){var i=t.makeArray(arguments);return i.unshift(e),t.validator.format.apply(this,i)}:(arguments.length>2&&i.constructor!==Array&&(i=t.makeArray(arguments).slice(1)),i.constructor!==Array&&(i=[i]),t.each(i,function(t,i){e=e.replace(RegExp("\\{"+t+"\\}","g"),function(){return i})}),e)},t.extend(t.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",validClass:"valid",errorElement:"label",focusInvalid:!0,errorContainer:t([]),errorLabelContainer:t([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(t){this.lastActive=t,this.settings.focusCleanup&&!this.blockFocusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,t,this.settings.errorClass,this.settings.validClass),this.addWrapper(this.errorsFor(t)).hide())},onfocusout:function(t){this.checkable(t)||!(t.name in this.submitted)&&this.optional(t)||this.element(t)},onkeyup:function(t,e){(9!==e.which||""!==this.elementValue(t))&&(t.name in this.submitted||t===this.lastElement)&&this.element(t)},onclick:function(t){t.name in this.submitted?this.element(t):t.parentNode.name in this.submitted&&this.element(t.parentNode)},highlight:function(e,i,s){"radio"===e.type?this.findByName(e.name).addClass(i).removeClass(s):t(e).addClass(i).removeClass(s)},unhighlight:function(e,i,s){"radio"===e.type?this.findByName(e.name).removeClass(i).addClass(s):t(e).removeClass(i).addClass(s)}},setDefaults:function(e){t.extend(t.validator.defaults,e)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",creditcard:"Please enter a valid credit card number.",equalTo:"Please enter the same value again.",maxlength:t.validator.format("Please enter no more than {0} characters."),minlength:t.validator.format("Please enter at least {0} characters."),rangelength:t.validator.format("Please enter a value between {0} and {1} characters long."),range:t.validator.format("Please enter a value between {0} and {1}."),max:t.validator.format("Please enter a value less than or equal to {0}."),min:t.validator.format("Please enter a value greater than or equal to {0}.")},autoCreateRanges:!1,prototype:{init:function(){function e(e){var i=t.data(this[0].form,"validator"),s="on"+e.type.replace(/^validate/,"");i.settings[s]&&i.settings[s].call(i,this[0],e)}this.labelContainer=t(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||t(this.currentForm),this.containers=t(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var i=this.groups={};t.each(this.settings.groups,function(e,s){"string"==typeof s&&(s=s.split(/\s/)),t.each(s,function(t,s){i[s]=e})});var s=this.settings.rules;t.each(s,function(e,i){s[e]=t.validator.normalizeRule(i)}),t(this.currentForm).validateDelegate(":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'] ,[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'] ","focusin focusout keyup",e).validateDelegate("[type='radio'], [type='checkbox'], select, option","click",e),this.settings.invalidHandler&&t(this.currentForm).bind("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),t.extend(this.submitted,this.errorMap),this.invalid=t.extend({},this.errorMap),this.valid()||t(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var t=0,e=this.currentElements=this.elements();e[t];t++)this.check(e[t]);return this.valid()},element:function(e){e=this.validationTargetFor(this.clean(e)),this.lastElement=e,this.prepareElement(e),this.currentElements=t(e);var i=this.check(e)!==!1;return i?delete this.invalid[e.name]:this.invalid[e.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),i},showErrors:function(e){if(e){t.extend(this.errorMap,e),this.errorList=[];for(var i in e)this.errorList.push({message:e[i],element:this.findByName(i)[0]});this.successList=t.grep(this.successList,function(t){return!(t.name in e)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){t.fn.resetForm&&t(this.currentForm).resetForm(),this.submitted={},this.lastElement=null,this.prepareForm(),this.hideErrors(),this.elements().removeClass(this.settings.errorClass).removeData("previousValue")},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(t){var e=0;for(var i in t)e++;return e},hideErrors:function(){this.addWrapper(this.toHide).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{t(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(e){}},findLastActive:function(){var e=this.lastActive;return e&&1===t.grep(this.errorList,function(t){return t.element.name===e.name}).length&&e},elements:function(){var e=this,i={};return t(this.currentForm).find("input, select, textarea").not(":submit, :reset, :image, [disabled]").not(this.settings.ignore).filter(function(){return!this.name&&e.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.name in i||!e.objectLength(t(this).rules())?!1:(i[this.name]=!0,!0)})},clean:function(e){return t(e)[0]},errors:function(){var e=this.settings.errorClass.replace(" ",".");return t(this.settings.errorElement+"."+e,this.errorContext)},reset:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=t([]),this.toHide=t([]),this.currentElements=t([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(t){this.reset(),this.toHide=this.errorsFor(t)},elementValue:function(e){var i=t(e).attr("type"),s=t(e).val();return"radio"===i||"checkbox"===i?t("input[name='"+t(e).attr("name")+"']:checked").val():"string"==typeof s?s.replace(/\r/g,""):s},check:function(e){e=this.validationTargetFor(this.clean(e));var i,s=t(e).rules(),r=!1,n=this.elementValue(e);for(var a in s){var u={method:a,parameters:s[a]};try{if(i=t.validator.methods[a].call(this,n,e,u.parameters),"dependency-mismatch"===i){r=!0;continue}if(r=!1,"pending"===i)return this.toHide=this.toHide.not(this.errorsFor(e)),void 0;if(!i)return this.formatAndAdd(e,u),!1}catch(o){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+e.id+", check the '"+u.method+"' method.",o),o}}return r?void 0:(this.objectLength(s)&&this.successList.push(e),!0)},customDataMessage:function(e,i){return t(e).data("msg-"+i.toLowerCase())||e.attributes&&t(e).attr("data-msg-"+i.toLowerCase())},customMessage:function(t,e){var i=this.settings.messages[t];return i&&(i.constructor===String?i:i[e])},findDefined:function(){for(var t=0;arguments.length>t;t++)if(void 0!==arguments[t])return arguments[t];return void 0},defaultMessage:function(e,i){return this.findDefined(this.customMessage(e.name,i),this.customDataMessage(e,i),!this.settings.ignoreTitle&&e.title||void 0,t.validator.messages[i],"Warning: No message defined for "+e.name+"")},formatAndAdd:function(e,i){var s=this.defaultMessage(e,i.method),r=/\$?\{(\d+)\}/g;"function"==typeof s?s=s.call(this,i.parameters,e):r.test(s)&&(s=t.validator.format(s.replace(r,"{$1}"),i.parameters)),this.errorList.push({message:s,element:e}),this.errorMap[e.name]=s,this.submitted[e.name]=s},addWrapper:function(t){return this.settings.wrapper&&(t=t.add(t.parent(this.settings.wrapper))),t},defaultShowErrors:function(){var t,e;for(t=0;this.errorList[t];t++){var i=this.errorList[t];this.settings.highlight&&this.settings.highlight.call(this,i.element,this.settings.errorClass,this.settings.validClass),this.showLabel(i.element,i.message)}if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(t=0;this.successList[t];t++)this.showLabel(this.successList[t]);if(this.settings.unhighlight)for(t=0,e=this.validElements();e[t];t++)this.settings.unhighlight.call(this,e[t],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return t(this.errorList).map(function(){return this.element})},showLabel:function(e,i){var s=this.errorsFor(e);s.length?(s.removeClass(this.settings.validClass).addClass(this.settings.errorClass),s.html(i)):(s=t("<"+this.settings.errorElement+">").attr("for",this.idOrName(e)).addClass(this.settings.errorClass).html(i||""),this.settings.wrapper&&(s=s.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.append(s).length||(this.settings.errorPlacement?this.settings.errorPlacement(s,t(e)):s.insertAfter(e))),!i&&this.settings.success&&(s.text(""),"string"==typeof this.settings.success?s.addClass(this.settings.success):this.settings.success(s,e)),this.toShow=this.toShow.add(s)},errorsFor:function(e){var i=this.idOrName(e);return this.errors().filter(function(){return t(this).attr("for")===i})},idOrName:function(t){return this.groups[t.name]||(this.checkable(t)?t.name:t.id||t.name)},validationTargetFor:function(t){return this.checkable(t)&&(t=this.findByName(t.name).not(this.settings.ignore)[0]),t},checkable:function(t){return/radio|checkbox/i.test(t.type)},findByName:function(e){return t(this.currentForm).find("[name='"+e+"']")},getLength:function(e,i){switch(i.nodeName.toLowerCase()){case"select":return t("option:selected",i).length;case"input":if(this.checkable(i))return this.findByName(i.name).filter(":checked").length}return e.length},depend:function(t,e){return this.dependTypes[typeof t]?this.dependTypes[typeof t](t,e):!0},dependTypes:{"boolean":function(t){return t},string:function(e,i){return!!t(e,i.form).length},"function":function(t,e){return t(e)}},optional:function(e){var i=this.elementValue(e);return!t.validator.methods.required.call(this,i,e)&&"dependency-mismatch"},startRequest:function(t){this.pending[t.name]||(this.pendingRequest++,this.pending[t.name]=!0)},stopRequest:function(e,i){this.pendingRequest--,0>this.pendingRequest&&(this.pendingRequest=0),delete this.pending[e.name],i&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(t(this.currentForm).submit(),this.formSubmitted=!1):!i&&0===this.pendingRequest&&this.formSubmitted&&(t(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(e){return t.data(e,"previousValue")||t.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,"remote")})}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(e,i){e.constructor===String?this.classRuleSettings[e]=i:t.extend(this.classRuleSettings,e)},classRules:function(e){var i={},s=t(e).attr("class");return s&&t.each(s.split(" "),function(){this in t.validator.classRuleSettings&&t.extend(i,t.validator.classRuleSettings[this])}),i},attributeRules:function(e){var i={},s=t(e),r=s[0].getAttribute("type");for(var n in t.validator.methods){var a;"required"===n?(a=s.get(0).getAttribute(n),""===a&&(a=!0),a=!!a):a=s.attr(n),/min|max/.test(n)&&(null===r||/number|range|text/.test(r))&&(a=Number(a)),a?i[n]=a:r===n&&"range"!==r&&(i[n]=!0)}return i.maxlength&&/-1|2147483647|524288/.test(i.maxlength)&&delete i.maxlength,i},dataRules:function(e){var i,s,r={},n=t(e);for(i in t.validator.methods)s=n.data("rule-"+i.toLowerCase()),void 0!==s&&(r[i]=s);return r},staticRules:function(e){var i={},s=t.data(e.form,"validator");return s.settings.rules&&(i=t.validator.normalizeRule(s.settings.rules[e.name])||{}),i},normalizeRules:function(e,i){return t.each(e,function(s,r){if(r===!1)return delete e[s],void 0;if(r.param||r.depends){var n=!0;switch(typeof r.depends){case"string":n=!!t(r.depends,i.form).length;break;case"function":n=r.depends.call(i,i)}n?e[s]=void 0!==r.param?r.param:!0:delete e[s]}}),t.each(e,function(s,r){e[s]=t.isFunction(r)?r(i):r}),t.each(["minlength","maxlength"],function(){e[this]&&(e[this]=Number(e[this]))}),t.each(["rangelength","range"],function(){var i;e[this]&&(t.isArray(e[this])?e[this]=[Number(e[this][0]),Number(e[this][1])]:"string"==typeof e[this]&&(i=e[this].split(/[\s,]+/),e[this]=[Number(i[0]),Number(i[1])]))}),t.validator.autoCreateRanges&&(e.min&&e.max&&(e.range=[e.min,e.max],delete e.min,delete e.max),e.minlength&&e.maxlength&&(e.rangelength=[e.minlength,e.maxlength],delete e.minlength,delete e.maxlength)),e},normalizeRule:function(e){if("string"==typeof e){var i={};t.each(e.split(/\s/),function(){i[this]=!0}),e=i}return e},addMethod:function(e,i,s){t.validator.methods[e]=i,t.validator.messages[e]=void 0!==s?s:t.validator.messages[e],3>i.length&&t.validator.addClassRules(e,t.validator.normalizeRule(e))},methods:{required:function(e,i,s){if(!this.depend(s,i))return"dependency-mismatch";if("select"===i.nodeName.toLowerCase()){var r=t(i).val();return r&&r.length>0}return this.checkable(i)?this.getLength(e,i)>0:t.trim(e).length>0},email:function(t,e){return this.optional(e)||/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(t)},url:function(t,e){return this.optional(e)||/^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(t)},date:function(t,e){return this.optional(e)||!/Invalid|NaN/.test(""+new Date(t))},dateISO:function(t,e){return this.optional(e)||/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(t)},number:function(t,e){return this.optional(e)||/^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(t)},digits:function(t,e){return this.optional(e)||/^\d+$/.test(t)},creditcard:function(t,e){if(this.optional(e))return"dependency-mismatch";if(/[^0-9 \-]+/.test(t))return!1;var i=0,s=0,r=!1;t=t.replace(/\D/g,"");for(var n=t.length-1;n>=0;n--){var a=t.charAt(n);s=parseInt(a,10),r&&(s*=2)>9&&(s-=9),i+=s,r=!r}return 0===i%10},minlength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||r>=s},maxlength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||s>=r},rangelength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||r>=s[0]&&s[1]>=r},min:function(t,e,i){return this.optional(e)||t>=i},max:function(t,e,i){return this.optional(e)||i>=t},range:function(t,e,i){return this.optional(e)||t>=i[0]&&i[1]>=t},equalTo:function(e,i,s){var r=t(s);return this.settings.onfocusout&&r.unbind(".validate-equalTo").bind("blur.validate-equalTo",function(){t(i).valid()}),e===r.val()},remote:function(e,i,s){if(this.optional(i))return"dependency-mismatch";var r=this.previousValue(i);if(this.settings.messages[i.name]||(this.settings.messages[i.name]={}),r.originalMessage=this.settings.messages[i.name].remote,this.settings.messages[i.name].remote=r.message,s="string"==typeof s&&{url:s}||s,r.old===e)return r.valid;r.old=e;var n=this;this.startRequest(i);var a={};return a[i.name]=e,t.ajax(t.extend(!0,{url:s,mode:"abort",port:"validate"+i.name,dataType:"json",data:a,success:function(s){n.settings.messages[i.name].remote=r.originalMessage;var a=s===!0||"true"===s;if(a){var u=n.formSubmitted;n.prepareElement(i),n.formSubmitted=u,n.successList.push(i),delete n.invalid[i.name],n.showErrors()}else{var o={},l=s||n.defaultMessage(i,"remote");o[i.name]=r.message=t.isFunction(l)?l(e):l,n.invalid[i.name]=!0,n.showErrors(o)}r.valid=a,n.stopRequest(i,a)}},s)),"pending"}}}),t.format=t.validator.format})(jQuery),function(t){var e={};if(t.ajaxPrefilter)t.ajaxPrefilter(function(t,i,s){var r=t.port;"abort"===t.mode&&(e[r]&&e[r].abort(),e[r]=s)});else{var i=t.ajax;t.ajax=function(s){var r=("mode"in s?s:t.ajaxSettings).mode,n=("port"in s?s:t.ajaxSettings).port;return"abort"===r?(e[n]&&e[n].abort(),e[n]=i.apply(this,arguments),e[n]):i.apply(this,arguments)}}}(jQuery),function(t){t.extend(t.fn,{validateDelegate:function(e,i,s){return this.bind(i,function(i){var r=t(i.target);return r.is(e)?s.apply(r,arguments):void 0})}})}(jQuery); -------------------------------------------------------------------------------- /Scripts/jquery.validate.unobtrusive.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | 20 | /*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */ 21 | /*global document: false, jQuery: false */ 22 | 23 | (function ($) { 24 | var $jQval = $.validator, 25 | adapters, 26 | data_validation = "unobtrusiveValidation"; 27 | 28 | function setValidationValues(options, ruleName, value) { 29 | options.rules[ruleName] = value; 30 | if (options.message) { 31 | options.messages[ruleName] = options.message; 32 | } 33 | } 34 | 35 | function splitAndTrim(value) { 36 | return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); 37 | } 38 | 39 | function escapeAttributeValue(value) { 40 | // As mentioned on http://api.jquery.com/category/selectors/ 41 | return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); 42 | } 43 | 44 | function getModelPrefix(fieldName) { 45 | return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); 46 | } 47 | 48 | function appendModelPrefix(value, prefix) { 49 | if (value.indexOf("*.") === 0) { 50 | value = value.replace("*.", prefix); 51 | } 52 | return value; 53 | } 54 | 55 | function onError(error, inputElement) { // 'this' is the form element 56 | var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), 57 | replaceAttrValue = container.attr("data-valmsg-replace"), 58 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; 59 | 60 | container.removeClass("field-validation-valid").addClass("field-validation-error"); 61 | error.data("unobtrusiveContainer", container); 62 | 63 | if (replace) { 64 | container.empty(); 65 | error.removeClass("input-validation-error").appendTo(container); 66 | } 67 | else { 68 | error.hide(); 69 | } 70 | } 71 | 72 | function onErrors(event, validator) { // 'this' is the form element 73 | var container = $(this).find("[data-valmsg-summary=true]"), 74 | list = container.find("ul"); 75 | 76 | if (list && list.length && validator.errorList.length) { 77 | list.empty(); 78 | container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); 79 | 80 | $.each(validator.errorList, function () { 81 | $("
  • ").html(this.message).appendTo(list); 82 | }); 83 | } 84 | } 85 | 86 | function onSuccess(error) { // 'this' is the form element 87 | var container = error.data("unobtrusiveContainer"), 88 | replaceAttrValue = container.attr("data-valmsg-replace"), 89 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; 90 | 91 | if (container) { 92 | container.addClass("field-validation-valid").removeClass("field-validation-error"); 93 | error.removeData("unobtrusiveContainer"); 94 | 95 | if (replace) { 96 | container.empty(); 97 | } 98 | } 99 | } 100 | 101 | function onReset(event) { // 'this' is the form element 102 | var $form = $(this); 103 | $form.data("validator").resetForm(); 104 | $form.find(".validation-summary-errors") 105 | .addClass("validation-summary-valid") 106 | .removeClass("validation-summary-errors"); 107 | $form.find(".field-validation-error") 108 | .addClass("field-validation-valid") 109 | .removeClass("field-validation-error") 110 | .removeData("unobtrusiveContainer") 111 | .find(">*") // If we were using valmsg-replace, get the underlying error 112 | .removeData("unobtrusiveContainer"); 113 | } 114 | 115 | function validationInfo(form) { 116 | var $form = $(form), 117 | result = $form.data(data_validation), 118 | onResetProxy = $.proxy(onReset, form); 119 | 120 | if (!result) { 121 | result = { 122 | options: { // options structure passed to jQuery Validate's validate() method 123 | errorClass: "input-validation-error", 124 | errorElement: "span", 125 | errorPlacement: $.proxy(onError, form), 126 | invalidHandler: $.proxy(onErrors, form), 127 | messages: {}, 128 | rules: {}, 129 | success: $.proxy(onSuccess, form) 130 | }, 131 | attachValidation: function () { 132 | $form 133 | .unbind("reset." + data_validation, onResetProxy) 134 | .bind("reset." + data_validation, onResetProxy) 135 | .validate(this.options); 136 | }, 137 | validate: function () { // a validation function that is called by unobtrusive Ajax 138 | $form.validate(); 139 | return $form.valid(); 140 | } 141 | }; 142 | $form.data(data_validation, result); 143 | } 144 | 145 | return result; 146 | } 147 | 148 | $jQval.unobtrusive = { 149 | adapters: [], 150 | 151 | parseElement: function (element, skipAttach) { 152 | /// 153 | /// Parses a single HTML element for unobtrusive validation attributes. 154 | /// 155 | /// The HTML element to be parsed. 156 | /// [Optional] true to skip attaching the 157 | /// validation to the form. If parsing just this single element, you should specify true. 158 | /// If parsing several elements, you should specify false, and manually attach the validation 159 | /// to the form when you are finished. The default is false. 160 | var $element = $(element), 161 | form = $element.parents("form")[0], 162 | valInfo, rules, messages; 163 | 164 | if (!form) { // Cannot do client-side validation without a form 165 | return; 166 | } 167 | 168 | valInfo = validationInfo(form); 169 | valInfo.options.rules[element.name] = rules = {}; 170 | valInfo.options.messages[element.name] = messages = {}; 171 | 172 | $.each(this.adapters, function () { 173 | var prefix = "data-val-" + this.name, 174 | message = $element.attr(prefix), 175 | paramValues = {}; 176 | 177 | if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy) 178 | prefix += "-"; 179 | 180 | $.each(this.params, function () { 181 | paramValues[this] = $element.attr(prefix + this); 182 | }); 183 | 184 | this.adapt({ 185 | element: element, 186 | form: form, 187 | message: message, 188 | params: paramValues, 189 | rules: rules, 190 | messages: messages 191 | }); 192 | } 193 | }); 194 | 195 | $.extend(rules, { "__dummy__": true }); 196 | 197 | if (!skipAttach) { 198 | valInfo.attachValidation(); 199 | } 200 | }, 201 | 202 | parse: function (selector) { 203 | /// 204 | /// Parses all the HTML elements in the specified selector. It looks for input elements decorated 205 | /// with the [data-val=true] attribute value and enables validation according to the data-val-* 206 | /// attribute values. 207 | /// 208 | /// Any valid jQuery selector. 209 | var $forms = $(selector) 210 | .parents("form") 211 | .andSelf() 212 | .add($(selector).find("form")) 213 | .filter("form"); 214 | 215 | // :input is a psuedoselector provided by jQuery which selects input and input-like elements 216 | // combining :input with other selectors significantly decreases performance. 217 | $(selector).find(":input").filter("[data-val=true]").each(function () { 218 | $jQval.unobtrusive.parseElement(this, true); 219 | }); 220 | 221 | $forms.each(function () { 222 | var info = validationInfo(this); 223 | if (info) { 224 | info.attachValidation(); 225 | } 226 | }); 227 | } 228 | }; 229 | 230 | adapters = $jQval.unobtrusive.adapters; 231 | 232 | adapters.add = function (adapterName, params, fn) { 233 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation. 234 | /// The name of the adapter to be added. This matches the name used 235 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 236 | /// [Optional] An array of parameter names (strings) that will 237 | /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and 238 | /// mmmm is the parameter name). 239 | /// The function to call, which adapts the values from the HTML 240 | /// attributes into jQuery Validate rules and/or messages. 241 | /// 242 | if (!fn) { // Called with no params, just a function 243 | fn = params; 244 | params = []; 245 | } 246 | this.push({ name: adapterName, params: params, adapt: fn }); 247 | return this; 248 | }; 249 | 250 | adapters.addBool = function (adapterName, ruleName) { 251 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 252 | /// the jQuery Validate validation rule has no parameter values. 253 | /// The name of the adapter to be added. This matches the name used 254 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 255 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 256 | /// of adapterName will be used instead. 257 | /// 258 | return this.add(adapterName, function (options) { 259 | setValidationValues(options, ruleName || adapterName, true); 260 | }); 261 | }; 262 | 263 | adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { 264 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 265 | /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and 266 | /// one for min-and-max). The HTML parameters are expected to be named -min and -max. 267 | /// The name of the adapter to be added. This matches the name used 268 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 269 | /// The name of the jQuery Validate rule to be used when you only 270 | /// have a minimum value. 271 | /// The name of the jQuery Validate rule to be used when you only 272 | /// have a maximum value. 273 | /// The name of the jQuery Validate rule to be used when you 274 | /// have both a minimum and maximum value. 275 | /// [Optional] The name of the HTML attribute that 276 | /// contains the minimum value. The default is "min". 277 | /// [Optional] The name of the HTML attribute that 278 | /// contains the maximum value. The default is "max". 279 | /// 280 | return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { 281 | var min = options.params.min, 282 | max = options.params.max; 283 | 284 | if (min && max) { 285 | setValidationValues(options, minMaxRuleName, [min, max]); 286 | } 287 | else if (min) { 288 | setValidationValues(options, minRuleName, min); 289 | } 290 | else if (max) { 291 | setValidationValues(options, maxRuleName, max); 292 | } 293 | }); 294 | }; 295 | 296 | adapters.addSingleVal = function (adapterName, attribute, ruleName) { 297 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 298 | /// the jQuery Validate validation rule has a single value. 299 | /// The name of the adapter to be added. This matches the name used 300 | /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name). 301 | /// [Optional] The name of the HTML attribute that contains the value. 302 | /// The default is "val". 303 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 304 | /// of adapterName will be used instead. 305 | /// 306 | return this.add(adapterName, [attribute || "val"], function (options) { 307 | setValidationValues(options, ruleName || adapterName, options.params[attribute]); 308 | }); 309 | }; 310 | 311 | $jQval.addMethod("__dummy__", function (value, element, params) { 312 | return true; 313 | }); 314 | 315 | $jQval.addMethod("regex", function (value, element, params) { 316 | var match; 317 | if (this.optional(element)) { 318 | return true; 319 | } 320 | 321 | match = new RegExp(params).exec(value); 322 | return (match && (match.index === 0) && (match[0].length === value.length)); 323 | }); 324 | 325 | $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { 326 | var match; 327 | if (nonalphamin) { 328 | match = value.match(/\W/g); 329 | match = match && match.length >= nonalphamin; 330 | } 331 | return match; 332 | }); 333 | 334 | if ($jQval.methods.extension) { 335 | adapters.addSingleVal("accept", "mimtype"); 336 | adapters.addSingleVal("extension", "extension"); 337 | } else { 338 | // for backward compatibility, when the 'extension' validation method does not exist, such as with versions 339 | // of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for 340 | // validating the extension, and ignore mime-type validations as they are not supported. 341 | adapters.addSingleVal("extension", "extension", "accept"); 342 | } 343 | 344 | adapters.addSingleVal("regex", "pattern"); 345 | adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); 346 | adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); 347 | adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength"); 348 | adapters.add("equalto", ["other"], function (options) { 349 | var prefix = getModelPrefix(options.element.name), 350 | other = options.params.other, 351 | fullOtherName = appendModelPrefix(other, prefix), 352 | element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; 353 | 354 | setValidationValues(options, "equalTo", element); 355 | }); 356 | adapters.add("required", function (options) { 357 | // jQuery Validate equates "required" with "mandatory" for checkbox elements 358 | if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { 359 | setValidationValues(options, "required", true); 360 | } 361 | }); 362 | adapters.add("remote", ["url", "type", "additionalfields"], function (options) { 363 | var value = { 364 | url: options.params.url, 365 | type: options.params.type || "GET", 366 | data: {} 367 | }, 368 | prefix = getModelPrefix(options.element.name); 369 | 370 | $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { 371 | var paramName = appendModelPrefix(fieldName, prefix); 372 | value.data[paramName] = function () { 373 | return $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']").val(); 374 | }; 375 | }); 376 | 377 | setValidationValues(options, "remote", value); 378 | }); 379 | adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { 380 | if (options.params.min) { 381 | setValidationValues(options, "minlength", options.params.min); 382 | } 383 | if (options.params.nonalphamin) { 384 | setValidationValues(options, "nonalphamin", options.params.nonalphamin); 385 | } 386 | if (options.params.regex) { 387 | setValidationValues(options, "regex", options.params.regex); 388 | } 389 | }); 390 | 391 | $(function () { 392 | $jQval.unobtrusive.parse(document); 393 | }); 394 | }(jQuery)); -------------------------------------------------------------------------------- /Scripts/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | (function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this);b.data("validator").resetForm();b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(c){var b=a(c),d=b.data(e),f=a.proxy(n,c);if(!d){d={options:{errorClass:"input-validation-error",errorElement:"span",errorPlacement:a.proxy(m,c),invalidHandler:a.proxy(l,c),messages:{},rules:{},success:a.proxy(k,c)},attachValidation:function(){b.unbind("reset."+e,f).bind("reset."+e,f).validate(this.options)},validate:function(){b.validate();return b.valid()}};b.data(e,d)}return d}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(b){var c=a(b).parents("form").andSelf().add(a(b).find("form")).filter("form");a(b).find(":input").filter("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});c.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){return a(b.form).find(":input").filter("[name='"+f(c)+"']").val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); -------------------------------------------------------------------------------- /Scripts/respond.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia = window.matchMedia || (function(doc, undefined){ 18 | 19 | var bool, 20 | docElem = doc.documentElement, 21 | refNode = docElem.firstElementChild || docElem.firstChild, 22 | // fakeBody required for 23 | fakeBody = doc.createElement('body'), 24 | div = doc.createElement('div'); 25 | 26 | div.id = 'mq-test-1'; 27 | div.style.cssText = "position:absolute;top:-100em"; 28 | fakeBody.style.background = "none"; 29 | fakeBody.appendChild(div); 30 | 31 | return function(q){ 32 | 33 | div.innerHTML = '­'; 34 | 35 | docElem.insertBefore(fakeBody, refNode); 36 | bool = div.offsetWidth == 42; 37 | docElem.removeChild(fakeBody); 38 | 39 | return { matches: bool, media: q }; 40 | }; 41 | 42 | })(document); 43 | 44 | 45 | 46 | 47 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 48 | (function( win ){ 49 | //exposed namespace 50 | win.respond = {}; 51 | 52 | //define update even in native-mq-supporting browsers, to avoid errors 53 | respond.update = function(){}; 54 | 55 | //expose media query support flag for external use 56 | respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches; 57 | 58 | //if media queries are supported, exit here 59 | if( respond.mediaQueriesSupported ){ return; } 60 | 61 | //define vars 62 | var doc = win.document, 63 | docElem = doc.documentElement, 64 | mediastyles = [], 65 | rules = [], 66 | appendedEls = [], 67 | parsedSheets = {}, 68 | resizeThrottle = 30, 69 | head = doc.getElementsByTagName( "head" )[0] || docElem, 70 | base = doc.getElementsByTagName( "base" )[0], 71 | links = head.getElementsByTagName( "link" ), 72 | requestQueue = [], 73 | 74 | //loop stylesheets, send text content to translate 75 | ripCSS = function(){ 76 | var sheets = links, 77 | sl = sheets.length, 78 | i = 0, 79 | //vars for loop: 80 | sheet, href, media, isCSS; 81 | 82 | for( ; i < sl; i++ ){ 83 | sheet = sheets[ i ], 84 | href = sheet.href, 85 | media = sheet.media, 86 | isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; 87 | 88 | //only links plz and prevent re-parsing 89 | if( !!href && isCSS && !parsedSheets[ href ] ){ 90 | // selectivizr exposes css through the rawCssText expando 91 | if (sheet.styleSheet && sheet.styleSheet.rawCssText) { 92 | translate( sheet.styleSheet.rawCssText, href, media ); 93 | parsedSheets[ href ] = true; 94 | } else { 95 | if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) 96 | || href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){ 97 | requestQueue.push( { 98 | href: href, 99 | media: media 100 | } ); 101 | } 102 | } 103 | } 104 | } 105 | makeRequests(); 106 | }, 107 | 108 | //recurse through request queue, get css text 109 | makeRequests = function(){ 110 | if( requestQueue.length ){ 111 | var thisRequest = requestQueue.shift(); 112 | 113 | ajax( thisRequest.href, function( styles ){ 114 | translate( styles, thisRequest.href, thisRequest.media ); 115 | parsedSheets[ thisRequest.href ] = true; 116 | makeRequests(); 117 | } ); 118 | } 119 | }, 120 | 121 | //find media blocks in css text, convert to style blocks 122 | translate = function( styles, href, media ){ 123 | var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ), 124 | ql = qs && qs.length || 0, 125 | //try to get CSS path 126 | href = href.substring( 0, href.lastIndexOf( "/" )), 127 | repUrls = function( css ){ 128 | return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" ); 129 | }, 130 | useMedia = !ql && media, 131 | //vars used in loop 132 | i = 0, 133 | j, fullq, thisq, eachq, eql; 134 | 135 | //if path exists, tack on trailing slash 136 | if( href.length ){ href += "/"; } 137 | 138 | //if no internal queries exist, but media attr does, use that 139 | //note: this currently lacks support for situations where a media attr is specified on a link AND 140 | //its associated stylesheet has internal CSS media queries. 141 | //In those cases, the media attribute will currently be ignored. 142 | if( useMedia ){ 143 | ql = 1; 144 | } 145 | 146 | 147 | for( ; i < ql; i++ ){ 148 | j = 0; 149 | 150 | //media attr 151 | if( useMedia ){ 152 | fullq = media; 153 | rules.push( repUrls( styles ) ); 154 | } 155 | //parse for styles 156 | else{ 157 | fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1; 158 | rules.push( RegExp.$2 && repUrls( RegExp.$2 ) ); 159 | } 160 | 161 | eachq = fullq.split( "," ); 162 | eql = eachq.length; 163 | 164 | for( ; j < eql; j++ ){ 165 | thisq = eachq[ j ]; 166 | mediastyles.push( { 167 | media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all", 168 | rules : rules.length - 1, 169 | hasquery: thisq.indexOf("(") > -1, 170 | minw : thisq.match( /\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ), 171 | maxw : thisq.match( /\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ) 172 | } ); 173 | } 174 | } 175 | 176 | applyMedia(); 177 | }, 178 | 179 | lastCall, 180 | 181 | resizeDefer, 182 | 183 | // returns the value of 1em in pixels 184 | getEmValue = function() { 185 | var ret, 186 | div = doc.createElement('div'), 187 | body = doc.body, 188 | fakeUsed = false; 189 | 190 | div.style.cssText = "position:absolute;font-size:1em;width:1em"; 191 | 192 | if( !body ){ 193 | body = fakeUsed = doc.createElement( "body" ); 194 | body.style.background = "none"; 195 | } 196 | 197 | body.appendChild( div ); 198 | 199 | docElem.insertBefore( body, docElem.firstChild ); 200 | 201 | ret = div.offsetWidth; 202 | 203 | if( fakeUsed ){ 204 | docElem.removeChild( body ); 205 | } 206 | else { 207 | body.removeChild( div ); 208 | } 209 | 210 | //also update eminpx before returning 211 | ret = eminpx = parseFloat(ret); 212 | 213 | return ret; 214 | }, 215 | 216 | //cached container for 1em value, populated the first time it's needed 217 | eminpx, 218 | 219 | //enable/disable styles 220 | applyMedia = function( fromResize ){ 221 | var name = "clientWidth", 222 | docElemProp = docElem[ name ], 223 | currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp, 224 | styleBlocks = {}, 225 | lastLink = links[ links.length-1 ], 226 | now = (new Date()).getTime(); 227 | 228 | //throttle resize calls 229 | if( fromResize && lastCall && now - lastCall < resizeThrottle ){ 230 | clearTimeout( resizeDefer ); 231 | resizeDefer = setTimeout( applyMedia, resizeThrottle ); 232 | return; 233 | } 234 | else { 235 | lastCall = now; 236 | } 237 | 238 | for( var i in mediastyles ){ 239 | var thisstyle = mediastyles[ i ], 240 | min = thisstyle.minw, 241 | max = thisstyle.maxw, 242 | minnull = min === null, 243 | maxnull = max === null, 244 | em = "em"; 245 | 246 | if( !!min ){ 247 | min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); 248 | } 249 | if( !!max ){ 250 | max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); 251 | } 252 | 253 | // if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true 254 | if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){ 255 | if( !styleBlocks[ thisstyle.media ] ){ 256 | styleBlocks[ thisstyle.media ] = []; 257 | } 258 | styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] ); 259 | } 260 | } 261 | 262 | //remove any existing respond style element(s) 263 | for( var i in appendedEls ){ 264 | if( appendedEls[ i ] && appendedEls[ i ].parentNode === head ){ 265 | head.removeChild( appendedEls[ i ] ); 266 | } 267 | } 268 | 269 | //inject active styles, grouped by media type 270 | for( var i in styleBlocks ){ 271 | var ss = doc.createElement( "style" ), 272 | css = styleBlocks[ i ].join( "\n" ); 273 | 274 | ss.type = "text/css"; 275 | ss.media = i; 276 | 277 | //originally, ss was appended to a documentFragment and sheets were appended in bulk. 278 | //this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one! 279 | head.insertBefore( ss, lastLink.nextSibling ); 280 | 281 | if ( ss.styleSheet ){ 282 | ss.styleSheet.cssText = css; 283 | } 284 | else { 285 | ss.appendChild( doc.createTextNode( css ) ); 286 | } 287 | 288 | //push to appendedEls to track for later removal 289 | appendedEls.push( ss ); 290 | } 291 | }, 292 | //tweaked Ajax functions from Quirksmode 293 | ajax = function( url, callback ) { 294 | var req = xmlHttp(); 295 | if (!req){ 296 | return; 297 | } 298 | req.open( "GET", url, true ); 299 | req.onreadystatechange = function () { 300 | if ( req.readyState != 4 || req.status != 200 && req.status != 304 ){ 301 | return; 302 | } 303 | callback( req.responseText ); 304 | } 305 | if ( req.readyState == 4 ){ 306 | return; 307 | } 308 | req.send( null ); 309 | }, 310 | //define ajax obj 311 | xmlHttp = (function() { 312 | var xmlhttpmethod = false; 313 | try { 314 | xmlhttpmethod = new XMLHttpRequest(); 315 | } 316 | catch( e ){ 317 | xmlhttpmethod = new ActiveXObject( "Microsoft.XMLHTTP" ); 318 | } 319 | return function(){ 320 | return xmlhttpmethod; 321 | }; 322 | })(); 323 | 324 | //translate CSS 325 | ripCSS(); 326 | 327 | //expose update for re-running respond later on 328 | respond.update = ripCSS; 329 | 330 | //adjust on resize 331 | function callMedia(){ 332 | applyMedia( true ); 333 | } 334 | if( win.addEventListener ){ 335 | win.addEventListener( "resize", callMedia, false ); 336 | } 337 | else if( win.attachEvent ){ 338 | win.attachEvent( "onresize", callMedia ); 339 | } 340 | })(this); 341 | -------------------------------------------------------------------------------- /Scripts/respond.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.style.background="none";d.appendChild(g);return function(h){g.innerHTML='­';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document); 18 | 19 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 20 | (function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this); -------------------------------------------------------------------------------- /Scripts/typings/angularjs/angular-animate.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular JS 1.2+ (ngAnimate module) 2 | // Project: http://angularjs.org 3 | // Definitions by: Michel Salib 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | /// 7 | 8 | 9 | /////////////////////////////////////////////////////////////////////////////// 10 | // ngAnimate module (angular-animate.js) 11 | /////////////////////////////////////////////////////////////////////////////// 12 | declare module ng.animate { 13 | 14 | /////////////////////////////////////////////////////////////////////////// 15 | // AnimateService 16 | // see http://docs.angularjs.org/api/ngAnimate.$animate 17 | /////////////////////////////////////////////////////////////////////////// 18 | interface IAnimateService extends ng.IAnimateService { 19 | enabled(value?: boolean, element?: JQuery): boolean; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Scripts/typings/angularjs/angular-cookies.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular JS 1.2 (ngCookies module) 2 | // Project: http://angularjs.org 3 | // Definitions by: Diego Vilar 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | 7 | /// 8 | 9 | /////////////////////////////////////////////////////////////////////////////// 10 | // ngCookies module (angular-cookies.js) 11 | /////////////////////////////////////////////////////////////////////////////// 12 | declare module ng.cookies { 13 | 14 | /////////////////////////////////////////////////////////////////////////// 15 | // CookieService 16 | // see http://docs.angularjs.org/api/ngCookies.$cookies 17 | /////////////////////////////////////////////////////////////////////////// 18 | interface ICookiesService {} 19 | 20 | /////////////////////////////////////////////////////////////////////////// 21 | // CookieStoreService 22 | // see http://docs.angularjs.org/api/ngCookies.$cookieStore 23 | /////////////////////////////////////////////////////////////////////////// 24 | interface ICookieStoreService { 25 | get(key: string): any; 26 | put(key: string, value: any): void; 27 | remove(key: string): void; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Scripts/typings/angularjs/angular-mocks.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular JS 1.2 (ngMock, ngMockE2E module) 2 | // Project: http://angularjs.org 3 | // Definitions by: Diego Vilar 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | /// 7 | 8 | /////////////////////////////////////////////////////////////////////////////// 9 | // functions attached to global object (window) 10 | /////////////////////////////////////////////////////////////////////////////// 11 | declare var module: (...modules: any[]) => any; 12 | declare var inject: (...fns: Function[]) => any; 13 | 14 | /////////////////////////////////////////////////////////////////////////////// 15 | // ngMock module (angular-mocks.js) 16 | /////////////////////////////////////////////////////////////////////////////// 17 | declare module ng { 18 | 19 | /////////////////////////////////////////////////////////////////////////// 20 | // AngularStatic 21 | // We reopen it to add the MockStatic definition 22 | /////////////////////////////////////////////////////////////////////////// 23 | interface IAngularStatic { 24 | mock: IMockStatic; 25 | } 26 | 27 | interface IMockStatic { 28 | // see http://docs.angularjs.org/api/angular.mock.dump 29 | dump(obj: any): string; 30 | 31 | // see http://docs.angularjs.org/api/angular.mock.inject 32 | inject(...fns: Function[]): any; 33 | inject(...inlineAnnotatedConstructor: any[]): any; // this overload is undocumented, but works 34 | 35 | // see http://docs.angularjs.org/api/angular.mock.module 36 | module(...modules: any[]): any; 37 | 38 | // see http://docs.angularjs.org/api/angular.mock.TzDate 39 | TzDate(offset: number, timestamp: number): Date; 40 | TzDate(offset: number, timestamp: string): Date; 41 | } 42 | 43 | /////////////////////////////////////////////////////////////////////////// 44 | // ExceptionHandlerService 45 | // see http://docs.angularjs.org/api/ngMock.$exceptionHandler 46 | // see http://docs.angularjs.org/api/ngMock.$exceptionHandlerProvider 47 | /////////////////////////////////////////////////////////////////////////// 48 | interface IExceptionHandlerProvider extends IServiceProvider { 49 | mode(mode: string): void; 50 | } 51 | 52 | /////////////////////////////////////////////////////////////////////////// 53 | // TimeoutService 54 | // see http://docs.angularjs.org/api/ngMock.$timeout 55 | // Augments the original service 56 | /////////////////////////////////////////////////////////////////////////// 57 | interface ITimeoutService { 58 | flush(delay?: number): void; 59 | flushNext(expectedDelay?: number): void; 60 | verifyNoPendingTasks(): void; 61 | } 62 | 63 | /////////////////////////////////////////////////////////////////////////// 64 | // IntervalService 65 | // see http://docs.angularjs.org/api/ngMock.$interval 66 | // Augments the original service 67 | /////////////////////////////////////////////////////////////////////////// 68 | interface IIntervalService { 69 | flush(millis?: number): number; 70 | } 71 | 72 | /////////////////////////////////////////////////////////////////////////// 73 | // LogService 74 | // see http://docs.angularjs.org/api/ngMock.$log 75 | // Augments the original service 76 | /////////////////////////////////////////////////////////////////////////// 77 | interface ILogService { 78 | assertEmpty(): void; 79 | reset(): void; 80 | } 81 | 82 | interface ILogCall { 83 | logs: string[]; 84 | } 85 | 86 | /////////////////////////////////////////////////////////////////////////// 87 | // HttpBackendService 88 | // see http://docs.angularjs.org/api/ngMock.$httpBackend 89 | /////////////////////////////////////////////////////////////////////////// 90 | interface IHttpBackendService { 91 | flush(count?: number): void; 92 | resetExpectations(): void; 93 | verifyNoOutstandingExpectation(): void; 94 | verifyNoOutstandingRequest(): void; 95 | 96 | expect(method: string, url: string, data?: string, headers?: Object): mock.IRequestHandler; 97 | expect(method: string, url: string, data?: string, headers?: (object: Object) => boolean): mock.IRequestHandler; 98 | expect(method: string, url: string, data?: RegExp, headers?: Object): mock.IRequestHandler; 99 | expect(method: string, url: string, data?: RegExp, headers?: (object: Object) => boolean): mock.IRequestHandler; 100 | expect(method: string, url: string, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 101 | expect(method: string, url: string, data?: (data: string) => boolean, headers?: (object: Object) => boolean): mock.IRequestHandler; 102 | expect(method: string, url: string, data?: Object, headers?: Object): mock.IRequestHandler; 103 | expect(method: string, url: string, data?: Object, headers?: (object: Object) => boolean): mock.IRequestHandler; 104 | expect(method: string, url: RegExp, data?: string, headers?: Object): mock.IRequestHandler; 105 | expect(method: string, url: RegExp, data?: string, headers?: (object: Object) => boolean): mock.IRequestHandler; 106 | expect(method: string, url: RegExp, data?: RegExp, headers?: Object): mock.IRequestHandler; 107 | expect(method: string, url: RegExp, data?: RegExp, headers?: (object: Object) => boolean): mock.IRequestHandler; 108 | expect(method: string, url: RegExp, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 109 | expect(method: string, url: RegExp, data?: (data: string) => boolean, headers?: (object: Object) => boolean): mock.IRequestHandler; 110 | expect(method: string, url: RegExp, data?: Object, headers?: Object): mock.IRequestHandler; 111 | expect(method: string, url: RegExp, data?: Object, headers?: (object: Object) => boolean): mock.IRequestHandler; 112 | 113 | expectDELETE(url: string, headers?: Object): mock.IRequestHandler; 114 | expectDELETE(url: RegExp, headers?: Object): mock.IRequestHandler; 115 | expectGET(url: string, headers?: Object): mock.IRequestHandler; 116 | expectGET(url: RegExp, headers?: Object): mock.IRequestHandler; 117 | expectHEAD(url: string, headers?: Object): mock.IRequestHandler; 118 | expectHEAD(url: RegExp, headers?: Object): mock.IRequestHandler; 119 | expectJSONP(url: string): mock.IRequestHandler; 120 | expectJSONP(url: RegExp): mock.IRequestHandler; 121 | 122 | expectPATCH(url: string, data?: string, headers?: Object): mock.IRequestHandler; 123 | expectPATCH(url: string, data?: RegExp, headers?: Object): mock.IRequestHandler; 124 | expectPATCH(url: string, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 125 | expectPATCH(url: string, data?: Object, headers?: Object): mock.IRequestHandler; 126 | expectPATCH(url: RegExp, data?: string, headers?: Object): mock.IRequestHandler; 127 | expectPATCH(url: RegExp, data?: RegExp, headers?: Object): mock.IRequestHandler; 128 | expectPATCH(url: RegExp, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 129 | expectPATCH(url: RegExp, data?: Object, headers?: Object): mock.IRequestHandler; 130 | 131 | expectPOST(url: string, data?: string, headers?: Object): mock.IRequestHandler; 132 | expectPOST(url: string, data?: RegExp, headers?: Object): mock.IRequestHandler; 133 | expectPOST(url: string, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 134 | expectPOST(url: string, data?: Object, headers?: Object): mock.IRequestHandler; 135 | expectPOST(url: RegExp, data?: string, headers?: Object): mock.IRequestHandler; 136 | expectPOST(url: RegExp, data?: RegExp, headers?: Object): mock.IRequestHandler; 137 | expectPOST(url: RegExp, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 138 | expectPOST(url: RegExp, data?: Object, headers?: Object): mock.IRequestHandler; 139 | 140 | expectPUT(url: string, data?: string, headers?: Object): mock.IRequestHandler; 141 | expectPUT(url: string, data?: RegExp, headers?: Object): mock.IRequestHandler; 142 | expectPUT(url: string, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 143 | expectPUT(url: string, data?: Object, headers?: Object): mock.IRequestHandler; 144 | expectPUT(url: RegExp, data?: string, headers?: Object): mock.IRequestHandler; 145 | expectPUT(url: RegExp, data?: RegExp, headers?: Object): mock.IRequestHandler; 146 | expectPUT(url: RegExp, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 147 | expectPUT(url: RegExp, data?: Object, headers?: Object): mock.IRequestHandler; 148 | 149 | when(method: string, url: string, data?: string, headers?: Object): mock.IRequestHandler; 150 | when(method: string, url: string, data?: string, headers?: (object: Object) => boolean): mock.IRequestHandler; 151 | when(method: string, url: string, data?: RegExp, headers?: Object): mock.IRequestHandler; 152 | when(method: string, url: string, data?: RegExp, headers?: (object: Object) => boolean): mock.IRequestHandler; 153 | when(method: string, url: string, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 154 | when(method: string, url: string, data?: (data: string) => boolean, headers?: (object: Object) => boolean): mock.IRequestHandler; 155 | when(method: string, url: string, data?: Object, headers?: Object): mock.IRequestHandler; 156 | when(method: string, url: string, data?: Object, headers?: (object: Object) => boolean): mock.IRequestHandler; 157 | when(method: string, url: RegExp, data?: string, headers?: Object): mock.IRequestHandler; 158 | when(method: string, url: RegExp, data?: string, headers?: (object: Object) => boolean): mock.IRequestHandler; 159 | when(method: string, url: RegExp, data?: RegExp, headers?: Object): mock.IRequestHandler; 160 | when(method: string, url: RegExp, data?: RegExp, headers?: (object: Object) => boolean): mock.IRequestHandler; 161 | when(method: string, url: RegExp, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 162 | when(method: string, url: RegExp, data?: (data: string) => boolean, headers?: (object: Object) => boolean): mock.IRequestHandler; 163 | when(method: string, url: RegExp, data?: Object, headers?: Object): mock.IRequestHandler; 164 | when(method: string, url: RegExp, data?: Object, headers?: (object: Object) => boolean): mock.IRequestHandler; 165 | 166 | whenDELETE(url: string, headers?: Object): mock.IRequestHandler; 167 | whenDELETE(url: string, headers?: (object: Object) => boolean): mock.IRequestHandler; 168 | whenDELETE(url: RegExp, headers?: Object): mock.IRequestHandler; 169 | whenDELETE(url: RegExp, headers?: (object: Object) => boolean): mock.IRequestHandler; 170 | 171 | whenGET(url: string, headers?: Object): mock.IRequestHandler; 172 | whenGET(url: string, headers?: (object: Object) => boolean): mock.IRequestHandler; 173 | whenGET(url: RegExp, headers?: Object): mock.IRequestHandler; 174 | whenGET(url: RegExp, headers?: (object: Object) => boolean): mock.IRequestHandler; 175 | 176 | whenHEAD(url: string, headers?: Object): mock.IRequestHandler; 177 | whenHEAD(url: string, headers?: (object: Object) => boolean): mock.IRequestHandler; 178 | whenHEAD(url: RegExp, headers?: Object): mock.IRequestHandler; 179 | whenHEAD(url: RegExp, headers?: (object: Object) => boolean): mock.IRequestHandler; 180 | 181 | whenJSONP(url: string): mock.IRequestHandler; 182 | whenJSONP(url: RegExp): mock.IRequestHandler; 183 | 184 | whenPATCH(url: string, data?: string, headers?: Object): mock.IRequestHandler; 185 | whenPATCH(url: string, data?: RegExp, headers?: Object): mock.IRequestHandler; 186 | whenPATCH(url: string, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 187 | whenPATCH(url: string, data?: Object, headers?: Object): mock.IRequestHandler; 188 | whenPATCH(url: RegExp, data?: string, headers?: Object): mock.IRequestHandler; 189 | whenPATCH(url: RegExp, data?: RegExp, headers?: Object): mock.IRequestHandler; 190 | whenPATCH(url: RegExp, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 191 | whenPATCH(url: RegExp, data?: Object, headers?: Object): mock.IRequestHandler; 192 | 193 | whenPOST(url: string, data?: string, headers?: Object): mock.IRequestHandler; 194 | whenPOST(url: string, data?: RegExp, headers?: Object): mock.IRequestHandler; 195 | whenPOST(url: string, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 196 | whenPOST(url: string, data?: Object, headers?: Object): mock.IRequestHandler; 197 | whenPOST(url: RegExp, data?: string, headers?: Object): mock.IRequestHandler; 198 | whenPOST(url: RegExp, data?: RegExp, headers?: Object): mock.IRequestHandler; 199 | whenPOST(url: RegExp, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 200 | whenPOST(url: RegExp, data?: Object, headers?: Object): mock.IRequestHandler; 201 | 202 | whenPUT(url: string, data?: string, headers?: Object): mock.IRequestHandler; 203 | whenPUT(url: string, data?: RegExp, headers?: Object): mock.IRequestHandler; 204 | whenPUT(url: string, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 205 | whenPUT(url: string, data?: Object, headers?: Object): mock.IRequestHandler; 206 | whenPUT(url: RegExp, data?: string, headers?: Object): mock.IRequestHandler; 207 | whenPUT(url: RegExp, data?: RegExp, headers?: Object): mock.IRequestHandler; 208 | whenPUT(url: RegExp, data?: (data: string) => boolean, headers?: Object): mock.IRequestHandler; 209 | whenPUT(url: RegExp, data?: Object, headers?: Object): mock.IRequestHandler; 210 | } 211 | 212 | export module mock { 213 | 214 | // returned interface by the the mocked HttpBackendService expect/when methods 215 | interface IRequestHandler { 216 | respond(func: Function): void; 217 | respond(status: number, data?: any, headers?: any): void; 218 | respond(data: any, headers?: any): void; 219 | 220 | // Available wehn ngMockE2E is loaded 221 | passThrough(): void; 222 | } 223 | 224 | } 225 | 226 | } 227 | -------------------------------------------------------------------------------- /Scripts/typings/angularjs/angular-resource.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular JS 1.2 (ngResource module) 2 | // Project: http://angularjs.org 3 | // Definitions by: Diego Vilar , Michael Jess 4 | // Definitions: https://github.com/daptiv/DefinitelyTyped 5 | 6 | /// 7 | 8 | 9 | /////////////////////////////////////////////////////////////////////////////// 10 | // ngResource module (angular-resource.js) 11 | /////////////////////////////////////////////////////////////////////////////// 12 | declare module ng.resource { 13 | 14 | /////////////////////////////////////////////////////////////////////////// 15 | // ResourceService 16 | // see http://docs.angularjs.org/api/ngResource.$resource 17 | // Most part of the following definitions were achieved by analyzing the 18 | // actual implementation, since the documentation doesn't seem to cover 19 | // that deeply. 20 | /////////////////////////////////////////////////////////////////////////// 21 | interface IResourceService { 22 | (url: string, paramDefaults?: any, 23 | /** example: {update: { method: 'PUT' }, delete: deleteDescriptor } 24 | where deleteDescriptor : IActionDescriptor */ 25 | actionDescriptors?: any): IResourceClass>; 26 | (url: string, paramDefaults?: any, 27 | /** example: {update: { method: 'PUT' }, delete: deleteDescriptor } 28 | where deleteDescriptor : IActionDescriptor */ 29 | actionDescriptors?: any): U; 30 | (url: string, paramDefaults?: any, 31 | /** example: {update: { method: 'PUT' }, delete: deleteDescriptor } 32 | where deleteDescriptor : IActionDescriptor */ 33 | actionDescriptors?: any): IResourceClass; 34 | } 35 | 36 | // Just a reference to facilitate describing new actions 37 | interface IActionDescriptor { 38 | method: string; 39 | isArray?: boolean; 40 | params?: any; 41 | headers?: any; 42 | } 43 | 44 | // Baseclass for everyresource with default actions. 45 | // If you define your new actions for the resource, you will need 46 | // to extend this interface and typecast the ResourceClass to it. 47 | // 48 | // In case of passing the first argument as anything but a function, 49 | // it's gonna be considered data if the action method is POST, PUT or 50 | // PATCH (in other words, methods with body). Otherwise, it's going 51 | // to be considered as parameters to the request. 52 | // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L461-L465 53 | // 54 | // Only those methods with an HTTP body do have 'data' as first parameter: 55 | // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L463 56 | // More specifically, those methods are POST, PUT and PATCH: 57 | // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L432 58 | // 59 | // Also, static calls always return the IResource (or IResourceArray) retrieved 60 | // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L538-L549 61 | interface IResourceClass { 62 | new(dataOrParams? : any) : T; 63 | get(): T; 64 | get(params: Object): T; 65 | get(success: Function, error?: Function): T; 66 | get(params: Object, success: Function, error?: Function): T; 67 | get(params: Object, data: Object, success?: Function, error?: Function): T; 68 | 69 | query(): IResourceArray; 70 | query(params: Object): IResourceArray; 71 | query(success: Function, error?: Function): IResourceArray; 72 | query(params: Object, success: Function, error?: Function): IResourceArray; 73 | query(params: Object, data: Object, success?: Function, error?: Function): IResourceArray; 74 | 75 | save(): T; 76 | save(data: Object): T; 77 | save(success: Function, error?: Function): T; 78 | save(data: Object, success: Function, error?: Function): T; 79 | save(params: Object, data: Object, success?: Function, error?: Function): T; 80 | 81 | remove(): T; 82 | remove(params: Object): T; 83 | remove(success: Function, error?: Function): T; 84 | remove(params: Object, success: Function, error?: Function): T; 85 | remove(params: Object, data: Object, success?: Function, error?: Function): T; 86 | 87 | delete(): T; 88 | delete(params: Object): T; 89 | delete(success: Function, error?: Function): T; 90 | delete(params: Object, success: Function, error?: Function): T; 91 | delete(params: Object, data: Object, success?: Function, error?: Function): T; 92 | } 93 | 94 | // Instance calls always return the the promise of the request which retrieved the object 95 | // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L538-L546 96 | interface IResource { 97 | $get(): ng.IPromise; 98 | $get(params?: Object, success?: Function, error?: Function): ng.IPromise; 99 | $get(success: Function, error?: Function): ng.IPromise; 100 | 101 | $query(): ng.IPromise>; 102 | $query(params?: Object, success?: Function, error?: Function): ng.IPromise>; 103 | $query(success: Function, error?: Function): ng.IPromise>; 104 | 105 | $save(): ng.IPromise; 106 | $save(params?: Object, success?: Function, error?: Function): ng.IPromise; 107 | $save(success: Function, error?: Function): ng.IPromise; 108 | 109 | $remove(): ng.IPromise; 110 | $remove(params?: Object, success?: Function, error?: Function): ng.IPromise; 111 | $remove(success: Function, error?: Function): ng.IPromise; 112 | 113 | $delete(): ng.IPromise; 114 | $delete(params?: Object, success?: Function, error?: Function): ng.IPromise; 115 | $delete(success: Function, error?: Function): ng.IPromise; 116 | 117 | /** the promise of the original server interaction that created this instance. **/ 118 | $promise : ng.IPromise; 119 | $resolved : boolean; 120 | } 121 | 122 | /** 123 | * Really just a regular Array object with $promise and $resolve attached to it 124 | */ 125 | interface IResourceArray extends Array { 126 | /** the promise of the original server interaction that created this collection. **/ 127 | $promise : ng.IPromise>; 128 | $resolved : boolean; 129 | } 130 | 131 | /** when creating a resource factory via IModule.factory */ 132 | interface IResourceServiceFactoryFunction { 133 | ($resource: ng.resource.IResourceService): IResourceClass; 134 | >($resource: ng.resource.IResourceService): U; 135 | } 136 | } 137 | 138 | /** extensions to base ng based on using angular-resource */ 139 | declare module ng { 140 | 141 | interface IModule { 142 | /** creating a resource service factory */ 143 | factory(name: string, resourceServiceFactoryFunction: ng.resource.IResourceServiceFactoryFunction): IModule; 144 | } 145 | } 146 | 147 | interface Array 148 | { 149 | /** the promise of the original server interaction that created this collection. **/ 150 | $promise : ng.IPromise>; 151 | $resolved : boolean; 152 | } 153 | -------------------------------------------------------------------------------- /Scripts/typings/angularjs/angular-route.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular JS 1.2 (ngRoute module) 2 | // Project: http://angularjs.org 3 | // Definitions by: Jonathan Park 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | /// 7 | 8 | 9 | /////////////////////////////////////////////////////////////////////////////// 10 | // ngRoute module (angular-route.js) 11 | /////////////////////////////////////////////////////////////////////////////// 12 | declare module ng.route { 13 | 14 | /////////////////////////////////////////////////////////////////////////// 15 | // RouteParamsService 16 | // see http://docs.angularjs.org/api/ngRoute.$routeParams 17 | /////////////////////////////////////////////////////////////////////////// 18 | interface IRouteParamsService { 19 | [key: string]: any; 20 | } 21 | 22 | /////////////////////////////////////////////////////////////////////////// 23 | // RouteService 24 | // see http://docs.angularjs.org/api/ngRoute.$route 25 | // see http://docs.angularjs.org/api/ngRoute.$routeProvider 26 | /////////////////////////////////////////////////////////////////////////// 27 | interface IRouteService { 28 | reload(): void; 29 | routes: any; 30 | 31 | // May not always be available. For instance, current will not be available 32 | // to a controller that was not initialized as a result of a route maching. 33 | current?: ICurrentRoute; 34 | } 35 | 36 | 37 | /** 38 | * see http://docs.angularjs.org/api/ngRoute/provider/$routeProvider#when for API documentation 39 | */ 40 | interface IRoute { 41 | /** 42 | * {(string|function()=} 43 | * Controller fn that should be associated with newly created scope or the name of a registered controller if passed as a string. 44 | */ 45 | controller?: any; 46 | /** 47 | * A controller alias name. If present the controller will be published to scope under the controllerAs name. 48 | */ 49 | controllerAs?: string; 50 | /** 51 | * Undocumented? 52 | */ 53 | name?: string; 54 | /** 55 | * {string=|function()=} 56 | * Html template as a string or a function that returns an html template as a string which should be used by ngView or ngInclude directives. This property takes precedence over templateUrl. 57 | * 58 | * If template is a function, it will be called with the following parameters: 59 | * 60 | * {Array.} - route parameters extracted from the current $location.path() by applying the current route 61 | */ 62 | template?: string; 63 | /** 64 | * {string=|function()=} 65 | * Path or function that returns a path to an html template that should be used by ngView. 66 | * 67 | * If templateUrl is a function, it will be called with the following parameters: 68 | * 69 | * {Array.} - route parameters extracted from the current $location.path() by applying the current route 70 | */ 71 | templateUrl?: any; 72 | /** 73 | * {Object.=} - 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 $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired. The map object is: 74 | * 75 | * - key - {string}: a name of a dependency to be injected into the controller. 76 | * - factory - {string|function}: If string then it is an alias for a service. Otherwise if function, then it is 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. 77 | */ 78 | resolve?: {[key: string]: any}; 79 | /** 80 | * {(string|function())=} 81 | * Value to update $location path with and trigger route redirection. 82 | * 83 | * If redirectTo is a function, it will be called with the following parameters: 84 | * 85 | * - {Object.} - route parameters extracted from the current $location.path() by applying the current route templateUrl. 86 | * - {string} - current $location.path() 87 | * - {Object} - current $location.search() 88 | * - The custom redirectTo function is expected to return a string which will be used to update $location.path() and $location.search(). 89 | */ 90 | redirectTo?: any; 91 | /** 92 | * Reload route when only $location.search() or $location.hash() changes. 93 | * 94 | * This option defaults to true. If the option is set to false and url in the browser changes, then $routeUpdate event is broadcasted on the root scope. 95 | */ 96 | reloadOnSearch?: boolean; 97 | /** 98 | * Match routes without being case sensitive 99 | * 100 | * This option defaults to false. If the option is set to true, then the particular route can be matched without being case sensitive 101 | */ 102 | caseInsensitiveMatch?: boolean; 103 | } 104 | 105 | // see http://docs.angularjs.org/api/ng.$route#current 106 | interface ICurrentRoute extends IRoute { 107 | locals: { 108 | $scope: IScope; 109 | $template: string; 110 | }; 111 | 112 | params: any; 113 | } 114 | 115 | interface IRouteProvider extends IServiceProvider { 116 | /** 117 | * Sets route definition that will be used on route change when no other route definition is matched. 118 | * 119 | * @params Mapping information to be assigned to $route.current. 120 | */ 121 | otherwise(params: IRoute): IRouteProvider; 122 | /** 123 | * Adds a new route definition to the $route service. 124 | * 125 | * @param 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. 126 | * 127 | * - 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. 128 | * - 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. 129 | * - path can contain optional named groups with a question mark: e.g.:name?. 130 | * 131 | * For example, routes like /color/:color/largecode/:largecode*\/edit will match /color/brown/largecode/code/with/slashes/edit and extract: color: brown and largecode: code/with/slashes. 132 | * 133 | * @param route Mapping information to be assigned to $route.current on route match. 134 | */ 135 | when(path: string, route: IRoute): IRouteProvider; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Scripts/typings/angularjs/angular-sanitize.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular JS 1.2 (ngSanitize module) 2 | // Project: http://angularjs.org 3 | // Definitions by: Diego Vilar 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | 7 | /// 8 | 9 | /////////////////////////////////////////////////////////////////////////////// 10 | // ngSanitize module (angular-sanitize.js) 11 | /////////////////////////////////////////////////////////////////////////////// 12 | declare module ng.sanitize { 13 | 14 | /////////////////////////////////////////////////////////////////////////// 15 | // SanitizeService 16 | // see http://docs.angularjs.org/api/ngSanitize.$sanitize 17 | /////////////////////////////////////////////////////////////////////////// 18 | interface ISanitizeService { 19 | (html: string): string; 20 | } 21 | 22 | /////////////////////////////////////////////////////////////////////////// 23 | // Filters included with the ngSanitize 24 | // see https://github.com/angular/angular.js/tree/v1.2.0/src/ngSanitize/filter 25 | /////////////////////////////////////////////////////////////////////////// 26 | export module filter { 27 | 28 | // Finds links in text input and turns them into html links. 29 | // Supports http/https/ftp/mailto and plain email address links. 30 | // see http://code.angularjs.org/1.2.0/docs/api/ngSanitize.filter:linky 31 | interface ILinky { 32 | (text: string, target?: string): string; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Scripts/typings/angularjs/angular-scenario.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular Scenario Testing 2 | // Project: http://angularjs.org 3 | // Definitions by: RomanoLindano 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | /// 7 | 8 | declare module ng { 9 | export interface IAngularStatic { 10 | scenario: any; 11 | } 12 | } 13 | 14 | declare module angularScenario { 15 | 16 | export interface RunFunction { 17 | (functionToRun: any): any; 18 | } 19 | export interface RunFunctionWithDescription { 20 | (description: string, functionToRun: any): any; 21 | } 22 | 23 | export interface PauseFunction { 24 | (): any; 25 | } 26 | 27 | export interface SleepFunction { 28 | (seconds: number): any; 29 | } 30 | 31 | export interface Future { 32 | } 33 | 34 | export interface testWindow { 35 | href(): Future; 36 | path(): Future; 37 | search(): Future; 38 | hash(): Future; 39 | } 40 | 41 | export interface testLocation { 42 | url(): Future; 43 | path(): Future; 44 | search(): Future; 45 | hash(): Future; 46 | } 47 | 48 | export interface Browser { 49 | navigateTo(url: string): void; 50 | navigateTo(urlDescription: string, urlFunction: () => string): void; 51 | reload(): void; 52 | window(): testWindow; 53 | location(): testLocation; 54 | } 55 | 56 | export interface Matchers { 57 | toEqual(value: any): void; 58 | toBe(value: any): void; 59 | toBeDefined(): void; 60 | toBeTruthy(): void; 61 | toBeFalsy(): void; 62 | toMatch(regularExpression: any): void; 63 | toBeNull(): void; 64 | toContain(value: any): void; 65 | toBeLessThan(value: any): void; 66 | toBeGreaterThan(value: any): void; 67 | } 68 | 69 | export interface CustomMatchers extends Matchers { 70 | } 71 | 72 | export interface Expect extends CustomMatchers { 73 | not(): angularScenario.CustomMatchers; 74 | } 75 | 76 | export interface UsingFunction { 77 | (selector: string, selectorDescription?: string): void; 78 | } 79 | 80 | export interface BindingFunction { 81 | (bracketBindingExpression: string): Future; 82 | } 83 | 84 | export interface Input { 85 | enter(value: any): any; 86 | check(): any; 87 | select(radioButtonValue: any): any; 88 | val(): Future; 89 | } 90 | 91 | export interface Repeater { 92 | count(): Future; 93 | row(index: number): Future; 94 | column(ngBindingExpression: string): Future; 95 | } 96 | 97 | export interface Select { 98 | option(value: any): any; 99 | option(...listOfValues: any[]): any; 100 | } 101 | 102 | export interface Element { 103 | count(): Future; 104 | click(): any; 105 | dblclick(): any; 106 | mouseover(): any; 107 | mousedown(): any; 108 | mouseup(): any; 109 | query(callback: (selectedDOMElements: JQuery, callbackWhenDone: (objNull: any, futureValue: any) => any) => any): any; 110 | val(): Future; 111 | text(): Future; 112 | html(): Future; 113 | height(): Future; 114 | innerHeight(): Future; 115 | outerHeight(): Future; 116 | width(): Future; 117 | innerWidth(): Future; 118 | outerWidth(): Future; 119 | position(): Future; 120 | scrollLeft(): Future; 121 | scrollTop(): Future; 122 | offset(): Future; 123 | 124 | val(value: any): void; 125 | text(value: any): void; 126 | html(value: any): void; 127 | height(value: any): void; 128 | innerHeight(value: any): void; 129 | outerHeight(value: any): void; 130 | width(value: any): void; 131 | innerWidth(value: any): void; 132 | outerWidth(value: any): void; 133 | position(value: any): void; 134 | scrollLeft(value: any): void; 135 | scrollTop(value: any): void; 136 | offset(value: any): void; 137 | 138 | attr(key: any): Future; 139 | prop(key: any): Future; 140 | css(key: any): Future; 141 | 142 | attr(key: any, value: any): void; 143 | prop(key: any, value: any): void; 144 | css(key: any, value: any): void; 145 | } 146 | } 147 | 148 | declare var describe: angularScenario.RunFunctionWithDescription; 149 | declare var ddescribe: angularScenario.RunFunctionWithDescription; 150 | declare var xdescribe: angularScenario.RunFunctionWithDescription; 151 | declare var beforeEach: angularScenario.RunFunction; 152 | declare var afterEach: angularScenario.RunFunction; 153 | declare var it: angularScenario.RunFunctionWithDescription; 154 | declare var iit: angularScenario.RunFunctionWithDescription; 155 | declare var xit: angularScenario.RunFunctionWithDescription; 156 | declare var pause: angularScenario.PauseFunction; 157 | declare var sleep: angularScenario.SleepFunction; 158 | declare function browser(): angularScenario.Browser; 159 | declare function expect(expectation: angularScenario.Future): angularScenario.Expect; 160 | declare var using: angularScenario.UsingFunction; 161 | declare var binding: angularScenario.BindingFunction; 162 | declare function input(ngModelBinding: string): angularScenario.Input; 163 | declare function repeater(selector: string, repeaterDescription?: string): angularScenario.Repeater; 164 | declare function select(ngModelBinding: string): angularScenario.Select; 165 | declare function element(selector: string, elementDescription?: string): angularScenario.Element; 166 | declare var angular: ng.IAngularStatic; 167 | -------------------------------------------------------------------------------- /app/components/oauth/oauth-directives.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var oauth2 = angular.module("oauth2"); 4 | 5 | oauth2.directive("oauthLoginButton", function (oauthService, $log) { 6 | return { 7 | scope: { 8 | state: "=" 9 | }, 10 | link: function (scope, element, attrs) { 11 | oauthService.createLoginUrl(scope.state).then(function (url) { 12 | element.attr("onclick", "location.href='" + url + "'"); 13 | }) 14 | .catch(function (error) { 15 | $log.error("oauthLoginButton-directive error"); 16 | $log.error(error); 17 | throw error; 18 | }); 19 | } 20 | }; 21 | }); 22 | 23 | oauth2.directive("oauthLoginForm", function (oauthService, $location, $timeout) { 24 | return { 25 | scope: { 26 | callback: "&", 27 | state: "=" 28 | }, 29 | link: function (scope, element, attrs) { 30 | 31 | window.onOAuthCallback = function (requestedUrl) { 32 | if (scope.callback) { 33 | scope.callback(); 34 | } 35 | 36 | if (requestedUrl) { 37 | $timeout(function () { 38 | $location.url(requestedUrl.substr(1)); 39 | }, 0); 40 | } 41 | } 42 | 43 | oauthService.createLoginUrl(scope.state).then(function (url) { 44 | var html = ""; 45 | element.html(html); 46 | }).catch(function (error) { 47 | $log.error("oauthLoginForm-directive error"); 48 | $log.error(error); 49 | }); 50 | } 51 | }; 52 | }); 53 | 54 | })(); -------------------------------------------------------------------------------- /app/components/oauth/oauth-service.js: -------------------------------------------------------------------------------- 1 | var oauth2 = oauth2 || {}; 2 | 3 | (function (namespace) { 4 | 5 | function OAuthService($document, $timeout, $q, $location, $http, $log, $state, $rootScope, $base64) { 6 | 7 | var that = this; 8 | 9 | this.clientId = ""; 10 | this.redirectUri = ""; 11 | this.loginUrl = ""; 12 | this.scope = ""; 13 | this.rngUrl = ""; 14 | this.oidc = false; 15 | 16 | this.createLoginUrl = function (state) { 17 | var that = this; 18 | 19 | if (typeof state === "undefined") { state = ""; } 20 | 21 | return this.createAndSaveNonce().then(function (nonce) { 22 | 23 | if (state) { 24 | state = nonce + ";" + state; 25 | } 26 | else { 27 | state = nonce; 28 | } 29 | 30 | var response_type = "token"; 31 | 32 | if (that.oidc) { 33 | response_type = "id_token+token"; 34 | } 35 | 36 | var url = that.loginUrl 37 | + "?response_type=" 38 | + response_type 39 | + "&client_id=" 40 | + encodeURIComponent(that.clientId) 41 | + "&state=" 42 | + encodeURIComponent(state) 43 | + "&redirect_uri=" 44 | + encodeURIComponent(that.redirectUri) 45 | + "&scope=" 46 | + encodeURIComponent(that.scope); 47 | 48 | if (that.oidc) { 49 | url += "&nonce=" + encodeURIComponent(nonce); 50 | } 51 | 52 | return url; 53 | }); 54 | }; 55 | 56 | this.initImplicitFlow = function (additionalState) { 57 | this.createLoginUrl(additionalState).then(function (url) { 58 | location.href = url; 59 | }) 60 | .catch(function (error) { 61 | $log.error("Error in initImplicitFlow"); 62 | $log.error(error); 63 | }); 64 | }; 65 | 66 | this.tryLogin = function (options) { 67 | 68 | options = options || { }; 69 | 70 | var parts = this.getFragment(); 71 | 72 | var accessToken = parts["access_token"]; 73 | var idToken = parts["id_token"]; 74 | var state = parts["state"]; 75 | 76 | var oidcSuccess = false; 77 | var oauthSuccess = false; 78 | 79 | if (!accessToken || !state) return false; 80 | if (this.oidc && !idToken) return false; 81 | 82 | var savedNonce = localStorage.getItem("nonce"); 83 | 84 | var stateParts = state.split(';'); 85 | var nonceInState = stateParts[0]; 86 | if (savedNonce === nonceInState) { 87 | 88 | localStorage.setItem("access_token", accessToken); 89 | 90 | var expiresIn = parts["expires_in"]; 91 | 92 | if (expiresIn) { 93 | var expiresInMilliSeconds = parseInt(expiresIn) * 1000; 94 | var now = new Date(); 95 | var expiresAt = now.getTime() + expiresInMilliSeconds; 96 | localStorage.setItem("expires_at", expiresAt); 97 | } 98 | if (stateParts.length > 1) { 99 | this.state = stateParts[1]; 100 | } 101 | 102 | oauthSuccess = true; 103 | 104 | } 105 | 106 | if (!oauthSuccess) return false; 107 | 108 | if (!this.oidc && options.onTokenReceived) { 109 | options.onTokenReceived({ accessToken: accessToken}); 110 | } 111 | 112 | if (this.oidc) { 113 | oidcSuccess = this.processIdToken(idToken, accessToken); 114 | if (!oidcSuccess) return false; 115 | } 116 | 117 | var callEventIfExists = function() { 118 | 119 | if (options.onTokenReceived) { 120 | var tokenParams = { 121 | idClaims: that.getIdentityClaims(), 122 | idToken: idToken, 123 | accessToken: accessToken 124 | }; 125 | options.onTokenReceived(tokenParams); 126 | } 127 | } 128 | 129 | if (options.validationHandler) { 130 | 131 | var validationParams = {accessToken: accessToken, idToken: idToken}; 132 | 133 | options 134 | .validationHandler(validationParams) 135 | .then(function() { 136 | callEventIfExists(); 137 | }) 138 | .catch(function(reason) { 139 | $log.error('Error validating tokens'); 140 | $log.error(reason); 141 | }) 142 | } 143 | else { 144 | callEventIfExists(); 145 | } 146 | 147 | var win = window; 148 | if (win.parent && win.parent.onOAuthCallback) { 149 | win.parent.onOAuthCallback(this.state); 150 | } 151 | 152 | return true; 153 | }; 154 | 155 | this.processIdToken = function(idToken, accessToken) { 156 | var tokenParts = idToken.split("."); 157 | var claimsBase64 = padBase64(tokenParts[1]); 158 | var claimsJson = $base64.decode(claimsBase64); 159 | var claims = JSON.parse(claimsJson); 160 | var savedNonce = localStorage.getItem("nonce"); 161 | 162 | if (claims.aud !== this.clientId) { 163 | $log.warn("Wrong audience: " + claims.aud); 164 | return false; 165 | } 166 | 167 | if (this.issuer && claims.iss !== this.issuer) { 168 | $log.warn("Wrong issuer: " + claims.issuer); 169 | return false; 170 | } 171 | 172 | if (claims.nonce !== savedNonce) { 173 | $log.warn("Wrong nonce: " + claims.nonce); 174 | return false; 175 | } 176 | 177 | if (accessToken && !this.checkAtHash(accessToken, claims)) { 178 | $log.warn("Wrong at_hash"); 179 | return false; 180 | } 181 | 182 | // Das Prüfen des Zertifikates wird der Serverseite überlassen! 183 | 184 | var now = Date.now(); 185 | var issuedAtMSec = claims.iat * 1000; 186 | var expiresAtMSec = claims.exp * 1000; 187 | 188 | var tenMinutesInMsec = 1000 * 60 * 10; 189 | 190 | if (issuedAtMSec - tenMinutesInMsec >= now || expiresAtMSec + tenMinutesInMsec <= now) { 191 | $log.warn("Token has been expired"); 192 | $log.warn({ 193 | now: now, 194 | issuedAtMSec: issuedAtMSec, 195 | expiresAtMSec: expiresAtMSec 196 | }); 197 | return false; 198 | } 199 | 200 | localStorage.setItem("id_token", idToken); 201 | localStorage.setItem("id_token_claims_obj", claimsJson); 202 | localStorage.setItem("id_token_expires_at", expiresAtMSec); 203 | 204 | if (this.validationHandler) { 205 | this.validationHandler(idToken) 206 | } 207 | 208 | return true; 209 | } 210 | 211 | this.getIdentityClaims = function() { 212 | var claims = localStorage.getItem("id_token_claims_obj"); 213 | if (!claims) return null; 214 | return JSON.parse(claims); 215 | } 216 | 217 | var padBase64 = function (base64data) { 218 | while (base64data.length % 4 !== 0) { 219 | base64data += "="; 220 | } 221 | return base64data; 222 | } 223 | 224 | this.tryLoginWithIFrame = function () { 225 | var that = this; 226 | var deferred = $q.defer(); 227 | 228 | var url = this.createLoginUrl(); 229 | 230 | var html = ""; 231 | var win = window; 232 | 233 | win.onOAuthCallback = function () { 234 | $timeout(function () { 235 | $document.find("#oauthFrame").remove(); 236 | }, 0); 237 | 238 | deferred.resolve(); 239 | }; 240 | 241 | $document.find("#oauthFrame").remove(); 242 | 243 | var elem = $(html); 244 | $document.find("body").children().first().append(elem); 245 | 246 | return deferred.promise; 247 | }; 248 | 249 | this.tryRefresh = function () { 250 | var that = this; 251 | var deferred = $q.defer(); 252 | 253 | return this.createLoginUrl().then(function (url) { 254 | 255 | var html = ""; 256 | 257 | var win = window; 258 | var callbackExecuted = false; 259 | var timeoutReached = false; 260 | 261 | // Wenn nach einer festgelegten Zeitspanne keine Antwort kommt: Timeout 262 | var timeoutPromise = $timeout(function () { 263 | if (!callbackExecuted) { 264 | timeoutReached = true; 265 | var x = $document.find("iframe"); 266 | 267 | $document.find("#oauthFrame").remove(); 268 | deferred.reject(); 269 | } 270 | }, 10000); 271 | 272 | win.onOAuthCallback = function () { 273 | if (timeoutReached) 274 | return; 275 | 276 | // Timer für Timeout abbrechen 277 | $timeout.cancel(timeoutPromise); 278 | 279 | // Der Aufrufer (= iframe) kann nicht im Zuge des Aufrufes entfernt werden 280 | // Deswegen wird das Entfernen mit einer Verzögerung von 0 Sekunden gesheduled 281 | // Das hat zur Folge, dass kurz *NACH* (weil nur ein Thread!) der Abarbeitung 282 | // dieses Codes der Timeout eintritt 283 | $timeout(function () { 284 | $document.find("#oauthFrame").remove(); 285 | }, 0); 286 | 287 | deferred.resolve(); 288 | }; 289 | 290 | $document.find("#oauthFrame").remove(); 291 | 292 | //var elem = $(html); 293 | //var e2 = angular.element(html); 294 | var elem = angular.element(html); 295 | $document.find("body").append(elem); 296 | 297 | return deferred.promise; 298 | }); 299 | }; 300 | 301 | this.getAccessToken = function () { 302 | return localStorage.getItem("access_token"); 303 | }; 304 | 305 | this.getIsLoggedIn = function () { 306 | if (this.getAccessToken()) { 307 | 308 | var expiresAt = localStorage.getItem("expires_at"); 309 | var now = new Date(); 310 | if (expiresAt && parseInt(expiresAt) < now.getTime()) { 311 | return false; 312 | } 313 | 314 | return true; 315 | } 316 | 317 | return false; 318 | }; 319 | 320 | this.logOut = function () { 321 | localStorage.removeItem("access_token"); 322 | localStorage.removeItem("id_token"); 323 | localStorage.removeItem("nonce"); 324 | localStorage.removeItem("expires_at"); 325 | localStorage.removeItem("id_token_claims_obj"); 326 | localStorage.removeItem("id_token_expires_at"); 327 | }; 328 | 329 | this.createAndSaveNonce = function () { 330 | // var state = this.createNonce(); 331 | 332 | return this.createNonce().then(function (nonce) { 333 | localStorage.setItem("nonce", nonce); 334 | return nonce; 335 | }) 336 | 337 | }; 338 | 339 | this.createNonce = function () { 340 | 341 | if (this.rngUrl) { 342 | return $http 343 | .get(this.rngUrl) 344 | .then(function (result) { 345 | return result.data; 346 | }); 347 | } 348 | else { 349 | var text = ""; 350 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 351 | 352 | for (var i = 0; i < 40; i++) 353 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 354 | 355 | return $q.when(text); 356 | 357 | } 358 | }; 359 | 360 | this.getFragment = function () { 361 | if (window.location.hash.indexOf("#") === 0) { 362 | return this.parseQueryString(window.location.hash.substr(1)); 363 | } else { 364 | return {}; 365 | } 366 | }; 367 | 368 | this.parseQueryString = function (queryString) { 369 | var data = {}, pairs, pair, separatorIndex, escapedKey, escapedValue, key, value; 370 | 371 | if (queryString === null) { 372 | return data; 373 | } 374 | 375 | pairs = queryString.split("&"); 376 | 377 | for (var i = 0; i < pairs.length; i++) { 378 | pair = pairs[i]; 379 | separatorIndex = pair.indexOf("="); 380 | 381 | if (separatorIndex === -1) { 382 | escapedKey = pair; 383 | escapedValue = null; 384 | } else { 385 | escapedKey = pair.substr(0, separatorIndex); 386 | escapedValue = pair.substr(separatorIndex + 1); 387 | } 388 | 389 | key = decodeURIComponent(escapedKey); 390 | value = decodeURIComponent(escapedValue); 391 | 392 | if (key.substr(0, 1) === '/') 393 | key = key.substr(1); 394 | 395 | data[key] = value; 396 | } 397 | 398 | return data; 399 | }; 400 | 401 | this.checkAtHash = function(accessToken, idClaims) { 402 | if (!accessToken || !idClaims || !idClaims.at_hash ) return true; 403 | 404 | var tokenHash = sha256(accessToken, { asString: true }); 405 | 406 | var leftMostHalf = tokenHash.substr(0, tokenHash.length/2 ); 407 | 408 | var tokenHashBase64 = $base64.encode(leftMostHalf); 409 | var atHash = tokenHashBase64.replace("+", "-").replace("/", "_").replace(/=/g, ""); 410 | 411 | return (atHash == idClaims.at_hash); 412 | } 413 | 414 | 415 | this.setup = function (options) { 416 | 417 | options = options || {}; 418 | options.loginState = options.loginState || "login"; 419 | 420 | $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) { 421 | 422 | if (toState.restricted && !that.getIsLoggedIn()) { 423 | event.preventDefault(); 424 | var requestedUrl = $state.href(toState, toParams); 425 | 426 | $state.transitionTo(options.loginState, { requestedUrl: requestedUrl }); 427 | } 428 | 429 | }); 430 | 431 | if (this.tryLogin(options)) { 432 | 433 | 434 | if (this.state) { 435 | $location.url(this.state.substr(1)); // cut # off 436 | } 437 | } 438 | 439 | } 440 | 441 | } 442 | 443 | 444 | namespace.OAuthService = OAuthService; 445 | 446 | var isAngularApp = (window.angular != undefined); 447 | 448 | if (isAngularApp) { 449 | var app = angular.module("oauth2"); 450 | app.service("oauthService", OAuthService); 451 | } 452 | })(oauth2); -------------------------------------------------------------------------------- /app/components/oauth/oauth.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | angular.module("oauth2", ['base64']); 4 | 5 | })(); -------------------------------------------------------------------------------- /app/demo/app.js: -------------------------------------------------------------------------------- 1 | var app = angular.module("demo", ["oauth2", "ui.router"]); 2 | 3 | app.config(function ($stateProvider, $urlRouterProvider, $locationProvider) { 4 | $locationProvider.html5Mode(false); 5 | 6 | $urlRouterProvider.otherwise('/home'); 7 | 8 | $stateProvider.state('home', { 9 | url: '/home', 10 | templateUrl: '/app/demo/home.html', 11 | }).state('voucher', { 12 | url: '/voucher', 13 | templateUrl: '/app/demo/voucher.html', 14 | controller: 'VoucherCtrl', 15 | restricted: true 16 | }).state('login', { 17 | url: '/login?requestedUrl', 18 | templateUrl: '/app/demo/login.html', 19 | controller: 'LoginCtrl' 20 | }).state('logout', { 21 | url: '/logout', 22 | templateUrl: '/app/demo/logout.html', 23 | controller: 'LogoutCtrl' 24 | }); 25 | 26 | }); 27 | 28 | app.service('userService', function() { userName: null }); 29 | 30 | /* 31 | // Local-Scenario 32 | app.constant("config", { 33 | apiUrl: "http://localhost:63669", 34 | rngUrl: "http://localhost:63669/api/rng", 35 | loginUrl: "https://localhost:44301/identity/connect/authorize", 36 | issuerUri: "https://localhost:44301/identity", 37 | validationUrl: "https://localhost:44301/identity/connect/identitytokenvalidation" 38 | }); 39 | */ 40 | 41 | // Online-Scenario 42 | app.constant("config", { 43 | apiUrl: "https://steyer-api.azurewebsites.net", 44 | loginUrl: "https://steyer-identity-server.azurewebsites.net/identity/connect/authorize", 45 | issuerUri: "https://steyer-identity-server.azurewebsites.net/identity" 46 | }); 47 | 48 | app.run(function (oauthService, $http, userService, config) { 49 | 50 | oauthService.loginUrl = config.loginUrl; 51 | oauthService.redirectUri = location.origin + "/index.html"; 52 | oauthService.clientId = "spa-demo"; 53 | oauthService.scope = "openid profile email voucher"; 54 | oauthService.issuer = config.issuerUri; 55 | oauthService.oidc = true; 56 | 57 | oauthService.setup({ 58 | loginState: 'login', 59 | onTokenReceived: function(context) { 60 | $http.defaults.headers.common['Authorization'] = 'Bearer ' + context.accessToken; 61 | userService.userName = context.idClaims['given_name']; 62 | } 63 | }); 64 | 65 | }); 66 | 67 | app.run(function ($rootScope, userService) { 68 | $rootScope.userService = userService; 69 | }); 70 | 71 | app.controller("VoucherCtrl", function ($scope, $http, oauthService, config) { 72 | 73 | $scope.model = {}; 74 | 75 | $scope.model.message = ""; 76 | $scope.model.buyVoucher = function () { 77 | $http 78 | .post(config.apiUrl + "/api/voucher?amount=150", null) 79 | .then(function (result) { 80 | $scope.model.message = result.data; 81 | }) 82 | .catch(function (message) { 83 | $scope.model.message = "Was not able to receive new voucher: " + message.status; 84 | }); 85 | } 86 | 87 | $scope.refresh = function () { 88 | oauthService 89 | .tryRefresh() 90 | .then(function () { 91 | $scope.model.message = "Got Token!"; 92 | $http.defaults.headers.common['Authorization'] = 'Bearer ' + oauthService.getAccessToken(); 93 | }) 94 | .catch(function () { 95 | $scope.model.message = "Error receiving new token!"; 96 | }); 97 | } 98 | 99 | }); 100 | 101 | 102 | app.factory('sampleHttpInjector', function($q) { 103 | 104 | return { 105 | 'request': function(config) { 106 | return config; 107 | }, 108 | 'requestError': function(rejection) { 109 | return $q.reject(rejection); 110 | }, 111 | 'response': function(response) { 112 | return response; 113 | }, 114 | 'responseError': function(rejection) { 115 | return $q.reject(rejection); 116 | } 117 | }; 118 | }); 119 | 120 | 121 | app.config(function($httpProvider) { 122 | 123 | $httpProvider.interceptors.push('sampleHttpInjector'); 124 | 125 | }); 126 | 127 | 128 | app.controller("LoginCtrl", function ($scope, $stateParams, oauthService, $http) { 129 | 130 | $scope.model = { 131 | requestedUrl: $stateParams.requestedUrl, 132 | callback: function(requestedUrl) { 133 | $http.defaults.headers.common['Authorization'] = 'Bearer ' + oauthService.getAccessToken(); 134 | } 135 | }; 136 | 137 | }); 138 | 139 | app.controller("LogoutCtrl", function (oauthService) { 140 | oauthService.logOut(); 141 | }) -------------------------------------------------------------------------------- /app/demo/app.js.bak: -------------------------------------------------------------------------------- 1 | var app = angular.module("demo", ["oauth2", "ui.router"]); 2 | 3 | app.config(function ($stateProvider, $urlRouterProvider, $locationProvider) { 4 | $locationProvider.html5Mode(false); 5 | 6 | $urlRouterProvider.otherwise('/home'); 7 | 8 | $stateProvider.state('home', { 9 | url: '/home', 10 | templateUrl: '/app/demo/home.html', 11 | }).state('voucher', { 12 | url: '/voucher', 13 | templateUrl: '/app/demo/voucher.html', 14 | controller: 'VoucherCtrl', 15 | restricted: true 16 | }).state('login', { 17 | url: '/login?requestedUrl', 18 | templateUrl: '/app/demo/login.html', 19 | controller: 'LoginCtrl' 20 | }).state('logout', { 21 | url: '/logout', 22 | templateUrl: '/app/demo/logout.html', 23 | controller: 'LogoutCtrl' 24 | }); 25 | 26 | }); 27 | 28 | app.service('userService', function() { userName: null }); 29 | 30 | /* 31 | // Local-Scenario 32 | app.constant("config", { 33 | apiUrl: "http://localhost:63669", 34 | rngUrl: "http://localhost:63669/api/rng", 35 | loginUrl: "https://localhost:44301/identity/connect/authorize", 36 | issuerUri: "https://localhost:44301/identity", 37 | validationUrl: "https://localhost:44301/identity/connect/identitytokenvalidation" 38 | }); 39 | */ 40 | 41 | // Online-Scenario 42 | app.constant("config", { 43 | apiUrl: "https://steyer-api.azurewebsites.net", 44 | loginUrl: "https://steyer-identity-server.azurewebsites.net/identity/connect/authorize", 45 | issuerUri: "https://steyer-identity-server.azurewebsites.net/identity", 46 | validationUrl: "https://steyer-identity-server.azurewebsites.net/identity/connect/identitytokenvalidation" 47 | }); 48 | 49 | app.run(function (oauthService, $http, userService, config) { 50 | 51 | oauthService.loginUrl = config.loginUrl; 52 | oauthService.redirectUri = location.origin + "/index.html"; 53 | oauthService.clientId = "spa-demo"; 54 | oauthService.scope = "openid profile email voucher"; 55 | oauthService.issuer = config.issuerUri; 56 | oauthService.oidc = true; 57 | 58 | oauthService.setup({ 59 | loginState: 'login', 60 | onTokenReceived: function(context) { 61 | $http.defaults.headers.common['Authorization'] = 'Bearer ' + context.accessToken; 62 | userService.userName = context.idClaims['given_name']; 63 | }/*, 64 | validationHandler: function(context) { 65 | var params = {token: context.idToken, client_id: oauthService.clientId}; 66 | return $http.get(config.validationUrl, { params: params}); 67 | }*/ 68 | }); 69 | 70 | }); 71 | 72 | app.run(function ($rootScope, userService) { 73 | $rootScope.userService = userService; 74 | }); 75 | 76 | app.controller("VoucherCtrl", function ($scope, $http, oauthService, config) { 77 | 78 | $scope.model = {}; 79 | 80 | $scope.model.message = ""; 81 | $scope.model.buyVoucher = function () { 82 | $http 83 | .post(config.apiUrl + "/api/voucher?amount=150", null) 84 | .then(function (result) { 85 | $scope.model.message = result.data; 86 | }) 87 | .catch(function (message) { 88 | $scope.model.message = "Was not able to receive new voucher: " + message.status; 89 | }); 90 | } 91 | 92 | $scope.refresh = function () { 93 | oauthService 94 | .tryRefresh() 95 | .then(function () { 96 | $scope.model.message = "Got Token!"; 97 | $http.defaults.headers.common['Authorization'] = 'Bearer ' + oauthService.getAccessToken(); 98 | }) 99 | .catch(function () { 100 | $scope.model.message = "Error receiving new token!"; 101 | }); 102 | } 103 | 104 | }); 105 | 106 | 107 | app.factory('sampleHttpInjector', function($q) { 108 | 109 | return { 110 | 'request': function(config) { 111 | return config; 112 | }, 113 | 'requestError': function(rejection) { 114 | return $q.reject(rejection); 115 | }, 116 | 'response': function(response) { 117 | return response; 118 | }, 119 | 'responseError': function(rejection) { 120 | return $q.reject(rejection); 121 | } 122 | }; 123 | }); 124 | 125 | 126 | app.config(function($httpProvider) { 127 | 128 | $httpProvider.interceptors.push('sampleHttpInjector'); 129 | 130 | }); 131 | 132 | 133 | app.controller("LoginCtrl", function ($scope, $stateParams, oauthService, $http) { 134 | 135 | $scope.model = { 136 | requestedUrl: $stateParams.requestedUrl, 137 | callback: function(requestedUrl) { 138 | $http.defaults.headers.common['Authorization'] = 'Bearer ' + oauthService.getAccessToken(); 139 | } 140 | }; 141 | 142 | }); 143 | 144 | app.controller("LogoutCtrl", function (oauthService) { 145 | oauthService.logOut(); 146 | }) -------------------------------------------------------------------------------- /app/demo/home.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |

    Welcome!

    5 |

    Welcome {{userService.userName}}!

    6 |

    This is a demo-app that shows how to use OAuth 2.0 with AngularJS

    7 | 8 |
    -------------------------------------------------------------------------------- /app/demo/login.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    Login

    4 | 5 |

    6 | You have to be logged to use the requested part of the app. 7 |

    8 |

    9 | 10 | 11 | 17 | 18 | 21 |

    22 |
    -------------------------------------------------------------------------------- /app/demo/logout.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    Logged out!

    4 | 5 |

    You've been logged out successfully.

    6 |
    -------------------------------------------------------------------------------- /app/demo/voucher.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    Buy a Voucher!

    4 |

    Buy a Voucher, {{userService.userName}}!

    5 | 6 | 7 |
    8 | {{ model.message }} 9 |
    10 | 11 |
    12 | 13 |
    14 | 15 | 24 |
    -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AnguarJS-with-OAuth2-Demo", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/manfredsteyer/AnguarJS-with-OAuth2", 5 | "license": "MIT", 6 | "ignore": [ 7 | "**/.*", 8 | "node_modules", 9 | "bower_components", 10 | "test", 11 | "tests" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /bower_components/angular-base64/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-base64", 3 | "version": "2.0.5", 4 | "author": "Ninjatronic", 5 | "description": "Base64 Conversion for AngularJS Apps", 6 | "contributors": [ 7 | { 8 | "name": "Pete Martin", 9 | "email": "pete@ninjatronic.com" 10 | }, 11 | { 12 | "name": "Nick Galbreath" 13 | } 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/ninjatronic/angular-base64.git" 18 | }, 19 | "main": [ 20 | "angular-base64.js" 21 | ], 22 | "ignore": [ 23 | "package.json", 24 | "Gruntfile.js" 25 | ], 26 | "dependencies": { 27 | "angular": ">= 1.0.8" 28 | }, 29 | "homepage": "https://github.com/ninjatronic/angular-base64", 30 | "_release": "2.0.5", 31 | "_resolution": { 32 | "type": "version", 33 | "tag": "v2.0.5", 34 | "commit": "ded42ea8e64aac28b066d8a10b073b73aef629fe" 35 | }, 36 | "_source": "git://github.com/ninjatronic/angular-base64.git", 37 | "_target": "~2.0.5", 38 | "_originalSource": "angular-base64", 39 | "_direct": true 40 | } -------------------------------------------------------------------------------- /bower_components/angular-base64/LICENSE: -------------------------------------------------------------------------------- 1 | ORIGINAL LICENSE: 2 | ----------------- 3 | 4 | Copyright (c) 2010 Nick Galbreath 5 | http://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript 6 | 7 | Permission is hereby granted, free of charge, to any person 8 | obtaining a copy of this software and associated documentation 9 | files (the "Software"), to deal in the Software without 10 | restriction, including without limitation the rights to use, 11 | copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the 13 | Software is furnished to do so, subject to the following 14 | conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | MODIFICATIONS: 29 | -------------- 30 | 31 | Copyright (c) 2013 Pete Martin 32 | https://github.com/ninjatronic/ 33 | 34 | Released under the same license as the original code. -------------------------------------------------------------------------------- /bower_components/angular-base64/README.md: -------------------------------------------------------------------------------- 1 | # angular-base64 2 | 3 | Encapsulation of Nick Galbreath's base64.js library for AngularJS 4 | 5 | For Base64 encoding whch supports UTF8 see [angular-utf8-base64](https://github.com/stranger82/angular-utf8-base64) 6 | 7 | ## Installation 8 | 9 | ### Bower 10 | 11 | ``` 12 | bower install angular-base64 13 | ``` 14 | 15 | **NB:** The `ngBase64` bower package is deprecated due to camel casing issues on case-sensitive file systems. 16 | 17 | ```html 18 | 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```javascript 24 | angular 25 | .module('myApp', ['base64']) 26 | .controller('myController', [ 27 | 28 | '$base64', '$scope', 29 | function($base64, $scope) { 30 | 31 | $scope.encoded = $base64.encode('a string'); 32 | $scope.decoded = $base64.decode('YSBzdHJpbmc='); 33 | }]); 34 | ``` 35 | 36 | ### Unicode 37 | 38 | You can encode unicode strings using base64 as described [here](https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22). 39 | 40 | ```javascript 41 | angular 42 | .module('myApp', ['base64']) 43 | .controller('myUnicodeController', [ 44 | 45 | '$base64', '$scope', 46 | function($base64, $scope) { 47 | 48 | $scope.encoded = $base64.encode(unescape(encodeURIComponent('✓ a string'))); 49 | $scope.decoded = decodeURIComponent(escape($base64.decode('4pyTIGEgc3RyaW5n'))); 50 | }]); 51 | ``` 52 | 53 | ### *URL Safety* 54 | 55 | If you want to transmit a base64 encoded string in a url you must make it "URL safe" by encoding it with `encodeURIComponent`. 56 | 57 | ```javascript 58 | var base64EncodedString = $base64.encode('a string'); 59 | var urlSafeBase64EncodedString = encodeURIComponent(base64EncodedString); 60 | ``` 61 | 62 | To decode the above string use `decodeURIComponent`, then `decode`. 63 | 64 | ```javascript 65 | var base64EncodedString = decodeURIComponent('YSBzdHJpbmc%3D'); 66 | var decodedString = $base64.decode(base64EncodedString); 67 | ``` 68 | -------------------------------------------------------------------------------- /bower_components/angular-base64/angular-base64.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | /* 5 | * Encapsulation of Nick Galbreath's base64.js library for AngularJS 6 | * Original notice included below 7 | */ 8 | 9 | /* 10 | * Copyright (c) 2010 Nick Galbreath 11 | * http://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without 16 | * restriction, including without limitation the rights to use, 17 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the 19 | * Software is furnished to do so, subject to the following 20 | * conditions: 21 | * 22 | * The above copyright notice and this permission notice shall be 23 | * included in all copies or substantial portions of the Software. 24 | * 25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | * OTHER DEALINGS IN THE SOFTWARE. 33 | */ 34 | 35 | /* base64 encode/decode compatible with window.btoa/atob 36 | * 37 | * window.atob/btoa is a Firefox extension to convert binary data (the "b") 38 | * to base64 (ascii, the "a"). 39 | * 40 | * It is also found in Safari and Chrome. It is not available in IE. 41 | * 42 | * if (!window.btoa) window.btoa = base64.encode 43 | * if (!window.atob) window.atob = base64.decode 44 | * 45 | * The original spec's for atob/btoa are a bit lacking 46 | * https://developer.mozilla.org/en/DOM/window.atob 47 | * https://developer.mozilla.org/en/DOM/window.btoa 48 | * 49 | * window.btoa and base64.encode takes a string where charCodeAt is [0,255] 50 | * If any character is not [0,255], then an exception is thrown. 51 | * 52 | * window.atob and base64.decode take a base64-encoded string 53 | * If the input length is not a multiple of 4, or contains invalid characters 54 | * then an exception is thrown. 55 | */ 56 | 57 | angular.module('base64', []).constant('$base64', (function() { 58 | 59 | var PADCHAR = '='; 60 | 61 | var ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 62 | 63 | function getbyte64(s,i) { 64 | var idx = ALPHA.indexOf(s.charAt(i)); 65 | if (idx == -1) { 66 | throw "Cannot decode base64"; 67 | } 68 | return idx; 69 | } 70 | 71 | function decode(s) { 72 | // convert to string 73 | s = "" + s; 74 | var pads, i, b10; 75 | var imax = s.length; 76 | if (imax == 0) { 77 | return s; 78 | } 79 | 80 | if (imax % 4 != 0) { 81 | throw "Cannot decode base64"; 82 | } 83 | 84 | pads = 0; 85 | if (s.charAt(imax -1) == PADCHAR) { 86 | pads = 1; 87 | if (s.charAt(imax -2) == PADCHAR) { 88 | pads = 2; 89 | } 90 | // either way, we want to ignore this last block 91 | imax -= 4; 92 | } 93 | 94 | var x = []; 95 | for (i = 0; i < imax; i += 4) { 96 | b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | 97 | (getbyte64(s,i+2) << 6) | getbyte64(s,i+3); 98 | x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff)); 99 | } 100 | 101 | switch (pads) { 102 | case 1: 103 | b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6); 104 | x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff)); 105 | break; 106 | case 2: 107 | b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12); 108 | x.push(String.fromCharCode(b10 >> 16)); 109 | break; 110 | } 111 | return x.join(''); 112 | } 113 | 114 | function getbyte(s,i) { 115 | var x = s.charCodeAt(i); 116 | if (x > 255) { 117 | throw "INVALID_CHARACTER_ERR: DOM Exception 5"; 118 | } 119 | return x; 120 | } 121 | 122 | function encode(s) { 123 | if (arguments.length != 1) { 124 | throw "SyntaxError: Not enough arguments"; 125 | } 126 | 127 | var i, b10; 128 | var x = []; 129 | 130 | // convert to string 131 | s = "" + s; 132 | 133 | var imax = s.length - s.length % 3; 134 | 135 | if (s.length == 0) { 136 | return s; 137 | } 138 | for (i = 0; i < imax; i += 3) { 139 | b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2); 140 | x.push(ALPHA.charAt(b10 >> 18)); 141 | x.push(ALPHA.charAt((b10 >> 12) & 0x3F)); 142 | x.push(ALPHA.charAt((b10 >> 6) & 0x3f)); 143 | x.push(ALPHA.charAt(b10 & 0x3f)); 144 | } 145 | switch (s.length - imax) { 146 | case 1: 147 | b10 = getbyte(s,i) << 16; 148 | x.push(ALPHA.charAt(b10 >> 18) + ALPHA.charAt((b10 >> 12) & 0x3F) + 149 | PADCHAR + PADCHAR); 150 | break; 151 | case 2: 152 | b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8); 153 | x.push(ALPHA.charAt(b10 >> 18) + ALPHA.charAt((b10 >> 12) & 0x3F) + 154 | ALPHA.charAt((b10 >> 6) & 0x3f) + PADCHAR); 155 | break; 156 | } 157 | return x.join(''); 158 | } 159 | 160 | return { 161 | encode: encode, 162 | decode: decode 163 | }; 164 | })()); 165 | 166 | })(); 167 | -------------------------------------------------------------------------------- /bower_components/angular-base64/angular-base64.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";angular.module("base64",[]).constant("$base64",function(){function a(a,b){var c=f.indexOf(a.charAt(b));if(-1==c)throw"Cannot decode base64";return c}function b(b){b=""+b;var c,d,f,g=b.length;if(0==g)return b;if(0!=g%4)throw"Cannot decode base64";c=0,b.charAt(g-1)==e&&(c=1,b.charAt(g-2)==e&&(c=2),g-=4);var h=[];for(d=0;g>d;d+=4)f=a(b,d)<<18|a(b,d+1)<<12|a(b,d+2)<<6|a(b,d+3),h.push(String.fromCharCode(f>>16,255&f>>8,255&f));switch(c){case 1:f=a(b,d)<<18|a(b,d+1)<<12|a(b,d+2)<<6,h.push(String.fromCharCode(f>>16,255&f>>8));break;case 2:f=a(b,d)<<18|a(b,d+1)<<12,h.push(String.fromCharCode(f>>16))}return h.join("")}function c(a,b){var c=a.charCodeAt(b);if(c>255)throw"INVALID_CHARACTER_ERR: DOM Exception 5";return c}function d(a){if(1!=arguments.length)throw"SyntaxError: Not enough arguments";var b,d,g=[];a=""+a;var h=a.length-a.length%3;if(0==a.length)return a;for(b=0;h>b;b+=3)d=c(a,b)<<16|c(a,b+1)<<8|c(a,b+2),g.push(f.charAt(d>>18)),g.push(f.charAt(63&d>>12)),g.push(f.charAt(63&d>>6)),g.push(f.charAt(63&d));switch(a.length-h){case 1:d=c(a,b)<<16,g.push(f.charAt(d>>18)+f.charAt(63&d>>12)+e+e);break;case 2:d=c(a,b)<<16|c(a,b+1)<<8,g.push(f.charAt(d>>18)+f.charAt(63&d>>12)+f.charAt(63&d>>6)+e)}return g.join("")}var e="=",f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";return{encode:d,decode:b}}())}(); -------------------------------------------------------------------------------- /bower_components/angular-base64/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-base64", 3 | "version": "2.0.5", 4 | "author": "Ninjatronic", 5 | "description": "Base64 Conversion for AngularJS Apps", 6 | "contributors": [ 7 | { 8 | "name": "Pete Martin", 9 | "email": "pete@ninjatronic.com" 10 | }, 11 | { 12 | "name": "Nick Galbreath" 13 | } 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/ninjatronic/angular-base64.git" 18 | }, 19 | "main": [ 20 | "angular-base64.js" 21 | ], 22 | "ignore": [ 23 | "package.json", 24 | "Gruntfile.js" 25 | ], 26 | "dependencies": { 27 | "angular": ">= 1.0.8" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bower_components/angular/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.4.7", 4 | "main": "./angular.js", 5 | "ignore": [], 6 | "dependencies": {}, 7 | "homepage": "https://github.com/angular/bower-angular", 8 | "_release": "1.4.7", 9 | "_resolution": { 10 | "type": "version", 11 | "tag": "v1.4.7", 12 | "commit": "6bdc6b4855b416bf029105324080ca7d6aca0e9f" 13 | }, 14 | "_source": "git://github.com/angular/bower-angular.git", 15 | "_target": ">= 1.0.8", 16 | "_originalSource": "angular" 17 | } -------------------------------------------------------------------------------- /bower_components/angular/README.md: -------------------------------------------------------------------------------- 1 | # packaged angular 2 | 3 | This repo is for distribution on `npm` and `bower`. The source for this module is in the 4 | [main AngularJS repo](https://github.com/angular/angular.js). 5 | Please file issues and pull requests against that repo. 6 | 7 | ## Install 8 | 9 | You can install this package either with `npm` or with `bower`. 10 | 11 | ### npm 12 | 13 | ```shell 14 | npm install angular 15 | ``` 16 | 17 | Then add a ` 21 | ``` 22 | 23 | Or `require('angular')` from your code. 24 | 25 | ### bower 26 | 27 | ```shell 28 | bower install angular 29 | ``` 30 | 31 | Then add a ` 35 | ``` 36 | 37 | ## Documentation 38 | 39 | Documentation is available on the 40 | [AngularJS docs site](http://docs.angularjs.org/). 41 | 42 | ## License 43 | 44 | The MIT License 45 | 46 | Copyright (c) 2010-2015 Google, Inc. http://angularjs.org 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy 49 | of this software and associated documentation files (the "Software"), to deal 50 | in the Software without restriction, including without limitation the rights 51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 52 | copies of the Software, and to permit persons to whom the Software is 53 | furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included in 56 | all copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 64 | THE SOFTWARE. 65 | -------------------------------------------------------------------------------- /bower_components/angular/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide:not(.ng-hide-animate) { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | 15 | .ng-animate-shim { 16 | visibility:hidden; 17 | } 18 | 19 | .ng-anchor { 20 | position:absolute; 21 | } 22 | -------------------------------------------------------------------------------- /bower_components/angular/angular.min.js.gzip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular-oauth-oidc/fc04c14d615ea97e5c3f96341df9bc085f852a29/bower_components/angular/angular.min.js.gzip -------------------------------------------------------------------------------- /bower_components/angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.4.7", 4 | "main": "./angular.js", 5 | "ignore": [], 6 | "dependencies": { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /bower_components/angular/index.js: -------------------------------------------------------------------------------- 1 | require('./angular'); 2 | module.exports = angular; 3 | -------------------------------------------------------------------------------- /bower_components/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.4.7", 4 | "description": "HTML enhanced for web apps", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "client-side" 18 | ], 19 | "author": "Angular Core Team ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/angular/angular.js/issues" 23 | }, 24 | "homepage": "http://angularjs.org" 25 | } 26 | -------------------------------------------------------------------------------- /callback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Login 5 | 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular-oauth-oidc/fc04c14d615ea97e5c3f96341df9bc085f852a29/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular-oauth-oidc/fc04c14d615ea97e5c3f96341df9bc085f852a29/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular-oauth-oidc/fc04c14d615ea97e5c3f96341df9bc085f852a29/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OAuth-Demo 5 | 6 | 13 | 14 | 15 | 16 | 26 | 27 |
    28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /node_modules/sha256/.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | test/ 3 | .DS_Store 4 | references/ -------------------------------------------------------------------------------- /node_modules/sha256/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.2.0 / 2015-03-18 2 | ------------------ 3 | - browserify fix https://github.com/cryptocoinjs/sha256/pull/4 4 | - removed `component.json` and `bower.json` 5 | - added **deprecation** notice 6 | 7 | 0.1.1 / ??????? 8 | --------------- 9 | - (changelog wasn't updated, by viewing commit history, it looks like node specific) 10 | 11 | 0.1.0 / 2013-11-20 12 | ------------------ 13 | * changed package name 14 | * removed AMD support 15 | 16 | 0.0.2 / 2013-11-06 17 | ------------------ 18 | * fixed ` 31 | ``` 32 | 33 | 34 | Usage 35 | ----- 36 | 37 | (if using script, the global is `convertHex`) 38 | 39 | ### bytesToHex(bytes) 40 | 41 | ```js 42 | var convertHex = require('convert-hex') 43 | 44 | var bytes = [0x34, 0x55, 0x1, 0xDF] 45 | console.log(convertHex.bytesToHex(bytes)) //"345501df" 46 | ``` 47 | 48 | 49 | ### hexToBytes(hexStr) 50 | 51 | ```js 52 | var hex = "34550122DF" //"0x" prefix is optional 53 | console.dir(conv.hexToBytes(hex).join(',')) //'[52,85,1,34,223]' 54 | ``` 55 | 56 | Credits 57 | ------- 58 | 59 | Loosely inspired by code from here: https://github.com/vbuterin/bitcoinjs-lib & CryptoJS 60 | 61 | 62 | License 63 | ------- 64 | 65 | (MIT License) 66 | 67 | Copyright 2013, JP Richardson -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-hex/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convert-hex", 3 | "version": "0.1.0", 4 | "description": "Convert to/from hex string and array of bytes", 5 | "keywords": [ 6 | "string", 7 | "strings", 8 | "convert", 9 | "hex", 10 | "bytes" 11 | ], 12 | "ignore": [ 13 | "node_modules" 14 | ], 15 | "repo": "https://github.com/cryptocoinjs/convert-hex", 16 | "main": "convert-hex.js" 17 | } -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-hex/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convert-hex", 3 | "version": "0.1.0", 4 | "description": "Convert to/from hex string and array of bytes", 5 | "keywords": [ 6 | "string", 7 | "strings", 8 | "convert", 9 | "hex", 10 | "bytes" 11 | ], 12 | "repo": "cryptocoinjs/convert-hex", 13 | "scripts": [ 14 | "convert-hex.js" 15 | ], 16 | "main": "convert-hex.js" 17 | } -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-hex/convert-hex.js: -------------------------------------------------------------------------------- 1 | !function(globals) { 2 | 'use strict' 3 | 4 | var convertHex = { 5 | bytesToHex: function(bytes) { 6 | /*if (typeof bytes.byteLength != 'undefined') { 7 | var newBytes = [] 8 | 9 | if (typeof bytes.buffer != 'undefined') 10 | bytes = new DataView(bytes.buffer) 11 | else 12 | bytes = new DataView(bytes) 13 | 14 | for (var i = 0; i < bytes.byteLength; ++i) { 15 | newBytes.push(bytes.getUint8(i)) 16 | } 17 | bytes = newBytes 18 | }*/ 19 | return arrBytesToHex(bytes) 20 | }, 21 | hexToBytes: function(hex) { 22 | if (hex.length % 2 === 1) throw new Error("hexToBytes can't have a string with an odd number of characters.") 23 | if (hex.indexOf('0x') === 0) hex = hex.slice(2) 24 | return hex.match(/../g).map(function(x) { return parseInt(x,16) }) 25 | } 26 | } 27 | 28 | 29 | // PRIVATE 30 | 31 | function arrBytesToHex(bytes) { 32 | return bytes.map(function(x) { return padLeft(x.toString(16),2) }).join('') 33 | } 34 | 35 | function padLeft(orig, len) { 36 | if (orig.length > len) return orig 37 | return Array(len - orig.length + 1).join('0') + orig 38 | } 39 | 40 | 41 | if (typeof module !== 'undefined' && module.exports) { //CommonJS 42 | module.exports = convertHex 43 | } else { 44 | globals.convertHex = convertHex 45 | } 46 | 47 | }(this); -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-hex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convert-hex", 3 | "version": "0.1.0", 4 | "description": "Convert to/from hex string and array of bytes", 5 | "keywords": [ 6 | "string", 7 | "strings", 8 | "convert", 9 | "hex", 10 | "bytes" 11 | ], 12 | "devDependencies": { 13 | "chai": "~1.8.1", 14 | "mocha": "1.*" 15 | }, 16 | "repository": { 17 | "url": "git+https://github.com/cryptocoinjs/convert-hex.git", 18 | "type": "git" 19 | }, 20 | "main": "convert-hex.js", 21 | "bugs": { 22 | "url": "https://github.com/cryptocoinjs/convert-hex/issues" 23 | }, 24 | "_id": "convert-hex@0.1.0", 25 | "dist": { 26 | "shasum": "08c04568922c27776b8a2e81a95d393362ea0b65", 27 | "tarball": "http://registry.npmjs.org/convert-hex/-/convert-hex-0.1.0.tgz" 28 | }, 29 | "_from": "convert-hex@>=0.1.0 <0.2.0", 30 | "_npmVersion": "1.3.11", 31 | "_npmUser": { 32 | "name": "jp", 33 | "email": "jprichardson@gmail.com" 34 | }, 35 | "maintainers": [ 36 | { 37 | "name": "jp", 38 | "email": "jprichardson@gmail.com" 39 | } 40 | ], 41 | "directories": {}, 42 | "_shasum": "08c04568922c27776b8a2e81a95d393362ea0b65", 43 | "_resolved": "https://registry.npmjs.org/convert-hex/-/convert-hex-0.1.0.tgz", 44 | "readme": "ERROR: No README data found!", 45 | "homepage": "https://github.com/cryptocoinjs/convert-hex#readme" 46 | } 47 | -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-string/.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | test/ 3 | .DS_Store 4 | references/ -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-string/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.1.0 / 2013-11-20 2 | ------------------ 3 | * changed package name from `cryptocoin-convert-string` to `convert-string`. 4 | * removed AMD support 5 | 6 | 0.0.1 / 2013-11-03 7 | ------------------ 8 | * initial release -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-string/README.md: -------------------------------------------------------------------------------- 1 | convert-string 2 | ============== 3 | 4 | JavaScript component to convert to/from strings and byte arrays. 5 | 6 | AMD/CommonJS compatible. 7 | 8 | 9 | Install 10 | ------- 11 | 12 | ### Node.js/Browserify 13 | 14 | npm install --save convert-string 15 | 16 | 17 | ### Component 18 | 19 | component install cryptocoin/convert-string 20 | 21 | 22 | ### Bower 23 | 24 | bower install convert-string 25 | 26 | 27 | ### Script 28 | 29 | ```html 30 | 31 | ``` 32 | 33 | 34 | Usage 35 | ----- 36 | 37 | ### stringToBytes(str) 38 | 39 | ### bytesToString(bytes) 40 | 41 | ### UTF8.stringToBytes(str) 42 | 43 | ### UTF8.bytesToString(bytes) 44 | 45 | 46 | Credits 47 | ------- 48 | 49 | Loosely inspired by code from here: https://github.com/vbuterin/bitcoinjs-lib & CryptoJS 50 | 51 | 52 | References on JavaScript UTF-8 forced encoding 53 | ---------------------------------------------- 54 | 55 | (these sources are also included as PDFs in the repo in case the links go dead) 56 | 57 | - http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html 58 | - http://hossa.in/2012/07/20/utf-8-in-javascript.html 59 | 60 | 61 | 62 | License 63 | ------- 64 | 65 | (MIT License) 66 | 67 | Copyright 2013, JP Richardson -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-string/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convert-string", 3 | "version": "0.1.0", 4 | "description": "Convert to/from strings and array of bytes", 5 | "keywords": [ 6 | "string", 7 | "strings", 8 | "convert", 9 | "bytes" 10 | ], 11 | "ignore": [ 12 | "node_modules", 13 | ".DS_Store", 14 | "references" 15 | ], 16 | "repo": "https://github.com/cryptocoinjs/convert-string", 17 | "main": "convert-string.js" 18 | } -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-string/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convert-string", 3 | "version": "0.1.0", 4 | "description": "Convert to/from strings and array of bytes", 5 | "keywords": [ 6 | "string", 7 | "strings", 8 | "convert", 9 | "bytes" 10 | ], 11 | "repo": "cryptocoinjs/convert-string", 12 | "scripts": [ 13 | "convert-string.js" 14 | ], 15 | "main": "convert-string.js" 16 | } -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-string/convert-string.js: -------------------------------------------------------------------------------- 1 | !function(globals) { 2 | 'use strict' 3 | 4 | var convertString = { 5 | bytesToString: function(bytes) { 6 | return bytes.map(function(x){ return String.fromCharCode(x) }).join('') 7 | }, 8 | stringToBytes: function(str) { 9 | return str.split('').map(function(x) { return x.charCodeAt(0) }) 10 | } 11 | } 12 | 13 | //http://hossa.in/2012/07/20/utf-8-in-javascript.html 14 | convertString.UTF8 = { 15 | bytesToString: function(bytes) { 16 | return decodeURIComponent(escape(convertString.bytesToString(bytes))) 17 | }, 18 | stringToBytes: function(str) { 19 | return convertString.stringToBytes(unescape(encodeURIComponent(str))) 20 | } 21 | } 22 | 23 | if (typeof module !== 'undefined' && module.exports) { //CommonJS 24 | module.exports = convertString 25 | } else { 26 | globals.convertString = convertString 27 | } 28 | 29 | }(this); -------------------------------------------------------------------------------- /node_modules/sha256/node_modules/convert-string/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convert-string", 3 | "version": "0.1.0", 4 | "description": "Convert to/from strings and array of bytes", 5 | "keywords": [ 6 | "string", 7 | "strings", 8 | "convert", 9 | "hex", 10 | "bytes" 11 | ], 12 | "devDependencies": { 13 | "chai": "~1.8.1", 14 | "mocha": "1.*", 15 | "terst": "0.0.1" 16 | }, 17 | "repository": { 18 | "url": "git+https://github.com/cryptocoinjs/convert-string.git", 19 | "type": "git" 20 | }, 21 | "main": "convert-string.js", 22 | "bugs": { 23 | "url": "https://github.com/cryptocoinjs/convert-string/issues" 24 | }, 25 | "_id": "convert-string@0.1.0", 26 | "dist": { 27 | "shasum": "79ce41a9bb0d03bcf72cdc6a8f3c56fbbc64410a", 28 | "tarball": "http://registry.npmjs.org/convert-string/-/convert-string-0.1.0.tgz" 29 | }, 30 | "_from": "convert-string@>=0.1.0 <0.2.0", 31 | "_npmVersion": "1.3.11", 32 | "_npmUser": { 33 | "name": "jp", 34 | "email": "jprichardson@gmail.com" 35 | }, 36 | "maintainers": [ 37 | { 38 | "name": "jp", 39 | "email": "jprichardson@gmail.com" 40 | } 41 | ], 42 | "directories": {}, 43 | "_shasum": "79ce41a9bb0d03bcf72cdc6a8f3c56fbbc64410a", 44 | "_resolved": "https://registry.npmjs.org/convert-string/-/convert-string-0.1.0.tgz", 45 | "readme": "ERROR: No README data found!", 46 | "homepage": "https://github.com/cryptocoinjs/convert-string#readme" 47 | } 48 | -------------------------------------------------------------------------------- /node_modules/sha256/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sha256", 3 | "version": "0.2.0", 4 | "description": "Compute SHA256 of bytes or strings.", 5 | "keywords": [ 6 | "string", 7 | "strings", 8 | "sha256", 9 | "bytes", 10 | "cryptography" 11 | ], 12 | "devDependencies": { 13 | "mocha": "1.*", 14 | "terst": "0.0.1" 15 | }, 16 | "repository": { 17 | "url": "git+https://github.com/cryptocoinjs/sha256.git", 18 | "type": "git" 19 | }, 20 | "main": "./lib/nodecrypto.js", 21 | "browser": "./lib/sha256.js", 22 | "dependencies": { 23 | "convert-hex": "~0.1.0", 24 | "convert-string": "~0.1.0" 25 | }, 26 | "scripts": { 27 | "test": "mocha" 28 | }, 29 | "gitHead": "fadfc62b13052bd0ea3d0030f618d4a35eed61dc", 30 | "bugs": { 31 | "url": "https://github.com/cryptocoinjs/sha256/issues" 32 | }, 33 | "homepage": "https://github.com/cryptocoinjs/sha256", 34 | "_id": "sha256@0.2.0", 35 | "_shasum": "73a0b418daab7035bff86e8491e363412fc2ab05", 36 | "_from": "sha256@*", 37 | "_npmVersion": "2.3.0", 38 | "_nodeVersion": "0.10.32", 39 | "_npmUser": { 40 | "name": "jprichardson", 41 | "email": "jprichardson@gmail.com" 42 | }, 43 | "maintainers": [ 44 | { 45 | "name": "vbuterin", 46 | "email": "vbuterin@gmail.com" 47 | }, 48 | { 49 | "name": "midnightlightning", 50 | "email": "boydb@midnightdesign.ws" 51 | }, 52 | { 53 | "name": "sidazhang", 54 | "email": "sidazhang89@gmail.com" 55 | }, 56 | { 57 | "name": "jprichardson", 58 | "email": "jprichardson@gmail.com" 59 | } 60 | ], 61 | "dist": { 62 | "shasum": "73a0b418daab7035bff86e8491e363412fc2ab05", 63 | "tarball": "http://registry.npmjs.org/sha256/-/sha256-0.2.0.tgz" 64 | }, 65 | "directories": {}, 66 | "_resolved": "https://registry.npmjs.org/sha256/-/sha256-0.2.0.tgz", 67 | "readme": "ERROR: No README data found!" 68 | } 69 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # AngularJS with OAuth2 and OpenId Connect Implicit Flow Sample 2 | 3 | ## Library 4 | 5 | This Sample has evolved to a simple lib that can be installed via npm. 6 | Please see: https://github.com/manfredsteyer/angular-oidc-lib 7 | 8 | ## Dependencies 9 | - angular-base64 10 | - sha256 11 | - angular-ui-router 12 | 13 | Have a look at the included scripts at the end of the [index.html](https://github.com/manfredsteyer/angular-oauth-oidc/blob/master/index.html). 14 | 15 | ## Authorization-Server 16 | The provided sample uses a hosted version of IdentityServer3 (https://github.com/IdentityServer/IdentityServer3), but it is aimed to work also with other OAuth2/OIDC-Authorization-Servers. You can **login with any Facebook-Account or with max/geheim**. 17 | 18 | ## Components 19 | 20 | You'll find the reusable ``oauthService`` within the folder ``components/oauth``. 21 | 22 | ## Configuration 23 | 24 | Just configure ``oauthService`` and call setup to let it hook into UI-Router. Users that require to log in are redirected to the mentioned ``loginState`` and after logging in and receiving a token, ``onTokenReceived`` is called. There you can grab the requested token. 25 | 26 | ``` 27 | app.constant("config", { 28 | apiUrl: "https://steyer-api.azurewebsites.net", 29 | loginUrl: "https://steyer-identity-server.azurewebsites.net/identity/connect/authorize", 30 | issuerUri: "https://steyer-identity-server.azurewebsites.net/identity" 31 | }); 32 | 33 | app.run(function (oauthService, $http, userService, config) { 34 | 35 | oauthService.loginUrl = config.loginUrl; 36 | oauthService.redirectUri = location.origin + "/index.html"; 37 | oauthService.clientId = "spa-demo"; 38 | oauthService.scope = "openid profile email voucher"; 39 | oauthService.issuer = config.issuerUri; 40 | oauthService.oidc = true; 41 | 42 | oauthService.setup({ 43 | loginState: 'login', 44 | onTokenReceived: function(context) { 45 | $http.defaults.headers.common['Authorization'] = 'Bearer ' + context.accessToken; 46 | userService.userName = context.idClaims['given_name']; 47 | } 48 | }); 49 | 50 | }); 51 | ``` 52 | 53 | UI-Router-Route that needs a logged-in user can be marked with ``restricted: true``. This is just about user experience and not about security. Security is done by validating the token at server-side. 54 | 55 | ``` 56 | app.config(function ($stateProvider, $urlRouterProvider, $locationProvider) { 57 | $locationProvider.html5Mode(false); 58 | 59 | $urlRouterProvider.otherwise('/home'); 60 | 61 | $stateProvider.state('home', { 62 | url: '/home', 63 | templateUrl: '/app/demo/home.html', 64 | }).state('voucher', { 65 | url: '/voucher', 66 | templateUrl: '/app/demo/voucher.html', 67 | controller: 'VoucherCtrl', 68 | restricted: true 69 | }).state('login', { 70 | url: '/login?requestedUrl', 71 | templateUrl: '/app/demo/login.html', 72 | controller: 'LoginCtrl' 73 | }).state('logout', { 74 | url: '/logout', 75 | templateUrl: '/app/demo/logout.html', 76 | controller: 'LogoutCtrl' 77 | }); 78 | 79 | }); 80 | ``` 81 | 82 | ## More Configuration-Options 83 | 84 | You can also register the URL of an web-api that creates a random string when called via GET. This is to create a nonce-using preventing some attacks. Otherwise it uses some simple java-script-fallback for this. In addition to this, you could use the ``validationHandler``-callback to validate the received tokens. The next sample uses this to send the token to a service that checks the signature of it. The ``validationHandler`` should return a promise that informs about the validity of the token by it's state. 85 | 86 | ``` 87 | app.run(function (oauthService, $http, userService, config) { 88 | 89 | oauthService.loginUrl = config.loginUrl; 90 | oauthService.redirectUri = location.origin + "/index.html"; 91 | oauthService.clientId = "spa-demo"; 92 | oauthService.scope = "openid profile email voucher"; 93 | oauthService.issuer = config.issuerUri; 94 | oauthService.oidc = true; 95 | oauthService.rngUrl = config.rngUrl; 96 | 97 | oauthService.setup({ 98 | loginState: 'login', 99 | onTokenReceived: function(context) { 100 | $http.defaults.headers.common['Authorization'] = 'Bearer ' + context.accessToken; 101 | userService.userName = context.idClaims['given_name']; 102 | }, 103 | validationHandler: function(context) { 104 | var params = {token: context.idToken, client_id: oauthService.clientId}; 105 | return $http.get(config.validationUrl, { params: params}); 106 | } 107 | }); 108 | 109 | }); 110 | ``` 111 | 112 | ## Redirect User 113 | 114 | To create the redirect-url that points the user to the Authorization-Server, just call ``createLoginUrl``. You can pass an ``optionState`` that denotes the UI-Router state the user should be redirected to after logging in. 115 | 116 | ``` 117 | oauthService.createLoginUrl(optinalState).then(function (url) { 118 | // do stuff with url 119 | }); 120 | ``` 121 | 122 | To directly redirect the user to the Authorization-Server, you can call ``initImplicitFlow``: 123 | 124 | ``` 125 | oauthService.initImplicitFlow(optionalState); 126 | ``` 127 | 128 | There is also an ``oauthLoginButton``-Directive you could use to create a login-button, that redirects the user to the Authorization-Server: 129 | 130 | ``` 131 | 137 | ``` 138 | --------------------------------------------------------------------------------