<commit_before>(function() {

cdb.vis.Overlay.register('logo', function(data, vis) {

});

cdb.vis.Overlay.register('mobile', function(data, vis) {

  var template = cdb.core.Template.compile(
    data.template || '\

\

\

\
        \
        \

\

\

\

\

\

\

\

\

\

\

\

\
    \

\
    ',
    data.templateType || 'mustache'
  );

  var mobile = new cdb.geo.ui.Mobile({
    template: template,
    mapView: vis.mapView,
    overlays: data.overlays,
    layerView: data.layerView,
    visibility_options: data.options,
    torqueLayer: data.torqueLayer,
    map: data.map
  });

  return mobile.render();
});

cdb.vis.Overlay.register('image', function(data, vis) {

  var options = data.options;

  var isDevice = options.device == "mobile" ? true : false;

  if (vis.device !== isDevice) return;

  var template = cdb.core.Template.compile(
    data.template || '\

\

{{{ content }}}
\

',
    data.templateType || 'mustache'
  );

  var widget = new cdb.geo.ui.Image({
    model: new cdb.core.Model(options),
    template: template
  });

  return widget.render();

});

cdb.vis.Overlay.register('text', function(data, vis) {

  var options = data.options;

  var isDevice = options.device == "mobile" ? true : false;

  if (vis.device !== isDevice) return;

  var template = cdb.core.Template.compile(
    data.template || '\

\

{{{ text }}}
\

',
    data.templateType || 'mustache'
  );

  var widget = new cdb.geo.ui.Text({
    model: new cdb.core.Model(options),
    template: template,
    className: "cartodb-overlay overlay-text " + options.device
  });

  return widget.render();

});

cdb.vis.Overlay.register('zoom_info', function(data, vis) {
  //console.log("placeholder for the zoom_info overlay");
});

cdb.vis.Overlay.register('header', function(data, vis) {

  if (vis.device) return;

  var options = data.options;

  var template = cdb.core.Template.compile(
    data.template || '\

\

{{{ title }}}
\

{{{ description }}}
\

',
    data.templateType || 'mustache'
  );

  var widget = new cdb.geo.ui.Header({
    model: new cdb.core.Model(options),
    template: template
  });

  return widget.render();

});

// map zoom control
cdb.vis.Overlay.register('zoom', function(data, vis) {

  if(!data.template) {
    vis.trigger('error', 'zoom template is empty')
    return;
  }

  var zoom = new cdb.geo.ui.Zoom({
    model: data.map,
    template: cdb.core.Template.compile(data.template)
  });

  return zoom.render();

});

// Tiles loader
cdb.vis.Overlay.register('loader', function(data) {

  var tilesLoader = new cdb.geo.ui.TilesLoader({
    template: cdb.core.Template.compile(data.template)
  });

  return tilesLoader.render();
});

cdb.vis.Overlay.register('time_slider', function(data, viz) {
  var slider = new cdb.geo.ui.TimeSlider(data);
  return slider.render();
});

// Header to show informtion (title and description)
cdb.vis.Overlay.register('_header', function(data, vis) {
  var MAX_SHORT_DESCRIPTION_LENGTH = 100;

  // Add the complete url for facebook and twitter
  if (location.href) {
    data.share_url = encodeURIComponent(location.href);
  } else {
    data.share_url = data.url;
  }

  var template = cdb.core.Template.compile(
    data.template || "\
      {{#title}}\

\
          {{#url}}\
            {{title}}\
          {{/url}}\
          {{^url}}\
            {{title}}\
          {{/url}}\

\
      {{/title}}\
      {{#description}}
{{{description}}}
{{/description}}\
      {{#mobile_shareable}}\

\
          F\
          T\

\
      {{/mobile_shareable}}\
    ",
    data.templateType || 'mustache'
  );

  function truncate(s, length) {
    return s.substr(0, length-1) + (s.length > length ? '…' : '');
  }

  var title       = data.map.get('title');
  var description = data.map.get('description');

  var facebook_title = title + ": " + description;
  var twitter_title;

  if (title && description) {
    twitter_title = truncate(title + ": " + description, 112) + " %23map "
  } else if (title) {
    twitter_title = truncate(title, 112) + " %23map"
  } else if (description){
    twitter_title = truncate(description, 112) + " %23map"
  } else {
    twitter_title = "%23map"
  }

  var shareable = (data.shareable == "false" || !data.shareable) ? null : data.shareable;
  var mobile_shareable = shareable;

  mobile_shareable = mobile_shareable && (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));

  var header = new cdb.geo.ui.Header({
    title: title,
    description: description,
    facebook_title: facebook_title,
    twitter_title: twitter_title,
    url: data.url,
    share_url: data.share_url,
    mobile_shareable: mobile_shareable,
    shareable: shareable && !mobile_shareable,
    template: template
  });

  return header.render();
});

// infowindow
cdb.vis.Overlay.register('infowindow', function(data, vis) {

  if (_.size(data.fields) == 0) {
    return null;
  }

  var infowindowModel = new cdb.geo.ui.InfowindowModel({
    template: data.template,
    alternative_names: data.alternative_names,
    fields: data.fields,
    template_name: data.template_name
  });

  var templateType = data.templateType || 'mustache';

  var infowindow = new cdb.geo.ui.Infowindow({
     model: infowindowModel,
     mapView: vis.mapView,
     template: new cdb.core.Template({ template: data.template, type: templateType}).asFunction()
  });

  return infowindow;
});

// layer_selector
cdb.vis.Overlay.register('layer_selector', function(data, vis) {

  var options = data.options;
  //if (!options.display) return;

  var template = cdb.core.Template.compile(
    data.template || '\
      Visible layers

\
      ',
    data.templateType || 'underscore'
  );

  var dropdown_template = cdb.core.Template.compile(
    data.template || '\

\
      ',
    data.templateType || 'underscore'
  );

  var layerSelector = new cdb.geo.ui.LayerSelector({
    model: new cdb.core.Model(options),
    mapView: vis.mapView,
    template: template,
    dropdown_template: dropdown_template,
    layer_names: data.layer_names
  });

  if (vis.legends) {

    layerSelector.bind('change:visible', function(visible, order, layer) {

      if (layer.get('type') === 'torque') {

        var timeSlider = vis.getOverlay('time_slider');

        if (timeSlider) {
          timeSlider[visible ? 'show': 'hide']();
        }

      }

      if (layer.get('type') === 'layergroup' || layer.get('type') === 'torque') {

        var legend = vis.legends && vis.legends.getLegendByIndex(order);

        if (legend) {
          legend[visible ? 'show': 'hide']();
        }

      }

    });
  }

  return layerSelector.render();

});

// fullscreen
cdb.vis.Overlay.register('fullscreen', function(data, vis) {

  var options = data.options;

  options.allowWheelOnFullscreen = false;

  var template = cdb.core.Template.compile(
    data.template || '',
    data.templateType || 'mustache'
  );

  var fullscreen = new cdb.ui.common.FullScreen({
    doc: "#map > div",
    model: new cdb.core.Model(options),
    mapView: vis.mapView,
    template: template
  });

  return fullscreen.render();

});

// share content
cdb.vis.Overlay.register('share', function(data, vis) {

  var options = data.options;

  var template = cdb.core.Template.compile(
    data.template || '',
    data.templateType || 'mustache'
  );

  var widget = new cdb.geo.ui.Share({
    model: new cdb.core.Model(options),
    vis: vis,
    map: vis.map,
    template: template
  });

  widget.createDialog();

  return widget.render();

});

// search content
cdb.vis.Overlay.register('search', function(data, vis) {

  var options = data.options;

  //if (!options.display) return;

  var template = cdb.core.Template.compile(
    data.template || '\
      <form>\
        \
        <input type="text" class="text" value="" />\
        <input type="submit" class="submit" value="" />\
      </form>\
    ',
    data.templateType || 'mustache'
  );

  var search = new cdb.geo.ui.Search({
    template: template,
    model: vis.map
  });

  return search.render();

});

// tooltip
cdb.vis.Overlay.register('tooltip', function(data, vis) {
  var layer;
  if (!data.layer) {
    var layers = vis.getLayers();
    if(layers.length > 1) {
      layer = layers[1];
    }
    data.layer = layer;
  }

  if (!data.layer) {
    throw new Error("layer is null");
  }

  data.layer.setInteraction(true);
  var tooltip = new cdb.geo.ui.Tooltip(data);
  return tooltip;

});

cdb.vis.Overlay.register('infobox', function(data, vis) {
  var layer;
  var layers = vis.getLayers();
  if (!data.layer) {
    if(layers.length > 1) {
      layer = layers[1];
    }
    data.layer = layer;
  }
  if(!data.layer) {
    throw new Error("layer is null");
  }
  data.layer.setInteraction(true);
  var infobox = new cdb.geo.ui.InfoBox(data);
  return infobox;

});

})();
<commit_msg>fixes name related bug that prevented showing the mobile overlays<commit_after>(function() {

cdb.vis.Overlay.register('logo', function(data, vis) {

});

cdb.vis.Overlay.register('mobile', function(data, vis) {

  var template = cdb.core.Template.compile(
    data.template || '\

\

\

\
        \
        \

\

\

\

\

\

\

\

\

\

\

\

\
    \

\
    ',
    data.templateType || 'mustache'
  );

  var mobile = new cdb.geo.ui.Mobile({
    template: template,
    mapView: vis.mapView,
    overlays: data.overlays,
    layerView: data.layerView,
    visibility_options: data.options,
    torqueLayer: data.torqueLayer,
    map: data.map
  });

  return mobile.render();
});

cdb.vis.Overlay.register('image', function(data, vis) {

  var options = data.options;

  var isDevice = options.device == "mobile" ? true : false;

  if (vis.mobile_enabled !== isDevice) return;

  var template = cdb.core.Template.compile(
    data.template || '\

\

{{{ content }}}
\

',
    data.templateType || 'mustache'
  );

  var widget = new cdb.geo.ui.Image({
    model: new cdb.core.Model(options),
    template: template
  });

  return widget.render();

});

cdb.vis.Overlay.register('text', function(data, vis) {

  var options = data.options;

  var isDevice = options.device == "mobile" ? true : false;

  if (vis.mobile_enabled !== isDevice) return;

  var template = cdb.core.Template.compile(
    data.template || '\

\

{{{ text }}}
\

',
    data.templateType || 'mustache'
  );

  var widget = new cdb.geo.ui.Text({
    model: new cdb.core.Model(options),
    template: template,
    className: "cartodb-overlay overlay-text " + options.device
  });

  return widget.render();

});

cdb.vis.Overlay.register('zoom_info', function(data, vis) {
  //console.log("placeholder for the zoom_info overlay");
});

cdb.vis.Overlay.register('header', function(data, vis) {

  if (vis.mobile_enabled) return;

  var options = data.options;

  var template = cdb.core.Template.compile(
    data.template || '\

\

{{{ title }}}
\

{{{ description }}}
\

',
    data.templateType || 'mustache'
  );

  var widget = new cdb.geo.ui.Header({
    model: new cdb.core.Model(options),
    template: template
  });

  return widget.render();

});

// map zoom control
cdb.vis.Overlay.register('zoom', function(data, vis) {

  if(!data.template) {
    vis.trigger('error', 'zoom template is empty')
    return;
  }

  var zoom = new cdb.geo.ui.Zoom({
    model: data.map,
    template: cdb.core.Template.compile(data.template)
  });

  return zoom.render();

});

// Tiles loader
cdb.vis.Overlay.register('loader', function(data) {

  var tilesLoader = new cdb.geo.ui.TilesLoader({
    template: cdb.core.Template.compile(data.template)
  });

  return tilesLoader.render();
});

cdb.vis.Overlay.register('time_slider', function(data, viz) {
  var slider = new cdb.geo.ui.TimeSlider(data);
  return slider.render();
});

// Header to show informtion (title and description)
cdb.vis.Overlay.register('_header', function(data, vis) {
  var MAX_SHORT_DESCRIPTION_LENGTH = 100;

  // Add the complete url for facebook and twitter
  if (location.href) {
    data.share_url = encodeURIComponent(location.href);
  } else {
    data.share_url = data.url;
  }

  var template = cdb.core.Template.compile(
    data.template || "\
      {{#title}}\

\
          {{#url}}\
            {{title}}\
          {{/url}}\
          {{^url}}\
            {{title}}\
          {{/url}}\

\
      {{/title}}\
      {{#description}}
{{{description}}}
{{/description}}\
      {{#mobile_shareable}}\

\
          F\
          T\

\
      {{/mobile_shareable}}\
    ",
    data.templateType || 'mustache'
  );

  function truncate(s, length) {
    return s.substr(0, length-1) + (s.length > length ? '…' : '');
  }

  var title       = data.map.get('title');
  var description = data.map.get('description');

  var facebook_title = title + ": " + description;
  var twitter_title;

  if (title && description) {
    twitter_title = truncate(title + ": " + description, 112) + " %23map "
  } else if (title) {
    twitter_title = truncate(title, 112) + " %23map"
  } else if (description){
    twitter_title = truncate(description, 112) + " %23map"
  } else {
    twitter_title = "%23map"
  }

  var shareable = (data.shareable == "false" || !data.shareable) ? null : data.shareable;
  var mobile_shareable = shareable;

  mobile_shareable = mobile_shareable && (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));

  var header = new cdb.geo.ui.Header({
    title: title,
    description: description,
    facebook_title: facebook_title,
    twitter_title: twitter_title,
    url: data.url,
    share_url: data.share_url,
    mobile_shareable: mobile_shareable,
    shareable: shareable && !mobile_shareable,
    template: template
  });

  return header.render();
});

// infowindow
cdb.vis.Overlay.register('infowindow', function(data, vis) {

  if (_.size(data.fields) == 0) {
    return null;
  }

  var infowindowModel = new cdb.geo.ui.InfowindowModel({
    template: data.template,
    alternative_names: data.alternative_names,
    fields: data.fields,
    template_name: data.template_name
  });

  var templateType = data.templateType || 'mustache';

  var infowindow = new cdb.geo.ui.Infowindow({
     model: infowindowModel,
     mapView: vis.mapView,
     template: new cdb.core.Template({ template: data.template, type: templateType}).asFunction()
  });

  return infowindow;
});

// layer_selector
cdb.vis.Overlay.register('layer_selector', function(data, vis) {

  var options = data.options;
  //if (!options.display) return;

  var template = cdb.core.Template.compile(
    data.template || '\
      Visible layers

\
      ',
    data.templateType || 'underscore'
  );

  var dropdown_template = cdb.core.Template.compile(
    data.template || '\

\
      ',
    data.templateType || 'underscore'
  );

  var layerSelector = new cdb.geo.ui.LayerSelector({
    model: new cdb.core.Model(options),
    mapView: vis.mapView,
    template: template,
    dropdown_template: dropdown_template,
    layer_names: data.layer_names
  });

  if (vis.legends) {

    layerSelector.bind('change:visible', function(visible, order, layer) {

      if (layer.get('type') === 'torque') {

        var timeSlider = vis.getOverlay('time_slider');

        if (timeSlider) {
          timeSlider[visible ? 'show': 'hide']();
        }

      }

      if (layer.get('type') === 'layergroup' || layer.get('type') === 'torque') {

        var legend = vis.legends && vis.legends.getLegendByIndex(order);

        if (legend) {
          legend[visible ? 'show': 'hide']();
        }

      }

    });
  }

  return layerSelector.render();

});

// fullscreen
cdb.vis.Overlay.register('fullscreen', function(data, vis) {

  var options = data.options;

  options.allowWheelOnFullscreen = false;

  var template = cdb.core.Template.compile(
    data.template || '',
    data.templateType || 'mustache'
  );

  var fullscreen = new cdb.ui.common.FullScreen({
    doc: "#map > div",
    model: new cdb.core.Model(options),
    mapView: vis.mapView,
    template: template
  });

  return fullscreen.render();

});

// share content
cdb.vis.Overlay.register('share', function(data, vis) {

  var options = data.options;

  var template = cdb.core.Template.compile(
    data.template || '',
    data.templateType || 'mustache'
  );

  var widget = new cdb.geo.ui.Share({
    model: new cdb.core.Model(options),
    vis: vis,
    map: vis.map,
    template: template
  });

  widget.createDialog();

  return widget.render();

});

// search content
cdb.vis.Overlay.register('search', function(data, vis) {

  var options = data.options;

  //if (!options.display) return;

  var template = cdb.core.Template.compile(
    data.template || '\
      <form>\
        \
        <input type="text" class="text" value="" />\
        <input type="submit" class="submit" value="" />\
      </form>\
    ',
    data.templateType || 'mustache'
  );

  var search = new cdb.geo.ui.Search({
    template: template,
    model: vis.map
  });

  return search.render();

});

// tooltip
cdb.vis.Overlay.register('tooltip', function(data, vis) {
  var layer;
  if (!data.layer) {
    var layers = vis.getLayers();
    if(layers.length > 1) {
      layer = layers[1];
    }
    data.layer = layer;
  }

  if (!data.layer) {
    throw new Error("layer is null");
  }

  data.layer.setInteraction(true);
  var tooltip = new cdb.geo.ui.Tooltip(data);
  return tooltip;

});

cdb.vis.Overlay.register('infobox', function(data, vis) {
  var layer;
  var layers = vis.getLayers();
  if (!data.layer) {
    if(layers.length > 1) {
      layer = layers[1];
    }
    data.layer = layer;
  }
  if(!data.layer) {
    throw new Error("layer is null");
  }
  data.layer.setInteraction(true);
  var infobox = new cdb.geo.ui.InfoBox(data);
  return infobox;

});

})();
<|endoftext|>


<commit_before>class Zoom {
    constructor(zoomUpperLimit) {
        this.zoomUpperLimit = zoomUpperLimit;
        this.img = document.getElementById("image");
        this.imgContainer = document.getElementById("image-container");
        this.iconFit = document.getElementById("icon-fit");
        this.iconExpand = document.getElementById("icon-expand");
        this.ctrlBar = document.getElementById("ctrl-container");
        this.status = undefined;
        let resetZoom = () => {
            this.reset();
        }
        let onclick = e => {
            if (e.button == 0) {
                let scale = 1 + (e.altKey ? -0.2 : 0.2);
                this.smoothZomm(this.status.zoom * scale, this.getMousePointer(e.clientX, e.clientY));
            } else if (e.button == 1) {
                if (this.iconFit.style.display == "")
                    resetZoom();
                else
                    this.smoothZomm(100, this.getMousePointer(e.clientX, e.clientY));
            }
        }
        let ondblclick = e => {
            if (this.iconFit.style.display == "")
                resetZoom();
            else
                this.smoothZomm(100, this.getMousePointer(e.clientX, e.clientY));
        }
        addClickEvent(this.imgContainer, onclick);
        document.getElementById("btnZoomIn").addEventListener("click", () => {
            this.smoothZomm(this.status.zoom * 1.2, this.getWindowCenterMousePointer());
        });
        document.getElementById("btnZoomOut").addEventListener("click", () => {
            this.smoothZomm(this.status.zoom / 1.2, this.getWindowCenterMousePointer());
        });
        document.getElementById("btnZoomToggle").addEventListener("click", () => {
            if (this.iconFit.style.display == "") {
                resetZoom();
            } else
                this.smoothZomm(100, this.getWindowCenterMousePointer());
        });
        document.body.addEventListener("mousewheel", e => {
            // console.log(event.ctrlKey, event.wheelDeltaX, event.wheelDeltaY);
            // scroll to zoom, or ctrl key pressed scroll
            if (event.ctrlKey) {
                // ctrlKey == true: pinch
                let delta = event.ctrlKey ? event.wheelDelta / 60 : event.wheelDelta / 12;
                let mouseAt = this.getMousePointer(e.clientX, e.clientY);
                if (this.zoomUpperLimit) {
                    this.pointZoom(this.status.zoom + delta, mouseAt);
                } else {
                    // zoom level increase / decrease by 30% for each wheel scroll
                    this.pointZoom(this.status.zoom * (delta / 50 + 1), mouseAt);
                }
                if (event.preventDefault) event.preventDefault();
                return false;
            }
        });
        window.addEventListener("mouseup", () => {
            this.status.x = document.body.scrollLeft;
            this.status.y = document.body.scrollTop;
            saveStatus();
        });
        window.addEventListener("resize", () => {
            this.reset();
        });
    }
    reset() {
        this.pointZoom(0);
    }
    smoothZomm(to, mouseAt, callback, ...args) {
        let winWidth = window.innerWidth;
        let minWidth = winWidth < this.img.naturalWidth ? winWidth : this.img.naturalWidth;
        let minZoom = minWidth / this.img.naturalWidth * 100;
        if (to < minZoom) to = minZoom;
        let from = this.status.zoom;
        if (from == to) return;
        const interval = 10;
        const level = 10;
        const delta = (to - from) / level;
        for (let i = 1; i <= level; i++) {
            setTimeout(() => {
                // console.log("before zoom:", this.img.x, this.img.y, document.body.scrollLeft, document.body.scrollTop);
                if (this.pointZoom(from + delta * i, mouseAt) && callback) callback(...args);
            }, interval * i);
        }
    }
    rectZoom(start, end) {
        let winWidth = window.innerWidth;
        let winHeight = window.innerHeight;
        let minWidth = winWidth < this.img.naturalWidth ? winWidth : this.img.naturalWidth;
        let minZoom = minWidth / this.img.naturalWidth * 100;
        const maxZoom = 100;

        let status = this.getRectZoomStatus(start, end);
        if (this.zoomUpperLimit && status.zoom > maxZoom) status.zoom = maxZoom;
        if (status.zoom < minZoom) status.zoom = minZoom;
        status.isReset = false;

        this.applyStatus(status);
    }
    pointZoom(zoom, point) {
        let isReset = false;
        let winWidth = window.innerWidth;
        let winHeight = window.innerHeight;
        let minWidth = winWidth < this.img.naturalWidth ? winWidth : this.img.naturalWidth;
        let minZoom = minWidth / this.img.naturalWidth * 100;
        const maxZoom = 100;

        if (!point) point = this.getWindowCenterMousePointer();
        if (this.zoomUpperLimit && zoom > maxZoom) zoom = maxZoom;
        if (zoom < minZoom + 0.1) {
            // if zoom <= minZoom, reset
            isReset = true;
            zoom = minZoom;
            let imgHeight = this.img.naturalHeight * zoom / 100;
            point = this.getWindowCenterMousePointer();
            point.imageX = 0.5;
            if (imgHeight < window.innerHeight) {
                point.imageY = 0.5;
            } else {
                point.y = 0;
                point.imageY = 0;
            }
        }

        let status = this.getPointZoomStatus(zoom, point);
        status.isReset = isReset;
        this.applyStatus(status);
        return true;
    }
    getRectZoomStatus(start, end) {
        let startPoint = this.getMousePointer(start.x, start.y);
        let endPoint = this.getMousePointer(end.x, end.y);
        let imgSelCenterX = startPoint.imageX + (endPoint.imageX - startPoint.imageX) / 2;
        let imgSelCenterY = startPoint.imageY + (endPoint.imageY - startPoint.imageY) / 2;
        let imgX = (endPoint.imageX - startPoint.imageX) * this.img.clientWidth;
        let imgY = (endPoint.imageY - startPoint.imageY) * this.img.clientHeight;
        let scaleX = window.innerWidth / imgX;
        let scaleY = window.innerHeight / imgY;
        let scale = Math.min(scaleX, scaleY);
        let zoom = this.status.zoom * scale;
        let point = this.getWindowCenterMousePointer();
        point.imageX = imgSelCenterX;
        point.imageY = imgSelCenterY;
        return this.getPointZoomStatus(zoom, point);
    }
    getPointZoomStatus(zoom, point) {
        let imgWidth = this.img.naturalWidth * zoom / 100;
        let imgHeight = this.img.naturalHeight * zoom / 100;
        let ctrlBarSapceY = window.innerHeight - this.ctrlBar.offsetTop;

        let blankRight = Math.floor(window.innerWidth - imgWidth * (1 - point.imageX) - point.x);
        let blankLeft = Math.floor(point.x - imgWidth * point.imageX);
        let blankBottom = Math.floor(window.innerHeight - imgHeight * (1 - point.imageY) - point.y);
        let blankTop = Math.floor(point.y - imgHeight * point.imageY);
        blankRight = blankRight < 0 ? 0 : blankRight;
        blankLeft = blankLeft < 0 ? 0 : blankLeft;
        blankBottom = blankBottom < ctrlBarSapceY ? ctrlBarSapceY : blankBottom;
        blankTop = blankTop < 0 ? 0 : blankTop;

        let status = {};
        status.blankTop = blankTop;
        status.blankRight = blankRight;
        status.blankBottom = blankBottom;
        status.blankLeft = blankLeft;
        status.x = Math.floor(imgWidth * point.imageX + status.blankLeft - point.x);
        status.y = Math.floor(imgHeight * point.imageY + status.blankTop - point.y);
        status.zoom = zoom
        return status;
    }
    applyStatus(status) {
        // console.log("apply status:", status);
        let imgWidth = this.img.naturalWidth * status.zoom / 100;
        let imgHeight = this.img.naturalHeight * status.zoom / 100;
        this.img.style.width = imgWidth + 'px';
        this.img.style.marginLeft = status.blankLeft + 'px';
        this.img.style.marginRight = status.blankRight + 'px';
        this.img.style.marginTop = status.blankTop + 'px';
        this.img.style.marginBottom = status.blankBottom + 'px';
        document.body.scrollLeft = status.x;
        document.body.scrollTop = status.y;
        this.status = status;
        this.setToggleIcon();
        saveStatus();
    }
    getMousePointer(x, y) {
        return {
            x: x,
            y: y,
            imageX: (x + document.body.scrollLeft - this.img.x) / this.img.clientWidth,
            imageY: (y + document.body.scrollTop - this.img.y) / this.img.clientHeight,
        }
    }
    getWindowCenterMousePointer() {
        let x = window.innerWidth / 2;
        let y = window.innerHeight / 2;
        return this.getMousePointer(x, y);
    }
    setToggleIcon() {
        if (!this.status.isReset) {
            this.iconFit.style.display = "";
            this.iconExpand.style.display = "none";
        } else {
            this.iconFit.style.display = "none";
            this.iconExpand.style.display = "";
        }
    }
}<commit_msg>improve saving scroll status<commit_after>class Zoom {
    constructor(zoomUpperLimit) {
        this.zoomUpperLimit = zoomUpperLimit;
        this.img = document.getElementById("image");
        this.imgContainer = document.getElementById("image-container");
        this.iconFit = document.getElementById("icon-fit");
        this.iconExpand = document.getElementById("icon-expand");
        this.ctrlBar = document.getElementById("ctrl-container");
        this.status = undefined;
        let resetZoom = () => {
            this.reset();
        }
        let onclick = e => {
            if (e.button == 0) {
                let scale = 1 + (e.altKey ? -0.2 : 0.2);
                this.smoothZomm(this.status.zoom * scale, this.getMousePointer(e.clientX, e.clientY));
            } else if (e.button == 1) {
                if (this.iconFit.style.display == "")
                    resetZoom();
                else
                    this.smoothZomm(100, this.getMousePointer(e.clientX, e.clientY));
            }
        }
        let ondblclick = e => {
            if (this.iconFit.style.display == "")
                resetZoom();
            else
                this.smoothZomm(100, this.getMousePointer(e.clientX, e.clientY));
        }
        addClickEvent(this.imgContainer, onclick);
        document.getElementById("btnZoomIn").addEventListener("click", () => {
            this.smoothZomm(this.status.zoom * 1.2, this.getWindowCenterMousePointer());
        });
        document.getElementById("btnZoomOut").addEventListener("click", () => {
            this.smoothZomm(this.status.zoom / 1.2, this.getWindowCenterMousePointer());
        });
        document.getElementById("btnZoomToggle").addEventListener("click", () => {
            if (this.iconFit.style.display == "") {
                resetZoom();
            } else
                this.smoothZomm(100, this.getWindowCenterMousePointer());
        });
        document.body.addEventListener("mousewheel", e => {
            // console.log(event.ctrlKey, event.wheelDeltaX, event.wheelDeltaY);
            // scroll to zoom, or ctrl key pressed scroll
            if (event.ctrlKey) {
                // ctrlKey == true: pinch
                let delta = event.ctrlKey ? event.wheelDelta / 60 : event.wheelDelta / 12;
                let mouseAt = this.getMousePointer(e.clientX, e.clientY);
                if (this.zoomUpperLimit) {
                    this.pointZoom(this.status.zoom + delta, mouseAt);
                } else {
                    // zoom level increase / decrease by 30% for each wheel scroll
                    this.pointZoom(this.status.zoom * (delta / 50 + 1), mouseAt);
                }
                if (event.preventDefault) event.preventDefault();
                return false;
            }
        });
        window.addEventListener("scroll", () => {
            this.status.x = document.body.scrollLeft;
            this.status.y = document.body.scrollTop;
            saveStatus();
        });
        window.addEventListener("resize", () => {
            this.reset();
        });
    }
    reset() {
        this.pointZoom(0);
    }
    smoothZomm(to, mouseAt, callback, ...args) {
        let winWidth = window.innerWidth;
        let minWidth = winWidth < this.img.naturalWidth ? winWidth : this.img.naturalWidth;
        let minZoom = minWidth / this.img.naturalWidth * 100;
        if (to < minZoom) to = minZoom;
        let from = this.status.zoom;
        if (from == to) return;
        const interval = 10;
        const level = 10;
        const delta = (to - from) / level;
        for (let i = 1; i <= level; i++) {
            setTimeout(() => {
                // console.log("before zoom:", this.img.x, this.img.y, document.body.scrollLeft, document.body.scrollTop);
                if (this.pointZoom(from + delta * i, mouseAt) && callback) callback(...args);
            }, interval * i);
        }
    }
    rectZoom(start, end) {
        let winWidth = window.innerWidth;
        let winHeight = window.innerHeight;
        let minWidth = winWidth < this.img.naturalWidth ? winWidth : this.img.naturalWidth;
        let minZoom = minWidth / this.img.naturalWidth * 100;
        const maxZoom = 100;

        let status = this.getRectZoomStatus(start, end);
        if (this.zoomUpperLimit && status.zoom > maxZoom) status.zoom = maxZoom;
        if (status.zoom < minZoom) status.zoom = minZoom;
        status.isReset = false;

        this.applyStatus(status);
    }
    pointZoom(zoom, point) {
        let isReset = false;
        let winWidth = window.innerWidth;
        let winHeight = window.innerHeight;
        let minWidth = winWidth < this.img.naturalWidth ? winWidth : this.img.naturalWidth;
        let minZoom = minWidth / this.img.naturalWidth * 100;
        const maxZoom = 100;

        if (!point) point = this.getWindowCenterMousePointer();
        if (this.zoomUpperLimit && zoom > maxZoom) zoom = maxZoom;
        if (zoom < minZoom + 0.1) {
            // if zoom <= minZoom, reset
            isReset = true;
            zoom = minZoom;
            let imgHeight = this.img.naturalHeight * zoom / 100;
            point = this.getWindowCenterMousePointer();
            point.imageX = 0.5;
            if (imgHeight < window.innerHeight) {
                point.imageY = 0.5;
            } else {
                point.y = 0;
                point.imageY = 0;
            }
        }

        let status = this.getPointZoomStatus(zoom, point);
        status.isReset = isReset;
        this.applyStatus(status);
        return true;
    }
    getRectZoomStatus(start, end) {
        let startPoint = this.getMousePointer(start.x, start.y);
        let endPoint = this.getMousePointer(end.x, end.y);
        let imgSelCenterX = startPoint.imageX + (endPoint.imageX - startPoint.imageX) / 2;
        let imgSelCenterY = startPoint.imageY + (endPoint.imageY - startPoint.imageY) / 2;
        let imgX = (endPoint.imageX - startPoint.imageX) * this.img.clientWidth;
        let imgY = (endPoint.imageY - startPoint.imageY) * this.img.clientHeight;
        let scaleX = window.innerWidth / imgX;
        let scaleY = window.innerHeight / imgY;
        let scale = Math.min(scaleX, scaleY);
        let zoom = this.status.zoom * scale;
        let point = this.getWindowCenterMousePointer();
        point.imageX = imgSelCenterX;
        point.imageY = imgSelCenterY;
        return this.getPointZoomStatus(zoom, point);
    }
    getPointZoomStatus(zoom, point) {
        let imgWidth = this.img.naturalWidth * zoom / 100;
        let imgHeight = this.img.naturalHeight * zoom / 100;
        let ctrlBarSapceY = window.innerHeight - this.ctrlBar.offsetTop;

        let blankRight = Math.floor(window.innerWidth - imgWidth * (1 - point.imageX) - point.x);
        let blankLeft = Math.floor(point.x - imgWidth * point.imageX);
        let blankBottom = Math.floor(window.innerHeight - imgHeight * (1 - point.imageY) - point.y);
        let blankTop = Math.floor(point.y - imgHeight * point.imageY);
        blankRight = blankRight < 0 ? 0 : blankRight;
        blankLeft = blankLeft < 0 ? 0 : blankLeft;
        blankBottom = blankBottom < ctrlBarSapceY ? ctrlBarSapceY : blankBottom;
        blankTop = blankTop < 0 ? 0 : blankTop;

        let status = {};
        status.blankTop = blankTop;
        status.blankRight = blankRight;
        status.blankBottom = blankBottom;
        status.blankLeft = blankLeft;
        status.x = Math.floor(imgWidth * point.imageX + status.blankLeft - point.x);
        status.y = Math.floor(imgHeight * point.imageY + status.blankTop - point.y);
        status.zoom = zoom
        return status;
    }
    applyStatus(status) {
        // console.log("apply status:", status);
        let imgWidth = this.img.naturalWidth * status.zoom / 100;
        let imgHeight = this.img.naturalHeight * status.zoom / 100;
        this.img.style.width = imgWidth + 'px';
        this.img.style.marginLeft = status.blankLeft + 'px';
        this.img.style.marginRight = status.blankRight + 'px';
        this.img.style.marginTop = status.blankTop + 'px';
        this.img.style.marginBottom = status.blankBottom + 'px';
        document.body.scrollLeft = status.x;
        document.body.scrollTop = status.y;
        this.status = status;
        this.setToggleIcon();
        saveStatus();
    }
    getMousePointer(x, y) {
        return {
            x: x,
            y: y,
            imageX: (x + document.body.scrollLeft - this.img.x) / this.img.clientWidth,
            imageY: (y + document.body.scrollTop - this.img.y) / this.img.clientHeight,
        }
    }
    getWindowCenterMousePointer() {
        let x = window.innerWidth / 2;
        let y = window.innerHeight / 2;
        return this.getMousePointer(x, y);
    }
    setToggleIcon() {
        if (!this.status.isReset) {
            this.iconFit.style.display = "";
            this.iconExpand.style.display = "none";
        } else {
            this.iconFit.style.display = "none";
            this.iconExpand.style.display = "";
        }
    }
}<|endoftext|>


<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

/**
 * APIs for accessibility VoiceReadout
 * @namespace webOS.voicereadout
 */

webOS.voicereadout = {

	/**
	 * Read alert text when accessibility VoiceReadout enabled.
	 * @param {string} s - String to voice readout.
	 */
	readAlert: function(s) {
		if(webOS && webOS.platform && webOS.platform.watch) {

			var locale, speechRate;

			/**
			* Check VoiceReadOut talkback is enabled or not.
			*
			* @private
			*/
			var checkVoiceReadOut = function() {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"category":"VoiceReadOut"},
					onSuccess: function(inResponse) {
						if (inResponse && inResponse.settings.talkbackEnable) {
							getLocaleInfo();
						}
					},
					onFailure: function(inError) {
						console.error("Failed to get system VoiceReadOut settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Get current locale info.
			*
			* @private
			*/
			var getLocaleInfo = function() {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"keys": ["localeInfo"]},
					onSuccess: function(inResponse) {
						locale = inResponse.settings.localeInfo.locales.TTS;
						getSpeechRate();
					},
					onFailure: function(inError) {
						console.error("Failed to get system localeInfo settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Get current speech rate.
			*
			* @private
			*/
			var getSpeechRate = function() {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"category":"option", "key":"ttsSpeechRate"},
					onSuccess: function(inResponse) {
						speechRate = Number(inResponse.settings.ttsSpeechRate);
						readAlertMessage();
					},
					onFailure: function(inError) {
						console.error("Failed to get system speechRate settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Read alert message using TTS api.
			*
			* @private
			*/
			var readAlertMessage = function() {
				webOS.service.request("luna://com.lge.service.tts", {
					method: "speak",
					parameters: {"locale":locale, "text":s, "speechRate":speechRate}
				});
			}

			checkVoiceReadOut();

		}
	}
};
<commit_msg>ENYO-1447: [W2_Accessibility] It doesn't support speech rate when reading title<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

/**
 * APIs for accessibility VoiceReadout
 * @namespace webOS.voicereadout
 */

webOS.voicereadout = {

	/**
	 * Read alert text when accessibility VoiceReadout enabled.
	 * @param {string} s - String to voice readout.
	 */
	readAlert: function(s) {
		if(webOS && webOS.platform && webOS.platform.watch) {

			var locale, speechRate;

			/**
			* Check VoiceReadOut talkback is enabled or not.
			*
			* @private
			*/
			var checkVoiceReadOut = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"category":"VoiceReadOut"},
					onSuccess: function(inResponse) {
						if (inResponse && inResponse.settings.talkbackEnable) {
							callback();
						}
					},
					onFailure: function(inError) {
						console.error("Failed to get system VoiceReadOut settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Get current locale info.
			*
			* @private
			*/
			var getLocaleInfo = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"keys": ["localeInfo"]},
					onSuccess: function(inResponse) {
						locale = inResponse.settings.localeInfo.locales.TTS;
						callback();
					},
					onFailure: function(inError) {
						console.error("Failed to get system localeInfo settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Get current speech rate.
			*
			* @private
			*/
			var getSpeechRate = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"category":"option", "key":"ttsSpeechRate"},
					onSuccess: function(inResponse) {
						speechRate = Number(inResponse.settings.ttsSpeechRate);
						callback();
					},
					onFailure: function(inError) {
						console.error("Failed to get system speechRate settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Read alert message using TTS api.
			*
			* @private
			*/
			var readAlertMessage = function() {
				webOS.service.request("luna://com.lge.service.tts", {
					method: "speak",
					parameters: {"locale":locale, "text":s, "speechRate":speechRate}
				});
			}

			checkVoiceReadOut(function() {
				getLocaleInfo(function() {
					getSpeechRate(readAlertMessage);
				});
			});
		}
	}
};
<|endoftext|>


<commit_before>const Koa = require('koa')
const app = new Koa()
const router = require('koa-router')()
const views = require('koa-views')
const co = require('co')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')()
const serve = require('koa-static')
const path = require('path')
const log4js = require('koa-log4')
const logger = log4js.getLogger('app')

const index = require('./routes/index')
const users = require('./routes/users')

// middlewares
app.use(bodyparser)
app.use(json())
app.use(log4js.koaLogger(log4js.getLogger('http'), { level: 'auto' }))
app.use(serve(path.join(__dirname, 'public')))

// handle error
onerror(app)

// setup view
app.use(views(path.join(__dirname, 'views'), {
  extension: '{views}'
}))

// logger
// app.use(async (ctx, next) => {
//   const start = new Date()
//   await next()
//   const ms = new Date() - start
//   logger.info(`${ctx.method} ${ctx.url} - ${ms}ms`)
// })
app.use(co.wrap(function * (ctx, next) {
  const start = new Date()
  yield next()
  const ms = new Date() - start
  logger.info(`${ctx.method} ${ctx.url} - ${ms}ms`)
}))

// routes definition
router.use('/', index.routes(), index.allowedMethods())
router.use('/users', users.routes(), users.allowedMethods())

// mount root routes
app.use(router.routes())
	.use(router.allowedMethods())

// log error
app.on('error', function (err, ctx) {
  logger.error(err)
  logger.error('server error', err, ctx)
})

module.exports = app
<commit_msg>tiny fix<commit_after>const Koa = require('koa')
const app = new Koa()
const router = require('koa-router')()
const views = require('koa-views')
const co = require('co')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const serve = require('koa-static')
const path = require('path')
const log4js = require('koa-log4')
const logger = log4js.getLogger('app')

const index = require('./routes/index')
const users = require('./routes/users')

// middlewares
app.use(bodyparser())
app.use(json())
app.use(log4js.koaLogger(log4js.getLogger('http'), { level: 'auto' }))
app.use(serve(path.join(__dirname, 'public')))

// handle error
onerror(app)

// setup view
app.use(views(path.join(__dirname, 'views'), {
  extension: '{views}'
}))

// logger
// app.use(async (ctx, next) => {
//   const start = new Date()
//   await next()
//   const ms = new Date() - start
//   logger.info(`${ctx.method} ${ctx.url} - ${ms}ms`)
// })
app.use(co.wrap(function * (ctx, next) {
  const start = new Date()
  yield next()
  const ms = new Date() - start
  logger.info(`${ctx.method} ${ctx.url} - ${ms}ms`)
}))

// routes definition
router.use('/', index.routes(), index.allowedMethods())
router.use('/users', users.routes(), users.allowedMethods())

// mount root routes
app.use(router.routes())
	.use(router.allowedMethods())

// log error
app.on('error', function (err, ctx) {
  logger.error(err)
  logger.error('server error', err, ctx)
})

module.exports = app
<|endoftext|>


<commit_before>var expect = require('unexpected-sinon'),
    sinon = require('sinon'),
    passError = require('passerror'),
    _ = require('underscore'),
    glob = require('glob'),
    Path = require('path'),
    GitWrappedFs = require('../lib/GitWrappedFs');

describe('GitWrappedFs', function () {
    var pathToTestRepo = Path.resolve(__dirname, 'testrepo.git'),
        gitWrappedFs;

    describe('applied to the built-in fs module', function () {
        var fs;
        beforeEach(function () {
            fs = _.extend({}, require('fs'));
            Object.keys(fs).forEach(function (propertyName) {
                if (typeof fs[propertyName] === 'function') {
                    sinon.spy(fs, propertyName);
                }
            });
            gitWrappedFs = new GitWrappedFs(fs);
        });

        ['stat', 'lstat'].forEach(function (methodName) {
            describe('#' + methodName + '()', function () {
                it('should report /contents/ as a directory', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents'), passError(done, function (stats) {
                        expect(stats.isDirectory(), 'to be', true);
                        expect(stats.isFile(), 'to be', false);
                        done();
                    }));
                });

                it('should report /contents/branches/ as a directory', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents', 'branches'), passError(done, function (stats) {
                        expect(stats.isDirectory(), 'to be', true);
                        expect(stats.isFile(), 'to be', false);
                        done();
                    }));
                });

                it('should report /contents/branches/master/ as a directory', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents', 'branches', 'master'), passError(done, function (stats) {
                        expect(stats.isDirectory(), 'to be', true);
                        expect(stats.isFile(), 'to be', false);
                        done();
                    }));
                });

                it('should report /contents/commits/738876c70f4f5243a6672def4233911678ce38db/ as a directory', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents', 'commits', '738876c70f4f5243a6672def4233911678ce38db'), passError(done, function (stats) {
                        expect(stats.isDirectory(), 'to be', true);
                        expect(stats.isFile(), 'to be', false);
                        done();
                    }));
                });

                it('should return an ENOENT error for an unsupported entry in /contents/', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents', 'foo'), function (err) {
                        expect(err, 'to be an', Error);
                        done();
                    });
                });
            });
        });

        describe('#readdir()', function () {
            it('should include the /contents/ directory when applied to the .git folder', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', 'contents');
                    done();
                }));
            });

            it('should return the types of objects when applied to the virtual /contents/ directory', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo, 'contents'), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to equal', ['branches', 'tags', 'commits', 'index']);
                    done();
                }));
            });

            it('should list the branches when applied to the virtual /contents/branches/ directory', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo, 'contents', 'branches'), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', 'master');
                    done();
                }));
            });

            it('should list the tags when applied to the virtual /contents/tag/ directory', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo, 'contents', 'tags'), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', 'myTag');
                    done();
                }));
            });

            it('should list the commits when applied to the virtual /contents/commits/ directory', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo, 'contents', 'commits'), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', '91fe03e2a9f37e49ddc0cf1a1fd19ef44d9b7c4b');
                    done();
                }));
            });

            it('should work outside the .git repository', function (done) {
                gitWrappedFs.readdir(__dirname, passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', Path.basename(__filename));
                    done();
                }));
            });
        });

        describe('#readFile()', function () {
            it('should proxy a path outside a .git repository to the wrapped fs implementation', function (done) {
                gitWrappedFs.readFile(__filename, 'utf-8', passError(done, function (contents) {
                    expect(fs.readFile, 'was called once');
                    expect(fs.readFile, 'was always called with', __filename, 'utf-8');
                    expect(contents, 'to match', /GitWrappedFs/);
                    done();
                }));
            });

            it('should proxy a path inside a branch to the GitFakeFs', function (done) {
                gitWrappedFs.readFile(Path.resolve(pathToTestRepo, 'contents', 'branches', 'master', 'foo.txt'), 'utf-8', passError(done, function (contents) {
                    expect(fs.readFile, 'was not called');
                    expect(contents, 'to match', /This is the second revision of foo\.txt/);
                    done();
                }));
            });

            it('should proxy a path inside a commit to the GitFakeFs', function (done) {
                gitWrappedFs.readFile(Path.resolve(pathToTestRepo, 'contents', 'commits', '738876c70f4f5243a6672def4233911678ce38db', 'foo.txt'), 'utf-8', passError(done, function (contents) {
                    expect(fs.readFile, 'was not called');
                    expect(contents, 'to match', /This is the first revision of foo\.txt/);
                    done();
                }));
            });
        });
    });

    describe('patching the built-in fs module "in-place"', function () {
        var originalReadFile = require('fs').readFile;
        beforeEach(function () {
            GitWrappedFs.patchInPlace();
        });

        it('should replace the original readFile', function () {
            var readFile = require('fs').readFile;
            expect(readFile, 'to be a', Function);
            expect(readFile, 'not to be', originalReadFile);
        });

        describe('#readdir()', function () {
            it('should include the /contents/ directory when applied to the .git folder', function (done) {
                require('fs').readdir(pathToTestRepo, passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', 'contents');
                    done();
                }));
            });
        });

        it('should make the glob work on the contents directory', function (done) {
            require('glob')(Path.resolve(pathToTestRepo, 'contents/**/*', {mark: true}), passError(done, function (fileNames) {
                expect(fileNames, 'to be an array');
                expect(fileNames, 'to contain', Path.resolve(pathToTestRepo, 'contents', 'commits', '39c5c09d660b1bac8eb66898e88f72907ccbb223', 'foo.txt'));
                expect(fileNames, 'to contain', Path.resolve(pathToTestRepo, 'contents', 'tags', 'myTag', 'symlinkToSymlinkToNonExistentFile'));
                expect(fileNames, 'to contain', Path.resolve(pathToTestRepo, 'contents', 'branches', 'master', 'symlinkToSubdir/subsubdir/bar.txt'));
                done();
            }));
        });

        afterEach(function () {
            require('fs').unpatch();
            expect(require('fs').readFile, 'to be', originalReadFile);
        });
    });
});
<commit_msg>Pass the glob options to glob instead of Path.resolve, whoops.<commit_after>var expect = require('unexpected-sinon'),
    sinon = require('sinon'),
    passError = require('passerror'),
    _ = require('underscore'),
    glob = require('glob'),
    Path = require('path'),
    GitWrappedFs = require('../lib/GitWrappedFs');

