<commit_before>/**
 * Created by Serhii Maksymchuk from Ubrainians for imCode
 * 21.08.17
 */
define(
    "imcms-image-editor-builder",
    [
        "imcms-window-builder", "imcms-images-rest-api", "jquery", "imcms-events", "imcms", "imcms-image-rotate",
        "imcms-image-editor-factory", 'imcms-editable-image', 'imcms-image-editor-body-head-builder',
        'imcms-image-resize', 'imcms-image-edit-size-controls', "imcms-modal-window-builder", "imcms-i18n-texts",
        'imcms-preview-image-area', 'imcms-bem-builder',
        'imcms-components-builder', 'imcms-image-zoom'
    ],
    function (WindowBuilder, imageRestApi, $, events, imcms, imageRotate, imageEditorFactory, editableImage,
              bodyHeadBuilder, imageResize, editSizeControls, modal, texts, prevImageArea, BEM, components,
              imageZoom) {

        texts = texts.editors.image;

        const imageDataContainers = {};
        const imageData = {};

        function buildEditor() {
            return imageEditorFactory.buildEditor({
                fillData: fillData,
                imageDataContainers: imageDataContainers,
                $tag: $tag,
                imageWindowBuilder: imageWindowBuilder,
                imageData: imageData
            });
        }

        function fillBodyHeadData(imageData) {
            let titleText = imageData.name;

            if (titleText.length > 15) {
                titleText = titleText.substr(0, 15) + "...";
            }

            const $imgUrl = bodyHeadBuilder.getImageUrl();
            $imgUrl.text(titleText);
            $imgUrl.attr('href', `/images${(imageData.path.startsWith('/') ? '' : '/') + imageData.path}`);
            $imgUrl.attr('target', '_blank');
            $imgUrl.attr('data-path', imageData.path);
            $imgUrl.attr("title", imageData.path);
        }

        function initSize(imageData, isOriginalImage) {
            const cropRegion = imageData.cropRegion;
            const image = isOriginalImage ? imageResize.getOriginal() : imageResize.getPreview();

            if (cropRegion
                && (cropRegion.cropX1 >= 0)
                && (cropRegion.cropX2 >= 1)
                && (cropRegion.cropY1 >= 0)
                && (cropRegion.cropY2 >= 1)) {
                const width = cropRegion.cropX2 - cropRegion.cropX1;
                const height = cropRegion.cropY2 - cropRegion.cropY1;

                imageResize.setCurrentSize(width, height);
                imageResize.setWidthStrict(cropRegion.cropX1, width, isOriginalImage);
                imageResize.setHeightStrict(cropRegion.cropY1, height, isOriginalImage);

                imageResize.setWidth(imageData.width, isOriginalImage);
                imageResize.setHeight(imageData.height, isOriginalImage);

            } else {
                imageResize.setWidthStrict(0, image.width, isOriginalImage);
                imageResize.setHeightStrict(0, image.height, isOriginalImage);
                imageResize.updateSizing(imageData, true, isOriginalImage);
            }

            if (!isOriginalImage) {
                const editPreviewWidth = editSizeControls.getPreviewWidthControl().getInput().val();
                const editPreviewHeight = editSizeControls.getPreviewHeightControl().getInput().val();

                const $wantedSizeContainer = new BEM({
                    block: 'imcms-wanted-size',
                    elements: {
                        'title': components.texts.titleText('<div>', texts.wantedSize),
                        'width': components.texts.textField('<div>', {
                            value: editPreviewWidth,
                            readonly: "readonly",
                            name: 'wanted-width'
                        }),
                        'height': components.texts.textField('<div>', {
                            value: editPreviewHeight,
                            readonly: "readonly",
                            name: 'wanted-height'
                        })
                    }
                }).buildBlockStructure('<div>');

                const $displaySizeContainer = new BEM({
                    block: 'imcms-display-size',
                    elements: {
                        'title': components.texts.titleText('<div>', texts.displaySize),
                        'width': components.texts.textNumber('<div>', {
                            placeholder: editPreviewWidth,
                            disabled: "disabled",
                            name: 'display-width'
                        }),
                        'height': components.texts.textNumber('<div>', {
                            placeholder: editPreviewHeight,
                            disabled: "disabled",
                            name: 'display-height'
                        })
                    }
                }).buildBlockStructure('<div>');

            }

            imageRotate.rotateImage(imageData.rotateDirection);
            imageZoom.fitImage();
        }

        function fillLeftSideData(imageData) {
            if (!imageData.path) return;

            prevImageArea.getPreviewImage().hide();

            editableImage.setImageSource(imageData, () => {
                initSize(imageData, true);
            });

            prevImageArea.setPreviewImageSource(imageData, () => {
                const style = $tag.data('style');
                const resultStyleObj = {};

                if (style) {
                    style.split(';')
                        .map(x => x.trim())
                        .filter(x => !!x)
                        .forEach(x => {
                            const styleKeyAndValue = x.split(':').map(x => x.trim());
                            resultStyleObj[styleKeyAndValue[0]] = styleKeyAndValue[1];
                        });

                    let maxWidth = resultStyleObj['max-width'];

                    if (maxWidth && !isNaN(maxWidth = parseInt(maxWidth, 10))) {
                        imageData.width = Math.min(imageData.width, maxWidth);
                        imageResize.setMaxWidth(maxWidth);
                        editSizeControls.getPreviewWidthControl().getInput().attr('disabled', 'disabled');
                    }

                    let maxHeight = resultStyleObj['max-height'];

                    if (maxHeight && !isNaN(maxHeight = parseInt(maxHeight, 10))) {
                        imageData.height = Math.min(imageData.height, maxHeight);
                        imageResize.setMaxHeight(maxHeight);
                        editSizeControls.getPreviewHeightControl().getInput().attr('disabled', 'disabled');
                    }

                    let width = parseInt(resultStyleObj.width);

                    if (width) {
                        imageData.width = width;
                        imageResize.setMaxWidth(width);
                        imageResize.setMinWidth(width);
                        editSizeControls.getPreviewWidthControl().getInput().attr('disabled', 'disabled');
                    }

                    let height = parseInt(resultStyleObj.height);

                    if (height) {
                        imageData.height = height;
                        imageResize.setMaxHeight(height);
                        imageResize.setMinHeight(height);
                        editSizeControls.getPreviewHeightControl().getInput().attr('disabled', 'disabled');
                    }

                    if (imageResize.isProportionsLockedByStyle()) {
                        imageResize.setCurrentPreviewSize(imageData.width, imageData.height);
                        imageResize.setFinalPreviewImageData(imageData);
                    }
                }

                initSize(imageData, false);
                imageResize.disabledSelectedImageFlag();
                prevImageArea.getPreviewImage().show();
            });
        }

        function fillData(image) {
            clearComponents();

            if (!image) return;

            bodyHeadBuilder.showPreviewImageArea();

            $.extend(imageData, image);

            if (imageData.inText) $tag.attr("data-index", imageData.index);

            imageEditorFactory.updateImageData($tag, imageData);

            fillBodyHeadData(imageData);
            fillLeftSideData(imageData);

            imageDataContainers.$altText.$input.val(imageData.alternateText);
            imageDataContainers.$imgLink.$input.val(image.linkUrl);

            if (image.allLanguages !== undefined) {
                imageDataContainers.$allLanguagesCheckBox.find("input").prop('checked', image.allLanguages);
            }
        }

        function loadData(opts) {
            /** @namespace opts.loopEntryIndex */
            /** @namespace opts.loopIndex */
            if (opts.loopEntryIndex && opts.loopIndex) {

                // note that this data have to be set in such way because of non-JSON GET AJAX call
                opts["loopEntryRef.loopEntryIndex"] = opts.loopEntryIndex;
                opts["loopEntryRef.loopIndex"] = opts.loopIndex;

                delete opts.loopEntryIndex;
                delete opts.loopIndex;
            }

            if (opts.inText) {
                imageDataContainers.$langFlags.hideLangFlagsAndCheckbox();
            } else {
                imageDataContainers.$langFlags.showLangFlagsAndCheckbox();
                imageDataContainers.$langFlags.setActive(imcms.language.code);
            }

            imageRestApi.read(opts)
                .done(fillData)
                .fail(() => modal.buildErrorWindow(texts.error.loadFailed));
        }

        function clearComponents() {
            bodyHeadBuilder.clearData();
            editableImage.clearData();
            imageResize.clearData();
            imageRotate.destroy();
        }

        function clearData() {
            clearComponents();
            events.trigger("enable text editor blur");
            editSizeControls.getPreviewWidthControl().getInput().removeAttr('disabled').val('');
            editSizeControls.getPreviewHeightControl().getInput().removeAttr('disabled').val('');
            $('.percentage-image-info').text('');
        }

        var imageWindowBuilder = new WindowBuilder({
            factory: buildEditor,
            loadDataStrategy: loadData,
            clearDataStrategy: clearData,
            onEscKeyPressed: "close",
            onEnterKeyPressed: () => {
                imageWindowBuilder.$editor.find(".imcms-image_editor__footer .imcms-button--save").click();
            }
        });

        var $tag;

        return {
            setTag: function ($editedTag) {
                $tag = $editedTag;
                return this;
            },
            build: function (opts) {
                events.trigger("disable text editor blur");
                imageWindowBuilder.buildWindow.apply(imageWindowBuilder, arguments);
            }
        };
    }
);
<commit_msg>Issue IMCMS-508: Improve the image editor - Change condition<commit_after>/**
 * Created by Serhii Maksymchuk from Ubrainians for imCode
 * 21.08.17
 */
