<commit_before>angular.module('JiNGle.directives').directive('jiservertable', function($http) {
    'use strict';

    return {
        restrict: 'AE',
        replace: 'true',
        templateUrl: JiNGle.viewPath + 'ServerTableDirective.html',

        scope: {
            columns: '=columns',
            source: '@source',

            create: '@create',
            show: '@show',
            pdf: '@pdf',
            edit: '@edit',
            remove: '@delete'
        },

        link: function($scope, element, attrs) {
            var load = function() {
                $scope.ready = false;

                var selection = {
                    skip: $scope.collection.skip,
                    limit: $scope.collection.limit,

                    query: ($scope.collection.query || ""),

                    filter: $scope.collection.filter,
                    order: $scope.collection.sortOrder
                };

                $http.put(attrs.source, selection).success(function(data) {
                    $scope.collection = data;

                    $scope.page = Math.ceil((data.skip || 0) / data.limit) + 1;
                    $scope.pageQuantity = Math.ceil(data.selectionSize / data.limit);

                    $scope.ready = true;
                });
            };

            $scope.sizes = [ 5, 10, 25, 50, 100 ];

            $scope.ready = false;

            $scope.page = 1;
            $scope.pageQuantity = 1;
            $scope.pagesInSelector = 10;

            $scope.collection = {
                collectionSize: 0,
                selectionSize: 0,

                skip: 0,
                limit: $scope.sizes[2],

                query: '',
                filter: {},
                sortOrder: {},

                data: [],
                meta: {}
            };

            $scope.isSortedAsc = function(field) { return $scope.collection.sortOrder[field] === 1; };
            $scope.isSortedDesc = function(field) { return $scope.collection.sortOrder[field] === -1; };

            $scope.isFiltered = function(field) { return $scope.collection.filter[field]; };

            $scope.sortName = function(field) {
                var rule = {};

                if ($scope.isSortedAsc(field)) {
                    rule[field] = -1;
                } else if ($scope.isSortedDesc(field)) {
                    if ($scope.collection.meta[field]
                        && $scope.collection.meta[field].defaultSort
                        && Object.keys($scope.collection.sortOrder).length === 1
                        && $scope.collection.sortOrder[field]) {

                        rule[field] = 1
                    }
                } else {
                    rule[field] = 1;
                }

                Object.keys($scope.collection.sortOrder, function(key) {
                    if (key == field) return;
                    rule[key] = $scope.collection.sortOrder[key];
                });

                $scope.collection.sortOrder = rule;

                load();
            };

            $scope.settings = {
                pageSize: $scope.collection.limit
            };

            $scope.changeSettings = function() {
                $scope.collection.limit = $scope.settings.pageSize;
                $scope.collection.skip = 0;

                load();
            };

            $scope.startSearch = function() {
                $scope.collection.skip = 0;
                $scope.collection.query = $scope.settings.query;

                load();
            };

            $scope.goToPage = function(page) {
                $scope.collection.skip = (page - 1) * $scope.settings.pageSize;

                load();
            };

            $scope.cellFormat = function(row, column) {
                return column.format ? column.format(row[column.field]) : row[column.field];
            };

            $scope.asRow = function(value, field) {
                var row = {};
                row[field] = value;
                return row;
            };

            $scope.isVisibleSelector = function(page) {
                return page == 1
                    || page == $scope.pageQuantity
                    || page <= $scope.page + $scope.pagesInSelector
                        && page >= $scope.page - $scope.pagesInSelector;
            };

            load();
        }
    }
});
<commit_msg>Add row format<commit_after>angular.module('JiNGle.directives').directive('jiservertable', function($http) {
    'use strict';

    return {
        restrict: 'AE',
        replace: 'true',
        templateUrl: JiNGle.viewPath + 'ServerTableDirective.html',

        scope: {
            columns: '=columns',
            source: '@source',

            create: '@create',
            show: '@show',
            pdf: '@pdf',
            edit: '@edit',
            remove: '@delete'
        },

        link: function($scope, element, attrs) {
            var load = function() {
                $scope.ready = false;

                var selection = {
                    skip: $scope.collection.skip,
                    limit: $scope.collection.limit,

                    query: ($scope.collection.query || ""),

                    filter: $scope.collection.filter,
                    order: $scope.collection.sortOrder
                };

                $http.put(attrs.source, selection).success(function(data) {
                    $scope.collection = data;

                    $scope.page = Math.ceil((data.skip || 0) / data.limit) + 1;
                    $scope.pageQuantity = Math.ceil(data.selectionSize / data.limit);

                    $scope.ready = true;
                });
            };

            $scope.sizes = [ 5, 10, 25, 50, 100 ];

            $scope.ready = false;

            $scope.page = 1;
            $scope.pageQuantity = 1;
            $scope.pagesInSelector = 10;

            $scope.collection = {
                collectionSize: 0,
                selectionSize: 0,

                skip: 0,
                limit: $scope.sizes[2],

                query: '',
                filter: {},
                sortOrder: {},

                data: [],
                meta: {}
            };

            $scope.isSortedAsc = function(field) { return $scope.collection.sortOrder[field] === 1; };
            $scope.isSortedDesc = function(field) { return $scope.collection.sortOrder[field] === -1; };

            $scope.isFiltered = function(field) { return $scope.collection.filter[field]; };

            $scope.sortName = function(field) {
                var rule = {};

                if ($scope.isSortedAsc(field)) {
                    rule[field] = -1;
                } else if ($scope.isSortedDesc(field)) {
                    if ($scope.collection.meta[field]
                        && $scope.collection.meta[field].defaultSort
                        && Object.keys($scope.collection.sortOrder).length === 1
                        && $scope.collection.sortOrder[field]) {

                        rule[field] = 1
                    }
                } else {
                    rule[field] = 1;
                }

                Object.keys($scope.collection.sortOrder, function(key) {
                    if (key == field) return;
                    rule[key] = $scope.collection.sortOrder[key];
                });

                $scope.collection.sortOrder = rule;

                load();
            };

            $scope.settings = {
                pageSize: $scope.collection.limit
            };

            $scope.changeSettings = function() {
                $scope.collection.limit = $scope.settings.pageSize;
                $scope.collection.skip = 0;

                load();
            };

            $scope.startSearch = function() {
                $scope.collection.skip = 0;
                $scope.collection.query = $scope.settings.query;

                load();
            };

            $scope.goToPage = function(page) {
                $scope.collection.skip = (page - 1) * $scope.settings.pageSize;

                load();
            };

            $scope.cellFormat = function(row, column) {
                if (column.format) {
                    if (column.field) return column.format(row[column.field]);

                    return column.format(row);
                }

                return row[column.field];
            };

            $scope.asRow = function(value, field) {
                var row = {};
                row[field] = value;
                return row;
            };

            $scope.isVisibleSelector = function(page) {
                return page == 1
                    || page == $scope.pageQuantity
                    || page <= $scope.page + $scope.pagesInSelector
                        && page >= $scope.page - $scope.pagesInSelector;
            };

            load();
        }
    }
});
<|endoftext|>


<commit_before>import core from 'core-js';

if (Element && !Element.prototype.matches) {
    var proto = Element.prototype;
    proto.matches = proto.matchesSelector ||
      proto.mozMatchesSelector || proto.msMatchesSelector ||
      proto.oMatchesSelector || proto.webkitMatchesSelector;
}

var placeholder = [];

function findInsertionPoint(groups, index){
  var insertionPoint;

  while(!insertionPoint && index >= 0){
    insertionPoint = groups[index][0];
    index--;
  }

  return insertionPoint || anchor;
}

export class ContentSelector {
  static applySelectors(view, contentSelectors, callback){
    var currentChild = view.fragment.firstChild,
                       contentMap = new Map(),
                       nextSibling, i, ii, contentSelector;

    while (currentChild) {
      nextSibling = currentChild.nextSibling;

      if(currentChild.viewSlot){
        var viewSlotSelectors = contentSelectors.map(x => x.copyForViewSlot());
        currentChild.viewSlot.installContentSelectors(viewSlotSelectors);
      }else{
        for(i = 0, ii = contentSelectors.length; i < ii; i++){
          contentSelector = contentSelectors[i];
          if(contentSelector.matches(currentChild)){
            var elements = contentMap.get(contentSelector);
            if(!elements){
              elements = [];
              contentMap.set(contentSelector, elements);
            }

            elements.push(currentChild);
            break;
          }
        }
      }

      currentChild = nextSibling;
    }

    for(i = 0, ii = contentSelectors.length; i < ii; ++i){
      contentSelector = contentSelectors[i];
      callback(contentSelector, contentMap.get(contentSelector) || placeholder);
    }
  }

  constructor(anchor, selector){
    this.anchor = anchor;
    this.selector = selector;
    this.all = !this.selector;
    this.groups = [];
  }

  copyForViewSlot(){
    return new ContentSelector(this.anchor, this.selector);
  }

  matches(node){
    return this.all ||
      (node.nodeType === 1 && node.matches(this.selector));
  }

  add(group){
    var anchor = this.anchor,
        parent = anchor.parentNode,
        i, ii;

    for(i = 0, ii = group.length; i < ii; ++i){
      parent.insertBefore(group[i], anchor);
    }

    this.groups.push(group);
  }

  insert(index, group){
    if(group.length){
      var anchor = findInsertionPoint(this.groups, index) || this.anchor,
          parent = anchor.parentNode,
          i, ii;

      for(i = 0, ii = group.length; i < ii; ++i){
        parent.insertBefore(group[i], anchor);
      }
    }

    this.groups.splice(index, 0, group);
  }

  removeAt(index, fragment){
    var group = this.groups[index],
        i, ii;

    for(i = 0, ii = group.length; i < ii; ++i){
      fragment.appendChild(group[i]);
    }

    this.groups.splice(index, 1);
  }
}
<commit_msg>fix(content-selector): remove undefined variable<commit_after>import core from 'core-js';