describe('GitWrappedFs', function () {
    var pathToTestRepo = Path.resolve(__dirname, 'testrepo.git'),
        gitWrappedFs;

    describe('applied to the built-in fs module', function () {
        var fs;
        beforeEach(function () {
            fs = _.extend({}, require('fs'));
            Object.keys(fs).forEach(function (propertyName) {
                if (typeof fs[propertyName] === 'function') {
                    sinon.spy(fs, propertyName);
                }
            });
            gitWrappedFs = new GitWrappedFs(fs);
        });

        ['stat', 'lstat'].forEach(function (methodName) {
            describe('#' + methodName + '()', function () {
                it('should report /contents/ as a directory', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents'), passError(done, function (stats) {
                        expect(stats.isDirectory(), 'to be', true);
                        expect(stats.isFile(), 'to be', false);
                        done();
                    }));
                });

                it('should report /contents/branches/ as a directory', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents', 'branches'), passError(done, function (stats) {
                        expect(stats.isDirectory(), 'to be', true);
                        expect(stats.isFile(), 'to be', false);
                        done();
                    }));
                });

                it('should report /contents/branches/master/ as a directory', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents', 'branches', 'master'), passError(done, function (stats) {
                        expect(stats.isDirectory(), 'to be', true);
                        expect(stats.isFile(), 'to be', false);
                        done();
                    }));
                });

                it('should report /contents/commits/738876c70f4f5243a6672def4233911678ce38db/ as a directory', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents', 'commits', '738876c70f4f5243a6672def4233911678ce38db'), passError(done, function (stats) {
                        expect(stats.isDirectory(), 'to be', true);
                        expect(stats.isFile(), 'to be', false);
                        done();
                    }));
                });

                it('should return an ENOENT error for an unsupported entry in /contents/', function (done) {
                    gitWrappedFs.stat(Path.resolve(pathToTestRepo, 'contents', 'foo'), function (err) {
                        expect(err, 'to be an', Error);
                        done();
                    });
                });
            });
        });

        describe('#readdir()', function () {
            it('should include the /contents/ directory when applied to the .git folder', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', 'contents');
                    done();
                }));
            });

            it('should return the types of objects when applied to the virtual /contents/ directory', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo, 'contents'), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to equal', ['branches', 'tags', 'commits', 'index']);
                    done();
                }));
            });

            it('should list the branches when applied to the virtual /contents/branches/ directory', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo, 'contents', 'branches'), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', 'master');
                    done();
                }));
            });

            it('should list the tags when applied to the virtual /contents/tag/ directory', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo, 'contents', 'tags'), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', 'myTag');
                    done();
                }));
            });

            it('should list the commits when applied to the virtual /contents/commits/ directory', function (done) {
                gitWrappedFs.readdir(Path.resolve(pathToTestRepo, 'contents', 'commits'), passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', '91fe03e2a9f37e49ddc0cf1a1fd19ef44d9b7c4b');
                    done();
                }));
            });

            it('should work outside the .git repository', function (done) {
                gitWrappedFs.readdir(__dirname, passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', Path.basename(__filename));
                    done();
                }));
            });
        });

        describe('#readFile()', function () {
            it('should proxy a path outside a .git repository to the wrapped fs implementation', function (done) {
                gitWrappedFs.readFile(__filename, 'utf-8', passError(done, function (contents) {
                    expect(fs.readFile, 'was called once');
                    expect(fs.readFile, 'was always called with', __filename, 'utf-8');
                    expect(contents, 'to match', /GitWrappedFs/);
                    done();
                }));
            });

            it('should proxy a path inside a branch to the GitFakeFs', function (done) {
                gitWrappedFs.readFile(Path.resolve(pathToTestRepo, 'contents', 'branches', 'master', 'foo.txt'), 'utf-8', passError(done, function (contents) {
                    expect(fs.readFile, 'was not called');
                    expect(contents, 'to match', /This is the second revision of foo\.txt/);
                    done();
                }));
            });

            it('should proxy a path inside a commit to the GitFakeFs', function (done) {
                gitWrappedFs.readFile(Path.resolve(pathToTestRepo, 'contents', 'commits', '738876c70f4f5243a6672def4233911678ce38db', 'foo.txt'), 'utf-8', passError(done, function (contents) {
                    expect(fs.readFile, 'was not called');
                    expect(contents, 'to match', /This is the first revision of foo\.txt/);
                    done();
                }));
            });
        });
    });

    describe('patching the built-in fs module "in-place"', function () {
        var originalReadFile = require('fs').readFile;
        beforeEach(function () {
            GitWrappedFs.patchInPlace();
        });

        it('should replace the original readFile', function () {
            var readFile = require('fs').readFile;
            expect(readFile, 'to be a', Function);
            expect(readFile, 'not to be', originalReadFile);
        });

        describe('#readdir()', function () {
            it('should include the /contents/ directory when applied to the .git folder', function (done) {
                require('fs').readdir(pathToTestRepo, passError(done, function (entryNames) {
                    expect(entryNames, 'to be an array');
                    expect(entryNames, 'to contain', 'contents');
                    done();
                }));
            });
        });

        it('should make the glob work on the contents directory', function (done) {
            require('glob')(Path.resolve(pathToTestRepo, 'contents/**/*'), {mark: true}, passError(done, function (fileNames) {
                expect(fileNames, 'to be an array');
                expect(fileNames, 'to contain', Path.resolve(pathToTestRepo, 'contents', 'commits', '39c5c09d660b1bac8eb66898e88f72907ccbb223', 'foo.txt'));
                expect(fileNames, 'to contain', Path.resolve(pathToTestRepo, 'contents', 'tags', 'myTag', 'symlinkToSymlinkToNonExistentFile'));
                expect(fileNames, 'to contain', Path.resolve(pathToTestRepo, 'contents', 'branches', 'master', 'symlinkToSubdir/subsubdir/bar.txt'));
                done();
            }));
        });

        afterEach(function () {
            require('fs').unpatch();
            expect(require('fs').readFile, 'to be', originalReadFile);
        });
    });
});
<|endoftext|>


