<commit_before>// Sticky Plugin v1.0.0 for jQuery
// =============
// Author: Anthony Garand
// Improvements by German M. Bravo (Kronuz) and Ruud Kamphuis (ruudk)
// Improvements by Leonardo C. Daronco (daronco)
// Created: 2/14/2011
// Date: 2/12/2012
// Website: http://labs.anthonygarand.com/sticky
// Description: Makes an element on the page stick on the screen as you scroll
//       It will only set the 'top' and 'position' of your element, you
//       might need to adjust the width in some cases.

(function($) {
  var defaults = {
      topSpacing: 0,
      bottomSpacing: 0,
      className: 'is-sticky',
      wrapperClassName: 'sticky-wrapper',
      center: false,
      getWidthFrom: ''
    },
    $window = $(window),
    $document = $(document),
    sticked = [],
    windowHeight = $window.height(),
    scroller = function() {
      var scrollTop = $window.scrollTop(),
        documentHeight = $document.height(),
        dwh = documentHeight - windowHeight,
        extra = (scrollTop > dwh) ? dwh - scrollTop : 0;

      for (var i = 0; i < sticked.length; i++) {
        var s = sticked[i],
          elementTop = s.stickyWrapper.offset().top,
          etse = elementTop - s.topSpacing - extra;

        if (scrollTop <= etse) {
          if (s.currentTop !== null) {
            s.stickyElement
              .css('position', '')
              .css('top', '');
            s.stickyElement.parent().removeClass(s.className);
            s.currentTop = null;
          }
        }
        else {
          var newTop = documentHeight - s.stickyElement.outerHeight()
            - s.topSpacing - s.bottomSpacing - scrollTop - extra;
          if (newTop < 0) {
            newTop = newTop + s.topSpacing;
          } else {
            newTop = s.topSpacing;
          }
          if (s.currentTop != newTop) {
            s.stickyElement
              .css('position', 'fixed')
              .css('top', newTop);

            if (typeof s.getWidthFrom !== 'undefined') {
              s.stickyElement.css('width', $(s.getWidthFrom).width());
            }

            s.stickyElement.parent().addClass(s.className);
            s.currentTop = newTop;
          }
        }
      }
    },
    resizer = function() {
      windowHeight = $window.height();
    },
    methods = {
      init: function(options) {
        var o = $.extend(defaults, options);
        return this.each(function() {
          var stickyElement = $(this);

          var stickyId = stickyElement.attr('id');
          var wrapper = $('

')
            .attr('id', stickyId + '-sticky-wrapper')
            .addClass(o.wrapperClassName);
          stickyElement.wrapAll(wrapper);

          if (o.center) {
            stickyElement.parent().css({width:stickyElement.outerWidth(),marginLeft:"auto",marginRight:"auto"});
          }

          if (stickyElement.css("float") == "right") {
            stickyElement.css({"float":"none"}).parent().css({"float":"right"});
          }

          var stickyWrapper = stickyElement.parent();
          stickyWrapper.css('height', stickyElement.outerHeight());
          sticked.push({
            topSpacing: o.topSpacing,
            bottomSpacing: o.bottomSpacing,
            stickyElement: stickyElement,
            currentTop: null,
            stickyWrapper: stickyWrapper,
            className: o.className,
            getWidthFrom: o.getWidthFrom
          });
        });
      },
      update: scroller,
      unstick: function(options) {
        return this.each(function() {
          var unstickyElement = $(this);

          var removeIdx = -1;
          for (var i = 0; i < sticked.length; i++)
          {
            if (sticked[i].stickyElement.get(0) == unstickyElement.get(0))
            {
                removeIdx = i;
            }
          }
          if(removeIdx != -1)
          {
            sticked.splice(removeIdx,1);
            unstickyElement.unwrap();
            unstickyElement.removeAttr('style');
          }
        });
      }
    };

  // should be more efficient than using $window.scroll(scroller) and $window.resize(resizer):
  if (window.addEventListener) {
    window.addEventListener('scroll', scroller, false);
    window.addEventListener('resize', resizer, false);
  } else if (window.attachEvent) {
    window.attachEvent('onscroll', scroller);
    window.attachEvent('onresize', resizer);
  }

  $.fn.sticky = function(method) {
    if (methods[method]) {
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    } else if (typeof method === 'object' || !method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error('Method ' + method + ' does not exist on jQuery.sticky');
    }
  };

  $.fn.unstick = function(method) {
    if (methods[method]) {
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    } else if (typeof method === 'object' || !method ) {
      return methods.unstick.apply( this, arguments );
    } else {
      $.error('Method ' + method + ' does not exist on jQuery.sticky');
    }

  };
  $(function() {
    setTimeout(scroller, 0);
  });
})(jQuery);
<commit_msg>Fix: undefined id in wrapper element<commit_after>// Sticky Plugin v1.0.0 for jQuery
// =============
// Author: Anthony Garand
// Improvements by German M. Bravo (Kronuz) and Ruud Kamphuis (ruudk)
// Improvements by Leonardo C. Daronco (daronco)
// Created: 2/14/2011
// Date: 2/12/2012
// Website: http://labs.anthonygarand.com/sticky
// Description: Makes an element on the page stick on the screen as you scroll
//       It will only set the 'top' and 'position' of your element, you
//       might need to adjust the width in some cases.

(function($) {
  var defaults = {
      topSpacing: 0,
      bottomSpacing: 0,
      className: 'is-sticky',
      wrapperClassName: 'sticky-wrapper',
      center: false,
      getWidthFrom: ''
    },
    $window = $(window),
    $document = $(document),
    sticked = [],
    windowHeight = $window.height(),
    scroller = function() {
      var scrollTop = $window.scrollTop(),
        documentHeight = $document.height(),
        dwh = documentHeight - windowHeight,
        extra = (scrollTop > dwh) ? dwh - scrollTop : 0;

      for (var i = 0; i < sticked.length; i++) {
        var s = sticked[i],
          elementTop = s.stickyWrapper.offset().top,
          etse = elementTop - s.topSpacing - extra;

        if (scrollTop <= etse) {
          if (s.currentTop !== null) {
            s.stickyElement
              .css('position', '')
              .css('top', '');
            s.stickyElement.parent().removeClass(s.className);
            s.currentTop = null;
          }
        }
        else {
          var newTop = documentHeight - s.stickyElement.outerHeight()
            - s.topSpacing - s.bottomSpacing - scrollTop - extra;
          if (newTop < 0) {
            newTop = newTop + s.topSpacing;
          } else {
            newTop = s.topSpacing;
          }
          if (s.currentTop != newTop) {
            s.stickyElement
              .css('position', 'fixed')
              .css('top', newTop);

            if (typeof s.getWidthFrom !== 'undefined') {
              s.stickyElement.css('width', $(s.getWidthFrom).width());
            }

            s.stickyElement.parent().addClass(s.className);
            s.currentTop = newTop;
          }
        }
      }
    },
    resizer = function() {
      windowHeight = $window.height();
    },
    methods = {
      init: function(options) {
        var o = $.extend(defaults, options);
        return this.each(function() {
          var stickyElement = $(this);

          var stickyId = stickyElement.attr('id');
          var wrapperId = stickyId ? stickyId + '-' + defaults.wrapperClassName : defaults.wrapperClassName
          var wrapper = $('

')
            .attr('id', stickyId + '-sticky-wrapper')
            .addClass(o.wrapperClassName);
          stickyElement.wrapAll(wrapper);

          if (o.center) {
            stickyElement.parent().css({width:stickyElement.outerWidth(),marginLeft:"auto",marginRight:"auto"});
          }

          if (stickyElement.css("float") == "right") {
            stickyElement.css({"float":"none"}).parent().css({"float":"right"});
          }

          var stickyWrapper = stickyElement.parent();
          stickyWrapper.css('height', stickyElement.outerHeight());
          sticked.push({
            topSpacing: o.topSpacing,
            bottomSpacing: o.bottomSpacing,
            stickyElement: stickyElement,
            currentTop: null,
            stickyWrapper: stickyWrapper,
            className: o.className,
            getWidthFrom: o.getWidthFrom
          });
        });
      },
      update: scroller,
      unstick: function(options) {
        return this.each(function() {
          var unstickyElement = $(this);

          var removeIdx = -1;
          for (var i = 0; i < sticked.length; i++)
          {
            if (sticked[i].stickyElement.get(0) == unstickyElement.get(0))
            {
                removeIdx = i;
            }
          }
          if(removeIdx != -1)
          {
            sticked.splice(removeIdx,1);
            unstickyElement.unwrap();
            unstickyElement.removeAttr('style');
          }
        });
      }
    };

  // should be more efficient than using $window.scroll(scroller) and $window.resize(resizer):
  if (window.addEventListener) {
    window.addEventListener('scroll', scroller, false);
    window.addEventListener('resize', resizer, false);
  } else if (window.attachEvent) {
    window.attachEvent('onscroll', scroller);
    window.attachEvent('onresize', resizer);
  }

  $.fn.sticky = function(method) {
    if (methods[method]) {
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    } else if (typeof method === 'object' || !method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error('Method ' + method + ' does not exist on jQuery.sticky');
    }
  };

  $.fn.unstick = function(method) {
    if (methods[method]) {
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    } else if (typeof method === 'object' || !method ) {
      return methods.unstick.apply( this, arguments );
    } else {
      $.error('Method ' + method + ' does not exist on jQuery.sticky');
    }

  };
  $(function() {
    setTimeout(scroller, 0);
  });
})(jQuery);
<|endoftext|>


<commit_before>//
// Copyright (C) 2014 edX
//
// Authors:
// Xavier Antoviaque <xavier@antoviaque.org>
//
// This software's license gives you freedom; you can copy, convey,
// propagate, redistribute and/or modify this program under the terms of
// the GNU Affero General Public License (AGPL) as published by the Free
// Software Foundation (FSF), either version 3 of the License, or (at your
// option) any later version of the AGPL published by the FSF.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
// General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in a file in the toplevel directory called
// "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
//

(function($) {

    $.xblock = {
        window: window,

        location: location,

        default_options: {
            usageId: null,       // [Mandatory] The usage id of the XBlock to display
            sessionId: null,     // [Mandatory] User session id from the LMS
            baseDomain: null,    // Common part of the client & LMS domain names
                                 // (eg: `example.com`, defaults to current domain)
            lmsSubDomain: 'lms', // The subdomain part for the LMS (eg, `lms` for `lms.example.com`)
            lmsSecureURL: false, // Is the LMS on HTTPS?
        },

        loadResource: function(resource, options, root) {
            console.log('Loading XBlock resource', resource);

            if (resource.kind === 'url') {
                var resourceURL = this.getLmsBaseURL(options) + resource.data,
                    deferred = $.Deferred().resolve(); // By default, don't wait for the resource to load

                if (resource.mimetype === 'text/css') {
                    $('head').append('<link href="' + resourceURL + '" rel="stylesheet" />')
                } else if (resource.mimetype === 'application/javascript') {
                    deferred = $.getScript(resourceURL);
                } else {
                    console.log('Unknown XBlock resource mimetype', resource.kind);
                }
            } else if (resource.kind === 'text') {
                if (resource.mimetype === 'text/css') {
                    $('head').append('<style type="text/css">' + resource.data + '</style>');
                } else if (resource.mimetype === 'application/javascript') {
                    deferred = $.globalEval(resource.data);
                } else if (resource.mimetype === 'text/html') {
                    $('head').append(resource.data);
                } else {
                    console.log('Unknown XBlock resource mimetype', resource.mimetype);
                }
            } else {
                console.log('Unknown XBlock resource kind', resource.kind);
            }
            return deferred;
        },

        getRuntime: function(options, root) {
            var $this = this;

            return {
                handlerUrl: function(element, handlerName) {
                    var usageId = $(element).data('usage-id'),
                        courseId = $(element).data('course-id'),
                        lmsBaseURL = $this.getLmsBaseURL(options);

                    return (lmsBaseURL + '/courses/' + courseId + '/xblock/' + usageId +
                            '/handler/' + handlerName);
                },
            };
        },

        jsInit: function(options, root) {
            var $this = this;

            $('.xblock', root).not('.xblock-initialized').each(function(index, blockDOM) {
                var initFnName = $(blockDOM).data('init');
                if (!initFnName) {
                    return;
                }

                // Don't fail when the page still contains XModules
                if (initFnName === 'XBlockToXModuleShim') {
                    console.log('Warning: Unsupported XModule JS init', blockDOM);
                    return;
                }

                if (typeof window[initFnName] != 'function') {
                    console.log('Warning: Undefined init function for XBlock', blockDOM, initFnName);
                    return;
                }

                console.log('Initializing XBlock JS', initFnName, blockDOM);
                window[initFnName]($this.getRuntime(options, root), blockDOM);
                $(blockDOM).addClass('xblock-initialized');
            });
        },

        eventsInit: function(options, root) {
            // Catch jump_to_id URLs
            $('a', root).not('.xblock-jump').each(function(index, linkDOM) {
                var link_url = $(linkDOM).attr('href'),
                    link_found = /^\/courses\/([^\/]+\/[^\/]+)\/([^\/]+)\/jump_to_id\/(.+)/.exec(link_url);

                if (link_found) {
                    var course_id = link_found[1],
                        block_type = link_found[2],
                        block_id = link_found[3];

                    $(linkDOM).on('click', function(e) {
                        e.preventDefault();
                        console.log(course_id, block_type, block_id);
                        $(linkDOM).trigger('xblock_jump', [course_id, block_type, block_id]);
                    });
                    $(linkDOM).addClass('xblock-jump');
                }
            });
        },

        csrfSafeMethod: function(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        },

        setAjaxCSRFToken: function(csrftoken, options, root) {
            var $this = this;

            $.cookie('csrftoken', csrftoken, { domain: '.' + options.baseDomain });
            $.ajaxSetup({
                xhrFields: {
                    withCredentials: true,
                },
                beforeSend: function(xhr, settings) {
                    var queryDomain = $('<a>').prop('href', settings.url).prop('hostname'),
                        lmsDomain = $this.getLmsDomain(options);

                    if (!$this.csrfSafeMethod(settings.type) && queryDomain === lmsDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
                    }
                }
            });
        },

        getCourseId: function(options) {
            return options.usageId.substr(8).replace(/(.+);_(.+);_(.+);_.+/, '$1/$2/$3');
        },

        getLmsDomain: function(options) {
            return options.lmsSubDomain + '.' + options.baseDomain;
        },

        getLmsBaseURL: function(options) {
            if (options.lmsSecureURL || 'https:' === document.location.protocol) {
                return 'https://' + this.getLmsDomain(options);
            } else {
                return 'http://' + this.getLmsDomain(options);
            }
        },

        getViewUrl: function(viewName, options) {
            return (this.getLmsBaseURL(options) + '/courses/' + this.getCourseId(options) +
                    '/xblock/' + options.usageId + '/view/' + viewName)
        },

        init: function(options, root) {
            var $this = this,
                blockURL = this.getViewUrl('student_view', options);

            // Set the LMS session cookie on the shared domain to authenticate on the LMS
            if (!options.sessionId) {
                console.log('Error: You must provide a session id from the LMS (cf options)');
                return;
            }
            $.cookie('sessionid', options.sessionId, { domain: '.' + options.baseDomain });

            // Avoid failing if the XBlock contains XModules
            window.setup_debug = function(){};

            $.ajax({
                url: blockURL,
                dataType: 'json',
                xhrFields: {
                    withCredentials: true,
                },
            }).done(function(response) {
                var deferreds = $.map(response.resources, function(resource) {
                    return $this.loadResource(resource, options, root);
                })

                root.html(response.html);

                $.when.apply($, deferreds).then(function() {
                    console.log('All XBlock resources successfully loaded');
                    $this.eventsInit(options, root);
                    $this.jsInit(options, root);
                });

                $this.setAjaxCSRFToken(response.csrf_token, options, root);
            }).error(function(response, text_status, error_msg) {
                console.log('Error getting XBlock: ' + text_status);
                console.log('Can be caused by a wrong session id, or missing CORS headers from the LMS');
            });
        },

        bootstrap: function(options, root) {
            var options = $.extend({}, this.default_options, options);

            if (!options.baseDomain) {
                options.baseDomain = this.location.host;
            }

            this.init(options, root);
        },
    };

    $.fn.xblock = function(options) {
        return this.each(function() {
            $.xblock.bootstrap(options, $(this));
        });
    };

})(jQuery);
<commit_msg>Shared LMS cookie should be shared across all pages<commit_after>//
// Copyright (C) 2014 edX
//
// Authors:
// Xavier Antoviaque <xavier@antoviaque.org>
//
// This software's license gives you freedom; you can copy, convey,
// propagate, redistribute and/or modify this program under the terms of
// the GNU Affero General Public License (AGPL) as published by the Free
// Software Foundation (FSF), either version 3 of the License, or (at your
// option) any later version of the AGPL published by the FSF.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
// General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in a file in the toplevel directory called
// "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
//

(function($) {

    $.xblock = {
        window: window,

        location: location,

        default_options: {
            usageId: null,       // [Mandatory] The usage id of the XBlock to display
            sessionId: null,     // [Mandatory] User session id from the LMS
            baseDomain: null,    // Common part of the client & LMS domain names
                                 // (eg: `example.com`, defaults to current domain)
            lmsSubDomain: 'lms', // The subdomain part for the LMS (eg, `lms` for `lms.example.com`)
            lmsSecureURL: false, // Is the LMS on HTTPS?
        },

        loadResource: function(resource, options, root) {
            console.log('Loading XBlock resource', resource);

            if (resource.kind === 'url') {
                var resourceURL = this.getLmsBaseURL(options) + resource.data,
                    deferred = $.Deferred().resolve(); // By default, don't wait for the resource to load

                if (resource.mimetype === 'text/css') {
                    $('head').append('<link href="' + resourceURL + '" rel="stylesheet" />')
                } else if (resource.mimetype === 'application/javascript') {
                    deferred = $.getScript(resourceURL);
                } else {
                    console.log('Unknown XBlock resource mimetype', resource.kind);
                }
            } else if (resource.kind === 'text') {
                if (resource.mimetype === 'text/css') {
                    $('head').append('<style type="text/css">' + resource.data + '</style>');
                } else if (resource.mimetype === 'application/javascript') {
                    deferred = $.globalEval(resource.data);
                } else if (resource.mimetype === 'text/html') {
                    $('head').append(resource.data);
                } else {
                    console.log('Unknown XBlock resource mimetype', resource.mimetype);
                }
            } else {
                console.log('Unknown XBlock resource kind', resource.kind);
            }
            return deferred;
        },

        getRuntime: function(options, root) {
            var $this = this;

            return {
                handlerUrl: function(element, handlerName) {
                    var usageId = $(element).data('usage-id'),
                        courseId = $(element).data('course-id'),
                        lmsBaseURL = $this.getLmsBaseURL(options);

                    return (lmsBaseURL + '/courses/' + courseId + '/xblock/' + usageId +
                            '/handler/' + handlerName);
                },
            };
        },

        jsInit: function(options, root) {
            var $this = this;

            $('.xblock', root).not('.xblock-initialized').each(function(index, blockDOM) {
                var initFnName = $(blockDOM).data('init');
                if (!initFnName) {
                    return;
                }

                // Don't fail when the page still contains XModules
                if (initFnName === 'XBlockToXModuleShim') {
                    console.log('Warning: Unsupported XModule JS init', blockDOM);
                    return;
                }

                if (typeof window[initFnName] != 'function') {
                    console.log('Warning: Undefined init function for XBlock', blockDOM, initFnName);
                    return;
                }

                console.log('Initializing XBlock JS', initFnName, blockDOM);
                window[initFnName]($this.getRuntime(options, root), blockDOM);
                $(blockDOM).addClass('xblock-initialized');
            });
        },

        eventsInit: function(options, root) {
            // Catch jump_to_id URLs
            $('a', root).not('.xblock-jump').each(function(index, linkDOM) {
                var link_url = $(linkDOM).attr('href'),
                    link_found = /^\/courses\/([^\/]+\/[^\/]+)\/([^\/]+)\/jump_to_id\/(.+)/.exec(link_url);

                if (link_found) {
                    var course_id = link_found[1],
                        block_type = link_found[2],
                        block_id = link_found[3];

                    $(linkDOM).on('click', function(e) {
                        e.preventDefault();
                        console.log(course_id, block_type, block_id);
                        $(linkDOM).trigger('xblock_jump', [course_id, block_type, block_id]);
                    });
                    $(linkDOM).addClass('xblock-jump');
                }
            });
        },

        csrfSafeMethod: function(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        },

        setAjaxCSRFToken: function(csrftoken, options, root) {
            var $this = this;

            $.cookie('csrftoken', csrftoken, { domain: '.' + options.baseDomain, path: '/' });
            $.ajaxSetup({
                xhrFields: {
                    withCredentials: true,
                },
                beforeSend: function(xhr, settings) {
                    var queryDomain = $('<a>').prop('href', settings.url).prop('hostname'),
                        lmsDomain = $this.getLmsDomain(options);

                    if (!$this.csrfSafeMethod(settings.type) && queryDomain === lmsDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
                    }
                }
            });
        },

        getCourseId: function(options) {
            return options.usageId.substr(8).replace(/(.+);_(.+);_(.+);_.+/, '$1/$2/$3');
        },

        getLmsDomain: function(options) {
            return options.lmsSubDomain + '.' + options.baseDomain;
        },

        getLmsBaseURL: function(options) {
            if (options.lmsSecureURL || 'https:' === document.location.protocol) {
                return 'https://' + this.getLmsDomain(options);
            } else {
                return 'http://' + this.getLmsDomain(options);
            }
        },

        getViewUrl: function(viewName, options) {
            return (this.getLmsBaseURL(options) + '/courses/' + this.getCourseId(options) +
                    '/xblock/' + options.usageId + '/view/' + viewName)
        },

        init: function(options, root) {
            var $this = this,
                blockURL = this.getViewUrl('student_view', options);

            // Set the LMS session cookie on the shared domain to authenticate on the LMS
            if (!options.sessionId) {
                console.log('Error: You must provide a session id from the LMS (cf options)');
                return;
            }
            $.cookie('sessionid', options.sessionId, { domain: '.' + options.baseDomain, path: '/' });

            // Avoid failing if the XBlock contains XModules
            window.setup_debug = function(){};

            $.ajax({
                url: blockURL,
                dataType: 'json',
                xhrFields: {
                    withCredentials: true,
                },
            }).done(function(response) {
                var deferreds = $.map(response.resources, function(resource) {
                    return $this.loadResource(resource, options, root);
                })

                root.html(response.html);

                $.when.apply($, deferreds).then(function() {
                    console.log('All XBlock resources successfully loaded');
                    $this.eventsInit(options, root);
                    $this.jsInit(options, root);
                });

                $this.setAjaxCSRFToken(response.csrf_token, options, root);
            }).error(function(response, text_status, error_msg) {
                console.log('Error getting XBlock: ' + text_status);
                console.log('Can be caused by a wrong session id, or missing CORS headers from the LMS');
            });
        },

        bootstrap: function(options, root) {
            var options = $.extend({}, this.default_options, options);

            if (!options.baseDomain) {
                options.baseDomain = this.location.host;
            }

            this.init(options, root);
        },
    };

    $.fn.xblock = function(options) {
        return this.each(function() {
            $.xblock.bootstrap(options, $(this));
        });
    };

})(jQuery);
<|endoftext|>


<commit_before>/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

var strip, strcountry, strcity, strregion, strlatitude, strlongitude, strtimezone;
jQuery(document).ready(function($) {

$.getJSON('http://smart-ip.net/geoip-json/?callback=?', function(data) {
    strip = data.host; strcountry = data.countryCode; strcity = data.city;
    strregion = data.region; strlatitude = data.latitude; strlongitude = data.longitude;
    strtimezone = data.timezone;
    if(strcountry !== 'US') strregion = 'NY';

 $('#vmap').vectorMap({
        map: 'usa_en',
        enableZoom: false,
        backgroundColor: null,
        showTooltip: true,
        selectedRegion: (strregion !== null)? strregion : 'NY' ,
        onRegionClick: function(element, code, region) {
            window.location = region.replace(/ /g, "-");
        }
    });
});
});

function GetUserInfo(data) {
    strip = data.host; strcountry = data.countryName; strcity = data.city;
    strregion = data.region; strlatitude = data.latitude; strlongitude = data.longitude;
    strtimezone = data.timezone;
    jQuery.holdReady(true);
    //if(strcountry
    vmap(true);

}

function vmap(us) {
    if(us === false)
       strregion = 'NY';

    jQuery.holdReady(false);

}<commit_msg>fixed state selection problem with long strings + added MIT license clause<commit_after>/*
// The MIT License (MIT)
//
// Copyright (c) 2014 Ashwin Murali
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
*/

var strip, strcountry, strcity, strregion, strlatitude, strlongitude, strtimezone, statecode;
var jsonstatestring = '{"states":[{"name":"Alabama","abbreviation":"AL"},{"name":"Alaska","abbreviation":"AK"},{"name":"American Samoa","abbreviation":"AS"},{"name":"Arizona","abbreviation":"AZ"},{"name":"Arkansas","abbreviation":"AR"},{"name":"California","abbreviation":"CA"},{"name":"Colorado","abbreviation":"CO"},{"name":"Connecticut","abbreviation":"CT"},{"name":"Delaware","abbreviation":"DE"},{"name":"District Of Columbia","abbreviation":"DC"},{"name":"Federated States Of Micronesia","abbreviation":"FM"},{"name":"Florida","abbreviation":"FL"},{"name":"Georgia","abbreviation":"GA"},{"name":"Guam","abbreviation":"GU"},{"name":"Hawaii","abbreviation":"HI"},{"name":"Idaho","abbreviation":"ID"},{"name":"Illinois","abbreviation":"IL"},{"name":"Indiana","abbreviation":"IN"},{"name":"Iowa","abbreviation":"IA"},{"name":"Kansas","abbreviation":"KS"},{"name":"Kentucky","abbreviation":"KY"},{"name":"Louisiana","abbreviation":"LA"},{"name":"Maine","abbreviation":"ME"},{"name":"Marshall Islands","abbreviation":"MH"},{"name":"Maryland","abbreviation":"MD"},{"name":"Massachusetts","abbreviation":"MA"},{"name":"Michigan","abbreviation":"MI"},{"name":"Minnesota","abbreviation":"MN"},{"name":"Mississippi","abbreviation":"MS"},{"name":"Missouri","abbreviation":"MO"},{"name":"Montana","abbreviation":"MT"},{"name":"Nebraska","abbreviation":"NE"},{"name":"Nevada","abbreviation":"NV"},{"name":"New Hampshire","abbreviation":"NH"},{"name":"New Jersey","abbreviation":"NJ"},{"name":"New Mexico","abbreviation":"NM"},{"name":"New York","abbreviation":"NY"},{"name":"North Carolina","abbreviation":"NC"},{"name":"North Dakota","abbreviation":"ND"},{"name":"Northern Mariana Islands","abbreviation":"MP"},{"name":"Ohio","abbreviation":"OH"},{"name":"Oklahoma","abbreviation":"OK"},{"name":"Oregon","abbreviation":"OR"},{"name":"Palau","abbreviation":"PW"},{"name":"Pennsylvania","abbreviation":"PA"},{"name":"Puerto Rico","abbreviation":"PR"},{"name":"Rhode Island","abbreviation":"RI"},{"name":"South Carolina","abbreviation":"SC"},{"name":"South Dakota","abbreviation":"SD"},{"name":"Tennessee","abbreviation":"TN"},{"name":"Texas","abbreviation":"TX"},{"name":"Utah","abbreviation":"UT"},{"name":"Vermont","abbreviation":"VT"},{"name":"Virgin Islands","abbreviation":"VI"},{"name":"Virginia","abbreviation":"VA"},{"name":"Washington","abbreviation":"WA"},{"name":"West Virginia","abbreviation":"WV"},{"name":"Wisconsin","abbreviation":"WI"},{"name":"Wyoming","abbreviation":"WY"}]}';

jQuery(document).ready(function($) {

$.getJSON('http://smart-ip.net/geoip-json/?callback=?', function(data) {
    strip = data.host; strcountry = data.countryCode; strcity = data.city;
    strregion = data.region; strlatitude = data.latitude; strlongitude = data.longitude;
    strtimezone = data.timezone;
    statecode = null;
    var obj = $.parseJSON(jsonstatestring);
    for(index in obj["states"])
    {
	if(obj["states"][index]["name"] === strregion)
	    statecode = obj["states"][index]["abbreviation"];
    }

    if(strcountry !== 'US') statecode = 'NY';

 $('#vmap').vectorMap({
        map: 'usa_en',
        enableZoom: false,
        backgroundColor: null,
        showTooltip: true,
        selectedRegion: (statecode !== null)? statecode : 'NY' ,
        onRegionClick: function(element, code, region) {
            window.location = region.replace(/ /g, "-");
        }
    });
});
});<|endoftext|>


<commit_before>var timeoutID;
var selectedFormat = null;
var selectedFields = [];
var forceSearch = false;

function onSearchKeyUp(event, input) {
	//console.log('keyup');
	//console.log(event.keyCode);

	var immediate = false;
	var timeout = 300;

	// Clear field on escape
	if (event.keyCode == 27 && input.value != '') {
		input.value = '';
		timeout = 1;
	}

	else if (event.keyCode == 8 || event.keyCode == 46) {
		// Ignore delete when already empty
		if (input.value == '') {
			//return;
		}
	}

	// Ignore tab and arrow keys
	else if ([9, 37, 38, 39, 40].indexOf(event.keyCode) != -1) {
		return;
	}

	// Ignore modifier keys
	else if ([16, 17, 18, 91, 224].indexOf(event.keyCode) != -1 && !forceSearch) {
		return;
	}

	// Immediate search on Enter
	else if (event.keyCode == 13) {
		immediate = true;
	}

	if (timeoutID) {
		clearTimeout(timeoutID);
		timeoutID = null;
	}

	forceSearch = false;

	if (immediate) {
		startSearch();
	}
	else {
		timeoutID = setTimeout(function () {
			startSearch();
		}, 400);
	}
}

// Pick up some things not caught by keyUp
function onSearchKeyPress(event) {
	//console.log('keypress');
	//console.log(event.keyCode);

	// Ignore tab and arrow keys
	if ([9, 37, 38, 39, 40].indexOf(event.keyCode) != -1) {
		return;
	}

	// Ignore modifier keys
	else if ([16, 17, 18, 91, 224].indexOf(event.keyCode) != -1) {
		return;
	}

	// Force search for cut/paste, which show up here but not
	// in onKeyUp(), where we ignore modifier keys
	forceSearch = true;
}

function onChange(input) {
	if (input.value == '') {
		startSearch();
	}
}

function startSearch() {
	document.getElementById('loading').style.visibility = 'visible';
	setTimeout(function () {
		updateSearchResults();
		document.getElementById('loading').style.visibility = 'hidden';
	}, 25);
}

function updateSearchResults() {
	//console.log("Searching");
	var t = new Date();

	var val = document.getElementById('searchField').value;
	var valLower = val.toLowerCase();
	var valLowerHyphenated = valLower.replace(/\s/g,"-");
	var numDisplayedStyles = 0;
	var formatCounts = {};
	var fieldCounts = {};

	var showDepStyles = !document.getElementById('dependentToggle').checked;

	// Filter the style list based on search parameters
	var uls = $("ul.styleList li.title a").each(function () {
		var container = $(this).parent().parent().parent();
		var name = $(this).attr("href").match(/([^\/]+(?:\/dev)?(?:\?dep=1)?)$/)[0];

		var show = true;

		// Hide dependent styles if unchecked
		if (!showDepStyles && $(this).hasClass("dependent")) {
			show = false;
		}

		// Hide styles that don't match the search text
		if (show) {
			if (name.indexOf(valLowerHyphenated) == -1 && $(this).text().toLowerCase().indexOf(valLower) == -1) {
				show = false;
			}
		}

		// Hide styles that don't match the selected categories
		if (show) {
			if (selectedFormat) {
				if (!styleData[name].cat || styleData[name].cat.format != selectedFormat) {
					show = false;
				}
			}

			if (show) {
				for (var i in selectedFields) {
					if (!styleData[name].cat || styleData[name].cat.fields.indexOf(selectedFields[i]) == -1) {
						show = false;
						break;
					}
				}
			}
		}

		if (show) {
			container.show();
			numDisplayedStyles++;

			// Count citation formats
			if (styleData[name].cat.format) {
				if (!formatCounts[styleData[name].cat.format]) {
					formatCounts[styleData[name].cat.format] = 1;
				}
				else {
					formatCounts[styleData[name].cat.format]++;
				}
			}

			// Count fields
			if (styleData[name].cat.fields) {
				for (var i in styleData[name].cat.fields) {
					var field = styleData[name].cat.fields[i];
					if (!fieldCounts[field]) {
						fieldCounts[field] = 1;
					}
					else {
						fieldCounts[field]++;
					}
				}
			}
		}
		else {
			container.hide();
		}
	});

	//
	// And now adjust the search parameters based on visible results
	//

	// Remove all category elements
	$("#formats").empty();
	$("#fields").empty();

	// Sort formats and add to category box
	var arr = [];
	for (var i in formatCounts) {
		arr.push([i, formatCounts[i]]);
	}
	arr.sort(function (a, b) { return a[0].localeCompare(b[0]) });
	for (var i in arr) {
		var selClass = selectedFormat == arr[i][0] ? ' class="selected"' : '';
		$("#formats").append('
' + arr[i][0] + '
');
	}

	// Sort fields and add to category box
	var arr = [];
	for (var i in fieldCounts) {
		arr.push([i, fieldCounts[i]]);
	}
	arr.sort(function (a, b) { return a[0].localeCompare(b[0]) });
	for (var i in arr) {
		var selClass = selectedFields.indexOf(arr[i][0]) != -1 ? ' class="selected"' : '';
		$("#fields").append('
' + arr[i][0] + '
');
	}

	// Fix disappearing categories in WebKit
	for (var i in document.styleSheets[0].cssRules) {
		var rules = document.styleSheets[0].cssRules[i];
		if (rules.selectorText == "#searchFields ul") {
			//rules.style.border = '0';
			rules.style.display = 'block';
			break;
		}
	}

	// Add/remove categories on click
	$(".categoryBox li").click(function () {
		var name = $(this).text();
		var selected = $(this).hasClass("selected");

		var div = $(this).closest(".categoryBox");

		if (div.attr("id") == "formatsBox") {
			selectedFormat = selected ? null : name;
		}
		else if (div.attr("id") == "fieldsBox") {
			if (selected) {
				var pos = selectedFields.indexOf(name);
				selectedFields.splice(pos, 1);
			}
			else {
				selectedFields.push(name);
			}
		}
		else {
			throw ("Category box not found");
		}

		if (selected) {
			$(this).removeClass("selected");
		}
		else {
			$(this).addClass("selected");
		}

		startSearch();
	});

	// Update number of search results
	switch (numDisplayedStyles) {
		case 0:
			var str =  "No styles found";
			break;

		case 1:
			var str =  "1 style found:";
			break;

		default:
			var num = numDisplayedStyles + '';
			num = num.replace(/\B(?=(?:\d{3})+(?!\d))/g, ",");
			var str = num + " styles found:";
	}
	$("#styleCount").text(str);

	//console.log((new Date() - t) + "ms");
}

// Add preview tooltips
$(function () {
	updateSearchResults();

	/*$("ul.styleList li.title a").each(function() {
		$(this).tooltip({
			bodyHandler: function() {
				return "Previews forthcoming";
			},
			showURL: false
		});
	});*/

	$("ul.styleList li.title a").each(function() {
		$(this).qtip({
			content: {
				text: "Loading preview...",
				ajax: {
					url: "/styles-files/previews/" + styleData[$(this).attr("href").match(/([^\/]+(?:\/dev)?(?:\?dep=1)?)$/)[0]]['prev'] + '.html',
					type: 'GET',
					data: {}
				}
			},
			position: {
				target: 'mouse',
				adjust: {
					x: 15
				},
				viewport: $(window),
				show: {
					delay: 120
				}
			},
			show: {
				effect: false
			}
		});
	});
});<commit_msg>Get rid of Zotero Style Repository JavaScript code.<commit_after><|endoftext|>


<commit_before>function loadScript(src, progressCallback, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        var result = xhr.response;
        var script = document.createElement('script');
        script.src = src;
        script.async = false;
        script.defer = false;
        document.head.appendChild(script);
        if (callback) callback();
    };
    xhr.onerror = callback;
    xhr.onprogress = progressCallback || null;
    xhr.open('get', src);
    xhr.responseType = 'text';
    xhr.send();
}

function addScriptTag(src) {
    var script = document.createElement('script');
    script.src = src;
    script.async = false;
    script.defer = false;
    document.head.appendChild(script);
}

var loadProgress = document.getElementById('loadProgress');
loadScript('lib/phaser.js', function (e) {
    if (e.lengthComputable) {
        loadProgress.value = e.loaded;
        loadProgress.max = e.total;
        console.log(e.loaded / e.total);
    }
}, function (e) {
    if (e) {
        loading.innerText = 'Unable to load Phaser.js';
        return;
    }
    loading.innerText = 'Loading my program';
    var srcs = [
      'js/Load.js',
      'js/model/Board.js',
      'js/model/Shape.js',
      'js/model/Swap.js',
      'js/model/Match.js',
      'js/model/Debug.js',
      'js/GameScreen.js',
      'js/Ball.js',
      'js/TouchDetector.js',
      'js/Boot.js'
    ];
    for (var i = 0; i < srcs.length; i++) {
        addScriptTag(srcs[i]);
    }
});
<commit_msg>fix load progress<commit_after>function loadScript(src, progressCallback, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        var result = xhr.response;
        var script = document.createElement('script');
        script.src = src;
        script.async = false;
        script.defer = false;
        document.head.appendChild(script);
        if (callback) callback();
    };
    xhr.onerror = callback;
    xhr.onprogress = progressCallback || null;
    xhr.open('get', src);
    xhr.responseType = 'text';
    xhr.send();
}

function addScriptTag(src) {
    var script = document.createElement('script');
    script.src = src;
    script.async = false;
    script.defer = false;
    document.head.appendChild(script);
}

var loadProgress = document.getElementById('loadProgress');
loadScript('lib/phaser.js', function (e) {
    if (e.lengthComputable) {
        loadProgress.value = e.loaded;
        loadProgress.max = e.total;
    }
    else {
        loadProgress.value = e.loaded;
        loadProgress.max = 3169095;
    }
}, function (e) {
    if (e) {
        loading.innerText = 'Unable to load Phaser.js';
        return;
    }
    loading.innerText = 'Loading my program';
    var srcs = [
      'js/Load.js',
      'js/model/Board.js',
      'js/model/Shape.js',
      'js/model/Swap.js',
      'js/model/Match.js',
      'js/model/Debug.js',
      'js/GameScreen.js',
      'js/Ball.js',
      'js/TouchDetector.js',
      'js/Boot.js'
    ];
    for (var i = 0; i < srcs.length; i++) {
        addScriptTag(srcs[i]);
    }
});
<|endoftext|>


<commit_before>// Copyright 2018-2022, University of Colorado Boulder

/**
 * Scenery node that shows a handle, which is made of two parts: the "grip" which is where you would grab it and the
 * "attachment" which are elbow-shaped bars that attach the handle to another surface.
 *
 * @author Chris Klusendorf (PhET Interactive Simulations)
 * @author Sam Reid (PhET Interactive Simulations)
 */

import Matrix3 from '../../dot/js/Matrix3.js';
import { Shape } from '../../kite/js/imports.js';
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import merge from '../../phet-core/js/merge.js';
import { Node } from '../../scenery/js/imports.js';
import { Path } from '../../scenery/js/imports.js';
import { Color } from '../../scenery/js/imports.js';
import { LinearGradient } from '../../scenery/js/imports.js';
import { PaintColorProperty } from '../../scenery/js/imports.js';
import sceneryPhet from './sceneryPhet.js';

// constants
// grip shape
const GRIP_WIDTH = 100;
const GRIP_HEIGHT = 42;
const GRIP_CORNER_RADIUS = GRIP_WIDTH * 0.03;
const GRIP_END_PAD = GRIP_WIDTH * 0.03; // horizontal line between the edge of the grip and the cubic curves
const GRIP_SINGLE_FINGER_INDENT_DEPTH = GRIP_HEIGHT * 0.11;
const GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH = ( GRIP_WIDTH - GRIP_CORNER_RADIUS * 2 - GRIP_END_PAD * 2 ) / 8;
const DEFAULT_GRIP_BASE_COLOR = new Color( 183, 184, 185 );

/**
 * @param {Object} [options]
 * @constructor
 */
class HandleNode extends Node {

  constructor( options ) {

    options = merge( {

      // options for the grip
      gripBaseColor: DEFAULT_GRIP_BASE_COLOR, // {ColorDef} base color of gradient on the grip
      gripStroke: 'black', // {ColorDef} stroke color of the grip
      gripLineWidth: 3,

      // options for the attachment(s)
      attachmentFill: 'gray', // {ColorDef} solid fill color for the attachments
      attachmentStroke: 'black', // {ColorDef} stroke color of the attachments
      attachmentLineWidth: 3,
      hasLeftAttachment: true,
      hasRightAttachment: true

    }, options );

    assert && assert( options.hasLeftAttachment || options.hasRightAttachment, 'at least one attachment is required' );

    // the grip shape begins on the left edge, middle y
    // this is the upper left corner before grip indents start
    const gripShape = new Shape()
      .moveTo( 0, GRIP_HEIGHT / 2 )
      .lineTo( 0, GRIP_CORNER_RADIUS )
      .arc( GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, Math.PI, Math.PI * 1.5, false )
      .lineToRelative( GRIP_END_PAD, 0 );

    // these are the top grip indents
    addGripIndent( gripShape, 1 );
    addGripIndent( gripShape, 1 );
    addGripIndent( gripShape, 1 );
    addGripIndent( gripShape, 1 );

    // this is the whole right edge
    gripShape.lineToRelative( GRIP_END_PAD, 0 )
      .arc( GRIP_WIDTH - GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, Math.PI * 1.5, 0, false )
      .lineToRelative( 0, GRIP_HEIGHT - ( GRIP_CORNER_RADIUS * 2 ) )
      .arc( GRIP_WIDTH - GRIP_CORNER_RADIUS, GRIP_HEIGHT - GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, 0, Math.PI / 2, false )
      .lineToRelative( -GRIP_END_PAD, 0 );

    // these are the bottom grip indents
    addGripIndent( gripShape, -1 );
    addGripIndent( gripShape, -1 );
    addGripIndent( gripShape, -1 );
    addGripIndent( gripShape, -1 );

    // this is the lower left hand corner
    gripShape.lineToRelative( -GRIP_END_PAD, 0 )
      .arc( GRIP_CORNER_RADIUS, GRIP_HEIGHT - GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, Math.PI / 2, Math.PI, false )
      .lineTo( 0, GRIP_HEIGHT / 2 )
      .close();

    // Use PaintColorProperty so that colors can be updated dynamically
    const gripBaseColorProperty = new PaintColorProperty( options.gripBaseColor );
    const brighterColorProperty = new PaintColorProperty( gripBaseColorProperty, { luminanceFactor: 0.95 } );
    const darkerColorProperty = new PaintColorProperty( gripBaseColorProperty, { luminanceFactor: -0.35 } );

    // add handle grip shape
    const gripPath = new Path( gripShape, {
      lineWidth: options.gripLineWidth,
      stroke: options.gripStroke,
      fill: new LinearGradient( 0, 0, 0, GRIP_HEIGHT )
        .addColorStop( 0, gripBaseColorProperty )
        .addColorStop( 0.4, brighterColorProperty )
        .addColorStop( 0.7, gripBaseColorProperty )
        .addColorStop( 1.0, darkerColorProperty )
    } );

    assert && assert( !options.hasOwnProperty( 'children' ), 'HandleNode sets children' );
    options = merge( {
      children: [ gripPath ]
    }, options );

    // handle attachment shape vars
    const attachmentShaftWidth = GRIP_HEIGHT * 0.35;
    const attachmentHeight = GRIP_HEIGHT * 1.15;
    const attachmentBaseNubWidth = attachmentShaftWidth * 0.4;
    const attachmentBaseNubHeight = attachmentHeight * 0.2;
    const attachmentMiddleHeight = attachmentHeight * 0.5;
    const attachmentSmallArcRadius = attachmentShaftWidth * 0.5;

    const attachmentOptions = {
      fill: options.attachmentFill,
      stroke: options.attachmentStroke,
      lineWidth: options.attachmentLineWidth,
      lineJoin: 'round',
      top: gripPath.centerY - attachmentShaftWidth / 2
    };

    const leftAttachmentShape = new Shape()

      // Starts at bottom-left and proceeds clockwise
      .moveTo( 0, attachmentHeight )
      .lineToRelative( attachmentBaseNubWidth, -attachmentBaseNubHeight )
      .lineToRelative( 0, -attachmentMiddleHeight )

      // The main arc at the top left of the attachment
      .arc(
        attachmentShaftWidth + attachmentBaseNubWidth + attachmentSmallArcRadius,
        attachmentHeight - attachmentBaseNubHeight - attachmentMiddleHeight,
        attachmentShaftWidth + attachmentSmallArcRadius,
        Math.PI,
        Math.PI * 1.5,
        false
      )
      .lineToRelative( 0, attachmentShaftWidth )

      // This is the smaller arc on the underside of the attachment
      .arc(
        attachmentShaftWidth + attachmentBaseNubWidth + attachmentSmallArcRadius,
        attachmentHeight - attachmentBaseNubHeight - attachmentMiddleHeight,
        attachmentSmallArcRadius,
        Math.PI * 1.5,
        Math.PI,
        true
      )
      .lineToRelative( 0, attachmentMiddleHeight )
      .lineToRelative( attachmentBaseNubWidth, attachmentBaseNubHeight )
      .lineToRelative( -attachmentShaftWidth - ( attachmentBaseNubWidth * 2 ), 0 )
      .close();

    // left attachment
    if ( options.hasLeftAttachment ) {

      const leftAttachmentPath = new Path( leftAttachmentShape, merge( {
        right: gripPath.left + options.gripLineWidth
      }, attachmentOptions ) );

      options.children.unshift( leftAttachmentPath ); // prepend so that attachment is behind grip
    }

    // right attachment, a mirror image of the left
    if ( options.hasRightAttachment ) {

      const rightAttachmentShape = leftAttachmentShape.transformed( Matrix3.scaling( -1, 1 ) );

      // handle right attachment
      const rightAttachmentPath = new Path( rightAttachmentShape, merge( {
        left: gripPath.right - options.gripLineWidth
      }, attachmentOptions ) );

      options.children.unshift( rightAttachmentPath );  // prepend so that attachment is behind grip
    }

    super( options );

    // @private
    this.disposeHandleNode = () => {
      gripBaseColorProperty.dispose();
      brighterColorProperty.dispose();
      darkerColorProperty.dispose();
    };

    // support for binder documentation, stripped out in builds and only runs when ?binder is specified
    assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'scenery-phet', 'HandleNode', this );
  }

  // @public
  dispose() {
    this.disposeHandleNode();
    super.dispose();
  }
}

sceneryPhet.register( 'HandleNode', HandleNode );

/**
 * Add an "up/down" combination to either the top or bottom of the grip.
 * @param {Shape} shape - the shape to append to
 * @param {number} sign - +1 for top side of grip, -1 for bottom side of grip
 */
function addGripIndent( shape, sign ) {

  // control points for cubic curve shape on grip
  // each single-finger indent is made of two cubic curves that are mirrored over the y-axis
  const controlPoint1X = GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH / 2;
  const controlPoint1Y = 0;
  const controlPoint2X = GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH / 4;
  const controlPoint2Y = GRIP_SINGLE_FINGER_INDENT_DEPTH;

  // this is a grip indent
  shape.cubicCurveToRelative(
    sign * controlPoint1X,
    sign * controlPoint1Y,
    sign * controlPoint2X,
    sign * controlPoint2Y,
    sign * GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH,
    sign * GRIP_SINGLE_FINGER_INDENT_DEPTH )
    .cubicCurveToRelative(
      sign * ( GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH - controlPoint2X ),
      sign * -controlPoint1Y,
      sign * ( GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH - controlPoint1X ),
      sign * -controlPoint2Y,
      sign * GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH,
      sign * -GRIP_SINGLE_FINGER_INDENT_DEPTH );
}

export default HandleNode;<commit_msg>// @ts-nocheck<commit_after>// Copyright 2018-2022, University of Colorado Boulder

// @ts-nocheck
/**
 * Scenery node that shows a handle, which is made of two parts: the "grip" which is where you would grab it and the
 * "attachment" which are elbow-shaped bars that attach the handle to another surface.
 *
 * @author Chris Klusendorf (PhET Interactive Simulations)
 * @author Sam Reid (PhET Interactive Simulations)
 */

import Matrix3 from '../../dot/js/Matrix3.js';
import { Shape } from '../../kite/js/imports.js';
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import merge from '../../phet-core/js/merge.js';
import { Node } from '../../scenery/js/imports.js';
import { Path } from '../../scenery/js/imports.js';
import { Color } from '../../scenery/js/imports.js';
import { LinearGradient } from '../../scenery/js/imports.js';
import { PaintColorProperty } from '../../scenery/js/imports.js';
import sceneryPhet from './sceneryPhet.js';

// constants
// grip shape
const GRIP_WIDTH = 100;
const GRIP_HEIGHT = 42;
const GRIP_CORNER_RADIUS = GRIP_WIDTH * 0.03;
const GRIP_END_PAD = GRIP_WIDTH * 0.03; // horizontal line between the edge of the grip and the cubic curves
const GRIP_SINGLE_FINGER_INDENT_DEPTH = GRIP_HEIGHT * 0.11;
const GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH = ( GRIP_WIDTH - GRIP_CORNER_RADIUS * 2 - GRIP_END_PAD * 2 ) / 8;
const DEFAULT_GRIP_BASE_COLOR = new Color( 183, 184, 185 );

/**
 * @param {Object} [options]
 * @constructor
 */
class HandleNode extends Node {

  constructor( options ) {

    options = merge( {

      // options for the grip
      gripBaseColor: DEFAULT_GRIP_BASE_COLOR, // {ColorDef} base color of gradient on the grip
      gripStroke: 'black', // {ColorDef} stroke color of the grip
      gripLineWidth: 3,

      // options for the attachment(s)
      attachmentFill: 'gray', // {ColorDef} solid fill color for the attachments
      attachmentStroke: 'black', // {ColorDef} stroke color of the attachments
      attachmentLineWidth: 3,
      hasLeftAttachment: true,
      hasRightAttachment: true

    }, options );

    assert && assert( options.hasLeftAttachment || options.hasRightAttachment, 'at least one attachment is required' );

    // the grip shape begins on the left edge, middle y
    // this is the upper left corner before grip indents start
    const gripShape = new Shape()
      .moveTo( 0, GRIP_HEIGHT / 2 )
      .lineTo( 0, GRIP_CORNER_RADIUS )
      .arc( GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, Math.PI, Math.PI * 1.5, false )
      .lineToRelative( GRIP_END_PAD, 0 );

    // these are the top grip indents
    addGripIndent( gripShape, 1 );
    addGripIndent( gripShape, 1 );
    addGripIndent( gripShape, 1 );
    addGripIndent( gripShape, 1 );

    // this is the whole right edge
    gripShape.lineToRelative( GRIP_END_PAD, 0 )
      .arc( GRIP_WIDTH - GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, Math.PI * 1.5, 0, false )
      .lineToRelative( 0, GRIP_HEIGHT - ( GRIP_CORNER_RADIUS * 2 ) )
      .arc( GRIP_WIDTH - GRIP_CORNER_RADIUS, GRIP_HEIGHT - GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, 0, Math.PI / 2, false )
      .lineToRelative( -GRIP_END_PAD, 0 );

    // these are the bottom grip indents
    addGripIndent( gripShape, -1 );
    addGripIndent( gripShape, -1 );
    addGripIndent( gripShape, -1 );
    addGripIndent( gripShape, -1 );

    // this is the lower left hand corner
    gripShape.lineToRelative( -GRIP_END_PAD, 0 )
      .arc( GRIP_CORNER_RADIUS, GRIP_HEIGHT - GRIP_CORNER_RADIUS, GRIP_CORNER_RADIUS, Math.PI / 2, Math.PI, false )
      .lineTo( 0, GRIP_HEIGHT / 2 )
      .close();

    // Use PaintColorProperty so that colors can be updated dynamically
    const gripBaseColorProperty = new PaintColorProperty( options.gripBaseColor );
    const brighterColorProperty = new PaintColorProperty( gripBaseColorProperty, { luminanceFactor: 0.95 } );
    const darkerColorProperty = new PaintColorProperty( gripBaseColorProperty, { luminanceFactor: -0.35 } );

    // add handle grip shape
    const gripPath = new Path( gripShape, {
      lineWidth: options.gripLineWidth,
      stroke: options.gripStroke,
      fill: new LinearGradient( 0, 0, 0, GRIP_HEIGHT )
        .addColorStop( 0, gripBaseColorProperty )
        .addColorStop( 0.4, brighterColorProperty )
        .addColorStop( 0.7, gripBaseColorProperty )
        .addColorStop( 1.0, darkerColorProperty )
    } );

    assert && assert( !options.hasOwnProperty( 'children' ), 'HandleNode sets children' );
    options = merge( {
      children: [ gripPath ]
    }, options );

    // handle attachment shape vars
    const attachmentShaftWidth = GRIP_HEIGHT * 0.35;
    const attachmentHeight = GRIP_HEIGHT * 1.15;
    const attachmentBaseNubWidth = attachmentShaftWidth * 0.4;
    const attachmentBaseNubHeight = attachmentHeight * 0.2;
    const attachmentMiddleHeight = attachmentHeight * 0.5;
    const attachmentSmallArcRadius = attachmentShaftWidth * 0.5;

    const attachmentOptions = {
      fill: options.attachmentFill,
      stroke: options.attachmentStroke,
      lineWidth: options.attachmentLineWidth,
      lineJoin: 'round',
      top: gripPath.centerY - attachmentShaftWidth / 2
    };

    const leftAttachmentShape = new Shape()

      // Starts at bottom-left and proceeds clockwise
      .moveTo( 0, attachmentHeight )
      .lineToRelative( attachmentBaseNubWidth, -attachmentBaseNubHeight )
      .lineToRelative( 0, -attachmentMiddleHeight )

      // The main arc at the top left of the attachment
      .arc(
        attachmentShaftWidth + attachmentBaseNubWidth + attachmentSmallArcRadius,
        attachmentHeight - attachmentBaseNubHeight - attachmentMiddleHeight,
        attachmentShaftWidth + attachmentSmallArcRadius,
        Math.PI,
        Math.PI * 1.5,
        false
      )
      .lineToRelative( 0, attachmentShaftWidth )

      // This is the smaller arc on the underside of the attachment
      .arc(
        attachmentShaftWidth + attachmentBaseNubWidth + attachmentSmallArcRadius,
        attachmentHeight - attachmentBaseNubHeight - attachmentMiddleHeight,
        attachmentSmallArcRadius,
        Math.PI * 1.5,
        Math.PI,
        true
      )
      .lineToRelative( 0, attachmentMiddleHeight )
      .lineToRelative( attachmentBaseNubWidth, attachmentBaseNubHeight )
      .lineToRelative( -attachmentShaftWidth - ( attachmentBaseNubWidth * 2 ), 0 )
      .close();

    // left attachment
    if ( options.hasLeftAttachment ) {

      const leftAttachmentPath = new Path( leftAttachmentShape, merge( {
        right: gripPath.left + options.gripLineWidth
      }, attachmentOptions ) );

      options.children.unshift( leftAttachmentPath ); // prepend so that attachment is behind grip
    }

    // right attachment, a mirror image of the left
    if ( options.hasRightAttachment ) {

      const rightAttachmentShape = leftAttachmentShape.transformed( Matrix3.scaling( -1, 1 ) );

      // handle right attachment
      const rightAttachmentPath = new Path( rightAttachmentShape, merge( {
        left: gripPath.right - options.gripLineWidth
      }, attachmentOptions ) );

      options.children.unshift( rightAttachmentPath );  // prepend so that attachment is behind grip
    }

    super( options );

    // @private
    this.disposeHandleNode = () => {
      gripBaseColorProperty.dispose();
      brighterColorProperty.dispose();
      darkerColorProperty.dispose();
    };

    // support for binder documentation, stripped out in builds and only runs when ?binder is specified
    assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'scenery-phet', 'HandleNode', this );
  }

  // @public
  dispose() {
    this.disposeHandleNode();
    super.dispose();
  }
}