if (Element && !Element.prototype.matches) {
    var proto = Element.prototype;
    proto.matches = proto.matchesSelector ||
      proto.mozMatchesSelector || proto.msMatchesSelector ||
      proto.oMatchesSelector || proto.webkitMatchesSelector;
}

var placeholder = [];

function findInsertionPoint(groups, index){
  var insertionPoint;

  while(!insertionPoint && index >= 0){
    insertionPoint = groups[index][0];
    index--;
  }

  return insertionPoint;
}

export class ContentSelector {
  static applySelectors(view, contentSelectors, callback){
    var currentChild = view.fragment.firstChild,
                       contentMap = new Map(),
                       nextSibling, i, ii, contentSelector;

    while (currentChild) {
      nextSibling = currentChild.nextSibling;

      if(currentChild.viewSlot){
        var viewSlotSelectors = contentSelectors.map(x => x.copyForViewSlot());
        currentChild.viewSlot.installContentSelectors(viewSlotSelectors);
      }else{
        for(i = 0, ii = contentSelectors.length; i < ii; i++){
          contentSelector = contentSelectors[i];
          if(contentSelector.matches(currentChild)){
            var elements = contentMap.get(contentSelector);
            if(!elements){
              elements = [];
              contentMap.set(contentSelector, elements);
            }

            elements.push(currentChild);
            break;
          }
        }
      }

      currentChild = nextSibling;
    }

    for(i = 0, ii = contentSelectors.length; i < ii; ++i){
      contentSelector = contentSelectors[i];
      callback(contentSelector, contentMap.get(contentSelector) || placeholder);
    }
  }

  constructor(anchor, selector){
    this.anchor = anchor;
    this.selector = selector;
    this.all = !this.selector;
    this.groups = [];
  }

  copyForViewSlot(){
    return new ContentSelector(this.anchor, this.selector);
  }

  matches(node){
    return this.all ||
      (node.nodeType === 1 && node.matches(this.selector));
  }

  add(group){
    var anchor = this.anchor,
        parent = anchor.parentNode,
        i, ii;

    for(i = 0, ii = group.length; i < ii; ++i){
      parent.insertBefore(group[i], anchor);
    }

    this.groups.push(group);
  }

  insert(index, group){
    if(group.length){
      var anchor = findInsertionPoint(this.groups, index) || this.anchor,
          parent = anchor.parentNode,
          i, ii;

      for(i = 0, ii = group.length; i < ii; ++i){
        parent.insertBefore(group[i], anchor);
      }
    }

    this.groups.splice(index, 0, group);
  }

  removeAt(index, fragment){
    var group = this.groups[index],
        i, ii;

    for(i = 0, ii = group.length; i < ii; ++i){
      fragment.appendChild(group[i]);
    }

    this.groups.splice(index, 1);
  }
}
<|endoftext|>


<commit_before>export const nicfiLayers = ["2015-12_2016-05",
                            "2016-06_2016-11",
                            "2016-12_2017-05",
                            "2017-06_2017-11",
                            "2017-12_2018-05",
                            "2018-06_2018-11",
                            "2018-12_2019-05",
                            "2019-06_2019-11",
                            "2019-12_2020-05",
                            "2020-06_2020-08",
                            "2020-09",
                            "2020-10",
                            "2020-11",
                            "2020-12",
                            "2021-01",
                            "2021-02"];

const outOfRange = (num, low, high) => isNaN(num) || parseInt(num) < low || parseInt(num) > high;

const dateRangeValidator = ({startDate, endDate}) => startDate && endDate
    && new Date(startDate) > new Date(endDate) ? "Start date must be smaller than the end date." : "";