<commit_before>describe('injector', function(){
  var providers;
  var cache;
  var injector;
  var scope;

  beforeEach(function(){
    providers = extensionMap({}, 'providers');
    cache = {};
    scope = {};
    injector = createInjector(scope, providers, cache);
  });

  it("should return same instance from calling provider", function(){
    providers('text', function(){ return scope.name; });
    scope.name = 'abc';
    expect(injector('text')).toEqual('abc');
    expect(cache.text).toEqual('abc');
    scope.name = 'deleted';
    expect(injector('text')).toEqual('abc');
  });

  it("should call function", function(){
    providers('a', function(){return 1;});
    providers('b', function(){return 2;});
    var args;
    function fn(a, b, c, d) {
      args = [this, a, b, c, d];
    }
    fn.$inject = ['a', 'b'];
    injector.invoke({name:"this"}, fn, [3, 4]);
    expect(args).toEqual([{name:'this'}, 1, 2, 3, 4]);
  });

  it('should inject providers', function(){
    providers('a', function(){return this.mi = 'Mi';});
    providers('b', function(mi){return this.name = mi+'sko';}, {$inject:['a']});
    expect(injector('b')).toEqual('Misko');
    expect(scope).toEqual({mi:'Mi', name:'Misko'});
  });

  it('should resolve dependency graph and instantiate all services just once', function(){
    var log = [];

//            s1
//        /   |\
//       /    s2\
//      /  /  | \\
//     /s3 < s4 > s5
//    //
//   s6

    providers('s1', function(){ log.push('s1'); }, {$inject: ['s2', 's5', 's6']});
    providers('s2', function(){ log.push('s2'); }, {$inject: ['s3', 's4', 's5']});
    providers('s3', function(){ log.push('s3'); }, {$inject: ['s6']});
    providers('s4', function(){ log.push('s4'); }, {$inject: ['s3', 's5']});
    providers('s5', function(){ log.push('s5'); });
    providers('s6', function(){ log.push('s6'); });

    injector('s1');

    expect(log).toEqual(['s6', 's5', 's3', 's4', 's2', 's1']);
  });

  it('should provide usefull message if no provider', function(){
    expect(function(){
      injector('idontexist');
    }).toThrow("Unknown provider for 'idontexist'.");
  });

  it('should autostart eager services', function(){
    var log = '';
    providers('eager', function(){log += 'eager;'; return 'foo';}, {$eager: true});
    injector.eager();
    expect(log).toEqual('eager;');
    expect(injector('eager')).toBe('foo');
  });

  describe('annotation', function(){
    it('should return $inject', function(){
      function fn(){}
      fn.$inject = ['a'];
      expect(injectionArgs(fn)).toBe(fn.$inject);
      expect(injectionArgs(function(){})).toEqual([]);
      expect(injectionArgs(function (){})).toEqual([]);
      expect(injectionArgs(function  (){})).toEqual([]);
      expect(injectionArgs(function /* */ (){})).toEqual([]);
    });

    it('should create $inject', function(){
      // keep the multi-line to make sure we can handle it
      function $f_n0 /*
          */(
          $a, // x, <-- looks like an arg but it is a comment
          b_, /* z, <-- looks like an arg but it is a
                 multi-line comment
                 function (a, b){}
                 */
          _c,
          /* {some type} */ d){ extraParans();}
      expect(injectionArgs($f_n0)).toEqual(['$a', 'b_', '_c',  'd']);
      expect($f_n0.$inject).toEqual(['$a', 'b_', '_c',  'd']);
    });

    it('should handle no arg functions', function(){
      function $f_n0(){}
      expect(injectionArgs($f_n0)).toEqual([]);
      expect($f_n0.$inject).toEqual([]);
    });

    it('should handle args with both $ and _', function(){
      function $f_n0($a_){}
      expect(injectionArgs($f_n0)).toEqual(['$a_']);
      expect($f_n0.$inject).toEqual(['$a_']);
    });

    it('should throw on non function arg', function(){
      expect(function(){
        injectionArgs({});
      }).toThrow();
    });

  });

  describe('inject', function(){
    it('should inject names', function(){
      expect(angular.annotate('a', {}).$inject).toEqual(['a']);
      expect(angular.annotate('a', 'b', {}).$inject).toEqual(['a', 'b']);
    });

    it('should inject array', function(){
      expect(angular.annotate(['a'], {}).$inject).toEqual(['a']);
      expect(angular.annotate(['a', 'b'], {}).$inject).toEqual(['a', 'b']);
    });
  });
});
<commit_msg>fix:tests: replace angular.annotate with annotate<commit_after>describe('injector', function(){
  var providers;
  var cache;
  var injector;
  var scope;

  beforeEach(function(){
    providers = extensionMap({}, 'providers');
    cache = {};
    scope = {};
    injector = createInjector(scope, providers, cache);
  });

  it("should return same instance from calling provider", function(){
    providers('text', function(){ return scope.name; });
    scope.name = 'abc';
    expect(injector('text')).toEqual('abc');
    expect(cache.text).toEqual('abc');
    scope.name = 'deleted';
    expect(injector('text')).toEqual('abc');
  });

  it("should call function", function(){
    providers('a', function(){return 1;});
    providers('b', function(){return 2;});
    var args;
    function fn(a, b, c, d) {
      args = [this, a, b, c, d];
    }
    fn.$inject = ['a', 'b'];
    injector.invoke({name:"this"}, fn, [3, 4]);
    expect(args).toEqual([{name:'this'}, 1, 2, 3, 4]);
  });

  it('should inject providers', function(){
    providers('a', function(){return this.mi = 'Mi';});
    providers('b', function(mi){return this.name = mi+'sko';}, {$inject:['a']});
    expect(injector('b')).toEqual('Misko');
    expect(scope).toEqual({mi:'Mi', name:'Misko'});
  });

  it('should resolve dependency graph and instantiate all services just once', function(){
    var log = [];

//            s1
//        /   |\
//       /    s2\
//      /  /  | \\
//     /s3 < s4 > s5
//    //
//   s6

    providers('s1', function(){ log.push('s1'); }, {$inject: ['s2', 's5', 's6']});
    providers('s2', function(){ log.push('s2'); }, {$inject: ['s3', 's4', 's5']});
    providers('s3', function(){ log.push('s3'); }, {$inject: ['s6']});
    providers('s4', function(){ log.push('s4'); }, {$inject: ['s3', 's5']});
    providers('s5', function(){ log.push('s5'); });
    providers('s6', function(){ log.push('s6'); });

    injector('s1');

    expect(log).toEqual(['s6', 's5', 's3', 's4', 's2', 's1']);
  });

  it('should provide usefull message if no provider', function(){
    expect(function(){
      injector('idontexist');
    }).toThrow("Unknown provider for 'idontexist'.");
  });

  it('should autostart eager services', function(){
    var log = '';
    providers('eager', function(){log += 'eager;'; return 'foo';}, {$eager: true});
    injector.eager();
    expect(log).toEqual('eager;');
    expect(injector('eager')).toBe('foo');
  });

  describe('annotation', function(){
    it('should return $inject', function(){
      function fn(){}
      fn.$inject = ['a'];
      expect(injectionArgs(fn)).toBe(fn.$inject);
      expect(injectionArgs(function(){})).toEqual([]);
      expect(injectionArgs(function (){})).toEqual([]);
      expect(injectionArgs(function  (){})).toEqual([]);
      expect(injectionArgs(function /* */ (){})).toEqual([]);
    });

    it('should create $inject', function(){
      // keep the multi-line to make sure we can handle it
      function $f_n0 /*
          */(
          $a, // x, <-- looks like an arg but it is a comment
          b_, /* z, <-- looks like an arg but it is a
                 multi-line comment
                 function (a, b){}
                 */
          _c,
          /* {some type} */ d){ extraParans();}
      expect(injectionArgs($f_n0)).toEqual(['$a', 'b_', '_c',  'd']);
      expect($f_n0.$inject).toEqual(['$a', 'b_', '_c',  'd']);
    });

    it('should handle no arg functions', function(){
      function $f_n0(){}
      expect(injectionArgs($f_n0)).toEqual([]);
      expect($f_n0.$inject).toEqual([]);
    });

    it('should handle args with both $ and _', function(){
      function $f_n0($a_){}
      expect(injectionArgs($f_n0)).toEqual(['$a_']);
      expect($f_n0.$inject).toEqual(['$a_']);
    });

    it('should throw on non function arg', function(){
      expect(function(){
        injectionArgs({});
      }).toThrow();
    });

  });

  describe('inject', function(){
    it('should inject names', function(){
      expect(annotate('a', {}).$inject).toEqual(['a']);
      expect(annotate('a', 'b', {}).$inject).toEqual(['a', 'b']);
    });

    it('should inject array', function(){
      expect(annotate(['a'], {}).$inject).toEqual(['a']);
      expect(annotate(['a', 'b'], {}).$inject).toEqual(['a', 'b']);
    });
  });
});
<|endoftext|>