define(
    "imcms-image-editor-builder",
    [
        "imcms-window-builder", "imcms-images-rest-api", "jquery", "imcms-events", "imcms", "imcms-image-rotate",
        "imcms-image-editor-factory", 'imcms-editable-image', 'imcms-image-editor-body-head-builder',
        'imcms-image-resize', 'imcms-image-edit-size-controls', "imcms-modal-window-builder", "imcms-i18n-texts",
        'imcms-preview-image-area', 'imcms-bem-builder',
        'imcms-components-builder', 'imcms-image-zoom'
    ],
    function (WindowBuilder, imageRestApi, $, events, imcms, imageRotate, imageEditorFactory, editableImage,
              bodyHeadBuilder, imageResize, editSizeControls, modal, texts, prevImageArea, BEM, components,
              imageZoom) {

        texts = texts.editors.image;

        const imageDataContainers = {};
        const imageData = {};

        function buildEditor() {
            return imageEditorFactory.buildEditor({
                fillData: fillData,
                imageDataContainers: imageDataContainers,
                $tag: $tag,
                imageWindowBuilder: imageWindowBuilder,
                imageData: imageData
            });
        }

        function fillBodyHeadData(imageData) {
            let titleText = imageData.name;

            if (titleText.length > 15) {
                titleText = titleText.substr(0, 15) + "...";
            }

            const $imgUrl = bodyHeadBuilder.getImageUrl();
            $imgUrl.text(titleText);
            $imgUrl.attr('href', `/images${(imageData.path.startsWith('/') ? '' : '/') + imageData.path}`);
            $imgUrl.attr('target', '_blank');
            $imgUrl.attr('data-path', imageData.path);
            $imgUrl.attr("title", imageData.path);
        }

        function initSize(imageData, isOriginalImage) {
            const cropRegion = imageData.cropRegion;
            const image = isOriginalImage ? imageResize.getOriginal() : imageResize.getPreview();

            if (cropRegion
                && (cropRegion.cropX1 >= 0)
                && (cropRegion.cropX2 >= 1)
                && (cropRegion.cropY1 >= 0)
                && (cropRegion.cropY2 >= 1)
                && !imageResize.getSelectedImgFlagValue()) {
                const width = cropRegion.cropX2 - cropRegion.cropX1;
                const height = cropRegion.cropY2 - cropRegion.cropY1;

                imageResize.setCurrentSize(width, height);
                imageResize.setWidthStrict(cropRegion.cropX1, width, isOriginalImage);
                imageResize.setHeightStrict(cropRegion.cropY1, height, isOriginalImage);

                imageResize.setWidth(imageData.width, isOriginalImage);
                imageResize.setHeight(imageData.height, isOriginalImage);
            } else {
                imageResize.setWidthStrict(0, image.width, isOriginalImage);
                imageResize.setHeightStrict(0, image.height, isOriginalImage);
                imageResize.updateSizing(imageData, true, isOriginalImage);
            }

            if (!isOriginalImage) {
                const editPreviewWidth = editSizeControls.getPreviewWidthControl().getInput().val();
                const editPreviewHeight = editSizeControls.getPreviewHeightControl().getInput().val();

                const $wantedSizeContainer = new BEM({
                    block: 'imcms-wanted-size',
                    elements: {
                        'title': components.texts.titleText('<div>', texts.wantedSize),
                        'width': components.texts.textField('<div>', {
                            value: editPreviewWidth,
                            readonly: "readonly",
                            name: 'wanted-width'
                        }),
                        'height': components.texts.textField('<div>', {
                            value: editPreviewHeight,
                            readonly: "readonly",
                            name: 'wanted-height'
                        })
                    }
                }).buildBlockStructure('<div>');

                const $displaySizeContainer = new BEM({
                    block: 'imcms-display-size',
                    elements: {
                        'title': components.texts.titleText('<div>', texts.displaySize),
                        'width': components.texts.textNumber('<div>', {
                            placeholder: editPreviewWidth,
                            disabled: "disabled",
                            name: 'display-width'
                        }),
                        'height': components.texts.textNumber('<div>', {
                            placeholder: editPreviewHeight,
                            disabled: "disabled",
                            name: 'display-height'
                        })
                    }
                }).buildBlockStructure('<div>');

            }

            imageRotate.rotateImage(imageData.rotateDirection);
            imageZoom.fitImage();
        }

        function fillLeftSideData(imageData) {
            if (!imageData.path) return;

            prevImageArea.getPreviewImage().hide();

            editableImage.setImageSource(imageData, () => {
                initSize(imageData, true);
            });

            prevImageArea.setPreviewImageSource(imageData, () => {
                const style = $tag.data('style');
                const resultStyleObj = {};

                if (style) {
                    style.split(';')
                        .map(x => x.trim())
                        .filter(x => !!x)
                        .forEach(x => {
                            const styleKeyAndValue = x.split(':').map(x => x.trim());
                            resultStyleObj[styleKeyAndValue[0]] = styleKeyAndValue[1];
                        });

                    let maxWidth = resultStyleObj['max-width'];

                    if (maxWidth && !isNaN(maxWidth = parseInt(maxWidth, 10))) {
                        imageData.width = Math.min(imageData.width, maxWidth);
                        imageResize.setMaxWidth(maxWidth);
                        editSizeControls.getPreviewWidthControl().getInput().attr('disabled', 'disabled');
                    }

                    let maxHeight = resultStyleObj['max-height'];

                    if (maxHeight && !isNaN(maxHeight = parseInt(maxHeight, 10))) {
                        imageData.height = Math.min(imageData.height, maxHeight);
                        imageResize.setMaxHeight(maxHeight);
                        editSizeControls.getPreviewHeightControl().getInput().attr('disabled', 'disabled');
                    }

                    let width = parseInt(resultStyleObj.width);

                    if (width) {
                        imageData.width = width;
                        imageResize.setMaxWidth(width);
                        imageResize.setMinWidth(width);
                        editSizeControls.getPreviewWidthControl().getInput().attr('disabled', 'disabled');
                    }

                    let height = parseInt(resultStyleObj.height);

                    if (height) {
                        imageData.height = height;
                        imageResize.setMaxHeight(height);
                        imageResize.setMinHeight(height);
                        editSizeControls.getPreviewHeightControl().getInput().attr('disabled', 'disabled');
                    }

                    if (imageResize.isProportionsLockedByStyle()) {
                        imageResize.setCurrentPreviewSize(imageData.width, imageData.height);
                        imageResize.setFinalPreviewImageData(imageData);
                    }
                }

                initSize(imageData, false);
                imageResize.disabledSelectedImageFlag();
                prevImageArea.getPreviewImage().show();
            });
        }

        function fillData(image) {
            clearComponents();

            if (!image) return;

            bodyHeadBuilder.showPreviewImageArea();

            $.extend(imageData, image);

            if (imageData.inText) $tag.attr("data-index", imageData.index);

            imageEditorFactory.updateImageData($tag, imageData);

            fillBodyHeadData(imageData);
            fillLeftSideData(imageData);

            imageDataContainers.$altText.$input.val(imageData.alternateText);
            imageDataContainers.$imgLink.$input.val(image.linkUrl);

            if (image.allLanguages !== undefined) {
                imageDataContainers.$allLanguagesCheckBox.find("input").prop('checked', image.allLanguages);
            }
        }

        function loadData(opts) {
            /** @namespace opts.loopEntryIndex */
            /** @namespace opts.loopIndex */
            if (opts.loopEntryIndex && opts.loopIndex) {

                // note that this data have to be set in such way because of non-JSON GET AJAX call
                opts["loopEntryRef.loopEntryIndex"] = opts.loopEntryIndex;
                opts["loopEntryRef.loopIndex"] = opts.loopIndex;

                delete opts.loopEntryIndex;
                delete opts.loopIndex;
            }

            if (opts.inText) {
                imageDataContainers.$langFlags.hideLangFlagsAndCheckbox();
            } else {
                imageDataContainers.$langFlags.showLangFlagsAndCheckbox();
                imageDataContainers.$langFlags.setActive(imcms.language.code);
            }

            imageRestApi.read(opts)
                .done(fillData)
                .fail(() => modal.buildErrorWindow(texts.error.loadFailed));
        }

        function clearComponents() {
            bodyHeadBuilder.clearData();
            editableImage.clearData();
            imageResize.clearData();
            imageRotate.destroy();
        }

        function clearData() {
            clearComponents();
            events.trigger("enable text editor blur");
            editSizeControls.getPreviewWidthControl().getInput().removeAttr('disabled').val('');
            editSizeControls.getPreviewHeightControl().getInput().removeAttr('disabled').val('');
            $('.percentage-image-info').text('');
        }

        var imageWindowBuilder = new WindowBuilder({
            factory: buildEditor,
            loadDataStrategy: loadData,
            clearDataStrategy: clearData,
            onEscKeyPressed: "close",
            onEnterKeyPressed: () => {
                imageWindowBuilder.$editor.find(".imcms-image_editor__footer .imcms-button--save").click();
            }
        });

        var $tag;

        return {
            setTag: function ($editedTag) {
                $tag = $editedTag;
                return this;
            },
            build: function (opts) {
                events.trigger("disable text editor blur");
                imageWindowBuilder.buildWindow.apply(imageWindowBuilder, arguments);
            }
        };
    }
);
<|endoftext|>


<commit_before>var Immutable = require("immutable");

/**
 * @type {Cache}
 */
module.exports = Cache;

/**
 * @constructor
 */
function Cache () {

    var cache = this;

    cache._items = Immutable.Map({});

    cache.add = function (item) {
        cache._items = cache._items.set(item.get("key"), item);
        return item;
    };

    cache.byType = function (type, filter) {
        var byType = cache._items.filter(function (item) {
            return item.get("type") === type;
        });
        if (filter) {
            byType = byType.filter(filter);
        }
        return byType.toList();
    };

    cache.byKey = function (key) {
        return cache._items.get(key);
    };
}
<commit_msg>Use ordered map for cache<commit_after>var Immutable = require("immutable");

/**
 * @type {Cache}
 */
module.exports = Cache;

/**
 * @constructor
 */
function Cache () {

    var cache = this;

    cache._items = Immutable.OrderedMap({});

    cache.add = function (item) {
        cache._items = cache._items.set(item.get("key"), item);
        return item;
    };

    cache.byType = function (type, filter) {
        var byType = cache._items.filter(function (item) {
            return item.get("type") === type;
        });
        if (filter) {
            byType = byType.filter(filter);
        }
        return byType.toList();
    };

    cache.byKey = function (key) {
        return cache._items.get(key);
    };
}
<|endoftext|>


