summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorJonatan Kronqvist <jonatan@vaadin.com>2014-03-18 10:21:16 +0200
committerVaadin Code Review <review@vaadin.com>2014-03-25 10:47:53 +0000
commit9dc242295d685670ae780db623c270322e34570d (patch)
treef505ee6b559fba866111cd1a9633a9b6830e0cd4 /client
parenta14eccdb97f486a6b451b671f1a0ab433629dec9 (diff)
downloadvaadin-framework-9dc242295d685670ae780db623c270322e34570d.tar.gz
vaadin-framework-9dc242295d685670ae780db623c270322e34570d.zip
Integrate the Responsive add-on #12394
Also fixes https://github.com/vaadin/responsive/issues/10 Change-Id: Id22d071529c91d6462f1dceaf169c9d4be69d86f
Diffstat (limited to 'client')
-rw-r--r--client/src/com/vaadin/client/extensions/ResponsiveConnector.java379
1 files changed, 379 insertions, 0 deletions
diff --git a/client/src/com/vaadin/client/extensions/ResponsiveConnector.java b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java
new file mode 100644
index 0000000000..fb1919e6c2
--- /dev/null
+++ b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.extensions;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.Element;
+import com.vaadin.client.LayoutManager;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.client.ui.layout.ElementResizeEvent;
+import com.vaadin.client.ui.layout.ElementResizeListener;
+import com.vaadin.server.Responsive;
+import com.vaadin.shared.ui.Connect;
+
+/**
+ * The client side connector for the Responsive extension.
+ *
+ * @author Vaadin Ltd
+ * @since 7.2
+ */
+@SuppressWarnings("GwtInconsistentSerializableClass")
+@Connect(Responsive.class)
+public class ResponsiveConnector extends AbstractExtensionConnector implements
+ ElementResizeListener {
+
+ /**
+ * The target component which we will monitor for width changes
+ */
+ protected AbstractComponentConnector target;
+
+ /**
+ * All the width breakpoints found for this particular instance
+ */
+ protected JavaScriptObject widthBreakpoints;
+
+ /**
+ * All the height breakpoints found for this particular instance
+ */
+ protected JavaScriptObject heightBreakpoints;
+
+ /**
+ * All width-range breakpoints found from the style sheets on the page.
+ * Common for all instances.
+ */
+ protected static JavaScriptObject widthRangeCache;
+
+ /**
+ * All height-range breakpoints found from the style sheets on the page.
+ * Common for all instances.
+ */
+ protected static JavaScriptObject heightRangeCache;
+
+ private static Logger getLogger() {
+ return Logger.getLogger(ResponsiveConnector.class.getName());
+ }
+
+ private static void error(String message) {
+ getLogger().log(Level.SEVERE, message);
+ }
+
+ @Override
+ protected void extend(ServerConnector target) {
+ // Initialize cache if not already done
+ if (widthRangeCache == null) {
+ searchForBreakPoints();
+ }
+
+ this.target = (AbstractComponentConnector) target;
+
+ // Get any breakpoints from the styles defined for this widget
+ getBreakPointsFor(constructSelectorsForTarget());
+
+ // Start listening for size changes
+ LayoutManager.get(getConnection()).addElementResizeListener(
+ this.target.getWidget().getElement(), this);
+ }
+
+ /**
+ * Construct the list of selectors that should be matched against in the
+ * range selectors
+ *
+ * @return The selectors in a comma delimited string.
+ */
+ protected String constructSelectorsForTarget() {
+ String primaryStyle = this.target.getState().primaryStyleName;
+ StringBuilder selectors = new StringBuilder();
+ selectors.append(".").append(primaryStyle);
+
+ if (this.target.getState().styles != null
+ && this.target.getState().styles.size() > 0) {
+ for (String style : this.target.getState().styles) {
+ selectors.append(",.").append(style);
+ selectors.append(",.").append(primaryStyle).append(".")
+ .append(style);
+ selectors.append(",.").append(style).append(".")
+ .append(primaryStyle);
+ selectors.append(",.").append(primaryStyle).append("-")
+ .append(style);
+ }
+ }
+
+ // Allow the ID to be used as the selector as well for ranges
+ if (this.target.getState().id != null) {
+ selectors.append(",#").append(this.target.getState().id);
+ }
+ return selectors.toString();
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+ LayoutManager.get(getConnection()).removeElementResizeListener(
+ this.target.getWidget().getElement(), this);
+ }
+
+ /**
+ * Build a cache of all 'width-range' and 'height-range' attribute selectors
+ * found in the stylesheets.
+ */
+ private static native void searchForBreakPoints()
+ /*-{
+
+ // Initialize variables
+ @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache = [];
+ @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache = [];
+
+ var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache;
+ var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache;
+
+ // Can't do squat if we can't parse stylesheets
+ if(!$doc.styleSheets)
+ return;
+
+ var sheets = $doc.styleSheets;
+
+ // Loop all stylesheets on the page and process them individually
+ for(var i = 0, len = sheets.length; i < len; i++) {
+ var sheet = sheets[i];
+ @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(sheet);
+ }
+
+ }-*/;
+
+ /**
+ * Process an individual stylesheet object. Any @import statements are
+ * handled recursively. Regular rule declarations are searched for
+ * 'width-range' and 'height-range' attribute selectors.
+ *
+ * @param sheet
+ */
+ private static native void searchStylesheetForBreakPoints(
+ final JavaScriptObject sheet)
+ /*-{
+
+ // Inline variables for easier reading
+ var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache;
+ var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache;
+
+ // Get all the rulesets from the stylesheet
+ var theRules = new Array();
+ var IE = @com.vaadin.client.BrowserInfo::get()().@com.vaadin.client.BrowserInfo::isIE()();
+ var IE8 = @com.vaadin.client.BrowserInfo::get()().@com.vaadin.client.BrowserInfo::isIE8()();
+
+ if (sheet.cssRules) {
+ theRules = sheet.cssRules
+ } else if (sheet.rules) {
+ theRules = sheet.rules
+ }
+
+ // Special import handling for IE8
+ if (IE8) {
+ try {
+ for(var i = 0, len = sheet.imports.length; i < len; i++) {
+ @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(sheet.imports[i]);
+ }
+ } catch(e) {
+ // This is added due to IE8 failing to handle imports of some sheets for unknown reason (throws a permission denied exception)
+ @com.vaadin.client.extensions.ResponsiveConnector::error(Ljava/lang/String;)("Failed to handle imports of CSS style sheet: " + sheet.href);
+ }
+ }
+
+ // Loop through the rulesets
+ for(var i = 0, len = theRules.length; i < len; i++) {
+ var rule = theRules[i];
+
+ if(rule.type == 3) {
+ // @import rule, traverse recursively
+ @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(rule.styleSheet);
+
+ } else if(rule.type == 1 || !rule.type) {
+ // Regular selector rule
+
+ // IE parses CSS like .class[attr="val"] into [attr="val"].class so we need to check for both
+
+ // Pattern for matching [width-range] selectors
+ var widths = IE? /\[width-range~?=["|'](.*)-(.*)["|']\]([\.|#]\S+)/i : /([\.|#]\S+)\[width-range~?=["|'](.*)-(.*)["|']\]/i;
+
+ // Patter for matching [height-range] selectors
+ var heights = IE? /\[height-range~?=["|'](.*)-(.*)["|']\]([\.|#]\S+)/i : /([\.|#]\S+)\[height-range~?=["|'](.*)-(.*)["|']\]/i;
+
+ // Array of all of the separate selectors in this ruleset
+ var haystack = rule.selectorText.split(",");
+
+ // Loop all the selectors in this ruleset
+ for(var k = 0, len2 = haystack.length; k < len2; k++) {
+ var result;
+
+ // Check for width-range matches
+ if(result = haystack[k].match(widths)) {
+ var selector = IE? result[3] : result[1]
+ var min = IE? result[1] : result[2];
+ var max = IE? result[2] : result[3];
+
+ // Avoid adding duplicates
+ var duplicate = false;
+ for(var l = 0, len3 = widthRanges.length; l < len3; l++) {
+ var bp = widthRanges[l];
+ if(selector == bp[0] && min == bp[1] && max == bp[2]) {
+ duplicate = true;
+ break;
+ }
+ }
+ if(!duplicate) {
+ widthRanges.push([selector, min, max]);
+ }
+ }
+
+ // Check for height-range matches
+ if(result = haystack[k].match(heights)) {
+ var selector = IE? result[3] : result[1]
+ var min = IE? result[1] : result[2];
+ var max = IE? result[2] : result[3];
+
+ // Avoid adding duplicates
+ var duplicate = false;
+ for(var l = 0, len3 = heightRanges.length; l < len3; l++) {
+ var bp = heightRanges[l];
+ if(selector == bp[0] && min == bp[1] && max == bp[2]) {
+ duplicate = true;
+ break;
+ }
+ }
+ if(!duplicate) {
+ heightRanges.push([selector, min, max]);
+ }
+ }
+ }
+ }
+ }
+
+ }-*/;
+
+ /**
+ * Get all matching ranges from the cache for this particular instance.
+ *
+ * @param selectors
+ */
+ private native void getBreakPointsFor(final String selectors)
+ /*-{
+
+ var selectors = selectors.split(",");
+
+ var widthBreakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::widthBreakpoints = [];
+ var heightBreakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::heightBreakpoints = [];
+
+ var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache;
+ var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache;
+
+ for(var i = 0, len = widthRanges.length; i < len; i++) {
+ var bp = widthRanges[i];
+ for(var j = 0, len2 = selectors.length; j < len2; j++) {
+ if(bp[0] == selectors[j])
+ widthBreakpoints.push(bp);
+ }
+ }
+
+ for(var i = 0, len = heightRanges.length; i < len; i++) {
+ var bp = heightRanges[i];
+ for(var j = 0, len2 = selectors.length; j < len2; j++) {
+ if(bp[0] == selectors[j])
+ heightBreakpoints.push(bp);
+ }
+ }
+
+ // Only for debugging
+ // console.log("Breakpoints for", selectors.join(","), widthBreakpoints, heightBreakpoints);
+
+ }-*/;
+
+ private String currentWidthRanges;
+ private String currentHeightRanges;
+
+ @Override
+ public void onElementResize(ElementResizeEvent e) {
+ int width = e.getLayoutManager().getOuterWidth(e.getElement());
+ int height = e.getLayoutManager().getOuterHeight(e.getElement());
+
+ // Loop through breakpoints and see which one applies to this width
+ currentWidthRanges = resolveBreakpoint("width", width, e.getElement());
+
+ if (!"".equals(currentWidthRanges)) {
+ this.target.getWidget().getElement()
+ .setAttribute("width-range", currentWidthRanges);
+ } else {
+ this.target.getWidget().getElement().removeAttribute("width-range");
+ }
+
+ // Loop through breakpoints and see which one applies to this height
+ currentHeightRanges = resolveBreakpoint("height", height,
+ e.getElement());
+
+ if (!"".equals(currentHeightRanges)) {
+ this.target.getWidget().getElement()
+ .setAttribute("height-range", currentHeightRanges);
+ } else {
+ this.target.getWidget().getElement()
+ .removeAttribute("height-range");
+ }
+ }
+
+ private native String resolveBreakpoint(String which, int size,
+ Element element)
+ /*-{
+
+ // Default to "width" breakpoints
+ var breakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::widthBreakpoints;
+
+ // Use height breakpoints if we're measuring the height
+ if(which == "height")
+ breakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::heightBreakpoints;
+
+ // Output string that goes into either the "width-range" or "height-range" attribute in the element
+ var ranges = "";
+
+ // Loop the breakpoints
+ for(var i = 0, len = breakpoints.length; i < len; i++) {
+ var bp = breakpoints[i];
+
+ var min = parseInt(bp[1]);
+ var max = parseInt(bp[2]);
+
+ if(min && max) {
+ if(min <= size && size <= max) {
+ ranges += " " + bp[1] + "-" + bp[2];
+ }
+ } else if (min) {
+ if(min <= size) {
+ ranges += " " + bp[1] + "-";
+ }
+ } else if (max) {
+ if (size <= max) {
+ ranges += " -" + bp[2];
+ }
+ }
+ }
+
+ // Trim the output and return it
+ return ranges.replace(/^\s+/, "");
+
+ }-*/;
+
+}