<commit_before>import browser from './browser';

const print = (elementId) =>
{
  const printElement = document.getElementById(elementId);

  if (!printElement)
  {
    return;
  }

  // Create a new iframe or embed element (IE prints blank pdf's if we use iframe)
  const iframeElement = document.createElement('iframe');

  // Hide iframe
  iframeElement.setAttribute('style', 'visibility: hidden; height: 0; width: 0; position: absolute;');

  // Append iframe element to document body
  document.getElementsByTagName('body')[0].appendChild(iframeElement);

  // Head: Add css
  const stylesheet = document.createElement('link');
  stylesheet.href = '/app.css';
  stylesheet.rel = 'stylesheet';
  stylesheet.type = 'text/css';
  iframeElement.contentWindow.document.head.appendChild(stylesheet);

  // Body: Fill target content
  iframeElement.contentWindow.document.body.innerHTML = printElement.innerHTML;
  iframeElement.contentWindow.document.body.setAttribute('id', 'printFrame');

  // Focus
  iframeElement.focus();

  stylesheet.onload = () =>
  {
    // If Edge or IE, try catch with execCommand
    if (browser.isEdge() || browser.isIE())
    {
      try
      {
        iframeElement.contentWindow.document.execCommand('print', false, null);
      }
      catch (e)
      {
        iframeElement.contentWindow.print();
      }
    }
    else
    {
      // Other browsers
      iframeElement.contentWindow.print();
    }

    // Remove DOM printableElement
    document.getElementsByTagName('body')[0].removeChild(iframeElement);
  };
};