sceneryPhet.register( 'HandleNode', HandleNode );

/**
 * Add an "up/down" combination to either the top or bottom of the grip.
 * @param {Shape} shape - the shape to append to
 * @param {number} sign - +1 for top side of grip, -1 for bottom side of grip
 */
function addGripIndent( shape, sign ) {

  // control points for cubic curve shape on grip
  // each single-finger indent is made of two cubic curves that are mirrored over the y-axis
  const controlPoint1X = GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH / 2;
  const controlPoint1Y = 0;
  const controlPoint2X = GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH / 4;
  const controlPoint2Y = GRIP_SINGLE_FINGER_INDENT_DEPTH;

  // this is a grip indent
  shape.cubicCurveToRelative(
    sign * controlPoint1X,
    sign * controlPoint1Y,
    sign * controlPoint2X,
    sign * controlPoint2Y,
    sign * GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH,
    sign * GRIP_SINGLE_FINGER_INDENT_DEPTH )
    .cubicCurveToRelative(
      sign * ( GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH - controlPoint2X ),
      sign * -controlPoint1Y,
      sign * ( GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH - controlPoint1X ),
      sign * -controlPoint2Y,
      sign * GRIP_SINGLE_FINGER_INDENT_HALF_WIDTH,
      sign * -GRIP_SINGLE_FINGER_INDENT_DEPTH );
}

