").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+ // Otherwise use the full result
+ responseText );
+
+ }).complete( callback && function( jqXHR, status ) {
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+ });
+ }
+
+ return this;
+};
+
+
+
+
+jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+};
+
+
+
+
+
+var docElem = window.document.documentElement;
+
+/**
+ * Gets a window from an element
+ */
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+jQuery.offset = {
+ setOffset: function( elem, options, i ) {
+ var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
+ position = jQuery.css( elem, "position" ),
+ curElem = jQuery( elem ),
+ props = {};
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ curOffset = curElem.offset();
+ curCSSTop = jQuery.css( elem, "top" );
+ curCSSLeft = jQuery.css( elem, "left" );
+ calculatePosition = ( position === "absolute" || position === "fixed" ) &&
+ jQuery.inArray("auto", [ curCSSTop, curCSSLeft ] ) > -1;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+jQuery.fn.extend({
+ offset: function( options ) {
+ if ( arguments.length ) {
+ return options === undefined ?
+ this :
+ this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ var docElem, win,
+ box = { top: 0, left: 0 },
+ elem = this[ 0 ],
+ doc = elem && elem.ownerDocument;
+
+ if ( !doc ) {
+ return;
+ }
+
+ docElem = doc.documentElement;
+
+ // Make sure it's not a disconnected DOM node
+ if ( !jQuery.contains( docElem, elem ) ) {
+ return box;
+ }
+
+ // If we don't have gBCR, just use 0,0 rather than error
+ // BlackBerry 5, iOS 3 (original iPhone)
+ if ( typeof elem.getBoundingClientRect !== strundefined ) {
+ box = elem.getBoundingClientRect();
+ }
+ win = getWindow( doc );
+ return {
+ top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
+ left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
+ };
+ },
+
+ position: function() {
+ if ( !this[ 0 ] ) {
+ return;
+ }
+
+ var offsetParent, offset,
+ parentOffset = { top: 0, left: 0 },
+ elem = this[ 0 ];
+
+ // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
+ if ( jQuery.css( elem, "position" ) === "fixed" ) {
+ // we assume that getBoundingClientRect is available when computed position is fixed
+ offset = elem.getBoundingClientRect();
+ } else {
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent();
+
+ // Get correct offsets
+ offset = this.offset();
+ if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+ parentOffset = offsetParent.offset();
+ }
+
+ // Add offsetParent borders
+ parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+ parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+ }
+
+ // Subtract parent offsets and element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ return {
+ top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+ left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || docElem;
+
+ while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent || docElem;
+ });
+ }
+});
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
+ var top = /Y/.test( prop );
+
+ jQuery.fn[ method ] = function( val ) {
+ return access( this, function( elem, method, val ) {
+ var win = getWindow( elem );
+
+ if ( val === undefined ) {
+ return win ? (prop in win) ? win[ prop ] :
+ win.document.documentElement[ method ] :
+ elem[ method ];
+ }
+
+ if ( win ) {
+ win.scrollTo(
+ !top ? val : jQuery( win ).scrollLeft(),
+ top ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ elem[ method ] = val;
+ }
+ }, method, val, arguments.length, null );
+ };
+});
+
+// Add the top/left cssHooks using jQuery.fn.position
+// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+// getComputedStyle returns percent when specified for top/left/bottom/right
+// rather than make the css module depend on the offset module, we just check for it here
+jQuery.each( [ "top", "left" ], function( i, prop ) {
+ jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
+ function( elem, computed ) {
+ if ( computed ) {
+ computed = curCSS( elem, prop );
+ // if curCSS returns percentage, fallback to offset
+ return rnumnonpx.test( computed ) ?
+ jQuery( elem ).position()[ prop ] + "px" :
+ computed;
+ }
+ }
+ );
+});
+
+
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+ // margin is only for outerHeight, outerWidth
+ jQuery.fn[ funcName ] = function( margin, value ) {
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+ return access( this, function( elem, type, value ) {
+ var doc;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
+ // https://github.com/jquery/jquery/pull/764
+ return elem.document.documentElement[ "client" + name ];
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ doc = elem.documentElement;
+
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+ // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+ return Math.max(
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
+ doc[ "client" + name ]
+ );
+ }
+
+ return value === undefined ?
+ // Get width or height on the element, requesting but not forcing parseFloat
+ jQuery.css( elem, type, extra ) :
+
+ // Set width or height on the element
+ jQuery.style( elem, type, value, extra );
+ }, type, chainable ? margin : undefined, chainable, null );
+ };
+ });
+});
+
+
+// The number of elements contained in the matched element set
+jQuery.fn.size = function() {
+ return this.length;
+};
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+
+
+
+// Register as a named AMD module, since jQuery can be concatenated with other
+// files that may use define, but not via a proper concatenation script that
+// understands anonymous AMD modules. A named AMD is safest and most robust
+// way to register. Lowercase jquery is used because AMD module names are
+// derived from file names, and jQuery is normally delivered in a lowercase
+// file name. Do this after creating the global so that if an AMD module wants
+// to call noConflict to hide this version of jQuery, it will work.
+
+// Note that for maximum portability, libraries that are not jQuery should
+// declare themselves as anonymous modules, and avoid setting a global if an
+// AMD loader is present. jQuery is a special case. For more information, see
+// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
+
+if ( typeof define === "function" && define.amd ) {
+ define( "jquery", [], function() {
+ return jQuery;
+ });
+}
+
+
+
+
+var
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$;
+
+jQuery.noConflict = function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+};
+
+// Expose jQuery and $ identifiers, even in
+// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
+// and CommonJS for browser emulators (#13566)
+if ( typeof noGlobal === strundefined ) {
+ window.jQuery = window.$ = jQuery;
+}
+
+
+
+
+return jQuery;
+
+}));
diff --git a/examples/AngularFaces-2.1/src/main/resources/META-INF/web.xml b/examples/AngularFaces-2.1/src/main/resources/META-INF/web.xml
new file mode 100644
index 0000000..1f34bef
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/resources/META-INF/web.xml
@@ -0,0 +1,9 @@
+
+
+
+ javax.faces.FACELETS_DECORATORS
+
+ xde.beyondjava.angularFaces.core.AngularTagDecorator
+
+
+
\ No newline at end of file
diff --git a/examples/AngularFaces-2.1/src/main/resources/logging.properties b/examples/AngularFaces-2.1/src/main/resources/logging.properties
new file mode 100644
index 0000000..7a1f76c
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/resources/logging.properties
@@ -0,0 +1,26 @@
+# commons-logging.properties
+# jdk handlers
+handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
+
+# default log level
+.level=FINE
+
+# Specific logger level
+MyClassLogger.level=FINE
+
+# FileHandler options - can also be set to the ConsoleHandler
+# FileHandler level can be set to override the global level:
+#java.util.logging.FileHandler.level=WARN
+
+# log file name for the File Handler
+java.util.logging.FileHandler.pattern=javalog%u.log
+
+# Specify the style of output (simple or xml)
+java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
+
+# Optional - Limit the size of the file (in bytes)
+java.util.logging.FileHandler.limit=50000
+
+# Optional - The number of files to cycle through, by
+# appending an integer to the base file name:
+java.util.logging.FileHandler.count=1
\ No newline at end of file
diff --git a/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/.gitignore b/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/.gitignore
new file mode 100644
index 0000000..35a8f0b
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/.gitignore
@@ -0,0 +1,3 @@
+/classes
+rebel.xml
+
diff --git a/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/faces-config.xml b/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/faces-config.xml
new file mode 100644
index 0000000..a75104a
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/faces-config.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ en
+ de
+ es
+ fr
+
+
+ org.omnifaces.resourcehandler.CombinedResourceHandler
+
+
+
\ No newline at end of file
diff --git a/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/web.xml b/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..9916fe4
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+
+
+ LabelComponent Demo
+
+ index.jsf
+
+
+ Faces Servlet
+ javax.faces.webapp.FacesServlet
+ 1
+
+
+ Faces Servlet
+ *.jsf
+
+
+ javax.faces.PROJECT_STAGE
+ Production
+
+
+ State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2
+ javax.faces.STATE_SAVING_METHOD
+ server
+
+
+ javax.faces.FACELETS_DECORATORS
+
+ de.beyondjava.angularFaces.core.tagTransformer.AngularTagDecorator
+
+
+
+ java.lang.Throwable
+ /error.jsf
+
+
\ No newline at end of file
diff --git a/examples/AngularFaces-2.1/src/main/webapp/main.js b/examples/AngularFaces-2.1/src/main/webapp/main.js
new file mode 100644
index 0000000..335489f
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/webapp/main.js
@@ -0,0 +1,23 @@
+angular.module("AngularFacesExamples", [ "angularfaces" ])
+.controller('MyCtrl', ['$scope', '$injector', function($scope, $injector) {
+ // This initializes the Angular Model with the values of the JSF bean attributes
+ initJSFScope($scope);
+
+ $scope.width = 0;
+ $scope.height = 20;
+ $scope.angularModelStyle="color:blue;";
+
+ //$scope.calculatorBean.result = $scope.calculatorBean.number1 + $scope.calculatorBean.number2;
+
+ $scope.$watch('calculatorBean.number1', function(newValue, oldValue) {
+ $scope.calculatorBean.result = $scope.calculatorBean.number1 + $scope.calculatorBean.number2;
+ $scope.calculatorBean.gridStyle='font-weight:bold';
+ $scope.calculatorBean.headerText = 'calculated by AngularJS watch on number1';
+ });
+ $scope.$watch('calculatorBean.number2', function(newValue, oldValue) {
+ $scope.calculatorBean.result = $scope.calculatorBean.number1 + $scope.calculatorBean.number2;
+ $scope.calculatorBean.gridStyle='';
+ $scope.calculatorBean.headerText = 'calculated by AngularJS watch on number2';
+ });
+}]);
+
diff --git a/examples/AngularFaces-2.1/src/main/webapp/mojarra.xhtml b/examples/AngularFaces-2.1/src/main/webapp/mojarra.xhtml
new file mode 100644
index 0000000..cdb33c6
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/webapp/mojarra.xhtml
@@ -0,0 +1,46 @@
+
+
+
+
+
+ Welcome to AngularFaces 2.0!
+ Based firmly in the future...
+
+
+
This demo uses the traditional JSF 2.2 components.
+
+
+
+
+
+
+
+
+
+
+
+ Note that AngularJS doesn't recover after traditional AJAX requests. The PrimeFaces version of this JSF view doesn't have that problem.
+
+
+
+
+
+
diff --git a/examples/AngularFaces-2.1/src/main/webapp/primefaces.xhtml b/examples/AngularFaces-2.1/src/main/webapp/primefaces.xhtml
new file mode 100644
index 0000000..651b036
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/webapp/primefaces.xhtml
@@ -0,0 +1,39 @@
+
+
+
+
+ Welcome to AngularFaces 2.0!
+ Based firmly in the future...
+
+
+
+
+
+
+
+
+
+
+ The PrimeFaces version uses a special oncomplete handler to reactivate AngularJS after a traditional JSF request.
+
+
+ The header text is set both by the JSF bean and the AngularJS controller. The header style is set by an AngularJS watch.
+
+
+
+
+
+
+
diff --git a/examples/AngularFaces-2.1/src/main/webapp/resources/AngularFaces/messages_de.js b/examples/AngularFaces-2.1/src/main/webapp/resources/AngularFaces/messages_de.js
new file mode 100644
index 0000000..676cd95
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/webapp/resources/AngularFaces/messages_de.js
@@ -0,0 +1,11 @@
+angularFacesMessages={
+ "This number must be at least {}.":"Dieser Wert muß größer oder gleich {} sein.",
+ "This number must be less or equal {}.":"Dieser Wert muß kleiner oder gleich {} sein.",
+ "Please enter a valid number.":"Bitte geben Sie eine gültige Zahl ein.",
+ "Please fill out this field.":"Bitte füllen Sie das Feld aus.",
+ "A validation rule is violated.":"Bitte überprüfen Sie Ihre Eingabe.",
+ "At least {} characters required.": "Bitte geben Sie mindestens {} Zeichen ein.",
+ "{} characters accepted at most.":"Maximal {} Zeichen erlaubt.",
+ "Please enter a valid integer number.":"Bitte geben Sie nur Ziffern ein.",
+ "ng-invalid-captcha" : "Bitte lösen Sie die Rechenaufgabe."
+ };
\ No newline at end of file
diff --git a/examples/AngularFaces-2.1/src/main/webapp/resources/AngularFaces/messages_en.js b/examples/AngularFaces-2.1/src/main/webapp/resources/AngularFaces/messages_en.js
new file mode 100644
index 0000000..7d46f6d
--- /dev/null
+++ b/examples/AngularFaces-2.1/src/main/webapp/resources/AngularFaces/messages_en.js
@@ -0,0 +1,11 @@
+angularFacesMessages={
+ "This number must be at least {}.":"This number must be at least {}.",
+ "This number must be less or equal {}.":"This number must be less or equal {}.",
+ "Please enter a valid number.":"Please enter a valid number.",
+ "Please fill out this field.":"Please fill out this field.",
+ "A validation rule is violated.":"A validation rule is violated.",
+ "At least {} characters required.": "At least {} characters required.",
+ "{} characters accepted at most.":"{} characters accepted at most.",
+ "Please enter a valid integer number.":"Please enter a valid integer number.",
+ "ng-invalid-captcha" : "Please solve the equation."
+ };
\ No newline at end of file
diff --git a/src/jsf-updates-angular.js b/src/jsf-updates-angular.js
index fc10c2e..a7f1e3b 100644
--- a/src/jsf-updates-angular.js
+++ b/src/jsf-updates-angular.js
@@ -1,5 +1,16 @@
+if (typeof(jsf)=='undefined') {
+ if (typeof(PrimeFaces)=='undefined')
+ alert("JUA requires JSF.");
+ else {
+ activateJUA(window, angular, null, PrimeFaces, document, jQuery);
+ }
+}
+else {
+ activateJUA(window, angular, jsf, null, document, jQuery);
+}
+
/* global jsf: true, angular: true, jQuery: true */
-(function (window, angular, jsf, document, $) {
+function activateJUA(window, angular, jsf, primefaces, document, $) {
"use strict";
var onCompleteCallbacks = [],
@@ -52,12 +63,14 @@
}
function destroyScopes(data) {
+ var controllerElement=angular.element(document.querySelector('[ng-controller]'));
+ var theInjector = controllerElement.injector();
var updates = data.responseXML.getElementsByTagName('update');
$.each(updates, function(index, update) {
var id = escapeJSFClientId(update.id);
- if (!id.contains("ViewState")) {
+ if (!(id.indexOf("ViewState")>=0)) {
$(id).find(".ng-scope, .ng-isolate-scope").each(function(index, scopedChildElement) {
if (window.jua.debug) {
console.log("destroying child scope for element", scopedChildElement);
@@ -67,17 +80,18 @@
});
}
});
+ return theInjector;
}
- function handleAjaxUpdates(data) {
- window.setTimeout(function () {
- var $compile = angular.element(document).injector().get('$compile'),
- updates = data.responseXML.getElementsByTagName('update');
+ function handleAjaxUpdates(data, theInjector) {
+ window.setTimeout(function () {
+ var $compile = theInjector.get('$compile');
+ var updates = data.responseXML.getElementsByTagName('update');
$.each(updates, function(index, update) {
var id = escapeJSFClientId(update.id), element;
- if (!id.contains("ViewState")) {
+ if (!(id.indexOf("ViewState")>=0)) {
element = angular.element($(id));
if (element) {
@@ -85,7 +99,11 @@
console.log("compiling angular element", element);
}
- $compile(element)(element.scope());
+ var myScope=element.scope();
+ if (typeof(myScope)=='undefined')
+ alert("JUA requests must not update the ng-controller!");
+ else
+ $compile(element)(myScope);
}
}
});
@@ -99,20 +117,44 @@
});
}
- jsf.ajax.addOnEvent(function (data) {
- if (data.status === 'begin') {
- requestOngoing = true;
- onCompleteCallbacks = [];
- }
- if (data.status === 'complete') {
- destroyScopes(data);
- }
- if (data.status === 'success') {
- handleAjaxUpdates(data);
- requestOngoing = false;
- }
- });
-
+ if (null != jsf) {
+ jsf.ajax.addOnEvent(function (data) {
+ if (data.status === 'begin') {
+ requestOngoing = true;
+ onCompleteCallbacks = [];
+ }
+ if (data.status === 'complete') {
+ }
+ if (data.status === 'success') {
+ // todo: handleAjaxUpdates() should be called in the 'complete' branch - find a way to pass the injector around
+ var theInjector=destroyScopes(data.responseXML);
+ handleAjaxUpdates(data.responseXML, theInjector);
+ requestOngoing = false;
+ }
+ });
+ }
+ else if (null != primefaces) {
+ var originalPrimeFacesAjaxUtilsSend = primefaces.ajax.Request.send;
+ primefaces.ajax.Request.send = function(cfg) {
+ requestOngoing = true;
+ onCompleteCallbacks = [];
+ var theInjector=null;
+ if (!cfg.onsuccess) {
+ cfg.onsuccess = function(data, status, xhr) {
+ theInjector=destroyScopes(data);
+ }
+ }
+ if (!cfg.oncomplete) {
+ cfg.oncomplete = function(xhr, status) {
+ handleAjaxUpdates(xhr.responseXML, theInjector);
+ requestOngoing = false;
+ return true;
+ }
+ }
+ originalPrimeFacesAjaxUtilsSend.apply(this, arguments);
+ };
+ }
+
window.jua = {
onComplete: onComplete,
onCompleteEvent: onCompleteEvent,
@@ -121,4 +163,4 @@
return requestOngoing;
}
};
-})(window, angular, jsf, document, jQuery);
\ No newline at end of file
+}
\ No newline at end of file