export default print;
<commit_msg>fix(window/print): remove child latency<commit_after>import browser from './browser';

const print = (element, callback) =>
{
  let printElement;

  if (element instanceof HTMLElement)
  {
    printElement = element;
  }
  else
  {
    printElement = document.getElementById(element);

    if (!printElement)
    {
      return;
    }
  }

  // Create a new iframe or embed element (IE prints blank pdf's if we use iframe)
  const iframeElement = document.createElement('iframe');

  // Hide iframe
  iframeElement.setAttribute('style', 'visibility: hidden; height: 0; width: 0; position: absolute;');

  // Append iframe element to document body
  document.getElementsByTagName('body')[0].appendChild(iframeElement);

  // Head: Add css
  const stylesheet = document.createElement('link');
  stylesheet.href = '/app.css';
  stylesheet.rel = 'stylesheet';
  stylesheet.type = 'text/css';
  iframeElement.contentWindow.document.head.appendChild(stylesheet);

  // Body: Fill target content
  iframeElement.contentWindow.document.body.innerHTML = printElement.innerHTML;
  iframeElement.contentWindow.document.body.setAttribute('id', 'printFrame');
  iframeElement.contentWindow.document.body.setAttribute('style', 'background-color: white');

  // Focus
  iframeElement.focus();

  stylesheet.onload = () =>
  {
    // If Edge or IE, try catch with execCommand
    if (browser.isEdge() || browser.isIE())
    {
      try
      {
        iframeElement.contentWindow.document.execCommand('print', false, null);
      }
      catch (e)
      {
        iframeElement.contentWindow.print();
      }
    }
    else
    {
      // Other browsers
      iframeElement.contentWindow.print();
    }

    // Remove DOM printableElement
    setTimeout(
      () =>
      {
        document.getElementsByTagName('body')[0].removeChild(iframeElement);

        if (callback)
        {
          callback();
        }
      },
      1000,
    );
  };
};