const urlValidator = url => /^(?:http|https):\/\/[\w.-]+(?:\.[\w-]+)+[\w\-.,@?^=%&:;/~\\+#]+$/.test(url);

const isValidJSON = str => {
    try {
        JSON.parse(str);
        return true;
    } catch (e) {
        return false;
    }
};

export const imageryOptions = [
    // Default type is text, default parent is none, a referenced parent must be entered as a json string
    // Parameters can be defined one level deep. {paramParent: {paramChild: "", fields: "", fromJsonStr: ""}}
    {
        type: "GeoServer",
        label: "WMS Imagery",
        params: [
            {
                key: "geoserverUrl",
                display: "WMS URL",
                sanitizer: value => value.endsWith("?") ? value.slice(0, -1) : value,
                validator: value => !urlValidator(value) ?
                    "The server address (URL) is not valid."
                : /\?.+/.test(value) ? "The field \"WMS Url\" should not contain the query string. Please put those values in the field \"Additional WMS Params (as JSON object)\"." : "",
            },
            {key: "LAYERS", display: "WMS Layer Name", parent: "geoserverParams"},
            {
                key: "geoserverParams",
                display: "Additional WMS Params (JSON format)",
                required: false,
                type: "JSON",
                sanitizer: value => `{${value
                    .replace(/[{} ]/g, "")
                    .split(",")
                    .filter(u => u !== "")
                    .map(u => {
                        const [k, v] = u.split(":");
                        return v && `"${k.replace(/["']/g, "")}": ${v}`;
                    })
                    .join(",")}}`,
                validator: value => !isValidJSON(value) ? "Invalid JSON in the \"Additional WMS Params\" field." : "",
            },
        ],
        // FIXME, add url if help document is created.
    },
    {
        type: "xyz",
        label: "XYZ Imagery",
        params: [{
            key: "url",
            display: "XYZ URL",
            validator: value => !urlValidator(value) ?
                "The server address (URL) is not valid."
            : !(/https?:\/\/.*(?=.*{-?x})(?=.*{-?z})(?=.*{-?y}).*/gi.test(value)) ? "The URL for an XYZ imagery type must include https://, {x}, {y}, and {z}." : "",
        }],
    },
    {
        type: "BingMaps",
        label: "Bing Maps",
        params: [
            {
                key: "imageryId",
                display: "Imagery Id",
                type: "select",
                options: [
                    {label: "Aerial", value: "Aerial"},
                    {label: "Aerial with Labels", value: "AerialWithLabels"},
                ],
            },
            {key: "accessToken", display: "Access Token"},
        ],
        url: "https://docs.microsoft.com/en-us/bingmaps/getting-started/bing-maps-dev-center-help/getting-a-bing-maps-key",
    },
    {
        type: "Planet",
        label: "Planet Monthly",
        params: [
            {
                key: "year",
                display: "Default Year",
                type: "number",
                validator: value => isNaN(value) || (value.toString().length !== 4) ? "Year should be 4 digit number" : "",
            },
            {
                key: "month",
                display: "Default Month",
                type: "number",
                validator: value => outOfRange(value, 1, 12) ? "Month should be between 1 and 12!" : "",
            },
            {key: "accessToken", display: "Access Token"},
        ],
        url: "https://developers.planet.com/docs/quickstart/getting-started/",
    },
    {
        type: "PlanetDaily",
        label: "Planet Daily",
        params: [
            {key: "accessToken", display: "Access Token"},
            {key: "startDate", display: "Start Date", type: "date"},
            {key: "endDate", display: "End Date", type: "date"},
        ],
        validator: dateRangeValidator,
        url: "https://developers.planet.com/docs/quickstart/getting-started/",
    },
    {
        type: "PlanetNICFI",
        label: "Planet NICFI",
        params: [
            {key: "accessToken", display: "Access Token"},
            {
                key: "time",
                display: "Default Time",
                type: "select",
                options: [
                    {label: "Newest Available", value: "newest"},
                    ...nicfiLayers.map(layer => ({label: layer, value: layer})),
                ],
            },
            {
                key: "band",
                display: "Default Band",
                type: "select",
                options: [
                    {label: "Visible", value: "rgb"},
                    {label: "Infrared", value: "cir"},
                ],
            },
        ],
        url: "https://assets.planet.com/docs/NICFI_UserGuidesFAQ.pdf",
    },
    {
        type: "SecureWatch",
        params: [
            {
                key: "baseUrl",
                display: "Base URL",
                type: "select",
                options: [
                    {label: "https://securewatch.digitalglobe.com", value: "https://securewatch.digitalglobe.com"},
                    {label: "https://services.digitalglobe.com", value: "https://services.digitalglobe.com"},
                ],
                validator: value => !urlValidator(value) ? "The server address (URL) is not valid." : "",
            },
            {key: "connectid", display: "Connect ID"},
            {
                key: "startDate",
                display: "Start Date",
                type: "date",
                options: {max: new Date().toJSON().split("T")[0]},
            },
            {
                key: "endDate",
                display: "End Date",
                type: "date",
            },
        ],
        validator: dateRangeValidator,
    },
    {
        type: "Sentinel1",
        label: "Sentinel 1",
        params: [
            {
                key: "year",
                display: "Default Year",
                type: "number",
                options: {min: "2014", max: new Date().getFullYear().toString(), step: "1"},
                validator: value => outOfRange(value, 2014, new Date().getFullYear()) || value.toString().length !== 4 ?
                    "Year should be 4 digit number and between 2014 and " + new Date().getFullYear()
                : "",
            },
            {
                key: "month",
                display: "Default Month",
                type: "number",
                options: {min: "1", max: "12", step: "1"},
                validator: value => outOfRange(value, 1, 12) ? "Month should be between 1 and 12!" : "",
            },
            {
                key: "bandCombination",
                display: "Band Combination",
                type: "select",
                options: [
                    {label: "VH,VV,VH/VV", value: "VH,VV,VH/VV"},
                    {label: "VH,VV,VV/VH", value: "VH,VV,VV/VH"},
                    {label: "VV,VH,VV/VH", value: "VV,VH,VV/VH"},
                    {label: "VV,VH,VH/VV", value: "VV,VH,VH/VV"},
                ],
            },
            {
                key: "min",
                display: "Min",
                type: "number",
                options: {
                    placeholder: "1-100",
                    step: "0.01",
                },
            },
            {
                key: "max",
                display: "Max",
                type: "number",
                options: {
                    placeholder: "2800-3200",
                    step: "0.01",
                },
            },
        ],
    },
    {
        type: "Sentinel2",
        label: "Sentinel 2",
        params: [
            {
                key: "year",
                display: "Default Year",
                type: "number",
                options: {min: "2015", max: new Date().getFullYear().toString(), step: "1"},
                validator: value => outOfRange(value, 2015, new Date().getFullYear()) || value.toString().length !== 4 ?
                    "Year should be 4 digit number and between 2015 and " + new Date().getFullYear()
                : "",
            },
            {
                key: "month",
                display: "Default Month",
                type: "number",
                options: {min: "1", max: "12", step: "1"},
                validator: value => outOfRange(value, 1, 12) ? "Month should be between 1 and 12!" : "",
            },
            {
                key: "bandCombination",
                display: "Band Combination",
                type: "select",
                options: [
                    {label: "True Color", value: "TrueColor"},
                    {label: "False Color Infrared", value: "FalseColorInfrared"},
                    {label: "False Color Urban", value: "FalseColorUrban"},
                    {label: "Agriculture", value: "Agriculture"},
                    {label: "Healthy Vegetation", value: "HealthyVegetation"},
                    {label: "Short Wave Infrared", value: "ShortWaveInfrared"},
                ],
            },
            {
                key: "min",
                display: "Min",
                type: "number",
                options: {
                    placeholder: "1-100",
                    step: "0.01",
                },
            },
            {
                key: "max",
                display: "Max",
                type: "number",
                options: {
                    placeholder: "2800-3200",
                    step: "0.01",
                },
            },
            {
                key: "cloudScore",
                display: "Cloud Score",
                type: "number",
                options: {
                    placeholder: "10-30",
                    min: "0",
                    max: "100",
                    step: "1",
                },
                validator: value => value && outOfRange(value, 0, 100) ? "Cloud Score should be between 0 and 100!" : "",
            },
        ],
    },
    {
        type: "GEEImage",
        label: "GEE Image Asset",
        params: [
            {
                key: "imageId",
                display: "Asset ID",
                options: {placeholder: "USDA/NAIP/DOQQ/n_4207309_se_18_1_20090525"},
            },
            {
                key: "imageVisParams",
                display: "Visualization Parameters (JSON format)",
                type: "JSON",
                options: {placeholder: "{\"bands\": [\"R\", \"G\", \"B\"], \"min\": 0-100, \"max\": 2800-3200}"},
                validator: value => !isValidJSON(value) ? "Invalid JSON in the \"Visualization Parameters\" field." : "",
            },
        ],
    },
    {
        type: "GEEImageCollection",
        label: "GEE ImageCollection Asset",
        params: [
            {
                key: "collectionId",
                display: "Asset ID",
                options: {placeholder: "LANDSAT/LC08/C01/T1_SR"},
            },
            {
                key: "startDate",
                display: "Start Date",
                type: "date",
                options: {max: new Date().toJSON().split("T")[0]},
            },
            {
                key: "endDate",
                display: "End Date",
                type: "date",
                options: {max: new Date().toJSON().split("T")[0]},
            },
            {
                key: "collectionVisParams",
                display: "Visualization Parameters (JSON format)",
                type: "JSON",
                options: {placeholder: "{\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 0-100, \"max\": 2800-3200}"},
                validator: value => !isValidJSON(value) ? "Invalid JSON in the \"Visualization Parameters\" field." : "",
            },
        ],
        validator: dateRangeValidator,
    },
    {
        type: "MapBoxRaster",
        label: "Mapbox Raster",
        params: [
            {key: "layerName", display: "Layer Name"},
            {key: "accessToken", display: "Access Token"},
        ],
        url: "https://docs.mapbox.com/help/glossary/raster-tiles-api/",
    },
    {
        type: "MapBoxStatic",
        label: "Mapbox Static",
        params: [
            {key: "userName", display: "User Name"},
            {key: "mapStyleId", display: "Map Style Id"},
            {key: "accessToken", display: "Access Token"},
        ],
        url: "https://docs.mapbox.com/help/glossary/static-tiles-api/",
    },
    {
        type: "OSM",
        label: "Open Street Map",
        params: [],
    },
];
<commit_msg>fix formatting<commit_after>export const nicfiLayers = ["2015-12_2016-05",
                            "2016-06_2016-11",
                            "2016-12_2017-05",
                            "2017-06_2017-11",
                            "2017-12_2018-05",
                            "2018-06_2018-11",
                            "2018-12_2019-05",
                            "2019-06_2019-11",
                            "2019-12_2020-05",
                            "2020-06_2020-08",
                            "2020-09",
                            "2020-10",
                            "2020-11",
                            "2020-12",
                            "2021-01",
                            "2021-02"];

const outOfRange = (num, low, high) => isNaN(num) || parseInt(num) < low || parseInt(num) > high;

const dateRangeValidator = ({startDate, endDate}) => startDate && endDate
    && new Date(startDate) > new Date(endDate) ? "Start date must be smaller than the end date." : "";

const urlValidator = url => /^(?:http|https):\/\/[\w.-]+(?:\.[\w-]+)+[\w\-.,@?^=%&:;/~\\+#]+$/.test(url);

const isValidJSON = str => {
    try {
        JSON.parse(str);
        return true;
    } catch (e) {
        return false;
    }
};

export const imageryOptions = [
    // Default type is text, default parent is none, a referenced parent must be entered as a json string
    // Parameters can be defined one level deep. {paramParent: {paramChild: "", fields: "", fromJsonStr: ""}}
    {
        type: "GeoServer",
        label: "WMS Imagery",
        params: [
            {
                key: "geoserverUrl",
                display: "WMS URL",
                sanitizer: value => value.endsWith("?") ? value.slice(0, -1) : value,
                validator: value => !urlValidator(value)
                    ? "The server address (URL) is not valid."
                    : /\?.+/.test(value)
                        ? "The field \"WMS Url\" should not contain the query string. Please put those values in the field \"Additional WMS Params (as JSON object)\"."
                        : "",
            },
            {key: "LAYERS", display: "WMS Layer Name", parent: "geoserverParams"},
            {
                key: "geoserverParams",
                display: "Additional WMS Params (JSON format)",
                required: false,
                type: "JSON",
                sanitizer: value => `{${value
                    .replace(/[{} ]/g, "")
                    .split(",")
                    .filter(u => u !== "")
                    .map(u => {
                        const [k, v] = u.split(":");
                        return v && `"${k.replace(/["']/g, "")}": ${v}`;
                    })
                    .join(",")}}`,
                validator: value => !isValidJSON(value) ? "Invalid JSON in the \"Additional WMS Params\" field." : "",
            },
        ],
        // FIXME, add url if help document is created.
    },
    {
        type: "xyz",
        label: "XYZ Imagery",
        params: [{
            key: "url",
            display: "XYZ URL",
            validator: value => !urlValidator(value)
                ? "The server address (URL) is not valid."
                : !(/.*(?=.*{-?x})(?=.*{-?z})(?=.*{-?y}).*/gi.test(value))
                    ? "The URL for an XYZ imagery type must include {x}, {y}, and {z}."
                    : "",
        }],
    },
    {
        type: "BingMaps",
        label: "Bing Maps",
        params: [
            {
                key: "imageryId",
                display: "Imagery Id",
                type: "select",
                options: [
                    {label: "Aerial", value: "Aerial"},
                    {label: "Aerial with Labels", value: "AerialWithLabels"},
                ],
            },
            {key: "accessToken", display: "Access Token"},
        ],
        url: "https://docs.microsoft.com/en-us/bingmaps/getting-started/bing-maps-dev-center-help/getting-a-bing-maps-key",
    },
    {
        type: "Planet",
        label: "Planet Monthly",
        params: [
            {
                key: "year",
                display: "Default Year",
                type: "number",
                validator: value => isNaN(value) || (value.toString().length !== 4) ? "Year should be 4 digit number" : "",
            },
            {
                key: "month",
                display: "Default Month",
                type: "number",
                validator: value => outOfRange(value, 1, 12) ? "Month should be between 1 and 12!" : "",
            },
            {key: "accessToken", display: "Access Token"},
        ],
        url: "https://developers.planet.com/docs/quickstart/getting-started/",
    },
    {
        type: "PlanetDaily",
        label: "Planet Daily",
        params: [
            {key: "accessToken", display: "Access Token"},
            {key: "startDate", display: "Start Date", type: "date"},
            {key: "endDate", display: "End Date", type: "date"},
        ],
        validator: dateRangeValidator,
        url: "https://developers.planet.com/docs/quickstart/getting-started/",
    },
    {
        type: "PlanetNICFI",
        label: "Planet NICFI",
        params: [
            {key: "accessToken", display: "Access Token"},
            {
                key: "time",
                display: "Default Time",
                type: "select",
                options: [
                    {label: "Newest Available", value: "newest"},
                    ...nicfiLayers.map(layer => ({label: layer, value: layer})),
                ],
            },
            {
                key: "band",
                display: "Default Band",
                type: "select",
                options: [
                    {label: "Visible", value: "rgb"},
                    {label: "Infrared", value: "cir"},
                ],
            },
        ],
        url: "https://assets.planet.com/docs/NICFI_UserGuidesFAQ.pdf",
    },
    {
        type: "SecureWatch",
        params: [
            {
                key: "baseUrl",
                display: "Base URL",
                type: "select",
                options: [
                    {label: "https://securewatch.digitalglobe.com", value: "https://securewatch.digitalglobe.com"},
                    {label: "https://services.digitalglobe.com", value: "https://services.digitalglobe.com"},
                ],
                validator: value => !urlValidator(value) ? "The server address (URL) is not valid." : "",
            },
            {key: "connectid", display: "Connect ID"},
            {
                key: "startDate",
                display: "Start Date",
                type: "date",
                options: {max: new Date().toJSON().split("T")[0]},
            },
            {
                key: "endDate",
                display: "End Date",
                type: "date",
            },
        ],
        validator: dateRangeValidator,
    },
    {
        type: "Sentinel1",
        label: "Sentinel 1",
        params: [
            {
                key: "year",
                display: "Default Year",
                type: "number",
                options: {min: "2014", max: new Date().getFullYear().toString(), step: "1"},
                validator: value => outOfRange(value, 2014, new Date().getFullYear()) || value.toString().length !== 4 ?
                    "Year should be 4 digit number and between 2014 and " + new Date().getFullYear()
                : "",
            },
            {
                key: "month",
                display: "Default Month",
                type: "number",
                options: {min: "1", max: "12", step: "1"},
                validator: value => outOfRange(value, 1, 12) ? "Month should be between 1 and 12!" : "",
            },
            {
                key: "bandCombination",
                display: "Band Combination",
                type: "select",
                options: [
                    {label: "VH,VV,VH/VV", value: "VH,VV,VH/VV"},
                    {label: "VH,VV,VV/VH", value: "VH,VV,VV/VH"},
                    {label: "VV,VH,VV/VH", value: "VV,VH,VV/VH"},
                    {label: "VV,VH,VH/VV", value: "VV,VH,VH/VV"},
                ],
            },
            {
                key: "min",
                display: "Min",
                type: "number",
                options: {
                    placeholder: "1-100",
                    step: "0.01",
                },
            },
            {
                key: "max",
                display: "Max",
                type: "number",
                options: {
                    placeholder: "2800-3200",
                    step: "0.01",
                },
            },
        ],
    },
    {
        type: "Sentinel2",
        label: "Sentinel 2",
        params: [
            {
                key: "year",
                display: "Default Year",
                type: "number",
                options: {min: "2015", max: new Date().getFullYear().toString(), step: "1"},
                validator: value => outOfRange(value, 2015, new Date().getFullYear()) || value.toString().length !== 4 ?
                    "Year should be 4 digit number and between 2015 and " + new Date().getFullYear()
                : "",
            },
            {
                key: "month",
                display: "Default Month",
                type: "number",
                options: {min: "1", max: "12", step: "1"},
                validator: value => outOfRange(value, 1, 12) ? "Month should be between 1 and 12!" : "",
            },
            {
                key: "bandCombination",
                display: "Band Combination",
                type: "select",
                options: [
                    {label: "True Color", value: "TrueColor"},
                    {label: "False Color Infrared", value: "FalseColorInfrared"},
                    {label: "False Color Urban", value: "FalseColorUrban"},
                    {label: "Agriculture", value: "Agriculture"},
                    {label: "Healthy Vegetation", value: "HealthyVegetation"},
                    {label: "Short Wave Infrared", value: "ShortWaveInfrared"},
                ],
            },
            {
                key: "min",
                display: "Min",
                type: "number",
                options: {
                    placeholder: "1-100",
                    step: "0.01",
                },
            },
            {
                key: "max",
                display: "Max",
                type: "number",
                options: {
                    placeholder: "2800-3200",
                    step: "0.01",
                },
            },
            {
                key: "cloudScore",
                display: "Cloud Score",
                type: "number",
                options: {
                    placeholder: "10-30",
                    min: "0",
                    max: "100",
                    step: "1",
                },
                validator: value => value && outOfRange(value, 0, 100) ? "Cloud Score should be between 0 and 100!" : "",
            },
        ],
    },
    {
        type: "GEEImage",
        label: "GEE Image Asset",
        params: [
            {
                key: "imageId",
                display: "Asset ID",
                options: {placeholder: "USDA/NAIP/DOQQ/n_4207309_se_18_1_20090525"},
            },
            {
                key: "imageVisParams",
                display: "Visualization Parameters (JSON format)",
                type: "JSON",
                options: {placeholder: "{\"bands\": [\"R\", \"G\", \"B\"], \"min\": 0-100, \"max\": 2800-3200}"},
                validator: value => !isValidJSON(value) ? "Invalid JSON in the \"Visualization Parameters\" field." : "",
            },
        ],
    },
    {
        type: "GEEImageCollection",
        label: "GEE ImageCollection Asset",
        params: [
            {
                key: "collectionId",
                display: "Asset ID",
                options: {placeholder: "LANDSAT/LC08/C01/T1_SR"},
            },
            {
                key: "startDate",
                display: "Start Date",
                type: "date",
                options: {max: new Date().toJSON().split("T")[0]},
            },
            {
                key: "endDate",
                display: "End Date",
                type: "date",
                options: {max: new Date().toJSON().split("T")[0]},
            },
            {
                key: "collectionVisParams",
                display: "Visualization Parameters (JSON format)",
                type: "JSON",
                options: {placeholder: "{\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 0-100, \"max\": 2800-3200}"},
                validator: value => !isValidJSON(value) ? "Invalid JSON in the \"Visualization Parameters\" field." : "",
            },
        ],
        validator: dateRangeValidator,
    },
    {
        type: "MapBoxRaster",
        label: "Mapbox Raster",
        params: [
            {key: "layerName", display: "Layer Name"},
            {key: "accessToken", display: "Access Token"},
        ],
        url: "https://docs.mapbox.com/help/glossary/raster-tiles-api/",
    },
    {
        type: "MapBoxStatic",
        label: "Mapbox Static",
        params: [
            {key: "userName", display: "User Name"},
            {key: "mapStyleId", display: "Map Style Id"},
            {key: "accessToken", display: "Access Token"},
        ],
        url: "https://docs.mapbox.com/help/glossary/static-tiles-api/",
    },
    {
        type: "OSM",
        label: "Open Street Map",
        params: [],
    },
];
<|endoftext|>


<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 * A QuadTree implementation in JavaScript, a 2d spatial subdivision algorithm.
 * Based on the QuadTree Library by Timo Hausmann and released under the MIT license
 * https://github.com/timohausmann/quadtree-js/
**/

(function () {

    /**
     * a pool of `QuadTree` objects
     */
    var QT_ARRAY = [];

    /**
     * will pop a quadtree object from the array
     * or create a new one if the array is empty
     */
    var QT_ARRAY_POP = function (bounds, max_objects, max_levels, level) {
        if (QT_ARRAY.length > 0) {
            var _qt =  QT_ARRAY.pop();
            _qt.bounds = bounds;
            _qt.max_objects = max_objects || 4;
            _qt.max_levels  = max_levels || 4;
            _qt.level = level || 0;
            return _qt;
        } else {
            return new me.QuadTree(bounds, max_objects, max_levels, level);
        }
    };

    /**
     * Push back a quadtree back into the array
     */
    var QT_ARRAY_PUSH = function (qt) {
        QT_ARRAY.push(qt);
    };

    /**
     * a temporary vector object to be reused
     */
    var QT_VECTOR = new me.Vector2d();

    /**
     * Quadtree Constructor

     * note: the global quadtree instance is available through `me.collision.quadTree`
     * @class
     * @name QuadTree
     * @extends Object
     * @memberOf me
     * @constructor
     * @see me.collision.quadTree
     * @param {me.Rect} bounds bounds of the node
     * @param {Number} [max_objects=4] max objects a node can hold before splitting into 4 subnodes
     * @param {Number} [max_levels=4] total max levels inside root Quadtree
     * @param {Number} [level] deepth level, required for subnodes
     */
    function Quadtree(bounds, max_objects, max_levels, level) {
        this.max_objects = max_objects || 4;
        this.max_levels  = max_levels || 4;

        this.level = level || 0;
        this.bounds = bounds;

        this.objects = [];
        this.nodes = [];
    }

    /*
     * Split the node into 4 subnodes
     */
    Quadtree.prototype.split = function () {

        var nextLevel = this.level + 1,
            subWidth  = ~~(0.5 + this.bounds.width / 2),
            subHeight = ~~(0.5 + this.bounds.height / 2),
            x = ~~(0.5 + this.bounds.pos.x),
            y = ~~(0.5 + this.bounds.pos.y);

         //top right node
        this.nodes[0] = QT_ARRAY_POP({
            pos : {
                x : x + subWidth,
                y : y
            },
            width : subWidth,
            height : subHeight
        }, this.max_objects, this.max_levels, nextLevel);

        //top left node
        this.nodes[1] = QT_ARRAY_POP({
            pos : {
                x : x,
                y : y
            },
            width : subWidth,
            height : subHeight
        }, this.max_objects, this.max_levels, nextLevel);

        //bottom left node
        this.nodes[2] = QT_ARRAY_POP({
            pos : {
                x : x,
                y : y + subHeight
            },
            width : subWidth,
            height : subHeight
        }, this.max_objects, this.max_levels, nextLevel);

        //bottom right node
        this.nodes[3] = QT_ARRAY_POP({
            pos : {
                x : x + subWidth,
                y : y + subHeight
            },
            width : subWidth,
            height : subHeight
        }, this.max_objects, this.max_levels, nextLevel);
    };

    /*
     * Determine which node the object belongs to
     * @param {me.Rect} rect bounds of the area to be checked
     * @return Integer index of the subnode (0-3), or -1 if rect cannot completely fit within a subnode and is part of the parent node
     */
    Quadtree.prototype.getIndex = function (item) {

        var rect = item.getBounds(),
            pos = rect.pos;

        // use world coordinates for floating items
        if (item.floating || (item.ancestor && item.ancestor.floating)) {
            pos = me.game.viewport.localToWorld(pos.x, pos.y, QT_VECTOR);
        }

        var index = -1,
            rx = pos.x,
            ry = pos.y,
            rw = rect.width,
            rh = rect.height,
            verticalMidpoint = this.bounds.pos.x + (this.bounds.width / 2),
            horizontalMidpoint = this.bounds.pos.y + (this.bounds.height / 2),
            //rect can completely fit within the top quadrants
            topQuadrant = (ry < horizontalMidpoint && ry + rh < horizontalMidpoint),
            //rect can completely fit within the bottom quadrants
            bottomQuadrant = (ry > horizontalMidpoint);

        //rect can completely fit within the left quadrants
        if (rx < verticalMidpoint && rx + rw < verticalMidpoint) {
            if (topQuadrant) {
                index = 1;
            } else if (bottomQuadrant) {
                index = 2;
            }
        } else if (rx > verticalMidpoint) {
            //rect can completely fit within the right quadrants
            if (topQuadrant) {
                index = 0;
            } else if (bottomQuadrant) {
                index = 3;
            }
        }

        return index;
    };

    /**
     * Insert the given object container into the node.
     * @name insertContainer
     * @memberOf me.QuadTree
     * @function
     * @param {me.Container} container group of objects to be added
     */
    Quadtree.prototype.insertContainer = function (container) {

        for (var i = container.children.length, child; i--, (child = container.children[i]);) {
            if (child instanceof me.Container) {
                if (child.name !== "rootContainer") {
                    this.insert(child);
                }
                // recursivly insert all childs
                this.insertContainer(child);
            } else {
                // only insert object with a bounding box
                if (typeof (child.getBounds) === "function") {
                    this.insert(child);
                }
            }
        }
    };

    /**
     * Insert the given object into the node. If the node
     * exceeds the capacity, it will split and add all
     * objects to their corresponding subnodes.
     * @name insert
     * @memberOf me.QuadTree
     * @function
     * @param {Object} item object to be added
     */
    Quadtree.prototype.insert = function (item) {

        var index = -1;

        //if we have subnodes ...
        if (this.nodes.length > 0) {
            index = this.getIndex(item);

            if (index !== -1) {
                this.nodes[index].insert(item);
                return;
            }
        }

        this.objects.push(item);

        if (this.objects.length > this.max_objects && this.level < this.max_levels) {

            //split if we don't already have subnodes
            if (this.nodes.length === 0) {
                this.split();
            }

            var i = 0;

            //add all objects to there corresponding subnodes
            while (i < this.objects.length) {

                index = this.getIndex(this.objects[i]);

                if (index !== -1) {
                    this.nodes[index].insert(this.objects.splice(i, 1)[0]);
                } else {
                    i = i + 1;
                }
            }
        }
    };

    /**
     * Return all objects that could collide with the given object
     * @name retrieve
     * @memberOf me.QuadTree
     * @function
     * @param {Object} object object to be checked against
     * @param {Object} [function] a sorting function for the returned array
     * @return {Object[]} array with all detected objects
     */
    Quadtree.prototype.retrieve = function (item, fn) {

        var returnObjects = this.objects;

        //if we have subnodes ...
        if (this.nodes.length > 0) {

            var index = this.getIndex(item);

            //if rect fits into a subnode ..
            if (index !== -1) {
                returnObjects = returnObjects.concat(this.nodes[index].retrieve(item));
            } else {
                 //if rect does not fit into a subnode, check it against all subnodes
                for (var i = 0; i < this.nodes.length; i = i + 1) {
                    returnObjects = returnObjects.concat(this.nodes[i].retrieve(item));
                }
            }
        }

        if (typeof(fn) === "function") {
            returnObjects.sort(fn);
        }

        return returnObjects;
    };

    /**
     * Remove the given item from the quadtree.
     * (this function won't recalculate the impacted node)
     * @name remove
     * @memberOf me.QuadTree
     * @function
     * @param {Object} object object to be removed
     * @return true if the item was found and removed.
     */
     Quadtree.prototype.remove = function (item) {
        var found = false;

        if (typeof (item.getBounds) === "undefined") {
            // ignore object that cannot be added in the first place
            return false;
        }

        //if we have subnodes ...
        if (this.nodes.length > 0) {
            // determine to which node the item belongs to
            var index = this.getIndex(item);

            if (index !== -1) {
                found = this.nodes[index].remove(item);
                // trim node if empty
                if (found && this.nodes[index].isPrunable()) {
                    this.nodes.splice(index, 1);
                }
            }
        }

        if (found === false) {
            // try and remove the item from the list of items in this node
            if (this.objects.indexOf(item) !== -1) {
                this.objects.remove(item);
                found = true;
            }
        }

        return found;
    };

    /**
     * return true if the node is prunable
     * @name isPrunable
     * @memberOf me.QuadTree
     * @function
     * @return true if the node is prunable
     */
    Quadtree.prototype.isPrunable = function () {
        return !(this.hasChildren() || (this.objects.length > 0));
    };

    /**
     * return true if the node has any children
     * @name hasChildren
     * @memberOf me.QuadTree
     * @function
     * @return true if the node has any children
     */
    Quadtree.prototype.hasChildren = function () {
        for (var i = 0; i < this.nodes.length; i = i + 1) {
            var subnode = this.nodes[i];
            if (subnode.length > 0 || subnode.objects.length > 0) {
                return true;
            }
        }
        return false;
    };

    /**
     * clear the quadtree
     * @name clear
     * @memberOf me.QuadTree
     * @function
     */
    Quadtree.prototype.clear = function (bounds) {

        this.objects.length = 0;

        for (var i = 0; i < this.nodes.length; i = i + 1) {
            this.nodes[i].clear();
            // recycle the quadTree object
            QT_ARRAY_PUSH(this.nodes[i]);
        }
        // empty the array
        this.nodes = [];

        // resize the root bounds if required
        if (typeof bounds !== "undefined") {
            this.bounds.setShape(bounds.pos.x, bounds.pos.y, bounds.width, bounds.height);
        }
    };

    //make Quadtree available in the me namespace
    me.QuadTree = Quadtree;

})();
<commit_msg>Paritcle emitter performance: Do not add me.ParticleContainer to QuadTree<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 * A QuadTree implementation in JavaScript, a 2d spatial subdivision algorithm.
 * Based on the QuadTree Library by Timo Hausmann and released under the MIT license
 * https://github.com/timohausmann/quadtree-js/
**/

(function () {

    /**
     * a pool of `QuadTree` objects
     */
    var QT_ARRAY = [];

    /**
     * will pop a quadtree object from the array
     * or create a new one if the array is empty
     */
    var QT_ARRAY_POP = function (bounds, max_objects, max_levels, level) {
        if (QT_ARRAY.length > 0) {
            var _qt =  QT_ARRAY.pop();
            _qt.bounds = bounds;
            _qt.max_objects = max_objects || 4;
            _qt.max_levels  = max_levels || 4;
            _qt.level = level || 0;
            return _qt;
        } else {
            return new me.QuadTree(bounds, max_objects, max_levels, level);
        }
    };

    /**
     * Push back a quadtree back into the array
     */
    var QT_ARRAY_PUSH = function (qt) {
        QT_ARRAY.push(qt);
    };

    /**
     * a temporary vector object to be reused
     */
    var QT_VECTOR = new me.Vector2d();

    /**
     * Quadtree Constructor

     * note: the global quadtree instance is available through `me.collision.quadTree`
     * @class
     * @name QuadTree
     * @extends Object
     * @memberOf me
     * @constructor
     * @see me.collision.quadTree
     * @param {me.Rect} bounds bounds of the node
     * @param {Number} [max_objects=4] max objects a node can hold before splitting into 4 subnodes
     * @param {Number} [max_levels=4] total max levels inside root Quadtree
     * @param {Number} [level] deepth level, required for subnodes
     */
    function Quadtree(bounds, max_objects, max_levels, level) {
        this.max_objects = max_objects || 4;
        this.max_levels  = max_levels || 4;

        this.level = level || 0;
        this.bounds = bounds;

        this.objects = [];
        this.nodes = [];
    }

    /*
     * Split the node into 4 subnodes
     */
    Quadtree.prototype.split = function () {

        var nextLevel = this.level + 1,
            subWidth  = ~~(0.5 + this.bounds.width / 2),
            subHeight = ~~(0.5 + this.bounds.height / 2),
            x = ~~(0.5 + this.bounds.pos.x),
            y = ~~(0.5 + this.bounds.pos.y);

         //top right node
        this.nodes[0] = QT_ARRAY_POP({
            pos : {
                x : x + subWidth,
                y : y
            },
            width : subWidth,
            height : subHeight
        }, this.max_objects, this.max_levels, nextLevel);

        //top left node
        this.nodes[1] = QT_ARRAY_POP({
            pos : {
                x : x,
                y : y
            },
            width : subWidth,
            height : subHeight
        }, this.max_objects, this.max_levels, nextLevel);

        //bottom left node
        this.nodes[2] = QT_ARRAY_POP({
            pos : {
                x : x,
                y : y + subHeight
            },
            width : subWidth,
            height : subHeight
        }, this.max_objects, this.max_levels, nextLevel);

        //bottom right node
        this.nodes[3] = QT_ARRAY_POP({
            pos : {
                x : x + subWidth,
                y : y + subHeight
            },
            width : subWidth,
            height : subHeight
        }, this.max_objects, this.max_levels, nextLevel);
    };

    /*
     * Determine which node the object belongs to
     * @param {me.Rect} rect bounds of the area to be checked
     * @return Integer index of the subnode (0-3), or -1 if rect cannot completely fit within a subnode and is part of the parent node
     */
    Quadtree.prototype.getIndex = function (item) {

        var rect = item.getBounds(),
            pos = rect.pos;

        // use world coordinates for floating items
        if (item.floating || (item.ancestor && item.ancestor.floating)) {
            pos = me.game.viewport.localToWorld(pos.x, pos.y, QT_VECTOR);
        }

        var index = -1,
            rx = pos.x,
            ry = pos.y,
            rw = rect.width,
            rh = rect.height,
            verticalMidpoint = this.bounds.pos.x + (this.bounds.width / 2),
            horizontalMidpoint = this.bounds.pos.y + (this.bounds.height / 2),
            //rect can completely fit within the top quadrants
            topQuadrant = (ry < horizontalMidpoint && ry + rh < horizontalMidpoint),
            //rect can completely fit within the bottom quadrants
            bottomQuadrant = (ry > horizontalMidpoint);

        //rect can completely fit within the left quadrants
        if (rx < verticalMidpoint && rx + rw < verticalMidpoint) {
            if (topQuadrant) {
                index = 1;
            } else if (bottomQuadrant) {
                index = 2;
            }
        } else if (rx > verticalMidpoint) {
            //rect can completely fit within the right quadrants
            if (topQuadrant) {
                index = 0;
            } else if (bottomQuadrant) {
                index = 3;
            }
        }

        return index;
    };

    /**
     * Insert the given object container into the node.
     * @name insertContainer
     * @memberOf me.QuadTree
     * @function
     * @param {me.Container} container group of objects to be added
     */
    Quadtree.prototype.insertContainer = function (container) {

        for (var i = container.children.length, child; i--, (child = container.children[i]);) {
            if (child instanceof me.Container && !(child instanceof me.ParticleContainer)) {
                if (child.name !== "rootContainer") {
                    this.insert(child);
                }
                // recursivly insert all childs
                this.insertContainer(child);
            } else {
                // only insert object with a bounding box
                if (typeof (child.getBounds) === "function") {
                    this.insert(child);
                }
            }
        }
    };

    /**
     * Insert the given object into the node. If the node
     * exceeds the capacity, it will split and add all
     * objects to their corresponding subnodes.
     * @name insert
     * @memberOf me.QuadTree
     * @function
     * @param {Object} item object to be added
     */
    Quadtree.prototype.insert = function (item) {

        var index = -1;

        //if we have subnodes ...
        if (this.nodes.length > 0) {
            index = this.getIndex(item);

            if (index !== -1) {
                this.nodes[index].insert(item);
                return;
            }
        }

        this.objects.push(item);

        if (this.objects.length > this.max_objects && this.level < this.max_levels) {

            //split if we don't already have subnodes
            if (this.nodes.length === 0) {
                this.split();
            }

            var i = 0;

            //add all objects to there corresponding subnodes
            while (i < this.objects.length) {

                index = this.getIndex(this.objects[i]);

                if (index !== -1) {
                    this.nodes[index].insert(this.objects.splice(i, 1)[0]);
                } else {
                    i = i + 1;
                }
            }
        }
    };

    /**
     * Return all objects that could collide with the given object
     * @name retrieve
     * @memberOf me.QuadTree
     * @function
     * @param {Object} object object to be checked against
     * @param {Object} [function] a sorting function for the returned array
     * @return {Object[]} array with all detected objects
     */
    Quadtree.prototype.retrieve = function (item, fn) {

        var returnObjects = this.objects;

        //if we have subnodes ...
        if (this.nodes.length > 0) {

            var index = this.getIndex(item);

            //if rect fits into a subnode ..
            if (index !== -1) {
                returnObjects = returnObjects.concat(this.nodes[index].retrieve(item));
            } else {
                 //if rect does not fit into a subnode, check it against all subnodes
                for (var i = 0; i < this.nodes.length; i = i + 1) {
                    returnObjects = returnObjects.concat(this.nodes[i].retrieve(item));
                }
            }
        }

        if (typeof(fn) === "function") {
            returnObjects.sort(fn);
        }

        return returnObjects;
    };

    /**
     * Remove the given item from the quadtree.
     * (this function won't recalculate the impacted node)
     * @name remove
     * @memberOf me.QuadTree
     * @function
     * @param {Object} object object to be removed
     * @return true if the item was found and removed.
     */
     Quadtree.prototype.remove = function (item) {
        var found = false;

        if (typeof (item.getBounds) === "undefined") {
            // ignore object that cannot be added in the first place
            return false;
        }

        //if we have subnodes ...
        if (this.nodes.length > 0) {
            // determine to which node the item belongs to
            var index = this.getIndex(item);

            if (index !== -1) {
                found = this.nodes[index].remove(item);
                // trim node if empty
                if (found && this.nodes[index].isPrunable()) {
                    this.nodes.splice(index, 1);
                }
            }
        }

        if (found === false) {
            // try and remove the item from the list of items in this node
            if (this.objects.indexOf(item) !== -1) {
                this.objects.remove(item);
                found = true;
            }
        }

        return found;
    };

    /**
     * return true if the node is prunable
     * @name isPrunable
     * @memberOf me.QuadTree
     * @function
     * @return true if the node is prunable
     */
    Quadtree.prototype.isPrunable = function () {
        return !(this.hasChildren() || (this.objects.length > 0));
    };

    /**
     * return true if the node has any children
     * @name hasChildren
     * @memberOf me.QuadTree
     * @function
     * @return true if the node has any children
     */
    Quadtree.prototype.hasChildren = function () {
        for (var i = 0; i < this.nodes.length; i = i + 1) {
            var subnode = this.nodes[i];
            if (subnode.length > 0 || subnode.objects.length > 0) {
                return true;
            }
        }
        return false;
    };

    /**
     * clear the quadtree
     * @name clear
     * @memberOf me.QuadTree
     * @function
     */
    Quadtree.prototype.clear = function (bounds) {

        this.objects.length = 0;

        for (var i = 0; i < this.nodes.length; i = i + 1) {
            this.nodes[i].clear();
            // recycle the quadTree object
            QT_ARRAY_PUSH(this.nodes[i]);
        }
        // empty the array
        this.nodes = [];

        // resize the root bounds if required
        if (typeof bounds !== "undefined") {
            this.bounds.setShape(bounds.pos.x, bounds.pos.y, bounds.width, bounds.height);
        }
    };

    //make Quadtree available in the me namespace
    me.QuadTree = Quadtree;

})();
<|endoftext|>


<commit_before>// Template.manageObjects

var managableTypes = [
  {
    collectionSlug: "data-sets",
    humanName: "Data Sets",
    singularName: "data set",
    collectionName: "DataSets",
    introTemplate: "introDataSets",
    createTemplate: "createDataSet",
    showTemplate: "showDataSet",
  },
  {
    collectionSlug: "sample-groups",
    humanName: "Sample Groups",
    singularName: "sample group",
    collectionName: "SampleGroups",
    introTemplate: "introSampleGroups",
    createTemplate: "createSampleGroup",
    showTemplate: "showSampleGroup",
    permissionDeniedTemplate: "waitAndThenPermissionDenied",
  },
  {
    collectionSlug: "gene-sets",
    humanName: "Gene Sets",
    singularName: "gene set",
    collectionName: "GeneSets",
    introTemplate: "introGeneSets",
    createTemplate: "createGeneSet",
    showTemplate: "showGeneSet",
  },
  {
    collectionSlug: "gene-set-groups",
    humanName: "Gene Set Groups",
    singularName: "gene set group",
    collectionName: "GeneSetGroups",
    introTemplate: "introGeneSetGroups",
    createTemplate: "createGeneSetGroup",
    showTemplate: "showGeneSetGroup",
  },
  {
    collectionSlug: "studies",
    humanName: "Studies",
    singularName: "study",
    collectionName: "Studies",
    introTemplate: "introStudies",
    createTemplate: "createStudy",
    showTemplate: "showStudy",
  },
  {
    collectionSlug: "clinical-forms",
    humanName: "Clinical Forms",
    singularName: "clinical form",
    collectionName: "Forms",
    introTemplate: "introForms",
    createTemplate: "createForm",
    showTemplate: "showForm",
  },
];

Template.manageObjects.helpers({
  managableTypes: managableTypes,
  tabActive() {
    return this.collectionSlug === FlowRouter.getParam("collectionSlug");
  },
  selectedType() {
    let selected = FlowRouter.getParam("collectionSlug");

    return _.findWhere(managableTypes, { collectionSlug: selected });
  },
});

// Template.dataTypeInfoIcon

Template.dataTypeInfoIcon.onRendered(function () {
  let instance = this;

  instance.$(".help.circle.icon").popup({
    position: "bottom left",
    hoverable: true,
  });
});

// Template.manageObjectsGrid

Template.manageObjectsGrid.onCreated(function () {
  let instance = this;

  instance.currentlyManaging = new ReactiveVar();

  // subscribe to the names of the available data, and
  // set instance.currentlyManaging
  instance.autorun(() => {
    let slug = FlowRouter.getParam("collectionSlug");
    let currObj = _.findWhere(managableTypes, { collectionSlug: slug });

    instance.currentlyManaging.set(currObj);
    instance.subscribe("allOfCollectionOnlyMetadata", currObj.collectionName);
  });

  // if the user selected something before, select that one
  var lastSlug;

  instance.autorun((computation) => {
    let selected = FlowRouter.getParam("selected");
    let collectionSlug = FlowRouter.getParam("collectionSlug");

    if (lastSlug !== collectionSlug && !selected) {
      let fromSession = Session.get("manageObjects-" + collectionSlug);

      // Only execute after a bit because of a race condition between
      // this firing and the URL actually getting set.
      // (FlowRouter.getParam("selected") works fine but the URL is wrong)
      Meteor.defer(() => {
        FlowRouter.setParams({
          selected: fromSession
        });
      });
    } else {
      Session.set("manageObjects-" + collectionSlug, selected);
    }

    lastSlug = collectionSlug;
  });
});

function getObjects (query = {}) {
  // get all the objects for this data type
  let slug = FlowRouter.getParam("collectionSlug");
  let managing = Template.instance().currentlyManaging.get();

  return MedBook.collections[managing.collectionName].find(query, {
    // need to sort by _id also to break ties: if two things have the same
    // name, clicking on one can cause the list to reorder itself
    sort: { name: 1, version: 1, _id: 1 },
  });
}

Template.manageObjectsGrid.helpers({
  getObjects,
  managingObject() {
    return this._id === FlowRouter.getParam("selected");
  },
  showVersion() {
    return getObjects({ name: this.name }).count() > 1 || this.version !== 1;
  },
});

// Template.manageObject

Template.manageObject.onCreated(function () {
  let instance = this;

  // subscribe to the selected object
  instance.autorun(() => {
    let { collectionName } = instance.data;
    let selectedId = FlowRouter.getParam("selected");

    if (selectedId) {
      instance.subscribe("objectFromCollection", collectionName, selectedId);
    }
  });
});

Template.manageObject.helpers({
  getObjects,
  getObject() {
    let slug = FlowRouter.getParam("collectionSlug");
    let managing = _.findWhere(managableTypes, { collectionSlug: slug });
    let selectedId = FlowRouter.getParam("selected");

    return MedBook.collections[managing.collectionName].findOne(selectedId);
  },
  onDelete() {
    return () => {
      FlowRouter.setParams({ selected: null });
    };
  },
});
<commit_msg>v1.0.4.1: UI bugfix<commit_after>// Template.manageObjects

var managableTypes = [
  {
    collectionSlug: "data-sets",
    humanName: "Data Sets",
    singularName: "data set",
    collectionName: "DataSets",
    introTemplate: "introDataSets",
    createTemplate: "createDataSet",
    showTemplate: "showDataSet",
  },
  {
    collectionSlug: "sample-groups",
    humanName: "Sample Groups",
    singularName: "sample group",
    collectionName: "SampleGroups",
    introTemplate: "introSampleGroups",
    createTemplate: "createSampleGroup",
    showTemplate: "showSampleGroup",
    permissionDeniedTemplate: "waitAndThenPermissionDenied",
  },
  {
    collectionSlug: "gene-sets",
    humanName: "Gene Sets",
    singularName: "gene set",
    collectionName: "GeneSets",
    introTemplate: "introGeneSets",
    createTemplate: "createGeneSet",
    showTemplate: "showGeneSet",
  },
  {
    collectionSlug: "gene-set-groups",
    humanName: "Gene Set Groups",
    singularName: "gene set group",
    collectionName: "GeneSetGroups",
    introTemplate: "introGeneSetGroups",
    createTemplate: "createGeneSetGroup",
    showTemplate: "showGeneSetGroup",
  },
  {
    collectionSlug: "studies",
    humanName: "Studies",
    singularName: "study",
    collectionName: "Studies",
    introTemplate: "introStudies",
    createTemplate: "createStudy",
    showTemplate: "showStudy",
  },
  {
    collectionSlug: "clinical-forms",
    humanName: "Clinical Forms",
    singularName: "clinical form",
    collectionName: "Forms",
    introTemplate: "introForms",
    createTemplate: "createForm",
    showTemplate: "showForm",
  },
];

Template.manageObjects.helpers({
  managableTypes: managableTypes,
  tabActive() {
    return this.collectionSlug === FlowRouter.getParam("collectionSlug");
  },
  selectedType() {
    let selected = FlowRouter.getParam("collectionSlug");

    return _.findWhere(managableTypes, { collectionSlug: selected });
  },
});

// Template.dataTypeInfoIcon

Template.dataTypeInfoIcon.onRendered(function () {
  let instance = this;

  instance.$(".help.circle.icon").popup({
    position: "bottom left",
    hoverable: true,
  });
});

// Template.manageObjectsGrid

Template.manageObjectsGrid.onCreated(function () {
  let instance = this;

  instance.currentlyManaging = new ReactiveVar();

  // subscribe to the names of the available data, and
  // set instance.currentlyManaging
  instance.autorun(() => {
    let slug = FlowRouter.getParam("collectionSlug");
    let currObj = _.findWhere(managableTypes, { collectionSlug: slug });

    instance.currentlyManaging.set(currObj);
    instance.subscribe("allOfCollectionOnlyMetadata", currObj.collectionName);
  });

  // if the user selected something before, select that one
  var lastSlug;

  instance.autorun((computation) => {
    let selected = FlowRouter.getParam("selected");
    let collectionSlug = FlowRouter.getParam("collectionSlug");

    if (lastSlug !== collectionSlug && !selected) {
      let fromSession = Session.get("manageObjects-" + collectionSlug);

      // Only execute after a bit because of a race condition between
      // this firing and the URL actually getting set.
      // (FlowRouter.getParam("selected") works fine but the URL is wrong)
      Meteor.defer(() => {
        FlowRouter.setParams({
          selected: fromSession
        });
      });
    } else {
      Session.set("manageObjects-" + collectionSlug, selected);
    }

    lastSlug = collectionSlug;
  });
});

function getObjects (instance, query = {}) {
  // get all the objects for this data type
  let slug = FlowRouter.getParam("collectionSlug");
  let managing = instance.currentlyManaging.get();

  return MedBook.collections[managing.collectionName].find(query, {
    // need to sort by _id also to break ties: if two things have the same
    // name, clicking on one can cause the list to reorder itself
    sort: { name: 1, version: 1, _id: 1 },
  });
}

Template.manageObjectsGrid.helpers({
  getObjects() { return getObjects(Template.instance()); },
  managingObject() {
    return this._id === FlowRouter.getParam("selected");
  },
  showVersion() {
    if (this.version) {
      let instance = Template.instance();

      // only show the version if the version isn't 1 or there's a duplicate
      return this.version !== 1 ||
          getObjects(instance, { name: this.name }).count() > 1;
    }
  },
});

// Template.manageObject

Template.manageObject.onCreated(function () {
  let instance = this;

  // subscribe to the selected object
  instance.autorun(() => {
    let { collectionName } = instance.data;
    let selectedId = FlowRouter.getParam("selected");

    if (selectedId) {
      instance.subscribe("objectFromCollection", collectionName, selectedId);
    }
  });
});

Template.manageObject.helpers({
  getObjects() {
    return getObjects(Template.instance().parent());
  },
  getObject() {
    let slug = FlowRouter.getParam("collectionSlug");
    let managing = _.findWhere(managableTypes, { collectionSlug: slug });
    let selectedId = FlowRouter.getParam("selected");

    return MedBook.collections[managing.collectionName].findOne(selectedId);
  },
  onDelete() {
    return () => {
      FlowRouter.setParams({ selected: null });
    };
  },
});
<|endoftext|>


<commit_before>(function($) {

	$.extend(mejs.MepDefaults, {
		forcePluginFullScreen: false,
		newWindowUrl: ''
	});

	$.extend(MediaElementPlayer.prototype, {

		isFullScreen: false,

		docStyleOverflow: null,

		isInIframe: false,

		buildfullscreen: function(player, controls, layers, media) {

			if (!player.isVideo)
				return;

			player.isInIframe = (window.location != window.parent.location);

			// native events
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
				//player.container.bind(mejs.MediaFeatures.fullScreenEventName, function(e) {
				player.container.bind('webkitfullscreenchange', function(e) {

					if (mejs.MediaFeatures.isFullScreen()) {
						// reset the controls once we are fully in full screen
						player.setControlsSize();
					} else {
						// when a user presses ESC
						// make sure to put the player back into place
						player.exitFullScreen();
					}
				});
			}

			var t = this,
				normalHeight = 0,
				normalWidth = 0,
				container = player.container,
				fullscreenBtn =
					$('
' +
						'<button type="button" aria-controls="' + t.id + '" title="Fullscreen"></button>' +
					'
')
					.appendTo(controls)
					.click(function() {
						var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;

						if (isFullScreen) {
							player.exitFullScreen();
						} else {
							player.enterFullScreen();
						}
					});

			player.fullscreenBtn = fullscreenBtn;

			$(document).bind('keydown',function (e) {
				if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
					player.exitFullScreen();
				}
			});

		},
		enterFullScreen: function() {

			var t = this;

			// store overflow
			docStyleOverflow = document.documentElement.style.overflow;
			// set it to not show scroll bars so 100% will work
			document.documentElement.style.overflow = 'hidden';

			// store sizing
			normalHeight = t.container.height();
			normalWidth = t.container.width();

			// attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

				mejs.MediaFeatures.requestFullScreen(t.container[0]);
				//return;

			} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
				t.media.webkitEnterFullscreen();
				return;
			}

			// check for iframe launch
			if (t.isInIframe && t.options.newWindowUrl !== '') {
				t.pause();
				//window.open(t.options.newWindowUrl, t.id, 'width=' + t.width + ',height=' + t.height + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
				window.open(t.options.newWindowUrl, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');

				return;
			}

			// full window code

			// firefox+flash can't adjust plugin sizes without resetting :(
			if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.forcePluginFullScreen)) {
				t.media.setFullscreen(true);
				//player.isFullScreen = true;
				return;
			}

			// make full size
			t.container
				.addClass('mejs-container-fullscreen')
				.width('100%')
				.height('100%');
				//.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
			setTimeout(function() {
				t.container.css({width: '100%', height: '100%'});
			}, 500);
			//console.log('fullscreen', t.container.width());

			if (t.pluginType === 'native') {
				t.$media
					.width('100%')
					.height('100%');
			} else {
				t.container.find('object embed')
					.width('100%')
					.height('100%');
				t.media.setVideoSize($(window).width(),$(window).height());
			}

			t.layers.children('div')
				.width('100%')
				.height('100%');

			if (t.fullscreenBtn) {
				t.fullscreenBtn
					.removeClass('mejs-fullscreen')
					.addClass('mejs-unfullscreen');
			}

			t.setControlsSize();
			t.isFullScreen = true;
		},

		exitFullScreen: function() {

			var t = this;

			// firefox can't adjust plugins
			if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
				t.media.setFullscreen(false);
				//player.isFullScreen = false;
				return;
			}

			// come outo of native fullscreen
			if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
				mejs.MediaFeatures.cancelFullScreen();
			}

			// restore scroll bars to document
			document.documentElement.style.overflow = docStyleOverflow;

			t.container
				.removeClass('mejs-container-fullscreen')
				.width(normalWidth)
				.height(normalHeight)
				.css('z-index', 1);
				//.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});

			if (t.pluginType === 'native') {
				t.$media
					.width(normalWidth)
					.height(normalHeight);
			} else {
				t.container.find('object embed')
					.width(normalWidth)
					.height(normalHeight);

				t.media.setVideoSize(normalWidth, normalHeight);
			}

			t.layers.children('div')
				.width(normalWidth)
				.height(normalHeight);

			t.fullscreenBtn
				.removeClass('mejs-unfullscreen')
				.addClass('mejs-fullscreen');

			t.setControlsSize();
			t.isFullScreen = false;
		}
	});

})(mejs.$);<commit_msg>fix for firefox's broken Flash support and mock fullscreen<commit_after>(function($) {

	$.extend(mejs.MepDefaults, {
		forcePluginFullScreen: false,
		newWindowUrl: ''
	});

	$.extend(MediaElementPlayer.prototype, {

		isFullScreen: false,

		docStyleOverflow: null,

		isInIframe: false,

		buildfullscreen: function(player, controls, layers, media) {

			if (!player.isVideo)
				return;

			player.isInIframe = (window.location != window.parent.location);

			// native events
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
				//player.container.bind(mejs.MediaFeatures.fullScreenEventName, function(e) {
				player.container.bind('webkitfullscreenchange', function(e) {

					if (mejs.MediaFeatures.isFullScreen()) {
						// reset the controls once we are fully in full screen
						player.setControlsSize();
					} else {
						// when a user presses ESC
						// make sure to put the player back into place
						player.exitFullScreen();
					}
				});
			}

			var t = this,
				normalHeight = 0,
				normalWidth = 0,
				container = player.container,
				fullscreenBtn =
					$('
' +
						'<button type="button" aria-controls="' + t.id + '" title="Fullscreen"></button>' +
					'
')
					.appendTo(controls)
					.click(function() {
						var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;

						if (isFullScreen) {
							player.exitFullScreen();
						} else {
							player.enterFullScreen();
						}
					});

			player.fullscreenBtn = fullscreenBtn;

			$(document).bind('keydown',function (e) {
				if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
					player.exitFullScreen();
				}
			});

		},
		enterFullScreen: function() {

			var t = this;

			// firefox+flash can't adjust plugin sizes without resetting :(
			if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isGecko || t.options.forcePluginFullScreen)) {
				t.media.setFullscreen(true);
				//player.isFullScreen = true;
				return;
			}

			// store overflow
			docStyleOverflow = document.documentElement.style.overflow;
			// set it to not show scroll bars so 100% will work
			document.documentElement.style.overflow = 'hidden';

			// store sizing
			normalHeight = t.container.height();
			normalWidth = t.container.width();

			// attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

				mejs.MediaFeatures.requestFullScreen(t.container[0]);
				//return;

			} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
				t.media.webkitEnterFullscreen();
				return;
			}

			// check for iframe launch
			if (t.isInIframe && t.options.newWindowUrl !== '') {
				t.pause();
				//window.open(t.options.newWindowUrl, t.id, 'width=' + t.width + ',height=' + t.height + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
				window.open(t.options.newWindowUrl, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');

				return;
			}

			// full window code

			// make full size
			t.container
				.addClass('mejs-container-fullscreen')
				.width('100%')
				.height('100%');
				//.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
			setTimeout(function() {
				t.container.css({width: '100%', height: '100%'});
			}, 500);
			//console.log('fullscreen', t.container.width());

			if (t.pluginType === 'native') {
				t.$media
					.width('100%')
					.height('100%');
			} else {
				t.container.find('object embed')
					.width('100%')
					.height('100%');
				t.media.setVideoSize($(window).width(),$(window).height());
			}

			t.layers.children('div')
				.width('100%')
				.height('100%');

			if (t.fullscreenBtn) {
				t.fullscreenBtn
					.removeClass('mejs-fullscreen')
					.addClass('mejs-unfullscreen');
			}

			t.setControlsSize();
			t.isFullScreen = true;
		},

		exitFullScreen: function() {

			var t = this;

			// firefox can't adjust plugins
			if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
				t.media.setFullscreen(false);
				//player.isFullScreen = false;
				return;
			}

			// come outo of native fullscreen
			if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
				mejs.MediaFeatures.cancelFullScreen();
			}

			// restore scroll bars to document
			document.documentElement.style.overflow = docStyleOverflow;

			t.container
				.removeClass('mejs-container-fullscreen')
				.width(normalWidth)
				.height(normalHeight)
				.css('z-index', 1);
				//.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});

			if (t.pluginType === 'native') {
				t.$media
					.width(normalWidth)
					.height(normalHeight);
			} else {
				t.container.find('object embed')
					.width(normalWidth)
					.height(normalHeight);

				t.media.setVideoSize(normalWidth, normalHeight);
			}

			t.layers.children('div')
				.width(normalWidth)
				.height(normalHeight);

			t.fullscreenBtn
				.removeClass('mejs-unfullscreen')
				.addClass('mejs-fullscreen');

			t.setControlsSize();
			t.isFullScreen = false;
		}
	});

})(mejs.$);<|endoftext|>


<commit_before>define(["dojo/_base/declare"], function(declare) {

  var _PreconditionViolationError = declare(null, {
    instance: null,
    method: null,
    condition: null,
    constructor: function(instance, method, condition) {
      this.instance = instance;
      this.method = method;
      this.condition = condition;
    },
    toString: function() {
      return "Precondition violation: " + this.condition +
        " (on " + this.instance + " in method " + this.method + ")";
    }
  });

  var _ContractMixin = declare(null, {
    _c_invar: [],

    _c_pre: function(condition) {
      if (! condition()) {
        throw new _PreconditionViolationError(this, null /* TODO method */, condition);
      }
    },

    _c_prop: function(subject, propName) {
      // summary:
      //   the property subject[propName] exists
      // description:
      //   if there is only 1 argument, subject is taken to be this
      var pName;
      var s;
      if (arguments.length < 2) {
        pName = subject;
        s = this;
      }
      else {
        pName = propName;
        s = subject;
      }
      return (pName in s);
    },

    _c_prop_mandatory: function(subject, propName) {
      // summary:
      //   the property subject[propName] is mandatory (exists and not-null)
      // description:
      //   if there is only 1 argument, subject is taken to be this
      var pName;
      var s;
      if (arguments.length < 2) {
        pName = subject;
        s = this;
      }
      else {
        pName = propName;
        s = subject;
      }
      return this._c_prop(s, pName) && s[pName] !== null;
    },

    _c_prop_mandatory_string: function(subject, propName) {
      // summary:
      //   the property subject[propName] is a non-empty string
      // description:
      //   if there is only 1 argument, subject is taken to be this
      var pName;
      var s;
      if (arguments.length < 2) {
        pName = subject;
        s = this;
      }
      else {
        pName = propName;
        s = subject;
      }
      var exists = this._c_prop_mandatory(s, pName);
      if (exists) {
        var value = s[pName];
        return typeof value === "string" && value != "";
      }
      else {
        return false;
      }
    },

    _c_ABSTRACT: function() {
      // summary:
      //   use this to annotate a function as abstract
      // description:
      //   This method throws an exception "ABSTRACT".

      //   An abstract method should be mentioned, to have a hook for documentation.
      //   Yet, an meaningful implementation is not possible.
      //   To detect errors early, and to avoid code inspection warnings, you
      //   can call this body in the abstract method. Because the method should
      //   be overwritten for every concrete subclass, this code should never be called.
      //
      //   Often, abstract methods also have parameters. These will not be used in the
      //   body of the abstract method declaration (the hook for the documentation),
      //   which will trigger code inspection warnings. To avoid these, you should
      //   add those parameters as arguments to this call.
      throw "ABSTRACT";
    }

  });

  return _ContractMixin;
});
<commit_msg>support NOP annotation<commit_after>define(["dojo/_base/declare"], function(declare) {

  var _PreconditionViolationError = declare(null, {
    instance: null,
    method: null,
    condition: null,
    constructor: function(instance, method, condition) {
      this.instance = instance;
      this.method = method;
      this.condition = condition;
    },
    toString: function() {
      return "Precondition violation: " + this.condition +
        " (on " + this.instance + " in method " + this.method + ")";
    }
  });

  var _ContractMixin = declare(null, {
    _c_invar: [],

    _c_pre: function(condition) {
      if (! condition()) {
        throw new _PreconditionViolationError(this, null /* TODO method */, condition);
      }
    },

    _c_prop: function(subject, propName) {
      // summary:
      //   the property subject[propName] exists
      // description:
      //   if there is only 1 argument, subject is taken to be this
      var pName;
      var s;
      if (arguments.length < 2) {
        pName = subject;
        s = this;
      }
      else {
        pName = propName;
        s = subject;
      }
      return (pName in s);
    },

    _c_prop_mandatory: function(subject, propName) {
      // summary:
      //   the property subject[propName] is mandatory (exists and not-null)
      // description:
      //   if there is only 1 argument, subject is taken to be this
      var pName;
      var s;
      if (arguments.length < 2) {
        pName = subject;
        s = this;
      }
      else {
        pName = propName;
        s = subject;
      }
      return this._c_prop(s, pName) && s[pName] !== null;
    },

    _c_prop_mandatory_string: function(subject, propName) {
      // summary:
      //   the property subject[propName] is a non-empty string
      // description:
      //   if there is only 1 argument, subject is taken to be this
      var pName;
      var s;
      if (arguments.length < 2) {
        pName = subject;
        s = this;
      }
      else {
        pName = propName;
        s = subject;
      }
      var exists = this._c_prop_mandatory(s, pName);
      if (exists) {
        var value = s[pName];
        return typeof value === "string" && value != "";
      }
      else {
        return false;
      }
    },

    _c_ABSTRACT: function() {
      // summary:
      //   use this to annotate a function as abstract
      // description:
      //   This method throws an exception "ABSTRACT".
      //
      //   An abstract method should be mentioned, to have a hook for documentation.
      //   Yet, an meaningful implementation is not possible.
      //   To detect errors early, and to avoid code inspection warnings, you
      //   can call this body in the abstract method. Because the method should
      //   be overwritten for every concrete subclass, this code should never be called.
      //
      //   Often, abstract methods also have parameters. These will not be used in the
      //   body of the abstract method declaration (the hook for the documentation),
      //   which will trigger code inspection warnings. To avoid these, you should
      //   add those parameters as arguments to this call.
      throw "ABSTRACT";
    },

    _c_NOP: function() {
      // summary:
      //   use this to annotate a function as having no body whatsoever
      // description:
      //   This method does NOP.
      //
      //   Methods like this must sometimes be mentioned, as a NOP implementation
      //   of an abstract method in some subclass, or in other circumstances.
      //   Using this method instead of a comment helps in avoiding unnecessary
      //   code inspection warnings.
      //
      //   Often, such NOP methods also have parameters. These will not be used in the
      //   empty body, which will trigger code inspection warnings. To avoid these, you
      //   should add those parameters as arguments to this call.
    }

  });

  return _ContractMixin;
});
<|endoftext|>