package er.extensions.components;
import com.webobjects.appserver.WOApplication;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WORequest;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSNotificationCenter;
import er.extensions.appserver.ERXSession;
import er.extensions.eof.ERXQ;
import er.extensions.foundation.ERXProperties;
import er.extensions.foundation.ERXValueUtilities;
/**
* <p>ERXModernizr uses the Modernizr library to detect what javascript
* capabilities a client browser has and then posts those values back
* to the server to store them on the session. To use it, just drop it
* in your page wrapper. For storage, this component relies on the
* {@link er.extensions.appserver.ERXSession#objectStore()}. At the moment the modernizr data
* is stored on the session, a notification is fired using the session
* as the notification object. This is to permit logging of modernizr data
* for later analysis. It also permits you to copy the modernizer data
* elsewhere if the session's objectStore is not appropriate for your
* needs.</p>
*
* <p>It is recommended that in your ERXSession subclass, you set
* <code>_javaScriptEnabled == Boolean.FALSE;</code> in the constructor.
* By default, it will be set to true when {@link er.extensions.appserver.ERXSession#javaScriptEnabled()}
* is called. This component will set the value to true whenever the
* modernizr data updates as a result of an ajax call... thus ensuring
* no false positives.</p>
*
* <p>As a convenience, this component defines two properties for naming the
* framework and filename for the modernizer script. This is to allow the
* min.js for deployment while the full js can be used in development. The
* component bindings are used as a default value should you prefer to use
* bindings instead of properties.</p>
*
* @binding filename
* @binding framework
*
* @property er.extensions.components.ERXModernizr.modernizrFileName (default is read from "filename" binding)
* @property er.extensions.components.ERXModernizr.modernizrFrameworkName (default is read from "framework" binding)
*
* @author Ramsey Gurley
*/
public class ERXModernizr extends ERXStatelessComponent {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
//TODO add support for additional tests
public static final String FRAMEWORK_NAME_PROPERTY = "er.extensions.components.ERXModernizr.modernizrFrameworkName";
public static final String FILE_NAME_PROPERTY = "er.extensions.components.ERXModernizr.modernizrFileName";
public static final String MODERNIZR_KEY = "modernizr";
public static final String MODERNIZR_UPDATED_NOTIFICATION = "ERXModernizrUpdatedNotification";
private static final String FORM_VALUE_PREFIX = "_modernizr.";
public ERXModernizr(WOContext context) {
super(context);
}
/**
* The filename for the modernizr javascript file. A default value
* can be provided with component bindings if no property is declared.
*
* @return a file name
* @property er.extensions.components.ERXModernizr.modernizrFileName
*/
public String filename() {
String filename = ERXProperties.stringForKeyWithDefault(FILE_NAME_PROPERTY, stringValueForBinding("filename"));
return filename;
}
/**
* The name of the framework where the modernizr javascript file is
* found. A default value can be provided with component bindings if
* no property is declared.
*
* @return the framework name
* @property er.extensions.components.ERXModernizr.modernizrFrameworkName
*/
public String framework() {
String framework = ERXProperties.stringForKeyWithDefault(FRAMEWORK_NAME_PROPERTY, stringValueForBinding("framework"));
return framework;
}
/**
* The ajax request URL for this component.
* @return the post URL for the ajax post request
*/
public String postURL() {
String key = WOApplication.application().ajaxRequestHandlerKey();
return context().componentActionURL(key);
}
/**
* @return prefix used to identify modernizr form values
*/
public String formValuePrefix() {
return FORM_VALUE_PREFIX;
}
/**
* Returns true if the component should include a script to post modernizr
* data back to the server. This remains true until the modernizr data is
* captured.
*
* @return true if ajax script should be included
*/
public boolean shouldPostData() {
ERXSession session = (ERXSession)context().session();
return !(session.objectStore().valueForKey(MODERNIZR_KEY) instanceof NSDictionary);
}
/**
* Overridden to capture the modernizr data being sent from the client.
*/
@Override
public void takeValuesFromRequest(WORequest request, WOContext context) {
super.takeValuesFromRequest(request, context);
if(shouldPostData()) {
NSArray<String> keys = request.formValueKeys();
keys = ERXQ.startsWith("toString", FORM_VALUE_PREFIX).filtered(keys);
if(keys.isEmpty()) { return; }
NSMutableDictionary<String, Boolean> modernizr = new NSMutableDictionary<>();
for(String key: keys) {
Boolean value = ERXValueUtilities.BooleanValueWithDefault(request.stringFormValueForKey(key), Boolean.FALSE);
key = key.substring(FORM_VALUE_PREFIX.length());
modernizr.setObjectForKey(value, key);
}
ERXSession session = ERXSession.session();
session.objectStore().takeValueForKey(modernizr.immutableClone(), MODERNIZR_KEY);
session.setJavaScriptEnabled(true);
postNotification(session);
}
}
/**
* Called to post a notification whenever modernizr data is stored
* on the session's object store.
*
* @param session
*/
protected void postNotification(ERXSession session) {
NSNotificationCenter nc = NSNotificationCenter.defaultCenter();
nc.postNotification(MODERNIZR_UPDATED_NOTIFICATION, session);
}
}