export default print;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Class constructor for Ripple MDL component.
 * Implements MDL component design pattern defined at:
 * https://github.com/jasonmayes/mdl-component-design-pattern
 * @param {HTMLElement} element The element that will be upgraded.
 */
function MaterialRipple(element) {
  'use strict';

  this.element_ = element;

  // Initialize instance.
  this.init();
}

/**
 * Store constants in one place so they can be updated easily.
 * @enum {string | number}
 * @private
 */
MaterialRipple.prototype.Constant_ = {
  INITIAL_SCALE: 'scale(0.0001, 0.0001)',
  INITIAL_SIZE: '1px',
  INITIAL_OPACITY: '0.4',
  FINAL_OPACITY: '0',
  FINAL_SCALE: ''
};

/**
 * Store strings for class names defined by this component that are used in
 * JavaScript. This allows us to simply change it in one place should we
 * decide to modify at a later date.
 * @enum {string}
 * @private
 */
MaterialRipple.prototype.CssClasses_ = {
  RIPPLE_CENTER: 'mdl-ripple--center',
  RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  RIPPLE: 'mdl-ripple',
  IS_ANIMATING: 'is-animating',
  IS_VISIBLE: 'is-visible'
};

/**
 * Handle mouse / finger down on element.
 * @param {Event} event The event that fired.
 * @private
 */