<commit_before>(function($) {
  var _options;
  var _self;

  var _boundingBoxes;
  var _zoneCentroids = {};
  var _selectedRegionKey;
  var _selectedPolygon;
  var _map;
  var _mapZones = {};
  var _transitions = {};

  var _currentHoverRegion;
  var _hoverRegions = {};
  var _hoverPolygons = [];

  var _loader;
  var _loaderGif;
  var _maskPng;
  var _needsLoader = 0;

  var gmaps;

  // Forward declarations to satisfy jshint
  var hideLoader, hitTestAndConvert, onSelected, selectPolygonZone,
    showInfoWindow, slugifyName;

  var clearHover = function() {
    $.each(_hoverPolygons, function(i, p) {
      p.setMap(null);
    });

    _hoverPolygons = [];
  };

  var clearZones = function() {
    $.each(_mapZones, function(i, zone) {
      $.each(zone, function(j, polygon) {
        polygon.setMap(null);
      });
    });

    _mapZones = {};
  };

  var drawZone = function(name, lat, lng, callback) {
    if (_mapZones[name]) {
      return;
    }

    $.get(_options.jsonRootUrl + 'polygons/' + name + '.json', function(data) {
      _needsLoader--;
      if (_needsLoader === 0 && _loader) {
        hideLoader();
      }

      if (callback) {
        callback();
      }

      data = typeof data === 'string' ? JSON.parse(data) : data;

      _mapZones[name] = [];
      $.extend(_transitions, data.transitions);

      var result = hitTestAndConvert(data.polygons, lat, lng);

      if (result.inZone) {
        _selectedRegionKey = name;
        $.each(result.allPolygons, function(i, polygonInfo) {
          var mapPolygon = new gmaps.Polygon({
            paths: polygonInfo.coords,
            strokeColor: '#ff0000',
            strokeOpacity: 0.7,
            strokeWeight: 1,
            fillColor: '#ffcccc',
            fillOpacity: 0.5
          });
          mapPolygon.setMap(_map);

          gmaps.event.addListener(mapPolygon, 'click', function() {
            selectPolygonZone(polygonInfo.polygon);
          });

          gmaps.event.addListener(mapPolygon, 'mousemove', clearHover);

          _mapZones[name].push(mapPolygon);
        });

        selectPolygonZone(result.selectedPolygon);
      }
    }).error(function() {
      console.warn(arguments);
    });
  };

  var getCurrentTransition = function(transitions) {
    if (transitions.length === 1) {
      return transitions[0];
    }

    var now = _options.date.getTime() / 1000;
    var selected = null;
    $.each(transitions, function(i, transition) {
      if (transition[0] < now && transitions[i + 1][0] > now) {
        selected = transition;
      }
    });

    // If we couldn't find a matching transition, just use the first one
    // NOTE: This will sometimes be wrong for events in the past
    if (!selected) {
      selected = transitions[0];
    }

    return selected;
  };

  var hideInfoWindow = function() {
    if (_map.lastInfoWindow) {
      _map.lastInfoWindow.close();
    }
  };

  hideLoader = function() {
    _loader.remove();
    _loader = null;
  };

  hitTestAndConvert = function(polygons, lat, lng) {
    var allPolygons = [];
    var inZone = false;
    var selectedPolygon;
    $.each(polygons, function(i, polygon) {
      // Ray casting counter for hit testing.
      var rayTest = 0;
      var lastPoint = polygon.points.slice(-2);

      var coords = [];
      var j = 0;
      for (j = 0; j < polygon.points.length; j += 2) {
        var point = polygon.points.slice(j, j + 2);

        coords.push(new gmaps.LatLng(point[0], point[1]));

        // Ray casting test
        if ((lastPoint[0] <= lat && point[0] >= lat) ||
          (lastPoint[0] > lat && point[0] < lat)) {
          var slope = (point[1] - lastPoint[1]) / (point[0] - lastPoint[0]);
          var testPoint = slope * (lat - lastPoint[0]) + lastPoint[1];
          if (testPoint < lng) {
            rayTest++;
          }
        }

        lastPoint = point;
      }

      allPolygons.push({
        polygon: polygon,
        coords: coords
      });

      // If the count is odd, we are in the polygon
      var odd = (rayTest % 2 === 1);
      inZone |= odd;
      if (odd) {
        selectedPolygon = polygon;
      }
    });

    return {
      allPolygons: allPolygons,
      inZone: inZone,
      selectedPolygon: selectedPolygon
    };
  };

  var mapClickHandler = function(e) {
    if (_needsLoader > 0) {
      return;
    }

    hideInfoWindow();

    var lat = e.latLng.Qa;
    var lng = e.latLng.Ra;

    var candidates = [];
    $.each(_boundingBoxes, function(i, v) {
      var bb = v.boundingBox;
      if (lat > bb.ymin && lat < bb.ymax &&
      lng > bb.xmin &&
      lng < bb.xmax) {
        candidates.push(slugifyName(v.name));
      }
    });

    _needsLoader = candidates.length;
    setTimeout(function() {
      if (_needsLoader > 0) {
        showLoader();
      }
    }, 500);

    clearZones();
    $.each(candidates, function(i, v) {
      drawZone(v, lat, lng, function() {
        $.each(_hoverPolygons, function(i, p) {
          p.setMap(null);
        });
        _hoverPolygons = [];
        _currentHoverRegion = null;
      });
    });
  };

  selectPolygonZone = function(polygon) {
    _selectedPolygon = polygon;

    var transition = getCurrentTransition(
      _transitions[polygon.name]);

    var olsonName = polygon.name;
    var utcOffset = transition[1];
    var tzName = transition[2];

    if (_options.onSelected) {
      _options.onSelected(olsonName, utcOffset, tzName);
    }
    else {
      var pad = function(d) {
        if (d < 10) {
          return '0' + d;
        }
        return d.toString();
      };

      var now = new Date();
      var adjusted = new Date();
      adjusted.setTime(adjusted.getTime() +
        (adjusted.getTimezoneOffset() + utcOffset) * 60 * 1000);

      showInfoWindow('
' +
        olsonName.split('/').slice(-1)[0].replace('_', ' ') + ' ' +
        '(' + tzName + ')
' +
        '
' +
        '
Current Time: ' +
        pad(adjusted.getHours()) + ':' +
        pad(adjusted.getMinutes()) + ':' +
        pad(adjusted.getSeconds()) + '
' +
        '
Your Time: ' +
        pad(now.getHours()) + ':' +
        pad(now.getMinutes()) + ':' +
        pad(now.getSeconds()) + '
' +
        '
UTC Offset (in hours): ' +
        (utcOffset / 60) + '
' +
        '
');
    }
  };

  showInfoWindow = function(content, callback) {
    // Hack to get the centroid of the largest polygon - we just check
    // which has the most edges
    var centroid;
    var maxPoints = 0;
    if (_selectedPolygon.points.length > maxPoints) {
      centroid = _selectedPolygon.centroid;
      maxPoints = _selectedPolygon.points.length;
    }

    hideInfoWindow();

    var infowindow = new gmaps.InfoWindow({
      content: '
' +
      content +
      '
'
    });

    gmaps.event.addListener(infowindow, 'domready', function() {
      // HACK: Put rounded corners on the infowindow
      $('#timezone_picker_infowindow').parent().parent().parent().prev().css('border-radius', '5px');

      if (callback) {
        callback.apply($('#timezone_picker_infowindow'));
      }
    });
    infowindow.setPosition(new gmaps.LatLng(centroid[1], centroid[0]));
    infowindow.open(_map);

    _map.lastInfoWindow = infowindow;
  };

  var showLoader = function() {
    _loader = $('
' +
    '
');
    _loader.height(_self.height()).width(_self.width());
    _self.append(_loader);
  };

  slugifyName = function(name) {
    return name.toLowerCase().replace(/[^a-z0-9]/g, '-');
  };

  var methods = {
    init: function(options) {
      _self = this;

      // Populate the options and set defaults
      _options = options || {};
      _options.initialZoom = _options.initialZoom || 2;
      _options.initialLat = _options.initialLat || 0;
      _options.initialLng = _options.initialLng || 0;
      _options.strokeColor = _options.strokeColor || '#ff0000';
      _options.strokeWeight = _options.strokeWeight || 2;
      _options.strokeOpacity = _options.strokeOpacity || 0.7;
      _options.fillColor = _options.fillColor || '#ffcccc';
      _options.fillOpacity = _options.fillOpacity || 0.5;
      _options.jsonRootUrl = _options.jsonRootUrl || 'tz_json/';
      _options.date = _options.date || new Date();

      _options.mapOptions = $.extend({
        zoom: _options.initialZoom,
        mapTypeId: gmaps.MapTypeId.ROADMAP,
        center: new gmaps.LatLng(_options.initialLat, _options.initialLng)
      }, _options.mapOptions);

      if (typeof _options.hoverRegions === 'undefined') {
        _options.hoverRegions = true;
      }

      // Create the maps instance
      _map = new gmaps.Map(_self.get(0), _options.mapOptions);

      // Load the necessary data files
      var loadCount = _options.hoverRegions ? 2 : 1;
      var checkLoading = function() {
        loadCount--;
        if (loadCount === 0) {
          hideLoader();

          if (_options.onReady) {
            _options.onReady();
          }
        }
      };

      showLoader();
      $.get(_options.jsonRootUrl + 'bounding_boxes.json', function(data) {
        _boundingBoxes = typeof data === 'string' ? JSON.parse(data) : data;
        $.each(_boundingBoxes, function(i, bb) {
          $.extend(_zoneCentroids, bb.zoneCentroids);
        });
        checkLoading();
      });

      if (_options.hoverRegions) {
        $.get(_options.jsonRootUrl + 'hover_regions.json', function(data) {
          var hoverData = typeof data === 'string' ? JSON.parse(data) : data;
          $.each(hoverData, function(i, v) {
            _hoverRegions[v.name] = v;
          });
          checkLoading();
        });
      }

      if (_options.hoverRegions) {
        gmaps.event.addListener(_map, 'mousemove', function(e) {
          var lat = e.latLng.Qa;
          var lng = e.latLng.Ra;

          $.each(_boundingBoxes, function(i, v) {
            var bb = v.boundingBox;
            if (lat > bb.ymin && lat < bb.ymax &&
            lng > bb.xmin &&
            lng < bb.xmax) {
              var hoverRegion = _hoverRegions[v.name];
              if (!hoverRegion) {
                return;
              }

              var result = hitTestAndConvert(hoverRegion.hoverRegion, lat, lng);
              var slugName = slugifyName(v.name);
              if (result.inZone && slugName !== _currentHoverRegion &&
                slugName !== _selectedRegionKey) {
                clearHover();
                _currentHoverRegion = slugName;

                $.each(result.allPolygons, function(i, polygonInfo) {
                  var mapPolygon = new gmaps.Polygon({
                    paths: polygonInfo.coords,
                    strokeColor: '#444444',
                    strokeOpacity: 0.7,
                    strokeWeight: 1,
                    fillColor: '#888888',
                    fillOpacity: 0.5
                  });
                  mapPolygon.setMap(_map);

                  gmaps.event.addListener(mapPolygon, 'click', mapClickHandler);

                  _hoverPolygons.push(mapPolygon);
                });

                if (_options.onHover) {
                  var transition = getCurrentTransition(hoverRegion.transitions);
                  _options.onHover(transition[1], transition[2]);
                }
              }
            }
          });
        });
      }

      gmaps.event.addListener(_map, 'click', mapClickHandler);
    },
    setDate: function(date) {
      hideInfoWindow();
      _options.date = date;
    },
    hideInfoWindow: hideInfoWindow,
    showInfoWindow: function(content, callback) {
      showInfoWindow(content, callback);
    },
    selectZone: function(olsonName) {
      var centroid = _zoneCentroids[olsonName];
      if (centroid) {
        mapClickHandler({
          latLng: {
            Qa: centroid[1],
            Ra: centroid[0]
          }
        });
      }
    }
  };

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

  _loaderGif = "data:image/gif;base64,R0lGODlhEAAQAPIAAKqqqv///729vejo6P///93d3dPT083NzSH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==";
  _maskPng = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDgA6CHKQBUUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADUlEQVQI12NgYGDwAQAAUQBNbrgEdAAAAABJRU5ErkJggg==";
})(jQuery);
<commit_msg>google changed latLng.Ra to latLng.Pa?<commit_after>(function($) {
  var _options;
  var _self;

  var _boundingBoxes;
  var _zoneCentroids = {};
  var _selectedRegionKey;
  var _selectedPolygon;
  var _map;
  var _mapZones = {};
  var _transitions = {};

  var _currentHoverRegion;
  var _hoverRegions = {};
  var _hoverPolygons = [];

  var _loader;
  var _loaderGif;
  var _maskPng;
  var _needsLoader = 0;

  var gmaps;

  // Forward declarations to satisfy jshint
  var hideLoader, hitTestAndConvert, onSelected, selectPolygonZone,
    showInfoWindow, slugifyName;

  var clearHover = function() {
    $.each(_hoverPolygons, function(i, p) {
      p.setMap(null);
    });

    _hoverPolygons = [];
  };

  var clearZones = function() {
    $.each(_mapZones, function(i, zone) {
      $.each(zone, function(j, polygon) {
        polygon.setMap(null);
      });
    });

    _mapZones = {};
  };

  var drawZone = function(name, lat, lng, callback) {
    if (_mapZones[name]) {
      return;
    }

    $.get(_options.jsonRootUrl + 'polygons/' + name + '.json', function(data) {
      _needsLoader--;
      if (_needsLoader === 0 && _loader) {
        hideLoader();
      }

      if (callback) {
        callback();
      }

      data = typeof data === 'string' ? JSON.parse(data) : data;

      _mapZones[name] = [];
      $.extend(_transitions, data.transitions);

      var result = hitTestAndConvert(data.polygons, lat, lng);

      if (result.inZone) {
        _selectedRegionKey = name;
        $.each(result.allPolygons, function(i, polygonInfo) {
          var mapPolygon = new gmaps.Polygon({
            paths: polygonInfo.coords,
            strokeColor: '#ff0000',
            strokeOpacity: 0.7,
            strokeWeight: 1,
            fillColor: '#ffcccc',
            fillOpacity: 0.5
          });
          mapPolygon.setMap(_map);

          gmaps.event.addListener(mapPolygon, 'click', function() {
            selectPolygonZone(polygonInfo.polygon);
          });

          gmaps.event.addListener(mapPolygon, 'mousemove', clearHover);

          _mapZones[name].push(mapPolygon);
        });

        selectPolygonZone(result.selectedPolygon);
      }
    }).error(function() {
      console.warn(arguments);
    });
  };

  var getCurrentTransition = function(transitions) {
    if (transitions.length === 1) {
      return transitions[0];
    }

    var now = _options.date.getTime() / 1000;
    var selected = null;
    $.each(transitions, function(i, transition) {
      if (transition[0] < now && transitions[i + 1][0] > now) {
        selected = transition;
      }
    });

    // If we couldn't find a matching transition, just use the first one
    // NOTE: This will sometimes be wrong for events in the past
    if (!selected) {
      selected = transitions[0];
    }

    return selected;
  };

  var hideInfoWindow = function() {
    if (_map.lastInfoWindow) {
      _map.lastInfoWindow.close();
    }
  };

  hideLoader = function() {
    _loader.remove();
    _loader = null;
  };

  hitTestAndConvert = function(polygons, lat, lng) {
    var allPolygons = [];
    var inZone = false;
    var selectedPolygon;
    $.each(polygons, function(i, polygon) {
      // Ray casting counter for hit testing.
      var rayTest = 0;
      var lastPoint = polygon.points.slice(-2);

      var coords = [];
      var j = 0;
      for (j = 0; j < polygon.points.length; j += 2) {
        var point = polygon.points.slice(j, j + 2);

        coords.push(new gmaps.LatLng(point[0], point[1]));

        // Ray casting test
        if ((lastPoint[0] <= lat && point[0] >= lat) ||
          (lastPoint[0] > lat && point[0] < lat)) {
          var slope = (point[1] - lastPoint[1]) / (point[0] - lastPoint[0]);
          var testPoint = slope * (lat - lastPoint[0]) + lastPoint[1];
          if (testPoint < lng) {
            rayTest++;
          }
        }

        lastPoint = point;
      }

      allPolygons.push({
        polygon: polygon,
        coords: coords
      });

      // If the count is odd, we are in the polygon
      var odd = (rayTest % 2 === 1);
      inZone |= odd;
      if (odd) {
        selectedPolygon = polygon;
      }
    });

    return {
      allPolygons: allPolygons,
      inZone: inZone,
      selectedPolygon: selectedPolygon
    };
  };

  var mapClickHandler = function(e) {
    if (_needsLoader > 0) {
      return;
    }

    hideInfoWindow();

    var lat = e.latLng.Qa;
    var lng = e.latLng.Ra || e.latLng.Pa;

    var candidates = [];
    $.each(_boundingBoxes, function(i, v) {
      var bb = v.boundingBox;
      if (lat > bb.ymin && lat < bb.ymax &&
      lng > bb.xmin &&
      lng < bb.xmax) {
        candidates.push(slugifyName(v.name));
      }
    });

    _needsLoader = candidates.length;
    setTimeout(function() {
      if (_needsLoader > 0) {
        showLoader();
      }
    }, 500);

    clearZones();
    $.each(candidates, function(i, v) {
      drawZone(v, lat, lng, function() {
        $.each(_hoverPolygons, function(i, p) {
          p.setMap(null);
        });
        _hoverPolygons = [];
        _currentHoverRegion = null;
      });
    });
  };

  selectPolygonZone = function(polygon) {
    _selectedPolygon = polygon;

    var transition = getCurrentTransition(
      _transitions[polygon.name]);

    var olsonName = polygon.name;
    var utcOffset = transition[1];
    var tzName = transition[2];

    if (_options.onSelected) {
      _options.onSelected(olsonName, utcOffset, tzName);
    }
    else {
      var pad = function(d) {
        if (d < 10) {
          return '0' + d;
        }
        return d.toString();
      };

      var now = new Date();
      var adjusted = new Date();
      adjusted.setTime(adjusted.getTime() +
        (adjusted.getTimezoneOffset() + utcOffset) * 60 * 1000);

      showInfoWindow('
' +
        olsonName.split('/').slice(-1)[0].replace('_', ' ') + ' ' +
        '(' + tzName + ')
' +
        '
' +
        '
Current Time: ' +
        pad(adjusted.getHours()) + ':' +
        pad(adjusted.getMinutes()) + ':' +
        pad(adjusted.getSeconds()) + '
' +
        '
Your Time: ' +
        pad(now.getHours()) + ':' +
        pad(now.getMinutes()) + ':' +
        pad(now.getSeconds()) + '
' +
        '
UTC Offset (in hours): ' +
        (utcOffset / 60) + '
' +
        '
');
    }
  };

  showInfoWindow = function(content, callback) {
    // Hack to get the centroid of the largest polygon - we just check
    // which has the most edges
    var centroid;
    var maxPoints = 0;
    if (_selectedPolygon.points.length > maxPoints) {
      centroid = _selectedPolygon.centroid;
      maxPoints = _selectedPolygon.points.length;
    }

    hideInfoWindow();

    var infowindow = new gmaps.InfoWindow({
      content: '
' +
      content +
      '
'
    });

    gmaps.event.addListener(infowindow, 'domready', function() {
      // HACK: Put rounded corners on the infowindow
      $('#timezone_picker_infowindow').parent().parent().parent().prev().css('border-radius', '5px');

      if (callback) {
        callback.apply($('#timezone_picker_infowindow'));
      }
    });
    infowindow.setPosition(new gmaps.LatLng(centroid[1], centroid[0]));
    infowindow.open(_map);

    _map.lastInfoWindow = infowindow;
  };

  var showLoader = function() {
    _loader = $('
' +
    '
');
    _loader.height(_self.height()).width(_self.width());
    _self.append(_loader);
  };

  slugifyName = function(name) {
    return name.toLowerCase().replace(/[^a-z0-9]/g, '-');
  };

  var methods = {
    init: function(options) {
      _self = this;

      // Populate the options and set defaults
      _options = options || {};
      _options.initialZoom = _options.initialZoom || 2;
      _options.initialLat = _options.initialLat || 0;
      _options.initialLng = _options.initialLng || 0;
      _options.strokeColor = _options.strokeColor || '#ff0000';
      _options.strokeWeight = _options.strokeWeight || 2;
      _options.strokeOpacity = _options.strokeOpacity || 0.7;
      _options.fillColor = _options.fillColor || '#ffcccc';
      _options.fillOpacity = _options.fillOpacity || 0.5;
      _options.jsonRootUrl = _options.jsonRootUrl || 'tz_json/';
      _options.date = _options.date || new Date();

      _options.mapOptions = $.extend({
        zoom: _options.initialZoom,
        mapTypeId: gmaps.MapTypeId.ROADMAP,
        center: new gmaps.LatLng(_options.initialLat, _options.initialLng)
      }, _options.mapOptions);

      if (typeof _options.hoverRegions === 'undefined') {
        _options.hoverRegions = true;
      }

      // Create the maps instance
      _map = new gmaps.Map(_self.get(0), _options.mapOptions);

      // Load the necessary data files
      var loadCount = _options.hoverRegions ? 2 : 1;
      var checkLoading = function() {
        loadCount--;
        if (loadCount === 0) {
          hideLoader();

          if (_options.onReady) {
            _options.onReady();
          }
        }
      };

      showLoader();
      $.get(_options.jsonRootUrl + 'bounding_boxes.json', function(data) {
        _boundingBoxes = typeof data === 'string' ? JSON.parse(data) : data;
        $.each(_boundingBoxes, function(i, bb) {
          $.extend(_zoneCentroids, bb.zoneCentroids);
        });
        checkLoading();
      });

      if (_options.hoverRegions) {
        $.get(_options.jsonRootUrl + 'hover_regions.json', function(data) {
          var hoverData = typeof data === 'string' ? JSON.parse(data) : data;
          $.each(hoverData, function(i, v) {
            _hoverRegions[v.name] = v;
          });
          checkLoading();
        });
      }

      if (_options.hoverRegions) {
        gmaps.event.addListener(_map, 'mousemove', function(e) {
          var lat = e.latLng.Qa;
          var lng = e.latLng.Ra;

          $.each(_boundingBoxes, function(i, v) {
            var bb = v.boundingBox;
            if (lat > bb.ymin && lat < bb.ymax &&
            lng > bb.xmin &&
            lng < bb.xmax) {
              var hoverRegion = _hoverRegions[v.name];
              if (!hoverRegion) {
                return;
              }

              var result = hitTestAndConvert(hoverRegion.hoverRegion, lat, lng);
              var slugName = slugifyName(v.name);
              if (result.inZone && slugName !== _currentHoverRegion &&
                slugName !== _selectedRegionKey) {
                clearHover();
                _currentHoverRegion = slugName;

                $.each(result.allPolygons, function(i, polygonInfo) {
                  var mapPolygon = new gmaps.Polygon({
                    paths: polygonInfo.coords,
                    strokeColor: '#444444',
                    strokeOpacity: 0.7,
                    strokeWeight: 1,
                    fillColor: '#888888',
                    fillOpacity: 0.5
                  });
                  mapPolygon.setMap(_map);

                  gmaps.event.addListener(mapPolygon, 'click', mapClickHandler);

                  _hoverPolygons.push(mapPolygon);
                });

                if (_options.onHover) {
                  var transition = getCurrentTransition(hoverRegion.transitions);
                  _options.onHover(transition[1], transition[2]);
                }
              }
            }
          });
        });
      }

      gmaps.event.addListener(_map, 'click', mapClickHandler);
    },
    setDate: function(date) {
      hideInfoWindow();
      _options.date = date;
    },
    hideInfoWindow: hideInfoWindow,
    showInfoWindow: function(content, callback) {
      showInfoWindow(content, callback);
    },
    selectZone: function(olsonName) {
      var centroid = _zoneCentroids[olsonName];
      if (centroid) {
        mapClickHandler({
          latLng: {
            Qa: centroid[1],
            Ra: centroid[0]
          }
        });
      }
    }
  };

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

  _loaderGif = "data:image/gif;base64,R0lGODlhEAAQAPIAAKqqqv///729vejo6P///93d3dPT083NzSH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==";
  _maskPng = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDgA6CHKQBUUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADUlEQVQI12NgYGDwAQAAUQBNbrgEdAAAAABJRU5ErkJggg==";
})(jQuery);
<|endoftext|>


<commit_before>/**
 * Gulpfile
 *
 * @see https://travismaynard.com/writing/getting-started-with-gulp
 *
 * @since 0.1.0
 */

'use strict'

// Include gulp
const gulp = require('gulp')

// Include plugins
const jshint = require('gulp-jshint')
const uglify = require('gulp-uglify')
const rename = require('gulp-rename')
const rtlcss = require('gulp-rtlcss')
const cleanCSS = require('gulp-clean-css')
const sass = require('gulp-sass')

// Files/Paths
const js_files = ['./assets/scripts/**/*.js']
const js_dest = './dist/assets/scripts'
const sass_files = ['./assets/styles/**/*.scss']
const sass_dest = './dist/assets/styles'

// Lint JS
gulp.task('lint_js', () =>
    gulp.src(js_files)
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
)

// Minify JS
gulp.task('minify_js', () =>
    gulp.src(js_files)
    .pipe(uglify())
    .pipe(rename({'suffix' : '.min'}))
    .pipe(gulp.dest(js_dest))
)

// Compile scss, rtl and minify scss
gulp.task('compile_sass', () =>
    gulp.src(sass_files)
    .pipe(sass().on('error', sass.logError))
    // .pipe(cleanCSS({format: 'beautify'}))
    .pipe(gulp.dest(sass_dest))
    .pipe(cleanCSS())
    .pipe(rename({'suffix' : '.min'}))
    .pipe(gulp.dest(sass_dest))
    .pipe(rtlcss())
    .pipe(rename((path) =>
        path.basename = path.basename.replace('.min', '-rtl.min')
    ))
    .pipe(gulp.dest(sass_dest))
)

// Watch files for changes
gulp.task('watch', () => {
    gulp.watch(js_files, ['lint_js', 'minify_js'])
    gulp.watch(sass_files, ['compile_sass'])
})

// Default task
gulp.task('default', [
    'lint_js',
    'minify_js',
    'compile_sass',
    'watch'
])
<commit_msg>minor doc block update<commit_after>/**
 * Gulpfile
 *
 * @since 0.1.0
 */

'use strict'

// Include gulp
const gulp = require('gulp')

// Include plugins
const jshint = require('gulp-jshint')
const uglify = require('gulp-uglify')
const rename = require('gulp-rename')
const rtlcss = require('gulp-rtlcss')
const cleanCSS = require('gulp-clean-css')
const sass = require('gulp-sass')

// Files/Paths
const js_files = ['./assets/scripts/**/*.js']
const js_dest = './dist/assets/scripts'
const sass_files = ['./assets/styles/**/*.scss']
const sass_dest = './dist/assets/styles'

// Lint JS
gulp.task('lint_js', () =>
    gulp.src(js_files)
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
)

// Minify JS
gulp.task('minify_js', () =>
    gulp.src(js_files)
    .pipe(uglify())
    .pipe(rename({'suffix' : '.min'}))
    .pipe(gulp.dest(js_dest))
)

// Compile scss, rtl and minify scss
gulp.task('compile_sass', () =>
    gulp.src(sass_files)
    .pipe(sass().on('error', sass.logError))
    // .pipe(cleanCSS({format: 'beautify'}))
    .pipe(gulp.dest(sass_dest))
    .pipe(cleanCSS())
    .pipe(rename({'suffix' : '.min'}))
    .pipe(gulp.dest(sass_dest))
    .pipe(rtlcss())
    .pipe(rename((path) =>
        path.basename = path.basename.replace('.min', '-rtl.min')
    ))
    .pipe(gulp.dest(sass_dest))
)

// Watch files for changes
gulp.task('watch', () => {
    gulp.watch(js_files, ['lint_js', 'minify_js'])
    gulp.watch(sass_files, ['compile_sass'])
})

// Default task
gulp.task('default', [
    'lint_js',
    'minify_js',
    'compile_sass',
    'watch'
])
<|endoftext|>


<commit_before>"use strict";

require('../core/setup');

var $ = require('jquery'),
    L = require('leaflet'),
    assert = require('chai').assert,
    sinon = require('sinon'),
    Marionette = require('../../shim/backbone.marionette'),
    App = require('../app'),
    models = require('./models'),
    utils = require('./utils'),
    views = require('./views'),
    settings = require('../core/settings'),
    testUtils = require('../core/testUtils');

var sandboxId = 'sandbox',
    sandboxSelector = '#' + sandboxId,
    TEST_SHAPE = {
        'type': 'MultiPolygon',
        'coordinates': [[[-5e6, -1e6], [-4e6, 1e6], [-3e6, -1e6]]]
    };

var SandboxRegion = Marionette.Region.extend({
    el: sandboxSelector
});

describe('Draw', function() {
    before(function() {
        // Ensure that draw tools are enabled before testing

        settings.set('draw_tools', [
            'Draw',         // Custom Area or 1 Sq Km stamp
            'ResetDraw',
        ]);
    });

    beforeEach(function() {
        $('body').append('
');
    });

    afterEach(function() {
        $(sandboxSelector).remove();
        window.location.hash = '';
        testUtils.resetApp(App);
    });

    describe('ToolbarView', function() {
        // Setup the toolbar controls, enable/disable them, and verify
        // the correct CSS classes are applied.
        it('enables/disables toolbar controls when the model enableTools/disableTools methods are called', function() {
            var sandbox = new SandboxRegion(),
                $el = sandbox.$el,
                model = new models.ToolbarModel(),
                view = new views.ToolbarView({
                    model: model
                });

            sandbox.show(view);

            // Nothing should be disabled at this point.
            // Test that toggling the `toolsEnabled` property on the model
            // will disable all drawing tools.
            assert.equal($el.find('.disabled').size(), 0);
            model.disableTools();
            assert.equal($el.find('.disabled').size(), 3);
            model.enableTools();
            assert.equal($el.find('.disabled').size(), 0);
        });

        it('resets the current area of interest on Reset', function() {
            var setup = setupResetTestObject();

            App.map.set('areaOfInterest', TEST_SHAPE);
            setup.resetRegion.currentView.resetDrawingState();

            assert.isNull(App.map.get('areaOfInterest',
                                      'Area of Interest was not removed on reset from the map'));

        });

        it('resets the boundary layer on Reset', function() {
            var setup = setupResetTestObject(),
                ofg = L.featureGroup(),
                testFeature = {
                    "type": "Feature",
                    "geometry": {
                        "type": "Point",
                        "coordinates": [-104.99404, 39.75621]
                    }
                };

            ofg.addLayer(L.geoJson(testFeature));
            assert.equal(ofg.getLayers().length, 1);

            setup.model.set('outlineFeatureGroup', ofg);
            setup.resetRegion.currentView.resetDrawingState();
            assert.equal(ofg.getLayers().length, 0,
                         'Boundary Layer should have been removed from layer group');
        });

        it('removes in progress drawing on Reset', function() {
            var setup = setupResetTestObject(),
                spy = sinon.spy(utils, 'cancelDrawing');

            utils.drawPolygon(setup.map);
            setup.resetRegion.currentView.resetDrawingState();

            assert.equal(spy.callCount, 1);
        });
    });
});

function setupResetTestObject() {

    var sandbox = new SandboxRegion(),
        model = new models.ToolbarModel(),
        view = new views.ToolbarView({
            model: model
        }),
        resetRegion = view.getRegion('resetRegion'),
        map = App.getLeafletMap();

        sandbox.show(view);

    return {
        sandbox: sandbox,
        model: model,
        view: view,
        resetRegion: resetRegion,
        map: map
    };
}

function assertTextEqual($el, sel, text) {
    assert.equal($el.find(sel).text().trim(), text);
}
<commit_msg>Update frontend tests to reflect 1 draw tool<commit_after>"use strict";

require('../core/setup');

var $ = require('jquery'),
    L = require('leaflet'),
    assert = require('chai').assert,
    sinon = require('sinon'),
    Marionette = require('../../shim/backbone.marionette'),
    App = require('../app'),
    models = require('./models'),
    utils = require('./utils'),
    views = require('./views'),
    settings = require('../core/settings'),
    testUtils = require('../core/testUtils');

var sandboxId = 'sandbox',
    sandboxSelector = '#' + sandboxId,
    TEST_SHAPE = {
        'type': 'MultiPolygon',
        'coordinates': [[[-5e6, -1e6], [-4e6, 1e6], [-3e6, -1e6]]]
    };

var SandboxRegion = Marionette.Region.extend({
    el: sandboxSelector
});

describe('Draw', function() {
    before(function() {
        // Ensure that draw tools are enabled before testing

        settings.set('draw_tools', [
            'Draw',         // Custom Area or 1 Sq Km stamp
            'ResetDraw',
        ]);
    });

    beforeEach(function() {
        $('body').append('
');
    });

    afterEach(function() {
        $(sandboxSelector).remove();
        window.location.hash = '';
        testUtils.resetApp(App);
    });

    describe('ToolbarView', function() {
        // Setup the toolbar controls, enable/disable them, and verify
        // the correct CSS classes are applied.
        it('enables/disables toolbar controls when the model enableTools/disableTools methods are called', function() {
            var sandbox = new SandboxRegion(),
                $el = sandbox.$el,
                model = new models.ToolbarModel(),
                view = new views.ToolbarView({
                    model: model
                });

            sandbox.show(view);

            // Nothing should be disabled at this point.
            // Test that toggling the `toolsEnabled` property on the model
            // will disable all drawing tools.
            assert.equal($el.find('.disabled').size(), 0);
            model.disableTools();
            assert.equal($el.find('.disabled').size(), 1);
            model.enableTools();
            assert.equal($el.find('.disabled').size(), 0);
        });

        it('resets the current area of interest on Reset', function() {
            var setup = setupResetTestObject();

            App.map.set('areaOfInterest', TEST_SHAPE);
            setup.resetRegion.currentView.resetDrawingState();

            assert.isNull(App.map.get('areaOfInterest',
                                      'Area of Interest was not removed on reset from the map'));

        });

        it('resets the boundary layer on Reset', function() {
            var setup = setupResetTestObject(),
                ofg = L.featureGroup(),
                testFeature = {
                    "type": "Feature",
                    "geometry": {
                        "type": "Point",
                        "coordinates": [-104.99404, 39.75621]
                    }
                };

            ofg.addLayer(L.geoJson(testFeature));
            assert.equal(ofg.getLayers().length, 1);

            setup.model.set('outlineFeatureGroup', ofg);
            setup.resetRegion.currentView.resetDrawingState();
            assert.equal(ofg.getLayers().length, 0,
                         'Boundary Layer should have been removed from layer group');
        });

        it('removes in progress drawing on Reset', function() {
            var setup = setupResetTestObject(),
                spy = sinon.spy(utils, 'cancelDrawing');

            utils.drawPolygon(setup.map);
            setup.resetRegion.currentView.resetDrawingState();

            assert.equal(spy.callCount, 1);
        });
    });
});

function setupResetTestObject() {

    var sandbox = new SandboxRegion(),
        model = new models.ToolbarModel(),
        view = new views.ToolbarView({
            model: model
        }),
        resetRegion = view.getRegion('resetRegion'),
        map = App.getLeafletMap();

        sandbox.show(view);

    return {
        sandbox: sandbox,
        model: model,
        view: view,
        resetRegion: resetRegion,
        map: map
    };
}

function assertTextEqual($el, sel, text) {
    assert.equal($el.find(sel).text().trim(), text);
}
<|endoftext|>


<commit_before>/*
 * blisp
 * http://iantruslove.github.com/blisp/
 *
 * Copyright (c) 2013 Ian Truslove
 * Licensed under the MIT license.
 */

// UMD "returnExports" pattern
// See http://github.com/umdjs
(function (root, factory) {
    if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like enviroments that support module.exports,
        // like Node.
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(factory);
    } else {
        // Browser globals (root is window)
        root.returnExports = factory();
  }
}(this, function () {
    var exports = {},
        Token, BooleanToken, NumberToken, BinaryExpressionToken,
        ExpressionStatementStartToken, ExpressionStatementEndToken, Tokenizer,
        createTokenizer, parser, generator;

  parser = {
    isBoolean: function(value) {
      return (value === "#t" || value === "#f");
    },
    isNumber: function(value) {
      return (value.match(/[^0-9]/) === null);
    },
    isBinaryOperation: function(value) {
      return (value === "+");
    },
    isStartParen: function(value) {
      return (value === "(");
    },
    isClosingParen: function(value) {
      return (value === ")");
    },
    createToken: function(value) {
      if (parser.isBoolean(value)) {
        return new BooleanToken(value);
      } else if (parser.isNumber(value)) {
        return new NumberToken(value);
      } else if (parser.isBinaryOperation(value)) {
        return new BinaryExpressionToken(value);
      } else if (parser.isStartParen(value)) {
        return new ExpressionStatementStartToken(value);
      } else if (parser.isClosingParen(value)) {
        return new ExpressionStatementEndToken(value);
      }
    }
  };

  generator = {
    // reads blisp statements and returns escodegen-compatible AST string.
    generate: function (blispCode) {
      var expressionStatement, tokenizer, generatedCode;

      expressionStatement = {
        type: "ExpressionStatement",
        expression: null
      };

      tokenizer = (blispCode instanceof Tokenizer ? blispCode : new Tokenizer(blispCode));

      generatedCode = generator.rGenerate(tokenizer);

      expressionStatement.expression = generatedCode.astFragment;
      return expressionStatement;
    },

    rGenerate: function (tokenizer) {
      var token,
          firstParamData, secondParamData,
          thingToReturn = {};

      token = tokenizer.first();

      if (token instanceof ExpressionStatementStartToken) {
        thingToReturn.astFragment = {};

        // operator
        tokenizer = tokenizer.rest();
        thingToReturn.astFragment = tokenizer.first().parse();

        // first param
        tokenizer = tokenizer.rest();
        firstParamData = generator.rGenerate(tokenizer);
        thingToReturn.astFragment.left = firstParamData.astFragment;
        tokenizer = firstParamData.tokenizer;

        // second param
        tokenizer = tokenizer.rest();
        secondParamData = generator.rGenerate(tokenizer);
        thingToReturn.astFragment.right = secondParamData.astFragment;
        tokenizer = secondParamData.tokenizer;

        // closing paren
        tokenizer = tokenizer.rest();

      } else {
        thingToReturn.astFragment = token.parse();
      }

      thingToReturn.tokenizer = tokenizer;
      return thingToReturn;
    }
  };

  BooleanToken = function (token) {
    this.token = token;
    return this;
  };
  BooleanToken.prototype.parse = function () {
    if (this.token === "#t") { return { type: "Literal", value: true }; }
    if (this.token === "#f") { return { type: "Literal", value: false }; }
  };

  NumberToken = function (token) {
    this.token = token;
    return this;
  };
  NumberToken.prototype.parse = function () {
    return { type: "Literal", value: parseInt(this.token, 10) };
  };

  BinaryExpressionToken = function (token) {
    this.token = token;
    return this;
  };
  BinaryExpressionToken.prototype.parse = function () {
    return {
      type: "BinaryExpression",
      operator: this.token,
      left: null,
      right: null
    };
  };

  ExpressionStatementStartToken = function (token) {
    this.token = token;
    return this;
  };
  ExpressionStatementStartToken.prototype.parse = function () {
    return {};
  };

  ExpressionStatementEndToken = function (token) {
    this.token = token;
    return this;
  };
  ExpressionStatementEndToken.prototype.parse = function () {
    return {};
  };

  Tokenizer = function (code) {
    var matchedTerms;

    this.untokenizedString = code.trimLeft();
    matchedTerms = this.untokenizedString.match(/^(\(|\)|(?:[^\s()]+))(.*)$/);

    this.firstToken = (matchedTerms !== null ? matchedTerms[1] : "");
    this.restOfTokens = (matchedTerms !== null ? matchedTerms[2] : "");

    return this;
  };

  Tokenizer.prototype.first = function () {
    return parser.createToken(this.firstToken.trimRight());
  };

  Tokenizer.prototype.rest = function () {
    if (this.restOfTokens.trimRight() === "") {
      return null;
    }
    return new Tokenizer(this.restOfTokens);
  };

  Tokenizer.prototype.getCode = function () {
    return this.untokenizedString;
  };

  createTokenizer = function(code) {
    return new Tokenizer(code);
  };

  exports.parser = parser;
  exports.generator = generator;
  exports.createTokenizer = createTokenizer;
  exports.Token = Token;
  exports.BooleanToken = BooleanToken;
  exports.Tokenizer = Tokenizer;
  exports.NumberToken = NumberToken;
  exports.BinaryExpressionToken = BinaryExpressionToken;
  exports.ExpressionStatementStartToken = ExpressionStatementStartToken;
  exports.ExpressionStatementEndToken = ExpressionStatementEndToken;

  return exports;
}));

<commit_msg>Threw a couple more arithmetic operators in<commit_after>/*
 * blisp
 * http://iantruslove.github.com/blisp/
 *
 * Copyright (c) 2013 Ian Truslove
 * Licensed under the MIT license.
 */

// UMD "returnExports" pattern
// See http://github.com/umdjs
(function (root, factory) {
    if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like enviroments that support module.exports,
        // like Node.
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(factory);
    } else {
        // Browser globals (root is window)
        root.returnExports = factory();
  }
}(this, function () {
    var exports = {},
        Token, BooleanToken, NumberToken, BinaryExpressionToken,
        ExpressionStatementStartToken, ExpressionStatementEndToken, Tokenizer,
        createTokenizer, parser, generator;

  parser = {
    isBoolean: function(value) {
      return (value === "#t" || value === "#f");
    },
    isNumber: function(value) {
      return (value.match(/[^0-9]/) === null);
    },
    isBinaryOperation: function(value) {
      return (value.match(/^[+-\/\*]$/) !== null);
    },
    isStartParen: function(value) {
      return (value === "(");
    },
    isClosingParen: function(value) {
      return (value === ")");
    },
    createToken: function(value) {
      if (parser.isBoolean(value)) {
        return new BooleanToken(value);
      } else if (parser.isNumber(value)) {
        return new NumberToken(value);
      } else if (parser.isBinaryOperation(value)) {
        return new BinaryExpressionToken(value);
      } else if (parser.isStartParen(value)) {
        return new ExpressionStatementStartToken(value);
      } else if (parser.isClosingParen(value)) {
        return new ExpressionStatementEndToken(value);
      }
    }
  };

  generator = {
    // reads blisp statements and returns escodegen-compatible AST string.
    generate: function (blispCode) {
      var expressionStatement, tokenizer, generatedCode;

      expressionStatement = {
        type: "ExpressionStatement",
        expression: null
      };

      tokenizer = (blispCode instanceof Tokenizer ? blispCode : new Tokenizer(blispCode));

      generatedCode = generator.rGenerate(tokenizer);

      expressionStatement.expression = generatedCode.astFragment;
      return expressionStatement;
    },

    rGenerate: function (tokenizer) {
      var token,
          firstParamData, secondParamData,
          thingToReturn = {};

      token = tokenizer.first();

      if (token instanceof ExpressionStatementStartToken) {
        thingToReturn.astFragment = {};

        // operator
        tokenizer = tokenizer.rest();
        thingToReturn.astFragment = tokenizer.first().parse();

        // first param
        tokenizer = tokenizer.rest();
        firstParamData = generator.rGenerate(tokenizer);
        thingToReturn.astFragment.left = firstParamData.astFragment;
        tokenizer = firstParamData.tokenizer;

        // second param
        tokenizer = tokenizer.rest();
        secondParamData = generator.rGenerate(tokenizer);
        thingToReturn.astFragment.right = secondParamData.astFragment;
        tokenizer = secondParamData.tokenizer;

        // closing paren
        tokenizer = tokenizer.rest();

      } else {
        thingToReturn.astFragment = token.parse();
      }

      thingToReturn.tokenizer = tokenizer;
      return thingToReturn;
    }
  };

  BooleanToken = function (token) {
    this.token = token;
    return this;
  };
  BooleanToken.prototype.parse = function () {
    if (this.token === "#t") { return { type: "Literal", value: true }; }
    if (this.token === "#f") { return { type: "Literal", value: false }; }
  };

  NumberToken = function (token) {
    this.token = token;
    return this;
  };
  NumberToken.prototype.parse = function () {
    return { type: "Literal", value: parseInt(this.token, 10) };
  };

  BinaryExpressionToken = function (token) {
    this.token = token;
    return this;
  };
  BinaryExpressionToken.prototype.parse = function () {
    return {
      type: "BinaryExpression",
      operator: this.token,
      left: null,
      right: null
    };
  };

  ExpressionStatementStartToken = function (token) {
    this.token = token;
    return this;
  };
  ExpressionStatementStartToken.prototype.parse = function () {
    return {};
  };

  ExpressionStatementEndToken = function (token) {
    this.token = token;
    return this;
  };
  ExpressionStatementEndToken.prototype.parse = function () {
    return {};
  };

  Tokenizer = function (code) {
    var matchedTerms;

    this.untokenizedString = code.trimLeft();
    matchedTerms = this.untokenizedString.match(/^(\(|\)|(?:[^\s()]+))(.*)$/);

    this.firstToken = (matchedTerms !== null ? matchedTerms[1] : "");
    this.restOfTokens = (matchedTerms !== null ? matchedTerms[2] : "");

    return this;
  };

  Tokenizer.prototype.first = function () {
    return parser.createToken(this.firstToken.trimRight());
  };

  Tokenizer.prototype.rest = function () {
    if (this.restOfTokens.trimRight() === "") {
      return null;
    }
    return new Tokenizer(this.restOfTokens);
  };

  Tokenizer.prototype.getCode = function () {
    return this.untokenizedString;
  };

  createTokenizer = function(code) {
    return new Tokenizer(code);
  };

  exports.parser = parser;
  exports.generator = generator;
  exports.createTokenizer = createTokenizer;
  exports.Token = Token;
  exports.BooleanToken = BooleanToken;
  exports.Tokenizer = Tokenizer;
  exports.NumberToken = NumberToken;
  exports.BinaryExpressionToken = BinaryExpressionToken;
  exports.ExpressionStatementStartToken = ExpressionStatementStartToken;
  exports.ExpressionStatementEndToken = ExpressionStatementEndToken;

  return exports;
}));

<|endoftext|>


<commit_before>const isInViewport = el => {
  let top = el.offsetTop,
    left = el.offsetLeft,
    width = el.offsetWidth,
    height = el.offsetHeight;

  if (el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }
  return (
    top < window.pageYOffset + window.innerHeight &&
    left < window.pageXOffset + window.innerWidth &&
    top + height > window.pageYOffset &&
    left + width > window.pageXOffset
  );
};

const debounce = (func, delay) => {
  let inDebounce;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(inDebounce);
    inDebounce = setTimeout(() => func.apply(context, args), delay);
  };
};

const throttle = (func, limit) => {
  let inThrottle;
  return function() {
    const args = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
};

const fadeInIfInViewport = els => {
  els.forEach(el => {
    if (isInViewport(el)) {
      el.classList.add('in');
    }
  });
};

document.addEventListener('readystatechange', event => {
  let elsToFadeIn = document.querySelectorAll('.js-fade-in');

  if (event.target.readyState === 'interactive') {
    // mobile nav
    document.querySelectorAll('.js-toggle-mobile-nav').forEach(el => {
      el.onclick = () => {
        document.querySelector('.takeover').classList.toggle('active');
      };
    });

    // ongoing fade-ins
    elsToFadeIn = document.querySelectorAll('.js-fade-in');
    window.addEventListener(
      'scroll',
      throttle(() => fadeInIfInViewport(elsToFadeIn), 200)
    );
    window.addEventListener(
      'scroll',
      debounce(() => fadeInIfInViewport(elsToFadeIn), 300)
    );
  }
  if (event.target.readyState === 'complete') {
    document.querySelector('.js-body-fade-in').classList.add('complete');

    // initial fade-ins
    fadeInIfInViewport(elsToFadeIn);
  }
});
<commit_msg>fixup fade-ins - accommodate window.resize<commit_after>const isInViewport = el => {
  let top = el.offsetTop,
    left = el.offsetLeft,
    width = el.offsetWidth,
    height = el.offsetHeight;

  if (el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }
  return (
    top < window.pageYOffset + window.innerHeight &&
    left < window.pageXOffset + window.innerWidth &&
    top + height > window.pageYOffset &&
    left + width > window.pageXOffset
  );
};

const debounce = (func, delay) => {
  let inDebounce;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(inDebounce);
    inDebounce = setTimeout(() => func.apply(context, args), delay);
  };
};

const throttle = (func, limit) => {
  let inThrottle;
  return function() {
    const args = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
};

const fadeInIfInViewport = els => {
  els.forEach(el => {
    if (isInViewport(el)) {
      el.classList.add('in');
    }
  });
};

document.addEventListener('readystatechange', event => {
  let elsToFadeIn = document.querySelectorAll('.js-fade-in');

  if (event.target.readyState === 'interactive') {
    // mobile nav
    document.querySelectorAll('.js-toggle-mobile-nav').forEach(el => {
      el.onclick = () => {
        document.querySelector('.takeover').classList.toggle('active');
      };
    });

    // ongoing fade-ins
    elsToFadeIn = document.querySelectorAll('.js-fade-in');
    window.addEventListener(
      'scroll',
      throttle(() => fadeInIfInViewport(elsToFadeIn), 200)
    );
    window.addEventListener(
      'scroll',
      debounce(() => fadeInIfInViewport(elsToFadeIn), 300)
    );
    window.onresize = debounce(() => fadeInIfInViewport(elsToFadeIn), 350);
  }

  if (event.target.readyState === 'complete') {
    document.querySelector('.js-body-fade-in').classList.add('complete');

    // initial fade-ins
    fadeInIfInViewport(elsToFadeIn);
  }
});
<|endoftext|>


<commit_before>define([
    'vendors/go-phantomizer/phantomizer',
    'vendors/go-qunit/loader',
    'vendors/go-dashboard/loader',
    'vendors/go-device-preview/loader'
],function(phantomizer, QUnitLoader, DashBoardLoader,DevicePreviewLoader){

    QUnitLoader = new QUnitLoader();
    DashBoardLoader = new DashBoardLoader();
    DevicePreviewLoader = new DevicePreviewLoader();

    if( window.is_built ){

        DevicePreviewLoader.load(function(has_loaded){
            if( has_loaded ){
                DevicePreviewLoader.start(function(){
                    DashBoardLoader.load();
                    DashBoardLoader.start();
                });
            }else{
                DashBoardLoader.load();
                DashBoardLoader.start(function(){
                    QUnitLoader.load(function(){
                        QUnitLoader.start();
                    });
                });
            }
        });

    }else{
        var has_loaded_preview = false;
        phantomizer.beforeRender(function(next){
            DevicePreviewLoader.load(function(has_loaded){
                has_loaded_preview = has_loaded;
                if( has_loaded ){
                    DevicePreviewLoader.start(function(){
                        DashBoardLoader.load();
                        DashBoardLoader.start(function(){ /* explicitly don't go next : to not render the page */ });
                    });
                }else{
                    QUnitLoader.load(next);
                }

            });
        });

        phantomizer.afterClientRender(function(next){
            if( has_loaded_preview ){
                //next(); // should never reach that statement
            }else{
                DashBoardLoader.load();
                DashBoardLoader.start(next);
            }
        });
        phantomizer.afterRender(function(next){
            if( has_loaded_preview ){
                //next();; // should never reach that statement, neither
            }else{
// starts Qunit testing phase
                QUnitLoader.start(next);
            }
        });
    }
});<commit_msg>Fixed too early triggering of Qunit when the page is built Soltuion is not perfect, but workable<commit_after>define([
    'vendors/go-phantomizer/phantomizer',
    'vendors/go-qunit/loader',
    'vendors/go-dashboard/loader',
    'vendors/go-device-preview/loader'
],function(phantomizer, QUnitLoader, DashBoardLoader,DevicePreviewLoader){

    QUnitLoader = new QUnitLoader();
    DashBoardLoader = new DashBoardLoader();
    DevicePreviewLoader = new DevicePreviewLoader();

    if( window.is_built ){

        DevicePreviewLoader.load(function(has_loaded){
            if( has_loaded ){
                DevicePreviewLoader.start(function(){
                    DashBoardLoader.load();
                    DashBoardLoader.start();
                });
            }else{
                DashBoardLoader.load();
                DashBoardLoader.start(function(){
                    QUnitLoader.load(function(){
                        window.setTimeout(function(){ // we must not rely on timeout, but that s the better solution found on that very moment
                            QUnitLoader.start();
                        },2500);
                    });
                });
            }
        });

    }else{
        var has_loaded_preview = false;
        phantomizer.beforeRender(function(next){
            DevicePreviewLoader.load(function(has_loaded){
                has_loaded_preview = has_loaded;
                if( has_loaded ){
                    DevicePreviewLoader.start(function(){
                        DashBoardLoader.load();
                        DashBoardLoader.start(function(){ /* explicitly don't go next : to not render the page */ });
                    });
                }else{
                    QUnitLoader.load(next);
                }

            });
        });

        phantomizer.afterClientRender(function(next){
            if( has_loaded_preview ){
                //next(); // should never reach that statement
            }else{
                DashBoardLoader.load();
                DashBoardLoader.start(next);
            }
        });
        phantomizer.afterRender(function(next){
            if( has_loaded_preview ){
                //next();; // should never reach that statement, neither
            }else{
// starts Qunit testing phase
                QUnitLoader.start(next);
            }
        });
    }
});<|endoftext|>


<commit_before>import React from 'react';
import State from './state';
import throwError from './throw-error';

export default function(Component) {
    return class DataWatcher extends Component {
        static contextTypes = {
            state: React.PropTypes.instanceOf(State)
        };

        constructor(props, context) {
            super(props, context);

            this.cursors = {};
            this.dataState = context.state;

            if (typeof this.dataState === 'undefined') {
                throwError('missing state. It can happen if you forgot to set state in DataInit or using any decorator before DataInit', this.constructor.displayName);
            }

            this._initCursors(props, context);

            this.state = {
                ...this.state,
                data: this._getCurrentData()
            };

            this._updateDataState = this._updateDataState.bind(this);
        }

        componentDidMount() {
            if (super.componentDidMount) {
                super.componentDidMount();
            }

            Object.keys(this.cursors).forEach(branch => {
                this.dataState.addWaitingCursor(this.cursors[branch]);
            });

            this._updateDataState();
            this._dataWatch();
        }

        componentWillUnmount() {
            if (super.componentWillUnmount) {
                super.componentWillUnmount();
            }

            this._dataUnwatch();
        }

        _reloadData(props = this.props) {
            this._dataUnwatch();
            this._initCursors(props);
            this._updateDataState();
            this._dataWatch();
        }

        _resetData() {
            this._resetDataIn(Object.keys(this.cursors));
        }

        _resetDataIn(cursorsTypes) {
            cursorsTypes.forEach(cursorType => {
                const cursor = this.cursors[cursorType];

                if (cursor.exists()) {
                    cursor.unset();
                }
            });
            this._updateDataState();
        }

        _isValidPath(path) {
            return path.every(
                pathChunk => typeof pathChunk !== 'undefined'
            );
        }

        _prepareCursorPath(path) {
            const stateTree = this.dataState.getTree();

            return path.map(pathChunk => {
                if (Array.isArray(pathChunk)) {
                    const preparedCursorPath = this._prepareCursorPath(pathChunk);

                    if (!this._isValidPath(preparedCursorPath)) {
                        return;
                    }

                    const pathChunkCursor = stateTree.select(preparedCursorPath);

                    pathChunkCursor.once('update', ::this._reloadData);

                    return pathChunkCursor.get();
                }

                return pathChunk;
            });
        }

        _prepareCursorPaths(props) {
            const cursorPaths = this.constructor.data(props, this.state);

            Object.keys(cursorPaths).forEach(branch => {
                cursorPaths[branch] = this._prepareCursorPath(cursorPaths[branch]);
            });

            return cursorPaths;
        }

        _initCursors(props) {
            const stateTree = this.dataState.getTree();

            this.cursorPaths = this._prepareCursorPaths(props);

            Object.keys(this.cursorPaths)
                .filter(branch => this._isValidPath(this.cursorPaths[branch]))
                .forEach(branch => {
                    this.cursors[branch] = stateTree.select(this.cursorPaths[branch]);
                });
        }

        _dataWatch() {
            Object.keys(this.cursors).forEach(
                cursorType => this.cursors[cursorType].on('update', this._updateDataState)
            );
        }

        _dataUnwatch() {
            Object.keys(this.cursors).forEach(
                cursorType => this.cursors[cursorType].off('update', this._updateDataState)
            );
        }

        _getCurrentData() {
            const data = {};

            Object.keys(this.cursors).forEach(branch => {
                data[branch] = this.cursors[branch].get();
            });

            return data;
        }

        _updateDataState() {
            this.setState({
                data: this._getCurrentData()
            });
        }
    };
}
<commit_msg>:wrench: refactor DataWatcher to accept dataFactory throw decorator arguments instead of class static property<commit_after>import React from 'react';
import State from './state';
import throwError from './throw-error';

export default function(dataFactory) {
    return function(Component) {
        return class DataWatcher extends Component {
            static contextTypes = {
                state: React.PropTypes.instanceOf(State)
            };

            constructor(props, context) {
                super(props, context);

                this.cursors = {};
                this.dataState = context.state;

                if (typeof this.dataState === 'undefined') {
                    throwError('missing state. It can happen if you forgot to set state in DataInit or using any decorator before DataInit', this.constructor.displayName);
                }

                this._initCursors(props, context);

                this.state = {
                    ...this.state,
                    data: this._getCurrentData()
                };

                this._updateDataState = this._updateDataState.bind(this);
            }

            componentDidMount() {
                if (super.componentDidMount) {
                    super.componentDidMount();
                }

                Object.keys(this.cursors).forEach(branch => {
                    this.dataState.addWaitingCursor(this.cursors[branch]);
                });

                this._updateDataState();
                this._dataWatch();
            }

            componentWillUnmount() {
                if (super.componentWillUnmount) {
                    super.componentWillUnmount();
                }

                this._dataUnwatch();
            }

            _reloadData(props = this.props) {
                this._dataUnwatch();
                this._initCursors(props);
                this._updateDataState();
                this._dataWatch();
            }

            _resetData() {
                this._resetDataIn(Object.keys(this.cursors));
            }

            _resetDataIn(cursorsTypes) {
                cursorsTypes.forEach(cursorType => {
                    const cursor = this.cursors[cursorType];

                    if (cursor.exists()) {
                        cursor.unset();
                    }
                });
                this._updateDataState();
            }

            _isValidPath(path) {
                return path.every(
                    pathChunk => typeof pathChunk !== 'undefined'
                );
            }

            _prepareCursorPath(path) {
                const stateTree = this.dataState.getTree();

                return path.map(pathChunk => {
                    if (Array.isArray(pathChunk)) {
                        const preparedCursorPath = this._prepareCursorPath(pathChunk);

                        if (!this._isValidPath(preparedCursorPath)) {
                            return;
                        }

                        const pathChunkCursor = stateTree.select(preparedCursorPath);

                        pathChunkCursor.once('update', ::this._reloadData);

                        return pathChunkCursor.get();
                    }

                    return pathChunk;
                });
            }

            _prepareCursorPaths(props) {
                const cursorPaths = dataFactory(props, this.state);

                Object.keys(cursorPaths).forEach(branch => {
                    cursorPaths[branch] = this._prepareCursorPath(cursorPaths[branch]);
                });

                return cursorPaths;
            }

            _initCursors(props) {
                const stateTree = this.dataState.getTree();

                this.cursorPaths = this._prepareCursorPaths(props);

                Object.keys(this.cursorPaths)
                    .filter(branch => this._isValidPath(this.cursorPaths[branch]))
                    .forEach(branch => {
                        this.cursors[branch] = stateTree.select(this.cursorPaths[branch]);
                    });
            }

            _dataWatch() {
                Object.keys(this.cursors).forEach(
                    cursorType => this.cursors[cursorType].on('update', this._updateDataState)
                );
            }

            _dataUnwatch() {
                Object.keys(this.cursors).forEach(
                    cursorType => this.cursors[cursorType].off('update', this._updateDataState)
                );
            }

            _getCurrentData() {
                const data = {};

                Object.keys(this.cursors).forEach(branch => {
                    data[branch] = this.cursors[branch].get();
                });

                return data;
            }

            _updateDataState() {
                this.setState({
                    data: this._getCurrentData()
                });
            }
        }
    }
}
<|endoftext|>