Source: src/main/javascript/oracle/oj/ojknockout/ResponsiveKnockoutUtils.js

Oracle® JavaScript Extension Toolkit (JET)
1.1.2

E65298-01

/*jslint browser: true*/
/*
** Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*/


/**
 * @class Utilities for creating knockout observable to implement responsive pages.
 * @since 1.1.0
 * @expose
 */
oj.ResponsiveKnockoutUtils = {};



/**
 * <p>creates an observable that 
 * returns true or false based on a media query string.</p>
 * 
 * <p>Example:</p>
 * <pre class="prettyprint">
 * <code>
 *    var customQuery = oj.ResponsiveKnockoutUtils.createMediaQueryObservable(
 *                                         '(min-width: 400px)');
 * </code></pre>
 * 
 * @param {string} queryString media query string, for example '(min-width: 400px)'
 * @return a knockout observable  that 
 *              returns true or false based on a media query string.
 * @expose
 * @static
 */
oj.ResponsiveKnockoutUtils.createMediaQueryObservable = function(queryString)
{

  if (queryString == null)
  {
    throw new Error("oj.ResponsiveKnockoutUtils.createMediaQueryObservable: aborting, queryString is null");
  }

  var query = window.matchMedia(queryString);

  var observable = ko.observable(query.matches);

  // add a listener for future changes
  query.addListener(function(query) {
      observable(query.matches);
      //console.log("query listener called! query.matches = " + query.matches + ", size = " + (document.outerWidth || document.body.clientWidth));
  });


 
  // There is a major bug in webkit, tested on ios 7 going from
  // landscape to portrait.
  //    https://bugs.webkit.org/show_bug.cgi?id=123293
  // 
  // Basically if you use a media query in css
  // then the js matchmedia call won't work! 
  //
  // According to the bug this is known to be on webkit 538.4,
  // but I see it on 537.51 as well which is earlier, so 
  // assume the problem exists generally on safari.
  // Chrome now uses blink instead of webkit, but chrome
  // still has webkit in their user agent string, however they
  // now only change the number after "Chrome".
  
  if (navigator.userAgent.indexOf("WebKit") != -1 && 
      navigator.userAgent.indexOf("Chrome") == -1) 
  {
    $(window).resize(function() {
      //console.log("Resize called! Size = " + (document.outerWidth || document.body.clientWidth));

      // Somehow if I change some text in the dom on resize 
      // the query listener is called
      var selector = "oj-webkit-bug-123293";

      if($('body').has("." + selector).length === 0) 
      {
        // setting display: none doesn't work, so using 
        // oj-helper-hidden-accessible because this visually 
        // hides the content without using display:none. 
        // However we don't want screen readers to read 
        // this so setting aria-hidden to true.
        // @HTMLUpdateOK trusted string
        $('body').append('<div aria-hidden="true" class="oj-helper-hidden-accessible ' + selector + '">');
      }

      // @HTMLUpdateOK trusted string
      $("." + selector).text((new Date().getMilliseconds()).toString());
    });
  }
  
  return observable;
}


/**
 * This function creates a computed observable, the 
 * value of which is one of the {@link oj.ResponsiveUtils.SCREEN_RANGE} constants. 
 * For example when the width is in the 
 * range defined by the sass variable $mediumScreenRange then 
 * the observable returns <code>oj.ResponsiveUtils.SCREEN_RANGE.MD</code>, 
 * but if it's in the range defined by $largeScreenRange then 
 * it returns <code>oj.ResponsiveUtils.SCREEN_RANGE.LG</code>, etc. 
 * 
 * 
 * 
 * <p>Example:</p>
 * <pre class="prettyprint">
 * <code>
 *        // create an observable which returns the current screen range
 *        self.screenRange = oj.ResponsiveKnockoutUtils.createScreenRangeObservable();
 *
 *        self.label2 = ko.computed(function() {
 *          var range = self.screenRange();
 * 
 *          if ( oj.ResponsiveUtils.compare( 
 *                       range, oj.ResponsiveUtils.SCREEN_RANGE.MD) <= 0)
 *          {
 *            // code for when screen is in small or medium range
 *          }
 *          else if (range == oj.ResponsiveUtils.SCREEN_RANGE.XL)
 *          {
 *            // code for when screen is in XL range
 *          }
 *        });
 * </code></pre>
 *
 * @return a knockout observable the value of which is one of the
 *  screen range constants, for example oj.ResponsiveUtils.SCREEN_RANGE.MD
 * @expose
 * @static
 */
oj.ResponsiveKnockoutUtils.createScreenRangeObservable = function()
{
  // queryies
  var xxlQuery = oj.ResponsiveUtils.getFrameworkQuery(
                                oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.XXL_UP);

  var xlQuery = oj.ResponsiveUtils.getFrameworkQuery(
                                oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.XL_UP);

  var lgQuery = oj.ResponsiveUtils.getFrameworkQuery(
                                oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.LG_UP);

  var mdQuery = oj.ResponsiveUtils.getFrameworkQuery(
                                oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.MD_UP);

  var smQuery = oj.ResponsiveUtils.getFrameworkQuery(
                                oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.SM_UP);



  // observables
  var xxlObservable = xxlQuery == null ? 
                      null : 
                      oj.ResponsiveKnockoutUtils.createMediaQueryObservable(xxlQuery);

  var xlObservable = xlQuery == null ? 
                     null : 
                     oj.ResponsiveKnockoutUtils.createMediaQueryObservable(xlQuery);

  var lgObservable = lgQuery == null ? 
                     null : 
                     oj.ResponsiveKnockoutUtils.createMediaQueryObservable(lgQuery);

  var mdObservable = mdQuery == null ? 
                     null : 
                     oj.ResponsiveKnockoutUtils.createMediaQueryObservable(mdQuery);

  var smObservable = smQuery == null ? 
                     null : 
                     oj.ResponsiveKnockoutUtils.createMediaQueryObservable(smQuery);


  return ko.computed(function() {
    if (xxlObservable && xxlObservable())
      return oj.ResponsiveUtils.SCREEN_RANGE.XXL; 

    if (xlObservable && xlObservable())
      return oj.ResponsiveUtils.SCREEN_RANGE.XL; 

    if (lgObservable && lgObservable())
      return oj.ResponsiveUtils.SCREEN_RANGE.LG; 

    if (mdObservable && mdObservable())
      return oj.ResponsiveUtils.SCREEN_RANGE.MD; 

    if (smObservable && smObservable())
      return oj.ResponsiveUtils.SCREEN_RANGE.SM; 

    throw new Error(" NO MATCH in oj.ResponsiveKnockoutUtils.createScreenRangeObservable");

  });
}