MaterialRipple.prototype.downHandler_ = function(event) {
  'use strict';

  if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) {
    var rect = this.element_.getBoundingClientRect();
    this.boundHeight = rect.height;
    this.boundWidth = rect.width;
    this.rippleSize_ = Math.sqrt(rect.width * rect.width +
        rect.height * rect.height) * 2 + 2;
    this.rippleElement_.style.width = this.rippleSize_ + 'px';
    this.rippleElement_.style.height = this.rippleSize_ + 'px';
  }

  this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE);

  if (event.type === 'mousedown' && this.ignoringMouseDown_) {
    this.ignoringMouseDown_ = false;
  } else {
    if (event.type === 'touchstart') {
      this.ignoringMouseDown_ = true;
    }
    var frameCount = this.getFrameCount();
    if (frameCount > 0) {
      return;
    }
    this.setFrameCount(1);
    var bound = event.currentTarget.getBoundingClientRect();
    var x;
    var y;
    // Check if we are handling a keyboard click.
    if (event.clientX === 0 && event.clientY === 0) {
      x = Math.round(bound.width / 2);
      y = Math.round(bound.height / 2);
    } else {
      var clientX = event.clientX ? event.clientX : event.touches[0].clientX;
      var clientY = event.clientY ? event.clientY : event.touches[0].clientY;
      x = Math.round(clientX - bound.left);
      y = Math.round(clientY - bound.top);
    }
    this.setRippleXY(x, y);
    this.setRippleStyles(true);
    window.requestAnimationFrame(this.animFrameHandler.bind(this));
  }
};

/**
 * Handle mouse / finger up on element.
 * @param {Event} event The event that fired.
 * @private
 */
MaterialRipple.prototype.upHandler_ = function(event) {
  'use strict';

  // Don't fire for the artificial "mouseup" generated by a double-click.
  if (event && event.detail !== 2) {
    // Allow a repaint to occur before removing this class, so the animation
    // shows for tap events, which seem to trigger a mouseup too soon after
    // mousedown.
    window.setTimeout(function() {
      this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);
    }.bind(this), 0);
  }
};

/**
 * Initialize element.
 */
