diff --git a/sdk-html/pom.xml b/sdk-html/pom.xml
index f8057eee55..3bb654a71c 100755
--- a/sdk-html/pom.xml
+++ b/sdk-html/pom.xml
@@ -13,6 +13,12 @@
+
+ org.keycloak
+ keycloak-social
+ ${project.version}
+
+
org.jboss.resteasyjaxrs-api
diff --git a/sdk-html/src/main/java/org/keycloak/sdk/resources/SdkApplication.java b/sdk-html/src/main/java/org/keycloak/sdk/resources/SdkApplication.java
new file mode 100644
index 0000000000..30f0586c0b
--- /dev/null
+++ b/sdk-html/src/main/java/org/keycloak/sdk/resources/SdkApplication.java
@@ -0,0 +1,9 @@
+package org.keycloak.sdk.resources;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+@ApplicationPath("sdk/api")
+public class SdkApplication extends Application {
+
+}
diff --git a/sdk-html/src/main/java/org/keycloak/sdk/resources/SdkResource.java b/sdk-html/src/main/java/org/keycloak/sdk/resources/SdkResource.java
index a5decf401c..172b238a37 100644
--- a/sdk-html/src/main/java/org/keycloak/sdk/resources/SdkResource.java
+++ b/sdk-html/src/main/java/org/keycloak/sdk/resources/SdkResource.java
@@ -25,24 +25,28 @@ import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.annotation.XmlRootElement;
+import org.keycloak.social.util.UriBuilder;
+
/**
* @author Stian Thorgersen
*/
-@Path("sdk")
+@Path("")
public class SdkResource {
@XmlRootElement
public static class LoginConfig {
private String callbackUrl;
+
+ private String id;
private String name;
@@ -52,6 +56,10 @@ public class SdkResource {
return callbackUrl;
}
+ public String getId() {
+ return id;
+ }
+
public String getName() {
return name;
}
@@ -64,6 +72,10 @@ public class SdkResource {
this.callbackUrl = callbackUrl;
}
+ public void setId(String id) {
+ this.id = id;
+ }
+
public void setName(String name) {
this.name = name;
}
@@ -80,38 +92,40 @@ public class SdkResource {
@Context
private UriInfo uriInfo;
+ /**
+ * TODO Retrieve configuration for application from IDM
+ */
@GET
- @Path("config/{application}")
+ @Path("{application}/login/config")
@Produces(MediaType.APPLICATION_JSON)
- public Response getConfig(@PathParam("application") String application) {
- String applicationCallbackUrl = null; // TODO Get application callback url
- String applicationJavaScriptOrigin = null; // TODO Get application javascript origin
-
+ public LoginConfig getLoginConfig(@PathParam("application") String application) {
LoginConfig loginConfig = new LoginConfig();
+ loginConfig.setId(application);
loginConfig.setName(application);
- loginConfig.setCallbackUrl(applicationCallbackUrl);
- loginConfig.setProviders(null); // TODO Get configured identity providers for application
+ loginConfig.setCallbackUrl("http://localhost:8080");
+ loginConfig.setProviders(new String[] { "google", "twitter" });
+ return loginConfig;
+ }
- ResponseBuilder response = Response.ok(loginConfig);
-
- if (applicationJavaScriptOrigin != null) {
- response.header("Access-Control-Allow-Origin", applicationJavaScriptOrigin);
+ @GET
+ @Path("{application}/login")
+ public Response login(@PathParam("application") String application, @QueryParam("error") String error) {
+ UriBuilder ub = new UriBuilder(headers, uriInfo, "sdk/login.html").setQueryParam("application", application);
+ if (error != null) {
+ ub.setQueryParam("error", error);
}
-
- return response.build();
+ return Response.seeOther(ub.build()).build();
}
@GET
- @Path("login/{application}")
- @Produces(MediaType.TEXT_HTML)
- public Response login(@PathParam("application") String application) {
- return Response.ok(getClass().getResourceAsStream("login.html")).build();
+ @Path("{application}/register")
+ public Response register(@PathParam("application") String application, @QueryParam("error") String error) {
+ UriBuilder ub = new UriBuilder(headers, uriInfo, "sdk/register.html").setQueryParam("application", application);
+ if (error != null) {
+ ub.setQueryParam("error", error);
+ }
+ return Response.seeOther(ub.build()).build();
}
- @GET
- @Path("register/{application}")
- @Produces(MediaType.TEXT_HTML)
- public Response register(@PathParam("application") String application) {
- return Response.ok(getClass().getResourceAsStream("register.html")).build();
- }
}
+
diff --git a/sdk-html/src/main/resources/META-INF/resources/sdk/css/bootstrap.css b/sdk-html/src/main/resources/META-INF/resources/sdk/css/bootstrap.css
index 2f56af33f3..b725064aab 100644
--- a/sdk-html/src/main/resources/META-INF/resources/sdk/css/bootstrap.css
+++ b/sdk-html/src/main/resources/META-INF/resources/sdk/css/bootstrap.css
@@ -1,5 +1,5 @@
/*!
- * Bootstrap v2.3.1
+ * Bootstrap v2.3.2
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
@@ -3009,6 +3009,15 @@ table th[class*="span"],
display: block;
}
+.dropdown-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 990;
+}
+
.pull-right > .dropdown-menu {
right: 0;
left: auto;
diff --git a/sdk-html/src/main/resources/META-INF/resources/sdk/example-config.json b/sdk-html/src/main/resources/META-INF/resources/sdk/example-config.json
deleted file mode 100644
index 82e8513796..0000000000
--- a/sdk-html/src/main/resources/META-INF/resources/sdk/example-config.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "id" : "acme-corp",
- "name" : "Acme Corp",
- "providers" : [ "google", "twitter" ]
-}
\ No newline at end of file
diff --git a/sdk-html/src/main/resources/META-INF/resources/sdk/js/angular-resource.js b/sdk-html/src/main/resources/META-INF/resources/sdk/js/angular-resource.js
index 1af44ef0fd..d67f501c43 100644
--- a/sdk-html/src/main/resources/META-INF/resources/sdk/js/angular-resource.js
+++ b/sdk-html/src/main/resources/META-INF/resources/sdk/js/angular-resource.js
@@ -1,5 +1,5 @@
/**
- * @license AngularJS v1.0.6
+ * @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
diff --git a/sdk-html/src/main/resources/META-INF/resources/sdk/js/angular.js b/sdk-html/src/main/resources/META-INF/resources/sdk/js/angular.js
index 5b431aebd9..a860c8594f 100644
--- a/sdk-html/src/main/resources/META-INF/resources/sdk/js/angular.js
+++ b/sdk-html/src/main/resources/META-INF/resources/sdk/js/angular.js
@@ -1,5 +1,5 @@
/**
- * @license AngularJS v1.0.6
+ * @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
@@ -67,6 +67,29 @@ var /** holds major version number for IE or NaN for real browsers */
nodeName_,
uid = ['0', '0', '0'];
+
+/**
+ * @private
+ * @param {*} obj
+ * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
+ */
+function isArrayLike(obj) {
+ if (!obj || (typeof obj.length !== 'number')) return false;
+
+ // We have on object which has length property. Should we treat it as array?
+ if (typeof obj.hasOwnProperty != 'function' &&
+ typeof obj.constructor != 'function') {
+ // This is here for IE8: it is a bogus object treat it as array;
+ return true;
+ } else {
+ return obj instanceof JQLite || // JQLite
+ (jQuery && obj instanceof jQuery) || // jQuery
+ toString.call(obj) !== '[object Object]' || // some browser native object
+ typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
+ }
+}
+
+
/**
* @ngdoc function
* @name angular.forEach
@@ -94,30 +117,6 @@ var /** holds major version number for IE or NaN for real browsers */
* @param {Object=} context Object to become context (`this`) for the iterator function.
* @returns {Object|Array} Reference to `obj`.
*/
-
-
-/**
- * @private
- * @param {*} obj
- * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
- */
-function isArrayLike(obj) {
- if (!obj || (typeof obj.length !== 'number')) return false;
-
- // We have on object which has length property. Should we treat it as array?
- if (typeof obj.hasOwnProperty != 'function' &&
- typeof obj.constructor != 'function') {
- // This is here for IE8: it is a bogus object treat it as array;
- return true;
- } else {
- return obj instanceof JQLite || // JQLite
- (jQuery && obj instanceof jQuery) || // jQuery
- toString.call(obj) !== '[object Object]' || // some browser native object
- typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
- }
-}
-
-
function forEach(obj, iterator, context) {
var key;
if (obj) {
@@ -201,6 +200,21 @@ function nextUid() {
return uid.join('');
}
+
+/**
+ * Set or clear the hashkey for an object.
+ * @param obj object
+ * @param h the hashkey (!truthy to delete the hashkey)
+ */
+function setHashKey(obj, h) {
+ if (h) {
+ obj.$$hashKey = h;
+ }
+ else {
+ delete obj.$$hashKey;
+ }
+}
+
/**
* @ngdoc function
* @name angular.extend
@@ -212,8 +226,10 @@ function nextUid() {
*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
+ * @returns {Object} Reference to `dst`.
*/
function extend(dst) {
+ var h = dst.$$hashKey;
forEach(arguments, function(obj){
if (obj !== dst) {
forEach(obj, function(value, key){
@@ -221,6 +237,8 @@ function extend(dst) {
});
}
});
+
+ setHashKey(dst,h);
return dst;
}
@@ -575,12 +593,14 @@ function copy(source, destination){
destination.push(copy(source[i]));
}
} else {
+ var h = destination.$$hashKey;
forEach(destination, function(value, key){
delete destination[key];
});
for ( var key in source) {
destination[key] = copy(source[key]);
}
+ setHashKey(destination,h);
}
}
return destination;
@@ -620,7 +640,7 @@ function shallowCopy(src, dst) {
* During a property comparision, properties of `function` type and properties with names
* that begin with `$` are ignored.
*
- * Scope and DOMWindow objects are being compared only be identify (`===`).
+ * Scope and DOMWindow objects are being compared only by identify (`===`).
*
* @param {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare.
@@ -680,7 +700,7 @@ function sliceArgs(args, startIndex) {
*
* @description
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
- * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
+ * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
* known as [function currying](http://en.wikipedia.org/wiki/Currying).
*
* @param {Object} self Context which `fn` should be evaluated in.
@@ -873,7 +893,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
*
* @description
*
- * Use this directive to auto-bootstrap on application. Only
+ * Use this directive to auto-bootstrap an application. Only
* one directive can be used per HTML document. The directive
* designates the root of the application and is typically placed
* at the root of the page.
@@ -1012,7 +1032,7 @@ function bindJQuery() {
}
/**
- * throw error of the argument is falsy.
+ * throw error if the argument is falsy.
*/
function assertArg(arg, name, reason) {
if (!arg) {
@@ -1293,11 +1313,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.0.6', // all of these placeholder strings will be replaced by grunt's
+ full: '1.0.7', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 0,
- dot: 6,
- codeName: 'universal-irreversibility'
+ dot: 7,
+ codeName: 'monochromatic-rainbow'
};
@@ -1442,18 +1462,18 @@ function publishExternalAPI(angular){
* - [after()](http://api.jquery.com/after/)
* - [append()](http://api.jquery.com/append/)
* - [attr()](http://api.jquery.com/attr/)
- * - [bind()](http://api.jquery.com/bind/)
- * - [children()](http://api.jquery.com/children/)
+ * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
+ * - [children()](http://api.jquery.com/children/) - Does not support selectors
* - [clone()](http://api.jquery.com/clone/)
* - [contents()](http://api.jquery.com/contents/)
* - [css()](http://api.jquery.com/css/)
* - [data()](http://api.jquery.com/data/)
* - [eq()](http://api.jquery.com/eq/)
- * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
+ * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
* - [hasClass()](http://api.jquery.com/hasClass/)
* - [html()](http://api.jquery.com/html/)
- * - [next()](http://api.jquery.com/next/)
- * - [parent()](http://api.jquery.com/parent/)
+ * - [next()](http://api.jquery.com/next/) - Does not support selectors
+ * - [parent()](http://api.jquery.com/parent/) - Does not support selectors
* - [prepend()](http://api.jquery.com/prepend/)
* - [prop()](http://api.jquery.com/prop/)
* - [ready()](http://api.jquery.com/ready/)
@@ -1465,7 +1485,7 @@ function publishExternalAPI(angular){
* - [text()](http://api.jquery.com/text/)
* - [toggleClass()](http://api.jquery.com/toggleClass/)
* - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
- * - [unbind()](http://api.jquery.com/unbind/)
+ * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
* - [val()](http://api.jquery.com/val/)
* - [wrap()](http://api.jquery.com/wrap/)
*
@@ -2012,23 +2032,43 @@ forEach({
if (!eventFns) {
if (type == 'mouseenter' || type == 'mouseleave') {
- var counter = 0;
+ var contains = document.body.contains || document.body.compareDocumentPosition ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && (
+ adown.contains ?
+ adown.contains( bup ) :
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+ ));
+ } :
+ function( a, b ) {
+ if ( b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
- events.mouseenter = [];
- events.mouseleave = [];
+ events[type] = [];
+
+ // Refer to jQuery's implementation of mouseenter & mouseleave
+ // Read about mouseenter and mouseleave:
+ // http://www.quirksmode.org/js/events_mouse.html#link8
+ var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
+ bindFn(element, eventmap[type], function(event) {
+ var ret, target = this, related = event.relatedTarget;
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !contains(target, related)) ){
+ handle(event, type);
+ }
- bindFn(element, 'mouseover', function(event) {
- counter++;
- if (counter == 1) {
- handle(event, 'mouseenter');
- }
- });
- bindFn(element, 'mouseout', function(event) {
- counter --;
- if (counter == 0) {
- handle(event, 'mouseleave');
- }
});
+
} else {
addEventListenerFn(element, type, handle);
events[type] = [];
@@ -2344,7 +2384,7 @@ function annotate(fn) {
}
} else if (isArray(fn)) {
last = fn.length - 1;
- assertArgFn(fn[last], 'fn')
+ assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
@@ -2378,7 +2418,7 @@ function annotate(fn) {
* # Injection Function Annotation
*
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
- * following ways are all valid way of annotating function with injection arguments and are equivalent.
+ * following are all valid ways of annotating function with injection arguments and are equivalent.
*
*
* // inferred (only works if code not minified/obfuscated)
@@ -2507,7 +2547,7 @@ function annotate(fn) {
* // ...
* };
* tmpFn.$inject = ['$compile', '$rootScope'];
- * injector.invoke(tempFn);
+ * injector.invoke(tmpFn);
*
* // To better support inline function the inline annotation is supported
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
@@ -2560,7 +2600,7 @@ function annotate(fn) {
*
* beforeEach(module(function($provide) {
* $provide.provider('greet', GreetProvider);
- * });
+ * }));
*
* it('should greet', inject(function(greet) {
* expect(greet('angular')).toEqual('Hello angular!');
@@ -2573,9 +2613,7 @@ function annotate(fn) {
* inject(function(greet) {
* expect(greet('angular')).toEqual('Ahoj angular!');
* });
- * )};
- *
- * });
+ * });
*
*/
@@ -2669,7 +2707,7 @@ function annotate(fn) {
*
* @param {string} name The name of the service to decorate.
* @param {function()} decorator This function will be invoked when the service needs to be
- * instanciated. The function is called using the {@link AUTO.$injector#invoke
+ * instantiated. The function is called using the {@link AUTO.$injector#invoke
* injector.invoke} method and is therefore fully injectable. Local injection arguments:
*
* * `$delegate` - The original service instance, which can be monkey patched, configured,
@@ -2869,6 +2907,8 @@ function createInjector(modulesToLoad) {
var Constructor = function() {},
instance, returnedValue;
+ // Check if Type is annotated and use just the given function at n-1 as parameter
+ // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
instance = new Constructor();
returnedValue = invoke(Type, instance, locals);
@@ -3249,7 +3289,13 @@ function Browser(window, document, $log, $sniffer) {
cookie = cookieArray[i];
index = cookie.indexOf('=');
if (index > 0) { //ignore nameless cookies
- lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
+ var name = unescape(cookie.substring(0, index));
+ // the first value that is seen for a cookie is the most
+ // specific one. values for the same cookie name that
+ // follow are for less specific paths.
+ if (lastCookies[name] === undefined) {
+ lastCookies[name] = unescape(cookie.substring(index + 1));
+ }
}
}
}
@@ -4054,9 +4100,9 @@ function $CompileProvider($provide) {
/**
- * Once the directives have been collected their compile functions is executed. This method
+ * Once the directives have been collected, their compile functions are executed. This method
* is responsible for inlining directive templates as well as terminating the application
- * of the directives if the terminal directive has been reached..
+ * of the directives if the terminal directive has been reached.
*
* @param {Array} directives Array of collected directives to execute their compile function.
* this needs to be pre-sorted by priority order.
@@ -4064,11 +4110,11 @@ function $CompileProvider($provide) {
* @param {Object} templateAttrs The shared attribute function
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
* scope argument is auto-generated to the new child of the transcluded parent scope.
- * @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
- * argument has the root jqLite array so that we can replace widgets on it.
+ * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
+ * argument has the root jqLite array so that we can replace nodes on it.
* @returns linkFn
*/
- function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
+ function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
var terminalPriority = -Number.MAX_VALUE,
preLinkFns = [],
postLinkFns = [],
@@ -4122,7 +4168,7 @@ function $CompileProvider($provide) {
$compileNode = templateAttrs.$$element =
jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
compileNode = $compileNode[0];
- replaceWith($rootElement, jqLite($template[0]), compileNode);
+ replaceWith(jqCollection, jqLite($template[0]), compileNode);
childTranscludeFn = compile($template, transcludeFn, terminalPriority);
} else {
$template = jqLite(JQLiteClone(compileNode)).contents();
@@ -4146,7 +4192,7 @@ function $CompileProvider($provide) {
throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
}
- replaceWith($rootElement, $compileNode, compileNode);
+ replaceWith(jqCollection, $compileNode, compileNode);
var newTemplateAttrs = {$attr: {}};
@@ -4174,7 +4220,7 @@ function $CompileProvider($provide) {
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
- nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
+ nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
childTranscludeFn);
ii = directives.length;
} else if (directive.compile) {
@@ -4307,7 +4353,7 @@ function $CompileProvider($provide) {
parentGet = $parse(attrs[attrName]);
scope[scopeName] = function(locals) {
return parentGet(parentScope, locals);
- }
+ };
break;
}
@@ -4795,7 +4841,7 @@ function $DocumentProvider(){
*
*/
function $ExceptionHandlerProvider() {
- this.$get = ['$log', function($log){
+ this.$get = ['$log', function($log) {
return function(exception, cause) {
$log.error.apply($log, arguments);
};
@@ -5560,6 +5606,10 @@ function $LocationProvider(){
// update $location when $browser url changes
$browser.onUrlChange(function(newUrl) {
if ($location.absUrl() != newUrl) {
+ if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) {
+ $browser.url($location.absUrl());
+ return;
+ }
$rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl();
@@ -5868,10 +5918,10 @@ function lex(text, csp){
function readIdent() {
var ident = "",
start = index,
- lastDot, peekIndex, methodName;
+ lastDot, peekIndex, methodName, ch;
while (index < text.length) {
- var ch = text.charAt(index);
+ ch = text.charAt(index);
if (ch == '.' || isIdent(ch) || isNumber(ch)) {
if (ch == '.') lastDot = index;
ident += ch;
@@ -5885,7 +5935,7 @@ function lex(text, csp){
if (lastDot) {
peekIndex = index;
while(peekIndex < text.length) {
- var ch = text.charAt(peekIndex);
+ ch = text.charAt(peekIndex);
if (ch == '(') {
methodName = ident.substr(lastDot - start + 1);
ident = ident.substr(0, lastDot - start);
@@ -6138,8 +6188,8 @@ function parser(text, json, $filter, csp){
text.substring(0, token.index) + "] can not be assigned to", token);
}
right = logicalOR();
- return function(self, locals){
- return left.assign(self, right(self, locals), locals);
+ return function(scope, locals){
+ return left.assign(scope, right(scope, locals), locals);
};
} else {
return left;
@@ -6256,12 +6306,12 @@ function parser(text, json, $filter, csp){
var field = expect().text;
var getter = getterFn(field, csp);
return extend(
- function(self, locals) {
- return getter(object(self, locals), locals);
+ function(scope, locals, self) {
+ return getter(self || object(scope, locals), locals);
},
{
- assign:function(self, value, locals) {
- return setter(object(self, locals), field, value);
+ assign:function(scope, value, locals) {
+ return setter(object(scope, locals), field, value);
}
}
);
@@ -6302,14 +6352,14 @@ function parser(text, json, $filter, csp){
} while (expect(','));
}
consume(')');
- return function(self, locals){
+ return function(scope, locals){
var args = [],
- context = contextGetter ? contextGetter(self, locals) : self;
+ context = contextGetter ? contextGetter(scope, locals) : scope;
for ( var i = 0; i < argsFn.length; i++) {
- args.push(argsFn[i](self, locals));
+ args.push(argsFn[i](scope, locals));
}
- var fnPtr = fn(self, locals) || noop;
+ var fnPtr = fn(scope, locals, context) || noop;
// IE stupidity!
return fnPtr.apply
? fnPtr.apply(context, args)
@@ -6351,8 +6401,7 @@ function parser(text, json, $filter, csp){
var object = {};
for ( var i = 0; i < keyValues.length; i++) {
var keyValue = keyValues[i];
- var value = keyValue.value(self, locals);
- object[keyValue.key] = value;
+ object[keyValue.key] = keyValue.value(self, locals);
}
return object;
};
@@ -6474,7 +6523,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) {
}
return pathVal;
};
-};
+}
function getterFn(path, csp) {
if (getterFnCache.hasOwnProperty(path)) {
@@ -6489,7 +6538,7 @@ function getterFn(path, csp) {
fn = (pathKeysLength < 6)
? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4])
: function(scope, locals) {
- var i = 0, val
+ var i = 0, val;
do {
val = cspSafeGetterFn(
pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++]
@@ -6702,7 +6751,7 @@ function $ParseProvider() {
* models and avoiding unnecessary browser repaints, which would result in flickering UI.
* - $q promises are recognized by the templating engine in angular, which means that in templates
* you can treat promises attached to a scope as if they were the resulting values.
- * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains
+ * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
* all the important functionality needed for common async tasks.
*
* # Testing
@@ -6897,10 +6946,7 @@ function qFactory(nextTick, exceptionHandler) {
* the promise comes from a source that can't be trusted.
*
* @param {*} value Value or a promise
- * @returns {Promise} Returns a single promise that will be resolved with an array of values,
- * each value corresponding to the promise at the same index in the `promises` array. If any of
- * the promises is resolved with a rejection, this resulting promise will be resolved with the
- * same rejection.
+ * @returns {Promise} Returns a promise of the passed value or promise
*/
var when = function(value, callback, errback) {
var result = defer(),
@@ -7496,22 +7542,22 @@ function $RouteParamsProvider() {
/**
* DESIGN NOTES
*
- * The design decisions behind the scope ware heavily favored for speed and memory consumption.
+ * The design decisions behind the scope are heavily favored for speed and memory consumption.
*
* The typical use of scope is to watch the expressions, which most of the time return the same
* value as last time so we optimize the operation.
*
- * Closures construction is expensive from speed as well as memory:
- * - no closures, instead ups prototypical inheritance for API
+ * Closures construction is expensive in terms of speed as well as memory:
+ * - No closures, instead use prototypical inheritance for API
* - Internal state needs to be stored on scope directly, which means that private state is
* exposed as $$____ properties
*
* Loop operations are optimized by using while(count--) { ... }
* - this means that in order to keep the same order of execution as addition we have to add
- * items to the array at the begging (shift) instead of at the end (push)
+ * items to the array at the beginning (shift) instead of at the end (push)
*
* Child scopes are created and removed often
- * - Using array would be slow since inserts in meddle are expensive so we use linked list
+ * - Using an array would be slow since inserts in middle are expensive so we use linked list
*
* There are few watches then a lot of observers. This is why you don't want the observer to be
* implemented in the same way as watch. Watch requires return of initialization function which
@@ -7533,7 +7579,7 @@ function $RouteParamsProvider() {
* @methodOf ng.$rootScopeProvider
* @description
*
- * Sets the number of digest iteration the scope should attempt to execute before giving up and
+ * Sets the number of digest iterations the scope should attempt to execute before giving up and
* assuming that the model is unstable.
*
* The current default is 10 iterations.
@@ -7813,7 +7859,7 @@ function $RootScopeProvider(){
* @function
*
* @description
- * Process all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
+ * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
* Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
* `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are
* firing. This means that it is possible to get into an infinite loop. This function will throw
@@ -8155,7 +8201,7 @@ function $RootScopeProvider(){
* Afterwards, the event traverses upwards toward the root scope and calls all registered
* listeners along the way. The event will stop propagating if one of the listeners cancels it.
*
- * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
+ * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
*
* @param {string} name Event name to emit.
@@ -8224,7 +8270,7 @@ function $RootScopeProvider(){
* Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
*
- * @param {string} name Event name to emit.
+ * @param {string} name Event name to broadcast.
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
* @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
*/
@@ -8370,10 +8416,23 @@ function $SnifferProvider() {
* @example
-
-
+
+
+
+
+
+ it('should display the greeting in the input box', function() {
+ input('greeting').enter('Hello, E2E Tests');
+ // If we click the button it will block the test runner
+ // element(':button').click();
+ });
*/
@@ -8526,7 +8585,7 @@ function $HttpProvider() {
*
* @description
* The `$http` service is a core Angular service that facilitates communication with the remote
- * HTTP servers via browser's {@link https://developer.mozilla.org/en/xmlhttprequest
+ * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
* XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
*
* For unit testing applications that use `$http` service, see
@@ -8536,13 +8595,13 @@ function $HttpProvider() {
* $resource} service.
*
* The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
- * the $q service. While for simple usage patters this doesn't matter much, for advanced usage,
- * it is important to familiarize yourself with these apis and guarantees they provide.
+ * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
+ * it is important to familiarize yourself with these APIs and the guarantees they provide.
*
*
* # General usage
* The `$http` service is a function which takes a single argument — a configuration object —
- * that is used to generate an http request and returns a {@link ng.$q promise}
+ * that is used to generate an HTTP request and returns a {@link ng.$q promise}
* with two $http specific methods: `success` and `error`.
*
*
@@ -8557,21 +8616,21 @@ function $HttpProvider() {
* });
*
*
- * Since the returned value of calling the $http function is a Promise object, you can also use
+ * Since the returned value of calling the $http function is a `promise`, you can also use
* the `then` method to register callbacks, and these callbacks will receive a single argument –
- * an object representing the response. See the api signature and type info below for more
+ * an object representing the response. See the API signature and type info below for more
* details.
*
- * A response status code that falls in the [200, 300) range is considered a success status and
+ * A response status code between 200 and 299 is considered a success status and
* will result in the success callback being called. Note that if the response is a redirect,
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
* called for such responses.
*
* # Shortcut methods
*
- * Since all invocation of the $http service require definition of the http method and url and
- * POST and PUT requests require response body/data to be provided as well, shortcut methods
- * were created to simplify using the api:
+ * Since all invocations of the $http service require passing in an HTTP method and URL, and
+ * POST/PUT requests require request data to be provided as well, shortcut methods
+ * were created:
*
*
* $http.get('/someUrl').success(successCallback);
@@ -8590,25 +8649,25 @@ function $HttpProvider() {
*
* # Setting HTTP Headers
*
- * The $http service will automatically add certain http headers to all requests. These defaults
+ * The $http service will automatically add certain HTTP headers to all requests. These defaults
* can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
* object, which currently contains this default configuration:
*
* - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
* - `Accept: application/json, text/plain, * / *`
* - `X-Requested-With: XMLHttpRequest`
- * - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests)
+ * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
* - `Content-Type: application/json`
- * - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests)
+ * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
* - `Content-Type: application/json`
*
- * To add or overwrite these defaults, simply add or remove a property from this configuration
+ * To add or overwrite these defaults, simply add or remove a property from these configuration
* objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
- * with name equal to the lower-cased http method name, e.g.
+ * with the lowercased HTTP method name as the key, e.g.
* `$httpProvider.defaults.headers.get['My-Header']='value'`.
*
- * Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar
- * fassion as described above.
+ * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
+ * fashion.
*
*
* # Transforming Requests and Responses
@@ -8618,36 +8677,36 @@ function $HttpProvider() {
*
* Request transformations:
*
- * - if the `data` property of the request config object contains an object, serialize it into
+ * - If the `data` property of the request configuration object contains an object, serialize it into
* JSON format.
*
* Response transformations:
*
- * - if XSRF prefix is detected, strip it (see Security Considerations section below)
- * - if json response is detected, deserialize it using a JSON parser
+ * - If XSRF prefix is detected, strip it (see Security Considerations section below).
+ * - If JSON response is detected, deserialize it using a JSON parser.
*
* To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
- * `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`. These properties are by default an
+ * `$httpProvider.defaults.transformResponse` properties. These properties are by default an
* array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
* transformation chain. You can also decide to completely override any default transformations by assigning your
* transformation functions to these properties directly without the array wrapper.
*
* Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
- * `transformResponse` properties of the config object passed into `$http`.
+ * `transformResponse` properties of the configuration object passed into `$http`.
*
*
* # Caching
*
- * To enable caching set the configuration property `cache` to `true`. When the cache is
+ * To enable caching, set the configuration property `cache` to `true`. When the cache is
* enabled, `$http` stores the response from the server in local cache. Next time the
* response is served from the cache without sending a request to the server.
*
* Note that even if the response is served from cache, delivery of the data is asynchronous in
* the same way that real requests are.
*
- * If there are multiple GET requests for the same url that should be cached using the same
+ * If there are multiple GET requests for the same URL that should be cached using the same
* cache, but the cache is not populated yet, only one request to the server will be made and
- * the remaining requests will be fulfilled using the response for the first request.
+ * the remaining requests will be fulfilled using the response from the first request.
*
*
* # Response interceptors
@@ -8699,7 +8758,7 @@ function $HttpProvider() {
* When designing web applications, consider security threats from:
*
* - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
- * JSON Vulnerability}
+ * JSON vulnerability}
* - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
*
* Both server and the client must cooperate in order to eliminate these threats. Angular comes
@@ -8709,8 +8768,8 @@ function $HttpProvider() {
* ## JSON Vulnerability Protection
*
* A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
- * JSON Vulnerability} allows third party web-site to turn your JSON resource URL into
- * {@link http://en.wikipedia.org/wiki/JSON#JSONP JSONP} request under some conditions. To
+ * JSON vulnerability} allows third party website to turn your JSON resource URL into
+ * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
* counter this your server can prefix all JSON requests with following string `")]}',\n"`.
* Angular will automatically strip the prefix before processing it as JSON.
*
@@ -8731,19 +8790,19 @@ function $HttpProvider() {
* ## Cross Site Request Forgery (XSRF) Protection
*
* {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
- * an unauthorized site can gain your user's private data. Angular provides following mechanism
+ * an unauthorized site can gain your user's private data. Angular provides a mechanism
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
* called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
* runs on your domain could read the cookie, your server can be assured that the XHR came from
* JavaScript running on your domain.
*
* To take advantage of this, your server needs to set a token in a JavaScript readable session
- * cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
+ * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
* server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
- * that only JavaScript running on your domain could have read the token. The token must be
- * unique for each user and must be verifiable by the server (to prevent the JavaScript making
+ * that only JavaScript running on your domain could have sent the request. The token must be
+ * unique for each user and must be verifiable by the server (to prevent the JavaScript from making
* up its own tokens). We recommend that the token is a digest of your site's authentication
- * cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
+ * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
*
*
* @param {object} config Object describing the request to be made and how it should be
@@ -8921,7 +8980,7 @@ function $HttpProvider() {
* @methodOf ng.$http
*
* @description
- * Shortcut method to perform `GET` request
+ * Shortcut method to perform `GET` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object
@@ -8934,7 +8993,7 @@ function $HttpProvider() {
* @methodOf ng.$http
*
* @description
- * Shortcut method to perform `DELETE` request
+ * Shortcut method to perform `DELETE` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object
@@ -8947,7 +9006,7 @@ function $HttpProvider() {
* @methodOf ng.$http
*
* @description
- * Shortcut method to perform `HEAD` request
+ * Shortcut method to perform `HEAD` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object
@@ -8960,7 +9019,7 @@ function $HttpProvider() {
* @methodOf ng.$http
*
* @description
- * Shortcut method to perform `JSONP` request
+ * Shortcut method to perform `JSONP` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request.
* Should contain `JSON_CALLBACK` string.
@@ -8975,7 +9034,7 @@ function $HttpProvider() {
* @methodOf ng.$http
*
* @description
- * Shortcut method to perform `POST` request
+ * Shortcut method to perform `POST` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
@@ -8989,7 +9048,7 @@ function $HttpProvider() {
* @methodOf ng.$http
*
* @description
- * Shortcut method to perform `PUT` request
+ * Shortcut method to perform `PUT` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
@@ -9041,7 +9100,7 @@ function $HttpProvider() {
/**
- * Makes the request
+ * Makes the request.
*
* !!! ACCESSES CLOSURE VARS:
* $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
@@ -9388,17 +9447,17 @@ function $TimeoutProvider() {
* block and delegates any exceptions to
* {@link ng.$exceptionHandler $exceptionHandler} service.
*
- * The return value of registering a timeout function is a promise which will be resolved when
+ * The return value of registering a timeout function is a promise, which will be resolved when
* the timeout is reached and the timeout function is executed.
*
- * To cancel a the timeout request, call `$timeout.cancel(promise)`.
+ * To cancel a timeout request, call `$timeout.cancel(promise)`.
*
* In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
* synchronously flush the queue of deferred functions.
*
- * @param {function()} fn A function, who's execution should be delayed.
+ * @param {function()} fn A function, whose execution should be delayed.
* @param {number=} [delay=0] Delay in milliseconds.
- * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
* promise will be resolved with is the return value of the `fn` function.
@@ -9438,7 +9497,7 @@ function $TimeoutProvider() {
* @methodOf ng.$timeout
*
* @description
- * Cancels a task associated with the `promise`. As a result of this the promise will be
+ * Cancels a task associated with the `promise`. As a result of this, the promise will be
* resolved with a rejection.
*
* @param {Promise=} promise Promise returned by the `$timeout` function.
@@ -9526,7 +9585,7 @@ function $TimeoutProvider() {
*
* The general syntax in templates is as follows:
*
- * {{ expression | [ filter_name ] }}
+ * {{ expression [| filter_name[:parameter_value] ... ] }}
*
* @param {String} name Name of the filter function to retrieve
* @return {Function} the filter function
@@ -9611,7 +9670,7 @@ function $FilterProvider($provide) {
Any:
Name only
- Phone only
+ Phone only
Name
Phone
@@ -9914,6 +9973,7 @@ function padNumber(num, digits, trim) {
function dateGetter(name, size, offset, trim) {
+ offset = offset || 0;
return function(date) {
var value = date['get' + name]();
if (offset > 0 || value > -offset)
@@ -10024,7 +10084,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
* (e.g. `"h o''clock"`).
*
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
- * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
+ * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
* specified in the string input, the time is considered to be in the local timezone.
* @param {string=} format Formatting rules (see Description). If not specified,
@@ -11142,8 +11202,8 @@ var inputType = {
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
- * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`.
- * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`.
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -11455,6 +11515,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
} else {
var timeout;
+ var deferListener = function() {
+ if (!timeout) {
+ timeout = $browser.defer(function() {
+ listener();
+ timeout = null;
+ });
+ }
+ };
+
element.bind('keydown', function(event) {
var key = event.keyCode;
@@ -11462,16 +11531,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// command modifiers arrows
if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
- if (!timeout) {
- timeout = $browser.defer(function() {
- listener();
- timeout = null;
- });
- }
+ deferListener();
});
// if user paste into input using mouse, we need "change" event to catch it
element.bind('change', listener);
+
+ // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
+ if ($sniffer.hasEvent('paste')) {
+ element.bind('paste cut', deferListener);
+ }
}
@@ -11770,7 +11839,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
myForm.userName.$valid = {{myForm.userName.$valid}} myForm.userName.$error = {{myForm.userName.$error}} myForm.lastName.$valid = {{myForm.lastName.$valid}}
- myForm.userName.$error = {{myForm.lastName.$error}}
+ myForm.lastName.$error = {{myForm.lastName.$error}} myForm.$valid = {{myForm.$valid}} myForm.$error.required = {{!!myForm.$error.required}} myForm.$error.minlength = {{!!myForm.$error.minlength}}
@@ -12033,7 +12102,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* For example {@link ng.directive:input input} or
* {@link ng.directive:select select} directives call it.
*
- * It internally calls all `formatters` and if resulted value is valid, updates the model and
+ * It internally calls all `parsers` and if resulted value is valid, updates the model and
* calls all registered change listeners.
*
* @param {string} value Value from the view.
@@ -12339,7 +12408,7 @@ var ngValueDirective = function() {
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
* `{{ expression }}` which is similar but less verbose.
*
- * Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when
+ * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
* it's desirable to put bindings into template that is momentarily displayed by the browser in its
* raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
* bindings invisible to the user while the page is loading.
@@ -12480,9 +12549,9 @@ function classDirective(name, selector) {
if (name !== 'ngClass') {
scope.$watch('$index', function($index, old$index) {
- var mod = $index % 2;
- if (mod !== old$index % 2) {
- if (mod == selector) {
+ var mod = $index & 1;
+ if (mod !== old$index & 1) {
+ if (mod === selector) {
addClass(scope.$eval(attr[name]));
} else {
removeClass(scope.$eval(attr[name]));
@@ -12494,12 +12563,12 @@ function classDirective(name, selector) {
function ngClassWatchAction(newVal) {
if (selector === true || scope.$index % 2 === selector) {
- if (oldVal && (newVal !== oldVal)) {
+ if (oldVal && !equals(newVal,oldVal)) {
removeClass(oldVal);
}
addClass(newVal);
}
- oldVal = newVal;
+ oldVal = copy(newVal);
}
@@ -12625,7 +12694,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
* @name ng.directive:ngClassEven
*
* @description
- * The `ngClassOdd` and `ngClassEven` works exactly as
+ * The `ngClassOdd` and `ngClassEven` directives work exactly as
* {@link ng.directive:ngClass ngClass}, except it works in
* conjunction with `ngRepeat` and takes affect only on odd (even) rows.
*
@@ -12742,8 +12811,7 @@ var ngCloakDirective = ngDirective({
* * Controller — The `ngController` directive specifies a Controller class; the class has
* methods that typically express the business logic behind the application.
*
- * Note that an alternative way to define controllers is via the `{@link ng.$route}`
- * service.
+ * Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
*
* @element ANY
* @scope
@@ -12834,16 +12902,32 @@ var ngControllerDirective = [function() {
* @name ng.directive:ngCsp
* @priority 1000
*
+ * @element html
* @description
* Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
- * This directive should be used on the root element of the application (typically the ``
- * element or other element with the {@link ng.directive:ngApp ngApp}
- * directive).
- *
- * If enabled the performance of template expression evaluator will suffer slightly, so don't enable
- * this mode unless you need it.
- *
- * @element html
+ *
+ * This is necessary when developing things like Google Chrome Extensions.
+ *
+ * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
+ * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
+ * any of these restrictions.
+ *
+ * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
+ * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
+ * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
+ * be raised.
+ *
+ * In order to use this feature put `ngCsp` directive on the root element of the application.
+ *
+ * @example
+ * This example shows how to apply the `ngCsp` directive to the `html` tag.
+
+
+
+ ...
+ ...
+
+
*/
var ngCspDirective = ['$sniffer', function($sniffer) {
@@ -13468,7 +13552,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
if (!isNaN(value)) {
//if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
//check it against pluralization rules in $locale service
- if (!whens[value]) value = $locale.pluralCat(value - offset);
+ if (!(value in whens)) value = $locale.pluralCat(value - offset);
return whensExpFns[value](scope, element, true);
} else {
return '';
@@ -14201,7 +14285,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
* `select` model to be bound to a non-string value. This is because an option element can currently
* be bound to string values only.
*
- * @param {string} name assignable expression to data-bind to.
+ * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the control is published.
* @param {string=} required The control is considered valid only if value is entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -14568,10 +14653,6 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
if (multiple) {
selectedSet = new HashMap(modelValue);
- } else if (modelValue === null || nullOption) {
- // if we are not multiselect, and we are null then we have to add the nullOption
- optionGroups[''].push({selected:modelValue === null, id:'', label:''});
- selectedSet = true;
}
// We now build up the list of options we need (we merge later)
@@ -14596,9 +14677,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
selected: selected // determine if we should be selected
});
}
- if (!multiple && !selectedSet) {
- // nothing was selected, we have to insert the undefined item
- optionGroups[''].unshift({id:'?', label:'', selected:true});
+ if (!multiple) {
+ if (nullOption || modelValue === null) {
+ // insert null option if we have a placeholder, or the model is null
+ optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
+ } else if (!selectedSet) {
+ // option could not be found, we have to insert the undefined item
+ optionGroups[''].unshift({id:'?', label:'', selected:true});
+ }
}
// Now we need to update the list of DOM nodes to match the optionGroups we computed above
@@ -14642,7 +14728,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
if (existingOption.id !== option.id) {
lastElement.val(existingOption.id = option.id);
}
- if (existingOption.element.selected !== option.selected) {
+ // lastElement.prop('selected') provided by jQuery has side-effects
+ if (lastElement[0].selected !== option.selected) {
lastElement.prop('selected', (existingOption.selected = option.selected));
}
} else {
diff --git a/sdk-html/src/main/resources/META-INF/resources/sdk/js/app.js b/sdk-html/src/main/resources/META-INF/resources/sdk/js/app.js
index fdbed27bb8..50d7f5b436 100644
--- a/sdk-html/src/main/resources/META-INF/resources/sdk/js/app.js
+++ b/sdk-html/src/main/resources/META-INF/resources/sdk/js/app.js
@@ -1,5 +1,25 @@
var keycloakModule = angular.module('keycloak', [ 'ngResource' ]);
-keycloakModule.controller('LoginCtrl', function($scope, $resource) {
- $scope.config = $resource("example-config.json").get();
+keycloakModule.factory('messages', function() {
+ var messages = {};
+ messages['user_registered'] = "User registered";
+ messages['invalid_provider'] = "Social provider not found";
+ messages['provider_error'] = "Failed to login with social provider";
+ return messages
});
+
+keycloakModule.factory('queryParams', function($location) {
+ var queryParams = {};
+ var locationParameters = window.location.search.substring(1).split("&");
+ for ( var i = 0; i < locationParameters.length; i++) {
+ var param = locationParameters[i].split("=");
+ queryParams[decodeURIComponent(param[0])] = decodeURIComponent(param[1]);
+ }
+ return queryParams;
+});
+
+keycloakModule.controller('GlobalCtrl', function($scope, $resource, queryParams, messages) {
+ $scope.config = $resource("/keycloak-server/sdk/api/" + queryParams.application + "/login/config").get();
+ $scope.info = queryParams.info && (messages[queryParams.info] || queryParams.info);
+ $scope.error = queryParams.error && (messages[queryParams.error] || queryParams.error);
+});
\ No newline at end of file
diff --git a/sdk-html/src/main/resources/META-INF/resources/sdk/keycloak.js b/sdk-html/src/main/resources/META-INF/resources/sdk/keycloak.js
deleted file mode 100644
index 55f8d62971..0000000000
--- a/sdk-html/src/main/resources/META-INF/resources/sdk/keycloak.js
+++ /dev/null
@@ -1,248 +0,0 @@
-window.Keycloak = (function () {
- var queryParameters = function (name) {
- var parameters = window.location.search.substring(1).split("&");
- for (var i = 0; i < parameters.length; i++) {
- var param = parameters[i].split("=");
- if (decodeURIComponent(param[0]) == name) {
- return decodeURIComponent(param[1]);
- }
- }
- };
-
- var messageError = queryParameters("error");
- var messageInfo = queryParameters("info");
-
- var keycloak = {
- appKey: queryParameters("app"),
- token: queryParameters("token"),
- baseUrl: "/ejs-identity",
- get loginUrl() {
- return this.baseUrl + "/api/login/" + this.appKey;
- },
- get registerUrl() {
- return this.baseUrl + "/api/register/" + this.appKey;
- },
- get userInfoUrl() {
- return this.baseUrl + "/api/auth/userinfo?appKey=" + this.appKey;
- }
- };
-
- keycloak.getConfig = function (success, error) {
- var req = new XMLHttpRequest();
- req.open("GET", keycloak.loginUrl);
- req.setRequestHeader("Accept", "application/json");
- req.onreadystatechange = function () {
- if (req.readyState == 4) {
- if (req.status == 200) {
- var config = JSON.parse(req.responseText);
- if (success) {
- success(config);
- }
- } else {
- if (error) {
- error(req.status);
- }
- }
- }
- };
- req.send();
- };
-
- keycloak.getUser = function (success, error) {
- var req = new XMLHttpRequest();
- req.open("GET", keycloak.userInfoUrl + "&token=" + keycloak.token);
- req.setRequestHeader("Accept", "application/json");
- req.onreadystatechange = function () {
- if (req.readyState == 4) {
- if (req.status == 200) {
- var user = JSON.parse(req.responseText);
- if (success) {
- success(user);
- }
- } else {
- if (error) {
- error(req.status);
- }
- }
- }
- };
- req.send();
- };
-
- var createLogin = function(containerId) {
- var login = document.createElement("div");
- login.setAttribute("class", "keycloak-login");
-
- var container = document.getElementById(containerId);
- container.setAttribute("class", "keycloak-login-container");
- container.innerHTML = null;
- container.appendChild(login);
-
- return login;
- };
-
- var createHeader = function(text) {
- var div = document.createElement("div");
- div.setAttribute("class", "keycloak-login-header");
-
- var h = document.createElement("h1");
- h.textContent = text;
- div.appendChild(h);
-
- return div;
- };
-
- var createInput = function(group, name, labelText, type) {
- var div = document.createElement("div");
- div.setAttribute("class", "keycloak-login-" + group + "-" + name);
-
- var label = document.createElement("label");
- label.setAttribute("for", name);
- label.textContent = labelText;
- div.appendChild(label);
-
- var input = document.createElement("input");
- input.setAttribute("name", name);
- if (type) {
- input.setAttribute("type", type);
- } else {
- input.setAttribute("type", "text");
- }
- div.appendChild(input);
-
- return div;
- };
-
- var createMessage = function(message, type) {
- var div = document.createElement("div");
- div.setAttribute("class", "keycloak-login-message-" + type);
-
- if (message == "login_failed") {
- div.textContent = "Failed to login";
- } else if (message == "register_failed") {
- div.textContent = "Failed to register user";
- } else if (message = "register_created") {
- div.textContent = "Created user";
- } else {
- div.textContent = message;
- }
-
- return div;
- };
-
- keycloak.renderLoginForm = function (containerId) {
- var success = function (config) {
- var login = createLogin(containerId);
-
- login.appendChild(createHeader("Login to " + config.name));
-
- if (messageError) {
- login.appendChild(createMessage(messageError, "warn"));
- }
-
- if (messageInfo) {
- login.appendChild(createMessage(messageInfo, "info"));
- }
-
- var standardLogin = document.createElement("div");
- standardLogin.setAttribute("class", "keycloak-login-standard");
- login.appendChild(standardLogin);
-
- var form = document.createElement("form");
- form.setAttribute("action", keycloak.loginUrl);
- form.setAttribute("method", "post");
- standardLogin.appendChild(form);
-
- form.appendChild(createInput("standard", "username", "Username"));
- form.appendChild(createInput("standard", "password", "Password", "password"));
-
- var buttonsDiv = document.createElement("div");
- buttonsDiv.setAttribute("class", "keycloak-login-buttons");
- form.appendChild(buttonsDiv);
-
- var submitButton = document.createElement("button");
- submitButton.setAttribute("type", "submit");
- submitButton.textContent = "Login";
- buttonsDiv.appendChild(submitButton);
-
- var registerButton = document.createElement("button");
- registerButton.setAttribute("type", "button");
- registerButton.setAttribute("onclick", "location.href='" + keycloak.registerUrl + "'");
- registerButton.textContent = "Register";
- buttonsDiv.appendChild(registerButton);
-
- var cancelButton = document.createElement("button");
- cancelButton.setAttribute("type", "button");
- cancelButton.setAttribute("onclick", "location.href='" + config.callbackUrl + "'");
- cancelButton.textContent = "Cancel";
- buttonsDiv.appendChild(cancelButton);
-
- var socialLogin = document.createElement("div");
- socialLogin.setAttribute("class", "keycloak-login-social");
- login.appendChild(socialLogin);
-
- for (var i = 0; i < config.providerConfigs.length; i++) {
- var provider = config.providerConfigs[i];
-
- var providerLink = document.createElement("a");
- providerLink.setAttribute("href", provider.loginUri);
- socialLogin.appendChild(providerLink);
-
- var providerImage = document.createElement("img");
- providerImage.setAttribute("src", provider.icon);
- providerLink.appendChild(providerImage);
- }
- };
-
- var error = function() {
- var login = createLogin(containerId);
-
- login.appendChild(createHeader("Invalid"));
- login.appendChild(createMessage("Invalid application key", "warn"));
- };
-
- keycloak.getConfig(success, error);
- };
-
- keycloak.renderRegistrationForm = function (containerId) {
- var login = createLogin(containerId);
-
- var success = function (config) {
- login.appendChild(createHeader("Register with " + config.name));
-
- if (messageError) {
- login.appendChild(createMessage(messageError, "warn"));
- }
-
- var form = document.createElement("form");
- form.setAttribute("action", keycloak.registerUrl);
- form.setAttribute("method", "post");
- login.appendChild(form);
-
- form.appendChild(createInput("register", "username", "Username"));
- form.appendChild(createInput("register", "email", "Email", "email"));
- form.appendChild(createInput("register", "firstName", "First name"));
- form.appendChild(createInput("register", "lastName", "Last name"));
- form.appendChild(createInput("register", "password", "Password", "password"));
-
- var buttonsDiv = document.createElement("div");
- buttonsDiv.setAttribute("class", "keycloak-login-buttons");
- form.appendChild(buttonsDiv);
-
- var submitButton = document.createElement("button");
- submitButton.setAttribute("type", "submit");
- submitButton.textContent = "Register";
- buttonsDiv.appendChild(submitButton);
-
- var cancelButton = document.createElement("button");
- cancelButton.setAttribute("type", "button");
- cancelButton.setAttribute("onclick", "location.href='" + keycloak.loginUrl + "'");
- cancelButton.textContent = "Cancel";
- buttonsDiv.appendChild(cancelButton);
- };
-
- keycloak.getConfig(success);
- };
-
- return keycloak;
-}());
\ No newline at end of file
diff --git a/sdk-html/src/main/resources/META-INF/resources/sdk/login.html b/sdk-html/src/main/resources/META-INF/resources/sdk/login.html
index 723634dd5c..43a9b3a6b5 100644
--- a/sdk-html/src/main/resources/META-INF/resources/sdk/login.html
+++ b/sdk-html/src/main/resources/META-INF/resources/sdk/login.html
@@ -11,10 +11,13 @@
-