export default HandleNode;<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Shows the home screen for a multi-screen simulation, which lets the user see all of the screens and select one.
 *
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  var PhetButton = require( 'JOIST/PhetButton' );
  var Node = require( 'SCENERY/nodes/Node' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var VBox = require( 'SCENERY/nodes/VBox' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Shape = require( 'KITE/Shape' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var Frame = require( 'JOIST/Frame' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Property = require( 'AXON/Property' );
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var FullScreenButton = require( 'JOIST/FullScreenButton' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );

  var HEIGHT = 70;

  function HomeScreen( sim ) {
    var homeScreen = this;

    //Rendering in SVG seems to solve the problem that the home screen consumes 100% disk and crashes, see https://github.com/phetsims/joist/issues/17
    //Also makes it more responsive (and crisper on retina displays)
    ScreenView.call( this, {renderer: 'svg'} );

    this.backgroundColor = 'black';

    //iPad doesn't support Century Gothic, so fall back to Futura, see http://wordpress.org/support/topic/font-not-working-on-ipad-browser
    var title = new Text( sim.name, {
      font: new PhetFont( {size: 52, family: '"Century Gothic", "Futura", sans-serif' }),
      fill: 'white',
      y: 110,
      centerX: this.layoutBounds.width / 2} );
    this.addChild( title );

    //Keep track of which screen is highlighted so the same screen can remain highlighted even if nodes are replaced (say when one grows larger or smaller)
    var highlightedIndex = new Property( -1 );

    var screenChildren = _.map( sim.screens, function( screen ) {
      var index = sim.screens.indexOf( screen );
      var largeIcon = new Node( {children: [screen.icon], scale: HEIGHT / screen.icon.height * 2} );
      var frame = new Frame( largeIcon );

      highlightedIndex.link( function( highlightedIndex ) { frame.setHighlighted( highlightedIndex === index ); } );

      var largeIconWithFrame = new Node( {children: [ frame, largeIcon]} );
      var large = new VBox( {cursor: 'pointer', children: [
        largeIconWithFrame,
        new Text( screen.name, { font: new PhetFont( 42 ), fill: 'yellow'} )
      ]} );

      //TODO: Switch to buttonListener, but make sure you test it because on 7/17/2013 there is a problem where ButtonListener won't fire if a node has appeared under the pointer
      large.addInputListener( {
        down: function() {
          sim.simModel.showHomeScreen = false;
          highlightedIndex.value = -1;
        }
      } );

      var small = new VBox( {spacing: 3, cursor: 'pointer', children: [
        new Node( {opacity: 0.5, children: [screen.icon], scale: sim.screens.length === 4 ? HEIGHT / screen.icon.height :
                                                              sim.screens.length === 3 ? 1.25 * HEIGHT / screen.icon.height :
                                                              sim.screens.length === 2 ? 1.75 * HEIGHT / screen.icon.height :
                                                              HEIGHT / screen.icon.height} ),
        new Text( screen.name, { font: new PhetFont( 18 ), fill: 'gray'} )
      ]} );
      small.mouseArea = small.touchArea = Shape.bounds( small.bounds ); //cover the gap in the vbox
      small.addInputListener( {
        down: function() { sim.simModel.screenIndex = index; },

        //On the home screen if you touch an inactive screen thumbnail, it grows.  If then without lifting your finger you swipe over
        // to the next thumbnail, that one would grow.
        over: function( event ) {
          if ( !event.pointer.isMouse ) {
            sim.simModel.screenIndex = index;
          }
        }
      } );

      var highlightListener = {
        over: function( event ) {
          if ( event.pointer.isMouse ) {
            highlightedIndex.value = index;

            //TODO: use named children instead of child indices?
            small.children[0].opacity = 1;
            small.children[1].fill = 'white';
          }
        },
        out: function( event ) {
          if ( event.pointer.isMouse ) {
            highlightedIndex.value = -1;
            small.children[0].opacity = 0.5;
            small.children[1].fill = 'gray';
          }
        }
      };
      small.addInputListener( highlightListener );

      large.addInputListener( highlightListener );
      large.mouseArea = large.touchArea = Shape.bounds( large.bounds ); //cover the gap in the vbox

      //TODO: Add accessibility peers
      //      screenChild.addPeer( '<input type="button" aria-label="' + screenChild.screen.name + '">', {click: function() {
//        var screen = screenChild.screen;
//        if ( sim.simModel.screenIndex === screen.index ) {
//          sim.simModel.showHomeScreen = false;
//        }
//        else {
//          sim.simModel.screenIndex = screen.index;
//        }
//      }} );
//    } );

      return {screen: screen, small: small, large: large, index: index};
    } );

    var center = new Node( {y: 170} );
    homeScreen.addChild( center );
    sim.simModel.screenIndexProperty.link( function( screenIndex ) {

      //Space the icons out more if there are fewer, so they will be spaced nicely
      //Cannot have only 1 screen because for 1-screen sims there is no home screen.
      var spacing = sim.screens.length === 2 ? 60 :
                    sim.screens.length === 3 ? 60 :
                    33;

      var icons = _.map( screenChildren, function( screenChild ) {return screenChild.index === screenIndex ? screenChild.large : screenChild.small;} );
      center.children = [new HBox( {spacing: spacing, children: icons, align: 'top'} )];
      center.centerX = homeScreen.layoutBounds.width / 2;
    } );

    //Only show the full screen button on supported platforms
    var deviceIsAndroid = navigator.userAgent.indexOf( 'Android' ) > 0;
    var deviceIsIOS = /iP(ad|hone|od)/.test( navigator.userAgent );

    function isIE( v ) {
      var r = new RegExp( 'msie' + (!isNaN( v ) ? ('\\s' + v) : ''), 'i' );
      return r.test( navigator.userAgent );
    }

    var ie = isIE( 9 ) || isIE( 10 );//might work on IE11

    var showFullScreenButton = !deviceIsAndroid && !deviceIsIOS && !ie;
    if ( showFullScreenButton && false ) {
      var fullScreenButton = new FullScreenButton();
      var phetButton = new PhetButton( sim );
      this.addChild( new HBox( {spacing: 10, children: [fullScreenButton, phetButton], right: this.layoutBounds.maxX - 5, bottom: this.layoutBounds.maxY - 5} ) );
    }
    else {
      this.addChild( new PhetButton( sim, {phetLogo: 'phet-logo-short.svg', phetLogoScale: 0.4, optionsButtonVerticalMargin: 6} ).mutate( {right: this.layoutBounds.maxX - 5, bottom: this.layoutBounds.maxY - 5} ) );
    }
  }

  return inherit( ScreenView, HomeScreen );
} );<commit_msg>remove fallback family from homescreen title, it's now added automatically by PhetFont<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Shows the home screen for a multi-screen simulation, which lets the user see all of the screens and select one.
 *
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  var PhetButton = require( 'JOIST/PhetButton' );
  var Node = require( 'SCENERY/nodes/Node' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var VBox = require( 'SCENERY/nodes/VBox' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Shape = require( 'KITE/Shape' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var Frame = require( 'JOIST/Frame' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Property = require( 'AXON/Property' );
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var FullScreenButton = require( 'JOIST/FullScreenButton' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );

  var HEIGHT = 70;

  function HomeScreen( sim ) {
    var homeScreen = this;

    //Rendering in SVG seems to solve the problem that the home screen consumes 100% disk and crashes, see https://github.com/phetsims/joist/issues/17
    //Also makes it more responsive (and crisper on retina displays)
    ScreenView.call( this, {renderer: 'svg'} );

    this.backgroundColor = 'black';

    //iPad doesn't support Century Gothic, so fall back to Futura, see http://wordpress.org/support/topic/font-not-working-on-ipad-browser
    var title = new Text( sim.name, {
      font: new PhetFont( {size: 52, family: 'Century Gothic, Futura' }),
      fill: 'white',
      y: 110,
      centerX: this.layoutBounds.width / 2} );
    this.addChild( title );

    //Keep track of which screen is highlighted so the same screen can remain highlighted even if nodes are replaced (say when one grows larger or smaller)
    var highlightedIndex = new Property( -1 );

    var screenChildren = _.map( sim.screens, function( screen ) {
      var index = sim.screens.indexOf( screen );
      var largeIcon = new Node( {children: [screen.icon], scale: HEIGHT / screen.icon.height * 2} );
      var frame = new Frame( largeIcon );

      highlightedIndex.link( function( highlightedIndex ) { frame.setHighlighted( highlightedIndex === index ); } );

      var largeIconWithFrame = new Node( {children: [ frame, largeIcon]} );
      var large = new VBox( {cursor: 'pointer', children: [
        largeIconWithFrame,
        new Text( screen.name, { font: new PhetFont( 42 ), fill: 'yellow'} )
      ]} );

      //TODO: Switch to buttonListener, but make sure you test it because on 7/17/2013 there is a problem where ButtonListener won't fire if a node has appeared under the pointer
      large.addInputListener( {
        down: function() {
          sim.simModel.showHomeScreen = false;
          highlightedIndex.value = -1;
        }
      } );

      var small = new VBox( {spacing: 3, cursor: 'pointer', children: [
        new Node( {opacity: 0.5, children: [screen.icon], scale: sim.screens.length === 4 ? HEIGHT / screen.icon.height :
                                                              sim.screens.length === 3 ? 1.25 * HEIGHT / screen.icon.height :
                                                              sim.screens.length === 2 ? 1.75 * HEIGHT / screen.icon.height :
                                                              HEIGHT / screen.icon.height} ),
        new Text( screen.name, { font: new PhetFont( 18 ), fill: 'gray'} )
      ]} );
      small.mouseArea = small.touchArea = Shape.bounds( small.bounds ); //cover the gap in the vbox
      small.addInputListener( {
        down: function() { sim.simModel.screenIndex = index; },

        //On the home screen if you touch an inactive screen thumbnail, it grows.  If then without lifting your finger you swipe over
        // to the next thumbnail, that one would grow.
        over: function( event ) {
          if ( !event.pointer.isMouse ) {
            sim.simModel.screenIndex = index;
          }
        }
      } );

      var highlightListener = {
        over: function( event ) {
          if ( event.pointer.isMouse ) {
            highlightedIndex.value = index;

            //TODO: use named children instead of child indices?
            small.children[0].opacity = 1;
            small.children[1].fill = 'white';
          }
        },
        out: function( event ) {
          if ( event.pointer.isMouse ) {
            highlightedIndex.value = -1;
            small.children[0].opacity = 0.5;
            small.children[1].fill = 'gray';
          }
        }
      };
      small.addInputListener( highlightListener );

      large.addInputListener( highlightListener );
      large.mouseArea = large.touchArea = Shape.bounds( large.bounds ); //cover the gap in the vbox

      //TODO: Add accessibility peers
      //      screenChild.addPeer( '<input type="button" aria-label="' + screenChild.screen.name + '">', {click: function() {
//        var screen = screenChild.screen;
//        if ( sim.simModel.screenIndex === screen.index ) {
//          sim.simModel.showHomeScreen = false;
//        }
//        else {
//          sim.simModel.screenIndex = screen.index;
//        }
//      }} );
//    } );

      return {screen: screen, small: small, large: large, index: index};
    } );

    var center = new Node( {y: 170} );
    homeScreen.addChild( center );
    sim.simModel.screenIndexProperty.link( function( screenIndex ) {

      //Space the icons out more if there are fewer, so they will be spaced nicely
      //Cannot have only 1 screen because for 1-screen sims there is no home screen.
      var spacing = sim.screens.length === 2 ? 60 :
                    sim.screens.length === 3 ? 60 :
                    33;

      var icons = _.map( screenChildren, function( screenChild ) {return screenChild.index === screenIndex ? screenChild.large : screenChild.small;} );
      center.children = [new HBox( {spacing: spacing, children: icons, align: 'top'} )];
      center.centerX = homeScreen.layoutBounds.width / 2;
    } );

    //Only show the full screen button on supported platforms
    var deviceIsAndroid = navigator.userAgent.indexOf( 'Android' ) > 0;
    var deviceIsIOS = /iP(ad|hone|od)/.test( navigator.userAgent );

    function isIE( v ) {
      var r = new RegExp( 'msie' + (!isNaN( v ) ? ('\\s' + v) : ''), 'i' );
      return r.test( navigator.userAgent );
    }

    var ie = isIE( 9 ) || isIE( 10 );//might work on IE11

    var showFullScreenButton = !deviceIsAndroid && !deviceIsIOS && !ie;
    if ( showFullScreenButton && false ) {
      var fullScreenButton = new FullScreenButton();
      var phetButton = new PhetButton( sim );
      this.addChild( new HBox( {spacing: 10, children: [fullScreenButton, phetButton], right: this.layoutBounds.maxX - 5, bottom: this.layoutBounds.maxY - 5} ) );
    }
    else {
      this.addChild( new PhetButton( sim, {phetLogo: 'phet-logo-short.svg', phetLogoScale: 0.4, optionsButtonVerticalMargin: 6} ).mutate( {right: this.layoutBounds.maxX - 5, bottom: this.layoutBounds.maxY - 5} ) );
    }
  }

  return inherit( ScreenView, HomeScreen );
} );<|endoftext|>


<commit_before>JQueryLens = {

	defaultParams: function() {
		return {
			locator: "locator",
			thumbnail: "thumbnail",
			lens: "lens",
			zoom: 4
		};
	},

	init: function(params) {
		params = jQuery.extend(this.defaultParams(), params);

		this._zoom = params.zoom;
		this.locator = this.getElement(params.locator);
		this.thumbnail = this.getElement(params.thumbnail);
		this.lens = this.getElement(params.lens);
		this.image = $(this.getIdForImage());

		this.resizeLocator();
		this.resizeThumbnail();
		this.resizeImage();
	},

	getIdForImage: function() {
		return this.getElement(this.lens.attr("id") + " img");
	},

	getElement: function(id) {
		return $("#" + id);
	},

	getBorder : function(element) {
		return parseInt(element.css("border-left-width"));
	},

	moveLocatorAccordingToBorder: function() {
		this.locator.offset({
			top: this.locator.offset().top +
				 this.getBorder(this.thumbnail),
			left: this.locator.offset().left +
				  this.getBorder(this.thumbnail)
		});
	},

	moveImageAccordingToBorder: function() {
		this.image.offset({
			top: this.image.offset().top +
				 this.getBorder(this.lens),
			left: this.image.offset().left +
				  this.getBorder(this.lens)
		});
	},

	resizeImage: function() {
		this.image.width(this.thumbnail.width() * this._zoom);
		this.image.height(this.thumbnail.height() * this._zoom);
	},

	resizeLocator: function() {
		this.locator.width(this.lens.width() / this._zoom);
		this.locator.height(this.lens.height() / this._zoom);
	},

	resizeThumbnail: function() {
		var imageHeight = this.image.height();
		var imageWidth = this.image.width();
		this.thumbnail.height(this.thumbnail.width() * imageHeight / imageWidth);
	},

	refreshImageInLens: function() {
		this.image.offset({
			top: this.lens.position().top -
				 (this.getBorder(this.thumbnail) * this._zoom) -
				 (this.locator.position().top * this._zoom) -
				 (this.getBorder(this.locator) * this._zoom),
			left: this.lens.position().left -
				  (this.getBorder(this.thumbnail) * this._zoom) -
				  (this.locator.position().left * this._zoom) -
				  (this.getBorder(this.locator) * this._zoom)
		});
	},

	getDifference: function(innerDiv, outerDiv) {
		return {
			vertical: outerDiv.height() - innerDiv.height() - this.getBorder(innerDiv)*2,
			horizontal: outerDiv.width() - innerDiv.width() - this.getBorder(innerDiv)*2,
		};
	},

	getLimits: function(innerDiv, outerDiv) {
		var outerTop = outerDiv.position().top;
		var outerLeft = outerDiv.position().left;
		var difference = this.getDifference(innerDiv, outerDiv);
		return {
			minTop: outerTop,
			maxTop: outerTop + difference.vertical,
			minLeft: outerLeft,
			maxLeft: outerLeft + difference.horizontal
		};
	},

	locatorLimits: function() {
		return this.getLimits(this.locator, this.thumbnail);
	},

	adjust: function(position, limits) {
		if (position.top < limits.minTop)
			position.top = limits.minTop;

		if (position.top > limits.maxTop)
			position.top = limits.maxTop;

		if (position.left < limits.minLeft)
			position.left = limits.minLeft;

		if (position.left > limits.maxLeft)
			position.left = limits.maxLeft;

		return position;
	},

	adjustLocatorPosition: function() {
		var position = {
			top: this.locator.offset().top,
			left: this.locator.offset().left
		};

		var adjustedPosition = this.adjust(position, this.locatorLimits());

		this.locator.offset(adjustedPosition);
	},

	refreshLocatorInThumbnail: function(x, y) {
		this.locator.offset({
			top: y - (this.locator.height() + this.getBorder(this.locator) * 2) / 2,
			left: x - (this.locator.width() + this.getBorder(this.locator) * 2) / 2
		});

		this.adjustLocatorPosition();
		this.refreshImageInLens();
		this.moveLocatorAccordingToBorder();
		this.moveImageAccordingToBorder();
	},
};

$(document).ready(function() {
	JQueryLens.init({
		zoom: 4
	});
	JQueryLens.thumbnail.mousemove(function(e) {
		JQueryLens.refreshLocatorInThumbnail(e.pageX, e.pageY);
	});
});<commit_msg>Round lens top and left values for eliminating firefox doubles<commit_after>JQueryLens = {

	defaultParams: function() {
		return {
			locator: "locator",
			thumbnail: "thumbnail",
			lens: "lens",
			zoom: 4
		};
	},

	init: function(params) {
		params = jQuery.extend(this.defaultParams(), params);

		this._zoom = params.zoom;
		this.locator = this.getElement(params.locator);
		this.thumbnail = this.getElement(params.thumbnail);
		this.lens = this.getElement(params.lens);
		this.image = $(this.getIdForImage());

		this.resizeLocator();
		this.resizeThumbnail();
		this.resizeImage();
	},

	getIdForImage: function() {
		return this.getElement(this.lens.attr("id") + " img");
	},

	getElement: function(id) {
		return $("#" + id);
	},

	getBorder : function(element) {
		return parseInt(element.css("border-left-width"));
	},

	moveLocatorAccordingToBorder: function() {
		this.locator.offset({
			top: this.locator.offset().top +
				 this.getBorder(this.thumbnail),
			left: this.locator.offset().left +
				  this.getBorder(this.thumbnail)
		});
	},

	moveImageAccordingToBorder: function() {
		this.image.offset({
			top: this.image.offset().top +
				 this.getBorder(this.lens),
			left: this.image.offset().left +
				  this.getBorder(this.lens)
		});
	},

	resizeImage: function() {
		this.image.width(this.thumbnail.width() * this._zoom);
		this.image.height(this.thumbnail.height() * this._zoom);
	},

	resizeLocator: function() {
		this.locator.width(this.lens.width() / this._zoom);
		this.locator.height(this.lens.height() / this._zoom);
	},

	resizeThumbnail: function() {
		var imageHeight = this.image.height();
		var imageWidth = this.image.width();
		this.thumbnail.height(this.thumbnail.width() * imageHeight / imageWidth);
	},

	refreshImageInLens: function() {
        var lensTop = Math.ceil(this.lens.position().top);
        var lensLeft = Math.ceil(this.lens.position().left);
		this.image.offset({
			top: lensTop -
				 (this.getBorder(this.thumbnail) * this._zoom) -
				 (this.locator.position().top * this._zoom) -
				 (this.getBorder(this.locator) * this._zoom),
			left: lensLeft  -
				  (this.getBorder(this.thumbnail) * this._zoom) -
				  (this.locator.position().left * this._zoom) -
				  (this.getBorder(this.locator) * this._zoom)
		});
	},

	getDifference: function(innerDiv, outerDiv) {
		return {
			vertical: outerDiv.height() - innerDiv.height() - this.getBorder(innerDiv)*2,
			horizontal: outerDiv.width() - innerDiv.width() - this.getBorder(innerDiv)*2,
		};
	},

	getLimits: function(innerDiv, outerDiv) {
		var outerTop = outerDiv.position().top;
		var outerLeft = outerDiv.position().left;
		var difference = this.getDifference(innerDiv, outerDiv);
		return {
			minTop: outerTop,
			maxTop: outerTop + difference.vertical,
			minLeft: outerLeft,
			maxLeft: outerLeft + difference.horizontal
		};
	},

	locatorLimits: function() {
		return this.getLimits(this.locator, this.thumbnail);
	},

	adjust: function(position, limits) {
		if (position.top < limits.minTop)
			position.top = limits.minTop;

		if (position.top > limits.maxTop)
			position.top = limits.maxTop;

		if (position.left < limits.minLeft)
			position.left = limits.minLeft;

		if (position.left > limits.maxLeft)
			position.left = limits.maxLeft;

		return position;
	},

	adjustLocatorPosition: function() {
		var position = {
			top: Math.ceil(this.locator.offset().top),
			left: Math.ceil(this.locator.offset().left)
		};

		var adjustedPosition = this.adjust(position, this.locatorLimits());

		this.locator.offset(adjustedPosition);
	},

	refreshLocatorInThumbnail: function(x, y) {
		this.locator.offset({
			top: y - (this.locator.height() + this.getBorder(this.locator) * 2) / 2,
			left: x - (this.locator.width() + this.getBorder(this.locator) * 2) / 2
		});

		this.adjustLocatorPosition();
		this.refreshImageInLens();
		this.moveLocatorAccordingToBorder();
		this.moveImageAccordingToBorder();
	},
};

$(document).ready(function() {
	JQueryLens.init({
		zoom: 4
	});
	JQueryLens.thumbnail.mousemove(function(e) {
		JQueryLens.refreshLocatorInThumbnail(e.pageX, e.pageY);
	});
});
<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * The button that pops up the PhET menu.
 */
define( function( require ) {
  'use strict';

  // modules
  var Node = require( 'SCENERY/nodes/Node' );
  var Image = require( 'SCENERY/nodes/Image' );
  var FontAwesomeNode = require( 'SUN/FontAwesomeNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var PhetMenu = require( 'JOIST/PhetMenu' );
  var Shape = require( 'KITE/Shape' );
  var NodesPushButton = require( 'SUN/buttons/NodesPushButton' );
  var HighlightNode = require( 'JOIST/HighlightNode' );

  // images
  var phetLogo = require( 'image!BRAND/logo.png' );
  //Makes the 'h' a bit darker so it will show up better against a white background
  var phetLogoDarker = require( 'image!BRAND/logo-on-white.png' );

  /**
   *
   * @param sim
   * @param whiteColorScheme
   * @param {Object} [options] Unused in client code.
   * @constructor
   */
  function PhetButton( sim, options ) {

    options = _.extend( {
      phetLogoScale: 0.28,
      optionsButtonVerticalMargin: 1.5
    }, options );

    var phetLabel = new Image( phetLogo, {scale: options.phetLogoScale, pickable: false} );
    sim.link( 'useInvertedColors', function( whiteColorScheme ) {
      phetLabel.image = whiteColorScheme ? phetLogoDarker : phetLogo;
    } );

    var optionsButton = new FontAwesomeNode( 'reorder', {
      scale: 0.6,
      left: phetLabel.width + 10,
      bottom: phetLabel.bottom - options.optionsButtonVerticalMargin,
      pickable: false
    } );
    sim.link( 'useInvertedColors', function( whiteColorScheme ) {
      optionsButton.fill = whiteColorScheme ? '#222' : 'white';
    } );

    var createNode = function( highlighted ) {
      var node = new Node( {children: [phetLabel, optionsButton]} );

      if ( highlighted ) {
        var normalHighlight = new HighlightNode( node.width + 6, node.height + 5, {
          centerX: node.centerX,
          centerY: node.centerY + 4,
          whiteHighlight: true
        } );
        var invertedHighlight = new HighlightNode( node.width + 6, node.height + 5, {
          centerX: node.centerX,
          centerY: node.centerY + 4,
          whiteHighlight: false
        } );
        node.addChild( normalHighlight );
        node.addChild( invertedHighlight );
        sim.link( 'useInvertedColors', function( whiteColorScheme ) {
          normalHighlight.visible = !whiteColorScheme;
          invertedHighlight.visible = whiteColorScheme;
        } );
      }
      return node;
    };

    // NodesPushButton( idleNode, overNode, pressedNode, disabledNode, options )
    NodesPushButton.call( this, createNode( false ), createNode( true ), createNode( true ), new Node(), {
      yAlign: 'top' //TODO #177 this shouldn't be needed, but is, so there's likely a bug in createNode
    } );

    //When the phet button is pressed, show the phet menu
    var phetButtonPressed = function() {

      var phetMenu = new PhetMenu( sim, {
        showSaveAndLoad: sim.options.showSaveAndLoad,
        closeCallback: function() {
          // hides the popup and barrier background
          sim.hidePopup( phetMenu, true );
        }
      } );
      function onResize( bounds, screenBounds, scale ) {
        // because it starts at null
        if ( bounds ) {
          phetMenu.setScaleMagnitude( Math.max( 1, scale * 0.7 ) ); // minimum size for small devices
          phetMenu.right = bounds.right - 10 * scale;
          phetMenu.bottom = ( bounds.bottom + screenBounds.bottom ) / 2;
        }
      }
      sim.on( 'resized', onResize );
      onResize( sim.bounds, sim.screenBounds, sim.scale );

      sim.showPopup( phetMenu, true );
    };
    this.addListener( phetButtonPressed );

    this.addPeer( '<input type="button" aria-label="PhET Menu">', {click: phetButtonPressed, tabIndex: 101} );

    // eliminate interactivity gap between label and button
    this.mouseArea = this.touchArea = Shape.bounds( this.bounds );

    this.mutate( options );
  }

  return inherit( NodesPushButton, PhetButton, {},

    //statics
    {

      //How much space between the PhetButton and the right side of the screen.
      HORIZONTAL_INSET: 5,

      //How much space between the PhetButton and the bottom of the screen
      VERTICAL_INSET: 0
    } );
} );<commit_msg>#114 remove bogus @param<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * The button that pops up the PhET menu.
 */
define( function( require ) {
  'use strict';

  // modules
  var Node = require( 'SCENERY/nodes/Node' );
  var Image = require( 'SCENERY/nodes/Image' );
  var FontAwesomeNode = require( 'SUN/FontAwesomeNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var PhetMenu = require( 'JOIST/PhetMenu' );
  var Shape = require( 'KITE/Shape' );
  var NodesPushButton = require( 'SUN/buttons/NodesPushButton' );
  var HighlightNode = require( 'JOIST/HighlightNode' );

  // images
  var phetLogo = require( 'image!BRAND/logo.png' );
  //Makes the 'h' a bit darker so it will show up better against a white background
  var phetLogoDarker = require( 'image!BRAND/logo-on-white.png' );

  /**
   *
   * @param sim
   * @param {Object} [options] Unused in client code.
   * @constructor
   */
  function PhetButton( sim, options ) {

    options = _.extend( {
      phetLogoScale: 0.28,
      optionsButtonVerticalMargin: 1.5
    }, options );

    var phetLabel = new Image( phetLogo, {scale: options.phetLogoScale, pickable: false} );
    sim.link( 'useInvertedColors', function( whiteColorScheme ) {
      phetLabel.image = whiteColorScheme ? phetLogoDarker : phetLogo;
    } );

    var optionsButton = new FontAwesomeNode( 'reorder', {
      scale: 0.6,
      left: phetLabel.width + 10,
      bottom: phetLabel.bottom - options.optionsButtonVerticalMargin,
      pickable: false
    } );
    sim.link( 'useInvertedColors', function( whiteColorScheme ) {
      optionsButton.fill = whiteColorScheme ? '#222' : 'white';
    } );

    var createNode = function( highlighted ) {
      var node = new Node( {children: [phetLabel, optionsButton]} );

      if ( highlighted ) {
        var normalHighlight = new HighlightNode( node.width + 6, node.height + 5, {
          centerX: node.centerX,
          centerY: node.centerY + 4,
          whiteHighlight: true
        } );
        var invertedHighlight = new HighlightNode( node.width + 6, node.height + 5, {
          centerX: node.centerX,
          centerY: node.centerY + 4,
          whiteHighlight: false
        } );
        node.addChild( normalHighlight );
        node.addChild( invertedHighlight );
        sim.link( 'useInvertedColors', function( whiteColorScheme ) {
          normalHighlight.visible = !whiteColorScheme;
          invertedHighlight.visible = whiteColorScheme;
        } );
      }
      return node;
    };

    // NodesPushButton( idleNode, overNode, pressedNode, disabledNode, options )
    NodesPushButton.call( this, createNode( false ), createNode( true ), createNode( true ), new Node(), {
      yAlign: 'top' //TODO #177 this shouldn't be needed, but is, so there's likely a bug in createNode
    } );

    //When the phet button is pressed, show the phet menu
    var phetButtonPressed = function() {

      var phetMenu = new PhetMenu( sim, {
        showSaveAndLoad: sim.options.showSaveAndLoad,
        closeCallback: function() {
          // hides the popup and barrier background
          sim.hidePopup( phetMenu, true );
        }
      } );
      function onResize( bounds, screenBounds, scale ) {
        // because it starts at null
        if ( bounds ) {
          phetMenu.setScaleMagnitude( Math.max( 1, scale * 0.7 ) ); // minimum size for small devices
          phetMenu.right = bounds.right - 10 * scale;
          phetMenu.bottom = ( bounds.bottom + screenBounds.bottom ) / 2;
        }
      }
      sim.on( 'resized', onResize );
      onResize( sim.bounds, sim.screenBounds, sim.scale );

      sim.showPopup( phetMenu, true );
    };
    this.addListener( phetButtonPressed );

    this.addPeer( '<input type="button" aria-label="PhET Menu">', {click: phetButtonPressed, tabIndex: 101} );

    // eliminate interactivity gap between label and button
    this.mouseArea = this.touchArea = Shape.bounds( this.bounds );

    this.mutate( options );
  }

  return inherit( NodesPushButton, PhetButton, {},

    //statics
    {

      //How much space between the PhetButton and the right side of the screen.
      HORIZONTAL_INSET: 5,

      //How much space between the PhetButton and the bottom of the screen
      VERTICAL_INSET: 0
    } );
} );<|endoftext|>