MaterialRipple.prototype.init = function() {
  'use strict';

  if (this.element_) {
    var recentering =
        this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);
    if (!this.element_.classList.contains(
        this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) {
      this.rippleElement_ = this.element_.querySelector('.' +
          this.CssClasses_.RIPPLE);
      this.frameCount_ = 0;
      this.rippleSize_ = 0;
      this.x_ = 0;
      this.y_ = 0;

      // Touch start produces a compat mouse down event, which would cause a
      // second ripples. To avoid that, we use this property to ignore the first
      // mouse down after a touch start.
      this.ignoringMouseDown_ = false;

      this.boundDownHandler = this.downHandler_.bind(this);
      this.element_.addEventListener('mousedown',
        this.boundDownHandler);
      this.element_.addEventListener('touchstart',
          this.boundDownHandler);

      this.boundUpHandler = this.upHandler_.bind(this);
      this.element_.addEventListener('mouseup', this.boundUpHandler);
      this.element_.addEventListener('mouseleave', this.boundUpHandler);
      this.element_.addEventListener('touchend', this.boundUpHandler);
      this.element_.addEventListener('blur', this.boundUpHandler);

      this.getFrameCount = function() {
        return this.frameCount_;
      };

      this.setFrameCount = function(fC) {
        this.frameCount_ = fC;
      };

      this.getRippleElement = function() {
        return this.rippleElement_;
      };

      this.setRippleXY = function(newX, newY) {
        this.x_ = newX;
        this.y_ = newY;
      };

      this.setRippleStyles = function(start) {
        if (this.rippleElement_ !== null) {
          var transformString;
          var scale;
          var size;
          var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)';

          if (start) {
            scale = this.Constant_.INITIAL_SCALE;
            size = this.Constant_.INITIAL_SIZE;
          } else {
            scale = this.Constant_.FINAL_SCALE;
            size = this.rippleSize_ + 'px';
            if (recentering) {
              offset = 'translate(' + this.boundWidth / 2 + 'px, ' +
                this.boundHeight / 2 + 'px)';
            }
          }

          transformString = 'translate(-50%, -50%) ' + offset + scale;

          this.rippleElement_.style.webkitTransform = transformString;
          this.rippleElement_.style.msTransform = transformString;
          this.rippleElement_.style.transform = transformString;

          if (start) {
            this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING);
          } else {
            this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING);
          }
        }
      };

      this.animFrameHandler = function() {
        if (this.frameCount_-- > 0) {
          window.requestAnimationFrame(this.animFrameHandler.bind(this));
        } else {
          this.setRippleStyles(false);
        }
      };
    }
  }
};

/*
* Downgrade the component
*/
MaterialRipple.prototype.mdlDowngrade_ = function() {
  'use strict';
  this.element_.removeEventListener('mousedown',
  this.boundDownHandler);
  this.element_.removeEventListener('touchstart',
      this.boundDownHandler);

  this.element_.removeEventListener('mouseup', this.boundUpHandler);
  this.element_.removeEventListener('mouseleave', this.boundUpHandler);
  this.element_.removeEventListener('touchend', this.boundUpHandler);
  this.element_.removeEventListener('blur', this.boundUpHandler);
};

// The component registers itself. It can assume componentHandler is available
// in the global scope.
componentHandler.register({
  constructor: MaterialRipple,
  classAsString: 'MaterialRipple',
  cssClass: 'mdl-js-ripple-effect',
  widget: false
});
<commit_msg>Wrap ripple in IIFE<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function() {
  'use strict';

  /**
   * Class constructor for Ripple MDL component.
   * Implements MDL component design pattern defined at:
   * https://github.com/jasonmayes/mdl-component-design-pattern
   *
   * @param {HTMLElement} element The element that will be upgraded.
   */
  var MaterialRipple = function MaterialRipple(element) {
    this.element_ = element;

    // Initialize instance.
    this.init();
  };
  window.MaterialRipple = MaterialRipple;

  /**
   * Store constants in one place so they can be updated easily.
   *
   * @enum {String | Number}
   * @private
   */
  MaterialRipple.prototype.Constant_ = {
    INITIAL_SCALE: 'scale(0.0001, 0.0001)',
    INITIAL_SIZE: '1px',
    INITIAL_OPACITY: '0.4',
    FINAL_OPACITY: '0',
    FINAL_SCALE: ''
  };

  /**
   * Store strings for class names defined by this component that are used in
   * JavaScript. This allows us to simply change it in one place should we
   * decide to modify at a later date.
   *
   * @enum {String}
   * @private
   */
  MaterialRipple.prototype.CssClasses_ = {
    RIPPLE_CENTER: 'mdl-ripple--center',
    RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
    RIPPLE: 'mdl-ripple',
    IS_ANIMATING: 'is-animating',
    IS_VISIBLE: 'is-visible'
  };

  /**
   * Handle mouse / finger down on element.
   *
   * @param {Event} event The event that fired.
   * @private
   */
  MaterialRipple.prototype.downHandler_ = function(event) {
    if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) {
      var rect = this.element_.getBoundingClientRect();
      this.boundHeight = rect.height;
      this.boundWidth = rect.width;
      this.rippleSize_ = Math.sqrt(rect.width * rect.width +
          rect.height * rect.height) * 2 + 2;
      this.rippleElement_.style.width = this.rippleSize_ + 'px';
      this.rippleElement_.style.height = this.rippleSize_ + 'px';
    }

    this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE);

    if (event.type === 'mousedown' && this.ignoringMouseDown_) {
      this.ignoringMouseDown_ = false;
    } else {
      if (event.type === 'touchstart') {
        this.ignoringMouseDown_ = true;
      }
      var frameCount = this.getFrameCount();
      if (frameCount > 0) {
        return;
      }
      this.setFrameCount(1);
      var bound = event.currentTarget.getBoundingClientRect();
      var x;
      var y;
      // Check if we are handling a keyboard click.
      if (event.clientX === 0 && event.clientY === 0) {
        x = Math.round(bound.width / 2);
        y = Math.round(bound.height / 2);
      } else {
        var clientX = event.clientX ? event.clientX : event.touches[0].clientX;
        var clientY = event.clientY ? event.clientY : event.touches[0].clientY;
        x = Math.round(clientX - bound.left);
        y = Math.round(clientY - bound.top);
      }
      this.setRippleXY(x, y);
      this.setRippleStyles(true);
      window.requestAnimationFrame(this.animFrameHandler.bind(this));
    }
  };

  /**
   * Handle mouse / finger up on element.
   *
   * @param {Event} event The event that fired.
   * @private
   */
  MaterialRipple.prototype.upHandler_ = function(event) {
    // Don't fire for the artificial "mouseup" generated by a double-click.
    if (event && event.detail !== 2) {
      this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);
    }
    // Allow a repaint to occur before removing this class, so the animation
    // shows for tap events, which seem to trigger a mouseup too soon after
    // mousedown.
    window.setTimeout(function() {
      this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);
    }.bind(this), 0);
  };

  /**
   * Initialize element.
   */
  MaterialRipple.prototype.init = function() {
    if (this.element_) {
      var recentering =
          this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);
      if (!this.element_.classList.contains(
          this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) {
        this.rippleElement_ = this.element_.querySelector('.' +
            this.CssClasses_.RIPPLE);
        this.frameCount_ = 0;
        this.rippleSize_ = 0;
        this.x_ = 0;
        this.y_ = 0;

        // Touch start produces a compat mouse down event, which would cause a
        // second ripples. To avoid that, we use this property to ignore the first
        // mouse down after a touch start.
        this.ignoringMouseDown_ = false;

        this.boundDownHandler = this.downHandler_.bind(this);
        this.element_.addEventListener('mousedown',
          this.boundDownHandler);
        this.element_.addEventListener('touchstart',
            this.boundDownHandler);

        this.boundUpHandler = this.upHandler_.bind(this);
        this.element_.addEventListener('mouseup', this.boundUpHandler);
        this.element_.addEventListener('mouseleave', this.boundUpHandler);
        this.element_.addEventListener('touchend', this.boundUpHandler);
        this.element_.addEventListener('blur', this.boundUpHandler);

        this.getFrameCount = function() {
          return this.frameCount_;
        };

        this.setFrameCount = function(fC) {
          this.frameCount_ = fC;
        };

        this.getRippleElement = function() {
          return this.rippleElement_;
        };

        this.setRippleXY = function(newX, newY) {
          this.x_ = newX;
          this.y_ = newY;
        };

        this.setRippleStyles = function(start) {
          if (this.rippleElement_ !== null) {
            var transformString;
            var scale;
            var size;
            var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)';

            if (start) {
              scale = this.Constant_.INITIAL_SCALE;
              size = this.Constant_.INITIAL_SIZE;
            } else {
              scale = this.Constant_.FINAL_SCALE;
              size = this.rippleSize_ + 'px';
              if (recentering) {
                offset = 'translate(' + this.boundWidth / 2 + 'px, ' +
                  this.boundHeight / 2 + 'px)';
              }
            }

            transformString = 'translate(-50%, -50%) ' + offset + scale;

            this.rippleElement_.style.webkitTransform = transformString;
            this.rippleElement_.style.msTransform = transformString;
            this.rippleElement_.style.transform = transformString;

            if (start) {
              this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING);
            } else {
              this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING);
            }
          }
        };

        this.animFrameHandler = function() {
          if (this.frameCount_-- > 0) {
            window.requestAnimationFrame(this.animFrameHandler.bind(this));
          } else {
            this.setRippleStyles(false);
          }
        };
      }
    }
  };

  /**
   * Downgrade the component
   *
   * @private
   */
  MaterialRipple.prototype.mdlDowngrade_ = function() {
    this.element_.removeEventListener('mousedown',
    this.boundDownHandler);
    this.element_.removeEventListener('touchstart',
        this.boundDownHandler);

    this.element_.removeEventListener('mouseup', this.boundUpHandler);
    this.element_.removeEventListener('mouseleave', this.boundUpHandler);
    this.element_.removeEventListener('touchend', this.boundUpHandler);
    this.element_.removeEventListener('blur', this.boundUpHandler);
  };

  // The component registers itself. It can assume componentHandler is available
  // in the global scope.
  componentHandler.register({
    constructor: MaterialRipple,
    classAsString: 'MaterialRipple',
    cssClass: 'mdl-js-ripple-effect',
    widget: false
  });
})();
<|endoftext|>