<commit_before>var numRows = 500;

var names = [
    'Aden Moreno',
    'Alton Watson',
    'Caleb Scott',
    'Cathy Wilkins',
    'Charlie Dodd',
    'Jermaine Price',
    'Reis Vasquez',
];

var phones = [
    {handset: 'Hauwei P40', price: 599},
    {handset: 'Google Pixel 5', price: 589},
    {handset: 'Apple iPhone 12', price: 849},
    {handset: 'Samsung Galaxy S10', price: 499},
    {handset: 'Motorola Edge', price: 549},
    {handset: 'Sony Xperia', price: 279},
];

function generateData() {
    var data = [];
    for(let i = 0; i < numRows; i++) {
        var phone = phones[getRandomNumber(0, phones.length)];
        var saleDate = randomDate(new Date(2020, 0, 1), new Date(2020, 11, 31));
        var quarter = 'Q' + Math.floor((saleDate.getMonth() + 3) / 3);

        data.push({
            salesRep: names[getRandomNumber(0, names.length)],
            handset: phone.handset,
            sale: phone.price,
            saleDate,
            quarter
        });
    }

    data.sort(function(a,b) {
        return a.saleDate.getTime() - b.saleDate.getTime();
    });

    data.forEach(d => {
        d.saleDate = d.saleDate.toISOString().substring(0, 10);
    });

    return data;
}

function getRandomNumber(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
}

function randomDate(start, end) {
    return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
}<commit_msg>AG-4787 - fixed validate-es5 issue<commit_after>var numRows = 500;

var names = [
    'Aden Moreno',
    'Alton Watson',
    'Caleb Scott',
    'Cathy Wilkins',
    'Charlie Dodd',
    'Jermaine Price',
    'Reis Vasquez',
];

var phones = [
    {handset: 'Hauwei P40', price: 599},
    {handset: 'Google Pixel 5', price: 589},
    {handset: 'Apple iPhone 12', price: 849},
    {handset: 'Samsung Galaxy S10', price: 499},
    {handset: 'Motorola Edge', price: 549},
    {handset: 'Sony Xperia', price: 279},
];

function generateData() {
    var data = [];
    for(var i = 0; i < numRows; i++) {
        var phone = phones[getRandomNumber(0, phones.length)];
        var saleDate = randomDate(new Date(2020, 0, 1), new Date(2020, 11, 31));
        var quarter = 'Q' + Math.floor((saleDate.getMonth() + 3) / 3);

        data.push({
            salesRep: names[getRandomNumber(0, names.length)],
            handset: phone.handset,
            sale: phone.price,
            saleDate,
            quarter
        });
    }

    data.sort(function(a,b) {
        return a.saleDate.getTime() - b.saleDate.getTime();
    });

    data.forEach(d => {
        d.saleDate = d.saleDate.toISOString().substring(0, 10);
    });

    return data;
}

function getRandomNumber(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
}

function randomDate(start, end) {
    return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
}<|endoftext|>


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

const ChatMessage = require('./ChatMessage');
const padutils = require('./pad_utils').padutils;
const padcookie = require('./pad_cookie').padcookie;
const Tinycon = require('tinycon/tinycon');
const hooks = require('./pluginfw/hooks');
const padeditor = require('./pad_editor').padeditor;

// Removes diacritics and lower-cases letters. https://stackoverflow.com/a/37511463
const normalize = (s) => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();

exports.chat = (() => {
  let isStuck = false;
  let userAndChat = false;
  let chatMentions = 0;
  return {
    show() {
      $('#chaticon').removeClass('visible');
      $('#chatbox').addClass('visible');
      this.scrollDown(true);
      chatMentions = 0;
      Tinycon.setBubble(0);
      $('.chat-gritter-msg').each(function () {
        $.gritter.remove(this.id);
      });
    },
    focus: () => {
      setTimeout(() => {
        $('#chatinput').focus();
      }, 100);
    },
    // Make chat stick to right hand side of screen
    stickToScreen(fromInitialCall) {
      if (pad.settings.hideChat) {
        return;
      }
      this.show();
      isStuck = (!isStuck || fromInitialCall);
      $('#chatbox').hide();
      // Add timeout to disable the chatbox animations
      setTimeout(() => {
        $('#chatbox, .sticky-container').toggleClass('stickyChat', isStuck);
        $('#chatbox').css('display', 'flex');
      }, 0);

      padcookie.setPref('chatAlwaysVisible', isStuck);
      $('#options-stickychat').prop('checked', isStuck);
    },
    chatAndUsers(fromInitialCall) {
      const toEnable = $('#options-chatandusers').is(':checked');
      if (toEnable || !userAndChat || fromInitialCall) {
        this.stickToScreen(true);
        $('#options-stickychat').prop('checked', true);
        $('#options-chatandusers').prop('checked', true);
        $('#options-stickychat').prop('disabled', 'disabled');
        userAndChat = true;
      } else {
        $('#options-stickychat').prop('disabled', false);
        userAndChat = false;
      }
      padcookie.setPref('chatAndUsers', userAndChat);
      $('#users, .sticky-container')
          .toggleClass('chatAndUsers popup-show stickyUsers', userAndChat);
      $('#chatbox').toggleClass('chatAndUsersChat', userAndChat);
    },
    hide() {
      // decide on hide logic based on chat window being maximized or not
      if ($('#options-stickychat').prop('checked')) {
        this.stickToScreen();
        $('#options-stickychat').prop('checked', false);
      } else {
        $('#chatcounter').text('0');
        $('#chaticon').addClass('visible');
        $('#chatbox').removeClass('visible');
      }
    },
    scrollDown(force) {
      if ($('#chatbox').hasClass('visible')) {
        if (force || !this.lastMessage || !this.lastMessage.position() ||
            this.lastMessage.position().top < ($('#chattext').outerHeight() + 20)) {
          // if we use a slow animate here we can have a race condition
          // when a users focus can not be moved away from the last message recieved.
          $('#chattext').animate(
              {scrollTop: $('#chattext')[0].scrollHeight},
              {duration: 400, queue: false});
          this.lastMessage = $('#chattext > p').eq(-1);
        }
      }
    },
    async send() {
      const text = $('#chatinput').val();
      if (text.replace(/\s+/, '').length === 0) return;
      const message = new ChatMessage(text);
      await hooks.aCallAll('chatSendMessage', Object.freeze({message}));
      this._pad.collabClient.sendMessage({type: 'CHAT_MESSAGE', message});
      $('#chatinput').val('');
    },
    async addMessage(msg, increment, isHistoryAdd) {
      msg = ChatMessage.fromObject(msg);
      // correct the time
      msg.time += this._pad.clientTimeOffset;

      if (!msg.authorId) {
        /*
         * If, for a bug or a database corruption, the message coming from the
         * server does not contain the authorId field (see for example #3731),
         * let's be defensive and replace it with "unknown".
         */
        msg.authorId = 'unknown';
        console.warn(
            'The "authorId" field of a chat message coming from the server was not present. ' +
            'Replacing with "unknown". This may be a bug or a database corruption.');
      }

      const authorClass = (authorId) => `author-${authorId.replace(/[^a-y0-9]/g, (c) => {
        if (c === '.') return '-';
        return `z${c.charCodeAt(0)}z`;
      })}`;

      // the hook args
      const ctx = {
        authorName: msg.displayName != null ? msg.displayName : html10n.get('pad.userlist.unnamed'),
        author: msg.authorId,
        text: padutils.escapeHtmlWithClickableLinks(msg.text, '_blank'),
        message: msg,
        rendered: null,
        sticky: false,
        timestamp: msg.time,
        timeStr: (() => {
          let minutes = `${new Date(msg.time).getMinutes()}`;
          let hours = `${new Date(msg.time).getHours()}`;
          if (minutes.length === 1) minutes = `0${minutes}`;
          if (hours.length === 1) hours = `0${hours}`;
          return `${hours}:${minutes}`;
        })(),
        duration: 4000,
      };

      // is the users focus already in the chatbox?
      const alreadyFocused = $('#chatinput').is(':focus');

      // does the user already have the chatbox open?
      const chatOpen = $('#chatbox').hasClass('visible');

      // does this message contain this user's name? (is the current user mentioned?)
      const wasMentioned =
          msg.authorId !== window.clientVars.userId &&
          ctx.authorName !== html10n.get('pad.userlist.unnamed') &&
          normalize(ctx.text).includes(normalize(ctx.authorName));

      // If the user was mentioned, make the message sticky
      if (wasMentioned && !alreadyFocused && !isHistoryAdd && !chatOpen) {
        chatMentions++;
        Tinycon.setBubble(chatMentions);
        ctx.sticky = true;
      }

      await hooks.aCallAll('chatNewMessage', ctx);
      const cls = authorClass(ctx.author);
      const chatMsg = ctx.rendered != null ? $(ctx.rendered) : $('<p>')
          .attr('data-authorId', ctx.author)
          .addClass(cls)
          .append($('<b>').text(`${ctx.authorName}:`))
          .append($('<span>')
              .addClass('time')
              .addClass(cls)
              // Hook functions are trusted to not introduce an XSS vulnerability by adding
              // unescaped user input to ctx.timeStr.
              .html(ctx.timeStr))
          .append(' ')
          // ctx.text was HTML-escaped before calling the hook. Hook functions are trusted to not
          // introduce an XSS vulnerability by adding unescaped user input.
          .append($('<div>').html(ctx.text).contents());
      if (isHistoryAdd) chatMsg.insertAfter('#chatloadmessagesbutton');
      else $('#chattext').append(chatMsg);

      // should we increment the counter??
      if (increment && !isHistoryAdd) {
        // Update the counter of unread messages
        let count = Number($('#chatcounter').text());
        count++;
        $('#chatcounter').text(count);

        if (!chatOpen && ctx.duration > 0) {
          $.gritter.add({
            text: $('<p>')
                .append($('<span>').addClass('author-name').text(ctx.authorName))
                // ctx.text was HTML-escaped before calling the hook. Hook functions are trusted
                // to not introduce an XSS vulnerability by adding unescaped user input.
                .append($('<div>').html(ctx.text).contents()),
            sticky: ctx.sticky,
            time: ctx.duration,
            position: 'bottom',
            class_name: 'chat-gritter-msg',
          });
        }
      }
      if (!isHistoryAdd) this.scrollDown();
    },
    init(pad) {
      this._pad = pad;
      $('#chatinput').on('keydown', (evt) => {
        // If the event is Alt C or Escape & we're already in the chat menu
        // Send the users focus back to the pad
        if ((evt.altKey === true && evt.which === 67) || evt.which === 27) {
          // If we're in chat already..
          $(':focus').blur(); // required to do not try to remove!
          padeditor.ace.focus(); // Sends focus back to pad
          evt.preventDefault();
          return false;
        }
      });
      // Clear the chat mentions when the user clicks on the chat input box
      $('#chatinput').click(() => {
        chatMentions = 0;
        Tinycon.setBubble(0);
      });

      const self = this;
      $('body:not(#chatinput)').on('keypress', function (evt) {
        if (evt.altKey && evt.which === 67) {
          // Alt c focuses on the Chat window
          $(this).blur();
          self.show();
          $('#chatinput').focus();
          evt.preventDefault();
        }
      });

      $('#chatinput').keypress((evt) => {
        // if the user typed enter, fire the send
        if (evt.key === 'Enter' && !evt.shiftKey) {
          evt.preventDefault();
          this.send();
        }
      });

      // initial messages are loaded in pad.js' _afterHandshake

      $('#chatcounter').text(0);
      $('#chatloadmessagesbutton').click(() => {
        const start = Math.max(this.historyPointer - 20, 0);
        const end = this.historyPointer;

        if (start === end) return; // nothing to load

        $('#chatloadmessagesbutton').css('display', 'none');
        $('#chatloadmessagesball').css('display', 'block');

        pad.collabClient.sendMessage({type: 'GET_CHAT_MESSAGES', start, end});
        this.historyPointer = start;
      });
    },
  };
})();
<commit_msg>chat: Translate newly created message entries<commit_after>'use strict';
/**
 * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const ChatMessage = require('./ChatMessage');
const padutils = require('./pad_utils').padutils;
const padcookie = require('./pad_cookie').padcookie;
const Tinycon = require('tinycon/tinycon');
const hooks = require('./pluginfw/hooks');
const padeditor = require('./pad_editor').padeditor;

// Removes diacritics and lower-cases letters. https://stackoverflow.com/a/37511463
const normalize = (s) => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();

exports.chat = (() => {
  let isStuck = false;
  let userAndChat = false;
  let chatMentions = 0;
  return {
    show() {
      $('#chaticon').removeClass('visible');
      $('#chatbox').addClass('visible');
      this.scrollDown(true);
      chatMentions = 0;
      Tinycon.setBubble(0);
      $('.chat-gritter-msg').each(function () {
        $.gritter.remove(this.id);
      });
    },
    focus: () => {
      setTimeout(() => {
        $('#chatinput').focus();
      }, 100);
    },
    // Make chat stick to right hand side of screen
    stickToScreen(fromInitialCall) {
      if (pad.settings.hideChat) {
        return;
      }
      this.show();
      isStuck = (!isStuck || fromInitialCall);
      $('#chatbox').hide();
      // Add timeout to disable the chatbox animations
      setTimeout(() => {
        $('#chatbox, .sticky-container').toggleClass('stickyChat', isStuck);
        $('#chatbox').css('display', 'flex');
      }, 0);

      padcookie.setPref('chatAlwaysVisible', isStuck);
      $('#options-stickychat').prop('checked', isStuck);
    },
    chatAndUsers(fromInitialCall) {
      const toEnable = $('#options-chatandusers').is(':checked');
      if (toEnable || !userAndChat || fromInitialCall) {
        this.stickToScreen(true);
        $('#options-stickychat').prop('checked', true);
        $('#options-chatandusers').prop('checked', true);
        $('#options-stickychat').prop('disabled', 'disabled');
        userAndChat = true;
      } else {
        $('#options-stickychat').prop('disabled', false);
        userAndChat = false;
      }
      padcookie.setPref('chatAndUsers', userAndChat);
      $('#users, .sticky-container')
          .toggleClass('chatAndUsers popup-show stickyUsers', userAndChat);
      $('#chatbox').toggleClass('chatAndUsersChat', userAndChat);
    },
    hide() {
      // decide on hide logic based on chat window being maximized or not
      if ($('#options-stickychat').prop('checked')) {
        this.stickToScreen();
        $('#options-stickychat').prop('checked', false);
      } else {
        $('#chatcounter').text('0');
        $('#chaticon').addClass('visible');
        $('#chatbox').removeClass('visible');
      }
    },
    scrollDown(force) {
      if ($('#chatbox').hasClass('visible')) {
        if (force || !this.lastMessage || !this.lastMessage.position() ||
            this.lastMessage.position().top < ($('#chattext').outerHeight() + 20)) {
          // if we use a slow animate here we can have a race condition
          // when a users focus can not be moved away from the last message recieved.
          $('#chattext').animate(
              {scrollTop: $('#chattext')[0].scrollHeight},
              {duration: 400, queue: false});
          this.lastMessage = $('#chattext > p').eq(-1);
        }
      }
    },
    async send() {
      const text = $('#chatinput').val();
      if (text.replace(/\s+/, '').length === 0) return;
      const message = new ChatMessage(text);
      await hooks.aCallAll('chatSendMessage', Object.freeze({message}));
      this._pad.collabClient.sendMessage({type: 'CHAT_MESSAGE', message});
      $('#chatinput').val('');
    },
    async addMessage(msg, increment, isHistoryAdd) {
      msg = ChatMessage.fromObject(msg);
      // correct the time
      msg.time += this._pad.clientTimeOffset;

      if (!msg.authorId) {
        /*
         * If, for a bug or a database corruption, the message coming from the
         * server does not contain the authorId field (see for example #3731),
         * let's be defensive and replace it with "unknown".
         */
        msg.authorId = 'unknown';
        console.warn(
            'The "authorId" field of a chat message coming from the server was not present. ' +
            'Replacing with "unknown". This may be a bug or a database corruption.');
      }

      const authorClass = (authorId) => `author-${authorId.replace(/[^a-y0-9]/g, (c) => {
        if (c === '.') return '-';
        return `z${c.charCodeAt(0)}z`;
      })}`;

      // the hook args
      const ctx = {
        authorName: msg.displayName != null ? msg.displayName : html10n.get('pad.userlist.unnamed'),
        author: msg.authorId,
        text: padutils.escapeHtmlWithClickableLinks(msg.text, '_blank'),
        message: msg,
        rendered: null,
        sticky: false,
        timestamp: msg.time,
        timeStr: (() => {
          let minutes = `${new Date(msg.time).getMinutes()}`;
          let hours = `${new Date(msg.time).getHours()}`;
          if (minutes.length === 1) minutes = `0${minutes}`;
          if (hours.length === 1) hours = `0${hours}`;
          return `${hours}:${minutes}`;
        })(),
        duration: 4000,
      };

      // is the users focus already in the chatbox?
      const alreadyFocused = $('#chatinput').is(':focus');

      // does the user already have the chatbox open?
      const chatOpen = $('#chatbox').hasClass('visible');

      // does this message contain this user's name? (is the current user mentioned?)
      const wasMentioned =
          msg.authorId !== window.clientVars.userId &&
          ctx.authorName !== html10n.get('pad.userlist.unnamed') &&
          normalize(ctx.text).includes(normalize(ctx.authorName));

      // If the user was mentioned, make the message sticky
      if (wasMentioned && !alreadyFocused && !isHistoryAdd && !chatOpen) {
        chatMentions++;
        Tinycon.setBubble(chatMentions);
        ctx.sticky = true;
      }

      await hooks.aCallAll('chatNewMessage', ctx);
      const cls = authorClass(ctx.author);
      const chatMsg = ctx.rendered != null ? $(ctx.rendered) : $('<p>')
          .attr('data-authorId', ctx.author)
          .addClass(cls)
          .append($('<b>').text(`${ctx.authorName}:`))
          .append($('<span>')
              .addClass('time')
              .addClass(cls)
              // Hook functions are trusted to not introduce an XSS vulnerability by adding
              // unescaped user input to ctx.timeStr.
              .html(ctx.timeStr))
          .append(' ')
          // ctx.text was HTML-escaped before calling the hook. Hook functions are trusted to not
          // introduce an XSS vulnerability by adding unescaped user input.
          .append($('<div>').html(ctx.text).contents());
      if (isHistoryAdd) chatMsg.insertAfter('#chatloadmessagesbutton');
      else $('#chattext').append(chatMsg);
      chatMsg.each((i, e) => html10n.translateElement(html10n.translations, e));

      // should we increment the counter??
      if (increment && !isHistoryAdd) {
        // Update the counter of unread messages
        let count = Number($('#chatcounter').text());
        count++;
        $('#chatcounter').text(count);

        if (!chatOpen && ctx.duration > 0) {
          const text = $('<p>')
              .append($('<span>').addClass('author-name').text(ctx.authorName))
              // ctx.text was HTML-escaped before calling the hook. Hook functions are trusted
              // to not introduce an XSS vulnerability by adding unescaped user input.
              .append($('<div>').html(ctx.text).contents());
          text.each((i, e) => html10n.translateElement(html10n.translations, e));
          $.gritter.add({
            text,
            sticky: ctx.sticky,
            time: ctx.duration,
            position: 'bottom',
            class_name: 'chat-gritter-msg',
          });
        }
      }
      if (!isHistoryAdd) this.scrollDown();
    },
    init(pad) {
      this._pad = pad;
      $('#chatinput').on('keydown', (evt) => {
        // If the event is Alt C or Escape & we're already in the chat menu
        // Send the users focus back to the pad
        if ((evt.altKey === true && evt.which === 67) || evt.which === 27) {
          // If we're in chat already..
          $(':focus').blur(); // required to do not try to remove!
          padeditor.ace.focus(); // Sends focus back to pad
          evt.preventDefault();
          return false;
        }
      });
      // Clear the chat mentions when the user clicks on the chat input box
      $('#chatinput').click(() => {
        chatMentions = 0;
        Tinycon.setBubble(0);
      });

      const self = this;
      $('body:not(#chatinput)').on('keypress', function (evt) {
        if (evt.altKey && evt.which === 67) {
          // Alt c focuses on the Chat window
          $(this).blur();
          self.show();
          $('#chatinput').focus();
          evt.preventDefault();
        }
      });

      $('#chatinput').keypress((evt) => {
        // if the user typed enter, fire the send
        if (evt.key === 'Enter' && !evt.shiftKey) {
          evt.preventDefault();
          this.send();
        }
      });

      // initial messages are loaded in pad.js' _afterHandshake

      $('#chatcounter').text(0);
      $('#chatloadmessagesbutton').click(() => {
        const start = Math.max(this.historyPointer - 20, 0);
        const end = this.historyPointer;

        if (start === end) return; // nothing to load

        $('#chatloadmessagesbutton').css('display', 'none');
        $('#chatloadmessagesball').css('display', 'block');

        pad.collabClient.sendMessage({type: 'GET_CHAT_MESSAGES', start, end});
        this.historyPointer = start;
      });
    },
  };
})();
<|endoftext|>


<commit_before>/* global URLS: false */
var L = require('leaflet');
require('leaflet.markercluster');
var $ = require('jquery');
var d3 = require('d3');
var colorbrewer = require('colorbrewer');
// window.d3 = d3;
// window.colorbrewer = colorbrewer;

// need to manually specify this
L.Icon.Default.imagePath = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/images';

// var map = L.map('map').setView([31.505, -98.09], 8);
var map = L.map('map').setView([30.27045435, -97.7414384914151], 15);  // DEBUG

L.tileLayer('http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.jpg', {
  maxZoom: 18,
  attribution: 'Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA.'
}).addTo(map);

var locationCache = {};
var showLocation = function (id, stuff) {
  var contentize = function (data) {
    var $container = $('
');
    $container.append('' + data.latest.name + '');
    var $list = $('
').appendTo($container);
    $.each(data.receipts, function (idx, x) {
      $list.append('
' + x.date.replace(/\-\d+$/, '') + '
');
      $list.append('
$' + x.tax + '
');
    });
    return $container[0];
  };
  // var self = this;
  var showInfo = function (data) {
    // self.bindPopup('test ' + data.latest.name).openPopup();
    L.popup({offset: [-1, 0]})
      .setLatLng(stuff.latlng)
      .setContent(contentize(data))
      .openOn(map);
  };

  var data = locationCache[id];
  if (!data) {
    $.getJSON(URLS.location + id + '/', function (data) {
      locationCache[id] = data;
      showInfo(data);
    });
  } else {
    showInfo(data);
  }
};

var taxColorScale = d3.scale.linear()
  .clamp(true)
  .domain([10000, 5000, 1000, 0])
  .range(colorbrewer.Spectral[4]);
var markerStyle = function (feature) {
  var style = {
    fillOpacity: 0.8,
    opacity: 1,
    radius: 7,
    weight: 1
  };
  var tax = parseInt(feature.properties.data.avg_tax, 10);  // actually a float, but we don't care about cents
  if (isNaN(tax)) {
    style.color = "#000";
    style.dashArray = "5,5";  // TODO get this working
    style.radius = 5;
  } else if (tax === 0) {
    style.color = taxColorScale(tax);
    style.radius = 5;
  } else {
    style.color = taxColorScale(tax);
  }
  return style;
};
var pointToLayer = function (feature, latlng) {
  return L.circleMarker(latlng, markerStyle(feature));
};

$.getJSON(URLS.geojson, function (data) {
  var markers = new L.MarkerClusterGroup({
    disableClusteringAtZoom: 15,
    maxClusterRadius: 50
  });
  L.geoJson(data, {
    pointToLayer: pointToLayer
    // style: markerStyle
  }).addTo(markers);
  markers.addTo(map);
  markers.on('click', function (a) {
    // a.layer.feature.properties
    console.log('marker', a.layer.feature.properties, this);
    showLocation.call(this, a.layer.feature.id, a);
  });
});
<commit_msg>refactor app js to shift stuff from unstuff<commit_after>/* global URLS: false */
var L = require('leaflet');
require('leaflet.markercluster');
var $ = require('jquery');
var d3 = require('d3');
var colorbrewer = require('colorbrewer');
// window.d3 = d3;
// window.colorbrewer = colorbrewer;

// need to manually specify this
L.Icon.Default.imagePath = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/images';

var locationCache = {};
var showLocation = function (id, stuff) {
  var contentize = function (data) {
    var $container = $('
');
    $container.append('' + data.latest.name + '');
    var $list = $('
').appendTo($container);
    $.each(data.receipts, function (idx, x) {
      $list.append('
' + x.date.replace(/\-\d+$/, '') + '
');
      $list.append('
$' + x.tax + '
');
    });
    return $container[0];
  };
  // var self = this;
  var showInfo = function (data) {
    // self.bindPopup('test ' + data.latest.name).openPopup();
    L.popup({offset: [-1, 0]})
      .setLatLng(stuff.latlng)
      .setContent(contentize(data))
      .openOn(map);
  };

  var data = locationCache[id];
  if (!data) {
    $.getJSON(URLS.location + id + '/', function (data) {
      locationCache[id] = data;
      showInfo(data);
    });
  } else {
    showInfo(data);
  }
};

var markerStyle = function (feature) {
  var taxColorScale = d3.scale.linear()
    .clamp(true)
    .domain([10000, 5000, 1000, 0])
    .range(colorbrewer.Spectral[4]);
  var style = {
    fillOpacity: 0.8,
    opacity: 1,
    radius: 7,
    weight: 1
  };
  var tax = parseInt(feature.properties.data.avg_tax, 10);  // actually a float, but we don't care about cents
  if (isNaN(tax)) {
    style.color = "#000";
    style.dashArray = "5,5";  // TODO get this working
    style.radius = 5;
  } else if (tax === 0) {
    style.color = taxColorScale(tax);
    style.radius = 5;
  } else {
    style.color = taxColorScale(tax);
  }
  return style;
};
var pointToLayer = function (feature, latlng) {
  return L.circleMarker(latlng, markerStyle(feature));
};

var _getJSON = function (data) {
  var markers = new L.MarkerClusterGroup({
    disableClusteringAtZoom: 15,
    maxClusterRadius: 50
  });
  L.geoJson(data, {
    pointToLayer: pointToLayer
    // style: markerStyle
  }).addTo(markers);
  markers.addTo(map);
  markers.on('click', function (a) {
    // a.layer.feature.properties
    console.log('marker', a.layer.feature.properties, this);
    showLocation.call(this, a.layer.feature.id, a);
  });
};

// BEGIN

// var map = L.map('map').setView([31.505, -98.09], 8);
var map = L.map('map').setView([30.27045435, -97.7414384914151], 15);  // DEBUG

L.tileLayer('http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.jpg', {
  maxZoom: 18,
  attribution: 'Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA.'
}).addTo(map);

$.getJSON(URLS.geojson, _getJSON);
<|endoftext|>


<commit_before>/**
 * System configuration for Angular samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
  System.config({
    paths: {
      // paths serve as alias
      'npm:': '../node_modules/'
    },
    // map tells the System loader where to look for things
    map: {
      // our app is within the app folder
      app: '',

      // angular bundles
      '@angular/core'             : 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common'           : 'npm:@angular/common/bundles/common.umd.js',
      '@angular/compiler'         : 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser' : 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser-dynamic'
                                  : 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http'             : 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router'           : 'npm:@angular/router/bundles/router.umd.js',
      '@angular/forms'            : 'npm:@angular/forms/bundles/forms.umd.js',
      '@angular/upgrade'          : 'npm:@angular/upgrade/bundles/upgrade.umd.js',

      // other libraries
      'chart.js'                  : 'npm:chart.js/dist/chart.js',
      'ng2-charts'                : 'npm:ng2-charts',
      'ng2-pagination'            : 'npm:ng2-pagination/dist',
      'moment'                    : 'npm:moment',
      'angular2-moment'           : 'npm:angular2-moment',
      'socket.io-client'          : 'npm:socket.io-client/dist/socket.io.js',
      'rxjs'                      : 'npm:rxjs',
      '@angular/material'         : 'npm:@angular/material/material.umd.js',
      'angular-in-memory-web-api' : 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      app               : { defaultExtension: 'js', main: './src/scripts/main.js'},
      rxjs              : { defaultExtension: 'js' },
      'socket.io-client': { defaultExtension: 'js' },
      'chart.js'        : { defaultExtension: 'js' },
      'ng2-sharts'      : { defaultExtension: 'js', main: 'ng2-charts.js' },
      'ng2-pagination'  : { defaultExtension: 'js', main: 'ng2-pagination.js' },
      'moment'          : { defaultExtension: 'js', main: 'moment.js' },
      'angular2-moment' : { defaultExtension: 'js', main: 'index.js' }
    },
    meta: {
      moment: {
        format: 'global',
        globals: {
          moment: 'moment'
        }
      }
    }
  });
})(this);
<commit_msg>Updated angular2 material umd location<commit_after>/**
 * System configuration for Angular samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
  System.config({
    paths: {
      // paths serve as alias
      'npm:': '../node_modules/'
    },
    // map tells the System loader where to look for things
    map: {
      // our app is within the app folder
      app: '',

      // angular bundles
      '@angular/core'             : 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common'           : 'npm:@angular/common/bundles/common.umd.js',
      '@angular/compiler'         : 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser' : 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser-dynamic'
                                  : 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http'             : 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router'           : 'npm:@angular/router/bundles/router.umd.js',
      '@angular/forms'            : 'npm:@angular/forms/bundles/forms.umd.js',
      '@angular/upgrade'          : 'npm:@angular/upgrade/bundles/upgrade.umd.js',

      // other libraries
      'chart.js'                  : 'npm:chart.js/dist/chart.js',
      'ng2-charts'                : 'npm:ng2-charts',
      'ng2-pagination'            : 'npm:ng2-pagination/dist',
      'moment'                    : 'npm:moment',
      'angular2-moment'           : 'npm:angular2-moment',
      'socket.io-client'          : 'npm:socket.io-client/dist/socket.io.js',
      'rxjs'                      : 'npm:rxjs',
      '@angular/material'         : 'npm:@angular/material/bundles/material.umd.js',
      'angular-in-memory-web-api' : 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      app               : { defaultExtension: 'js', main: './src/scripts/main.js'},
      rxjs              : { defaultExtension: 'js' },
      'socket.io-client': { defaultExtension: 'js' },
      'chart.js'        : { defaultExtension: 'js' },
      'ng2-sharts'      : { defaultExtension: 'js', main: 'ng2-charts.js' },
      'ng2-pagination'  : { defaultExtension: 'js', main: 'ng2-pagination.js' },
      'moment'          : { defaultExtension: 'js', main: 'moment.js' },
      'angular2-moment' : { defaultExtension: 'js', main: 'index.js' }
    },
    meta: {
      moment: {
        format: 'global',
        globals: {
          moment: 'moment'
        }
      }
    }
  });
})(this);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TextInput,
  TouchableNativeFeedback,
  Image,
  TouchableOpacity,
  TouchableHighlight,
  Platform,
} from 'react-native';
import LoadingView from '../components/loadingview';

export default class Profile extends Component {
    constructor(props){
        super(props);
        this.state = {
            textOp: 1,
            boxOp: 0,
            hasMemberData: false, //when we use Profile as arbitrary Member page, need to load data asynchronous
            memberData: {}
        };
        this.type = this.props.type || ""; //expecting "profile" or "memberPage"
        this._onGoEditProfile = this._onGoEditProfile.bind(this);
        this._onGoFindAClub = this._onGoFindAClub.bind(this);
        this._onGoClub = this._onGoClub.bind(this);
    }

    render() {
        var user = this.props.route.state.user;
        var clubList = this.props.clubList;
        var userClubIds = user.clubs;
        var userClubs = [];
        for(let userClubId of userClubIds) {
            //in the interest of saving coding time, this code is obviously slow...TODO: optimize later? (e.g. presort clubs by id and binary search?)
            for(let club of clubList) {
                if(club.id === userClubId) {
                    userClubs.push(club);
                    break;
                }
            }
        }
        var memberId = this.props.route.state.memberId || "";
        //default to "profile" settings, but override if we're a member page
        var name = user.name;
        var username = user.username;
        if(this.type === "memberPage") {
            if(!this.state.hasMemberData) {
                return <LoadingView/>;
            }
            name = this.state.memberData.name;
            username = this.state.memberData.username;
        }

        var TouchableElement = Platform.select({
            ios: TouchableOpacity,
            android: TouchableNativeFeedback
        });
        return (
        <View style={styles.container}>
            <View style={styles.box}>
                <Image style = {styles.profilepic} source={require('./images/sponge.jpeg')} />
                <View style={styles.longBox}>
                    <Text style={styles.welcome}>
                        {name}
                    </Text>
                    <Text style = {styles.instructions}>{username}</Text>
                </View>

            </View>
            {
            this.type === "profile" ?
                <TouchableHighlight onPress={()=>this._onGoEditProfile()}>
                    <View><Image style = {styles.edit} source={require('./images/edit.png')} /></View>
                </TouchableHighlight>
                : <View/>
            }
            <Text style = {styles.welcome}>Clubs:</Text>
            {
                userClubs.length ? (
                    userClubs.map((club) => {
                        return (
                            <TouchableElement onPress={()=>this._onGoClub(club)} key={"club-"+club.id}>
                                <View>
                                    <Text style={styles.instructions}>
                                        {club.name}
                                    </Text>
                                </View>
                            </TouchableElement>
                        );
                    })
                ) :
                <TouchableElement onPress={()=>{
                    if(this.type==="profile") {
                        this._onGoFindAClub()
                    }
                }}>
                    <View><Text style={styles.instructions}>{
                        this.type === "profile" ?
                            "You aren't in any clubs! How about finding a club?"
                            : "Not in any clubs."
                    }</Text></View>
                </TouchableElement>
            }
        </View>
        );
    }

      componentDidMount() {
            const url = "http://skeleton20161103012840.azurewebsites.net/api/Users/"+
                this.props.route.state.memberId;
            fetch(url)
                .then(res=>res.json())
                .then(json => {
                    this.setState({
                        hasMemberData: true,
                        memberData: json
                    })
                })
                .catch(e => {
                    console.error(e);
                    //TODO: figure out how to navigate back out if something went wrong
                })
        }

    _onGoEditProfile() {
        this.props.navigator.push({
            type: "editProfile",
            index: this.props.route.index+1,
            state: this.props.route.state
        });
    }

    _onGoFindAClub() {
        this.props.navigator.push({
            type: "findaclub",
            index: this.props.route.index+1,
            state: this.props.route.state
        });
    }

    _onGoClub(club) {
        this.props.navigator.push({
            type: "club",
            index: this.props.route.index+1,
            state: Object.assign({}, this.props.route.state, {club: club})
        });
    }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    //justifyContent: 'center',
    //alignItems: 'center',
    backgroundColor: '#F5FCFF',
    marginTop: 40
  },
  welcome: {
    fontSize: 20,
    ...Platform.select({android: {textAlign: 'center'}}),
    margin: 10,
    color: '#800000',
  },
  instructions: {
    ...Platform.select({android: {textAlign: 'center'}}),
    color: '#333333',
    marginBottom: 5,
  },
  button: {
    ...Platform.select({android: {textAlign: 'center'}}),
    color: '#333333',
    marginBottom: 5,
  },
  profilepic: {
      height: 100,
      width: 100

  },
  edit: {
      height: 25,
      width: 25,

  },
  textEdit: {
    height: 40,
    width: 200,
    borderColor: 'grey',
    backgroundColor: 'white',
    borderWidth: 1
  },
  box: {
      flexDirection: 'row',
      flexWrap: 'wrap'

  },
  longBox: {
      height: 125

  }
});
<commit_msg>fix profile<commit_after>import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TextInput,
  TouchableNativeFeedback,
  Image,
  TouchableOpacity,
  TouchableHighlight,
  Platform,
} from 'react-native';
import LoadingView from '../components/loadingview';

export default class Profile extends Component {
    constructor(props){
        super(props);
        this.state = {
            textOp: 1,
            boxOp: 0,
            hasMemberData: false, //when we use Profile as arbitrary Member page, need to load data asynchronous
            memberData: {}
        };
        this.type = this.props.type || ""; //expecting "profile" or "memberPage"
        this._onGoEditProfile = this._onGoEditProfile.bind(this);
        this._onGoFindAClub = this._onGoFindAClub.bind(this);
        this._onGoClub = this._onGoClub.bind(this);
    }

    render() {
        var user = this.props.route.state.user;
        var clubList = this.props.clubList;
        var userClubIds = user.clubs;
        var userClubs = [];
        for(let userClubId of userClubIds) {
            //in the interest of saving coding time, this code is obviously slow...TODO: optimize later? (e.g. presort clubs by id and binary search?)
            for(let club of clubList) {
                if(club.id === userClubId) {
                    userClubs.push(club);
                    break;
                }
            }
        }
        var memberId = this.props.route.state.memberId || "";
        //default to "profile" settings, but override if we're a member page
        var name = user.name;
        var username = user.username;
        if(this.type === "memberPage") {
            if(!this.state.hasMemberData) {
                return <LoadingView/>;
            }
            name = this.state.memberData.name;
            username = this.state.memberData.username;
        }

        var TouchableElement = Platform.select({
            ios: TouchableOpacity,
            android: TouchableNativeFeedback
        });
        return (
        <View style={styles.container}>
            <View style={styles.box}>
                <Image style = {styles.profilepic} source={require('./images/sponge.jpeg')} />
                <View style={styles.longBox}>
                    <Text style={styles.welcome}>
                        {name}
                    </Text>
                    <Text style = {styles.instructions}>{username}</Text>
                </View>

            </View>
            {
            this.type === "profile" ?
                <TouchableHighlight onPress={()=>this._onGoEditProfile()}>
                    <View><Image style = {styles.edit} source={require('./images/edit.png')} /></View>
                </TouchableHighlight>
                : <View/>
            }
            <Text style = {styles.welcome}>Clubs:</Text>
            {
                userClubs.length ? (
                    userClubs.map((club) => {
                        return (
                            <TouchableElement onPress={()=>this._onGoClub(club)} key={"club-"+club.id}>
                                <View>
                                    <Text style={styles.instructions}>
                                        {club.name}
                                    </Text>
                                </View>
                            </TouchableElement>
                        );
                    })
                ) :
                <TouchableElement onPress={()=>{
                    if(this.type==="profile") {
                        this._onGoFindAClub()
                    }
                }}>
                    <View><Text style={styles.instructions}>{
                        this.type === "profile" ?
                            "You aren't in any clubs! How about finding a club?"
                            : "Not in any clubs."
                    }</Text></View>
                </TouchableElement>
            }
        </View>
        );
    }

      componentDidMount() {
            if(this.type === "memberPage") {
            const url = "http://skeleton20161103012840.azurewebsites.net/api/Users/"+
                this.props.route.state.memberId;
            fetch(url)
                .then(res=>res.json())
                .then(json => {
                    this.setState({
                        hasMemberData: true,
                        memberData: json
                    })
                })
                .catch(e => {
                    console.error(e);
                    //TODO: figure out how to navigate back out if something went wrong
                })
        }
      }

    _onGoEditProfile() {
        this.props.navigator.push({
            type: "editProfile",
            index: this.props.route.index+1,
            state: this.props.route.state
        });
    }

    _onGoFindAClub() {
        this.props.navigator.push({
            type: "findaclub",
            index: this.props.route.index+1,
            state: this.props.route.state
        });
    }

    _onGoClub(club) {
        this.props.navigator.push({
            type: "club",
            index: this.props.route.index+1,
            state: Object.assign({}, this.props.route.state, {club: club})
        });
    }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    //justifyContent: 'center',
    //alignItems: 'center',
    backgroundColor: '#F5FCFF',
    marginTop: 40
  },
  welcome: {
    fontSize: 20,
    ...Platform.select({android: {textAlign: 'center'}}),
    margin: 10,
    color: '#800000',
  },
  instructions: {
    ...Platform.select({android: {textAlign: 'center'}}),
    color: '#333333',
    marginBottom: 5,
  },
  button: {
    ...Platform.select({android: {textAlign: 'center'}}),
    color: '#333333',
    marginBottom: 5,
  },
  profilepic: {
      height: 100,
      width: 100

  },
  edit: {
      height: 25,
      width: 25,

  },
  textEdit: {
    height: 40,
    width: 200,
    borderColor: 'grey',
    backgroundColor: 'white',
    borderWidth: 1
  },
  box: {
      flexDirection: 'row',
      flexWrap: 'wrap'

  },
  longBox: {
      height: 125

  }
});
<|endoftext|>


<commit_before>/*global define*/
define([
        '../Core/defined',
        '../Core/defineProperties',
        '../Core/DeveloperError',
        '../Core/Rectangle',
        './QuadtreeTileLoadState'
    ], function(
        defined,
        defineProperties,
        DeveloperError,
        Rectangle,
        QuadtreeTileLoadState) {
    'use strict';

    /**
     * A single tile in a {@link QuadtreePrimitive}.
     *
     * @alias QuadtreeTile
     * @constructor
     * @private
     *
     * @param {Number} options.level The level of the tile in the quadtree.
     * @param {Number} options.x The X coordinate of the tile in the quadtree.  0 is the westernmost tile.
     * @param {Number} options.y The Y coordinate of the tile in the quadtree.  0 is the northernmost tile.
     * @param {TilingScheme} options.tilingScheme The tiling scheme in which this tile exists.
     * @param {QuadtreeTile} [options.parent] This tile's parent, or undefined if this is a root tile.
     */
    function QuadtreeTile(options) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(options)) {
            throw new DeveloperError('options is required.');
        }
        if (!defined(options.x)) {
            throw new DeveloperError('options.x is required.');
        } else if (!defined(options.y)) {
            throw new DeveloperError('options.y is required.');
        } else if (options.x < 0 || options.y < 0) {
            throw new DeveloperError('options.x and options.y must be greater than or equal to zero.');
        }
        if (!defined(options.level)) {
            throw new DeveloperError('options.level is required and must be greater than or equal to zero.');
        }
        if (!defined(options.tilingScheme)) {
            throw new DeveloperError('options.tilingScheme is required.');
        }
        //>>includeEnd('debug');

        this._tilingScheme = options.tilingScheme;
        this._x = options.x;
        this._y = options.y;
        this._level = options.level;
        this._parent = options.parent;
        this._rectangle = this._tilingScheme.tileXYToRectangle(this._x, this._y, this._level);

        this._southwestChild = undefined;
        this._southeastChild = undefined;
        this._northwestChild = undefined;
        this._northeastChild = undefined;

        // QuadtreeTileReplacementQueue gets/sets these private properties.
        this._replacementPrevious = undefined;
        this._replacementNext = undefined;

        // The distance from the camera to this tile, updated when the tile is selected
        // for rendering.  We can get rid of this if we have a better way to sort by
        // distance - for example, by using the natural ordering of a quadtree.
        // QuadtreePrimitive gets/sets this private property.
        this._distance = 0.0;

        this._customData = [];
        this._frameUpdated = undefined;
        this._frameRendered = undefined;

        /**
         * Gets or sets the current state of the tile in the tile load pipeline.
         * @type {QuadtreeTileLoadState}
         * @default {@link QuadtreeTileLoadState.START}
         */
        this.state = QuadtreeTileLoadState.START;

        /**
         * Gets or sets a value indicating whether or not the tile is currently renderable.
         * @type {Boolean}
         * @default false
         */
        this.renderable = false;

        /**
         * Gets or set a value indicating whether or not the tile was entire upsampled from its
         * parent tile.  If all four children of a parent tile were upsampled from the parent,
         * we will render the parent instead of the children even if the LOD indicates that
         * the children would be preferable.
         * @type {Boolean}
         * @default false
         */
        this.upsampledFromParent = false;

        /**
         * Gets or sets the additional data associated with this tile.  The exact content is specific to the
         * {@link QuadtreeTileProvider}.
         * @type {Object}
         * @default undefined
         */
        this.data = undefined;
    }

    /**
     * Creates a rectangular set of tiles for level of detail zero, the coarsest, least detailed level.
     *
     * @memberof QuadtreeTile
     *
     * @param {TilingScheme} tilingScheme The tiling scheme for which the tiles are to be created.
     * @returns {QuadtreeTile[]} An array containing the tiles at level of detail zero, starting with the
     * tile in the northwest corner and followed by the tile (if any) to its east.
     */
    QuadtreeTile.createLevelZeroTiles = function(tilingScheme) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(tilingScheme)) {
            throw new DeveloperError('tilingScheme is required.');
        }
        //>>includeEnd('debug');

        var numberOfLevelZeroTilesX = tilingScheme.getNumberOfXTilesAtLevel(0);
        var numberOfLevelZeroTilesY = tilingScheme.getNumberOfYTilesAtLevel(0);

        var result = new Array(numberOfLevelZeroTilesX * numberOfLevelZeroTilesY);

        var index = 0;
        for (var y = 0; y < numberOfLevelZeroTilesY; ++y) {
            for (var x = 0; x < numberOfLevelZeroTilesX; ++x) {
                result[index++] = new QuadtreeTile({
                    tilingScheme : tilingScheme,
                    x : x,
                    y : y,
                    level : 0
                });
            }
        }

        return result;
    };

    QuadtreeTile.prototype._updateCustomData = function(frameNumber, added, removed) {
        var customData = this.customData;

        var i;
        var data;
        var rectangle;

        if (defined(added) && defined(removed)) {
            customData = customData.filter(function(value) {
                return removed.indexOf(value) === -1;
            });
            this._customData = customData;

            rectangle = this._rectangle;
            for (i = 0; i < added.length; ++i) {
                data = added[i];
                if (Rectangle.contains(rectangle, data.positionCartographic)) {
                    customData.push(data);
                }
            }

            this._frameUpdated = frameNumber;
        } else {
            // interior or leaf tile, update from parent
            var parent = this._parent;
            if (defined(parent) && this._frameUpdated !== parent._frameUpdated) {
                customData.length = 0;

                rectangle = this._rectangle;
                var parentCustomData = parent.customData;
                for (i = 0; i < parentCustomData.length; ++i) {
                    data = parentCustomData[i];
                    if (Rectangle.contains(rectangle, data.positionCartographic)) {
                        customData.push(data);
                    }
                }

                this._frameUpdated = parent._frameUpdated;
            }
        }
    };

    defineProperties(QuadtreeTile.prototype, {
        /**
         * Gets the tiling scheme used to tile the surface.
         * @memberof QuadtreeTile.prototype
         * @type {TilingScheme}
         */
        tilingScheme : {
            get : function() {
                return this._tilingScheme;
            }
        },

        /**
         * Gets the tile X coordinate.
         * @memberof QuadtreeTile.prototype
         * @type {Number}
         */
        x : {
            get : function() {
                return this._x;
            }
        },

        /**
         * Gets the tile Y coordinate.
         * @memberof QuadtreeTile.prototype
         * @type {Number}
         */
        y : {
            get : function() {
                return this._y;
            }
        },

        /**
         * Gets the level-of-detail, where zero is the coarsest, least-detailed.
         * @memberof QuadtreeTile.prototype
         * @type {Number}
         */
        level : {
            get : function() {
                return this._level;
            }
        },

        /**
         * Gets the parent tile of this tile.
         * @memberof QuadtreeTile.prototype
         * @type {QuadtreeTile}
         */
        parent : {
            get : function() {
                return this._parent;
            }
        },

        /**
         * Gets the cartographic rectangle of the tile, with north, south, east and
         * west properties in radians.
         * @memberof QuadtreeTile.prototype
         * @type {Rectangle}
         */
        rectangle : {
            get : function() {
                return this._rectangle;
            }
        },

        /**
         * An array of tiles that is at the next level of the tile tree.
         * @memberof QuadtreeTile.prototype
         * @type {QuadtreeTile[]}
         */
        children : {
            get : function() {
                return [this.northwestChild, this.northeastChild, this.southwestChild, this.southeastChild];
            }
        },

        /**
         * Gets the southwest child tile.
         * @type {QuadtreeTile}
         */
        southwestChild : {
            get : function() {
                if (!defined(this._southwestChild)) {
                    this._southwestChild = new QuadtreeTile({
                        tilingScheme : this.tilingScheme,
                        x : this.x * 2,
                        y : this.y * 2 + 1,
                        level : this.level + 1,
                        parent : this
                    });
                }
                return this._southwestChild;
            }
        },

        /**
         * Gets the southeast child tile.
         * @type {QuadtreeTile}
         */
        southeastChild : {
            get : function() {
                if (!defined(this._southeastChild)) {
                    this._southeastChild = new QuadtreeTile({
                        tilingScheme : this.tilingScheme,
                        x : this.x * 2 + 1,
                        y : this.y * 2 + 1,
                        level : this.level + 1,
                        parent : this
                    });
                }
                return this._southeastChild;
            }
        },

        /**
         * Gets the northwest child tile.
         * @type {QuadtreeTile}
         */
        northwestChild : {
            get : function() {
                if (!defined(this._northwestChild)) {
                    this._northwestChild = new QuadtreeTile({
                        tilingScheme : this.tilingScheme,
                        x : this.x * 2,
                        y : this.y * 2,
                        level : this.level + 1,
                        parent : this
                    });
                }
                return this._northwestChild;
            }
        },

        /**
         * Gets the northeast child tile.
         * @type {QuadtreeTile}
         */
        northeastChild : {
            get : function() {
                if (!defined(this._northeastChild)) {
                    this._northeastChild = new QuadtreeTile({
                        tilingScheme : this.tilingScheme,
                        x : this.x * 2 + 1,
                        y : this.y * 2,
                        level : this.level + 1,
                        parent : this
                    });
                }
                return this._northeastChild;
            }
        },

        /**
         * An array of objects associated with this tile.
         * @memberof QuadtreeTile.prototype
         * @type {Array}
         */
        customData : {
            get : function() {
                return this._customData;
            }
        },

        /**
         * Gets a value indicating whether or not this tile needs further loading.
         * This property will return true if the {@link QuadtreeTile#state} is
         * START or LOADING.
         * @memberof QuadtreeTile.prototype
         * @type {Boolean}
         */
        needsLoading : {
            get : function() {
                return this.state < QuadtreeTileLoadState.DONE;
            }
        },

        /**
         * Gets a value indicating whether or not this tile is eligible to be unloaded.
         * Typically, a tile is ineligible to be unloaded while an asynchronous operation,
         * such as a request for data, is in progress on it.  A tile will never be
         * unloaded while it is needed for rendering, regardless of the value of this
         * property.  If {@link QuadtreeTile#data} is defined and has an
         * eligibleForUnloading property, the value of that property is returned.
         * Otherwise, this property returns true.
         * @memberof QuadtreeTile.prototype
         * @type {Boolean}
         */
        eligibleForUnloading : {
            get : function() {
                var result = true;

                if (defined(this.data)) {
                    result = this.data.eligibleForUnloading;
                    if (!defined(result)) {
                        result = true;
                    }
                }

                return result;
            }
        }
    });

    /**
     * Frees the resources associated with this tile and returns it to the START
     * {@link QuadtreeTileLoadState}.  If the {@link QuadtreeTile#data} property is defined and it
     * has a freeResources method, the method will be invoked.
     *
     * @memberof QuadtreeTile
     */
    QuadtreeTile.prototype.freeResources = function() {
        this.state = QuadtreeTileLoadState.START;
        this.renderable = false;
        this.upsampledFromParent = false;

        if (defined(this.data) && defined(this.data.freeResources)) {
            this.data.freeResources();
        }

        freeTile(this._southwestChild);
        this._southwestChild = undefined;
        freeTile(this._southeastChild);
        this._southeastChild = undefined;
        freeTile(this._northwestChild);
        this._northwestChild = undefined;
        freeTile(this._northeastChild);
        this._northeastChild = undefined;
    };

    function freeTile(tile) {
        if (defined(tile)) {
            tile.freeResources();
        }
    }

    return QuadtreeTile;
});
<commit_msg>Whitespace<commit_after>/*global define*/
define([
        '../Core/defined',
        '../Core/defineProperties',
        '../Core/DeveloperError',
        '../Core/Rectangle',
        './QuadtreeTileLoadState'
    ], function(
        defined,
        defineProperties,
        DeveloperError,
        Rectangle,
        QuadtreeTileLoadState) {
    'use strict';

    /**
     * A single tile in a {@link QuadtreePrimitive}.
     *
     * @alias QuadtreeTile
     * @constructor
     * @private
     *
     * @param {Number} options.level The level of the tile in the quadtree.
     * @param {Number} options.x The X coordinate of the tile in the quadtree.  0 is the westernmost tile.
     * @param {Number} options.y The Y coordinate of the tile in the quadtree.  0 is the northernmost tile.
     * @param {TilingScheme} options.tilingScheme The tiling scheme in which this tile exists.
     * @param {QuadtreeTile} [options.parent] This tile's parent, or undefined if this is a root tile.
     */
    function QuadtreeTile(options) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(options)) {
            throw new DeveloperError('options is required.');
        }
        if (!defined(options.x)) {
            throw new DeveloperError('options.x is required.');
        } else if (!defined(options.y)) {
            throw new DeveloperError('options.y is required.');
        } else if (options.x < 0 || options.y < 0) {
            throw new DeveloperError('options.x and options.y must be greater than or equal to zero.');
        }
        if (!defined(options.level)) {
            throw new DeveloperError('options.level is required and must be greater than or equal to zero.');
        }
        if (!defined(options.tilingScheme)) {
            throw new DeveloperError('options.tilingScheme is required.');
        }
        //>>includeEnd('debug');

        this._tilingScheme = options.tilingScheme;
        this._x = options.x;
        this._y = options.y;
        this._level = options.level;
        this._parent = options.parent;
        this._rectangle = this._tilingScheme.tileXYToRectangle(this._x, this._y, this._level);

        this._southwestChild = undefined;
        this._southeastChild = undefined;
        this._northwestChild = undefined;
        this._northeastChild = undefined;

        // QuadtreeTileReplacementQueue gets/sets these private properties.
        this._replacementPrevious = undefined;
        this._replacementNext = undefined;

        // The distance from the camera to this tile, updated when the tile is selected
        // for rendering.  We can get rid of this if we have a better way to sort by
        // distance - for example, by using the natural ordering of a quadtree.
        // QuadtreePrimitive gets/sets this private property.
        this._distance = 0.0;

        this._customData = [];
        this._frameUpdated = undefined;
        this._frameRendered = undefined;

        /**
         * Gets or sets the current state of the tile in the tile load pipeline.
         * @type {QuadtreeTileLoadState}
         * @default {@link QuadtreeTileLoadState.START}
         */
        this.state = QuadtreeTileLoadState.START;

        /**
         * Gets or sets a value indicating whether or not the tile is currently renderable.
         * @type {Boolean}
         * @default false
         */
        this.renderable = false;

        /**
         * Gets or set a value indicating whether or not the tile was entire upsampled from its
         * parent tile.  If all four children of a parent tile were upsampled from the parent,
         * we will render the parent instead of the children even if the LOD indicates that
         * the children would be preferable.
         * @type {Boolean}
         * @default false
         */
        this.upsampledFromParent = false;

        /**
         * Gets or sets the additional data associated with this tile.  The exact content is specific to the
         * {@link QuadtreeTileProvider}.
         * @type {Object}
         * @default undefined
         */
        this.data = undefined;
    }

    /**
     * Creates a rectangular set of tiles for level of detail zero, the coarsest, least detailed level.
     *
     * @memberof QuadtreeTile
     *
     * @param {TilingScheme} tilingScheme The tiling scheme for which the tiles are to be created.
     * @returns {QuadtreeTile[]} An array containing the tiles at level of detail zero, starting with the
     * tile in the northwest corner and followed by the tile (if any) to its east.
     */
    QuadtreeTile.createLevelZeroTiles = function(tilingScheme) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(tilingScheme)) {
            throw new DeveloperError('tilingScheme is required.');
        }
        //>>includeEnd('debug');

        var numberOfLevelZeroTilesX = tilingScheme.getNumberOfXTilesAtLevel(0);
        var numberOfLevelZeroTilesY = tilingScheme.getNumberOfYTilesAtLevel(0);

        var result = new Array(numberOfLevelZeroTilesX * numberOfLevelZeroTilesY);

        var index = 0;
        for (var y = 0; y < numberOfLevelZeroTilesY; ++y) {
            for (var x = 0; x < numberOfLevelZeroTilesX; ++x) {
                result[index++] = new QuadtreeTile({
                    tilingScheme : tilingScheme,
                    x : x,
                    y : y,
                    level : 0
                });
            }
        }

        return result;
    };

    QuadtreeTile.prototype._updateCustomData = function(frameNumber, added, removed) {
        var customData = this.customData;

        var i;
        var data;
        var rectangle;

        if (defined(added) && defined(removed)) {
            customData = customData.filter(function(value) {
                return removed.indexOf(value) === -1;
            });
            this._customData = customData;

            rectangle = this._rectangle;
            for (i = 0; i < added.length; ++i) {
                data = added[i];
                if (Rectangle.contains(rectangle, data.positionCartographic)) {
                    customData.push(data);
                }
            }

            this._frameUpdated = frameNumber;
        } else {
            // interior or leaf tile, update from parent
            var parent = this._parent;
            if (defined(parent) && this._frameUpdated !== parent._frameUpdated) {
                customData.length = 0;

                rectangle = this._rectangle;
                var parentCustomData = parent.customData;
                for (i = 0; i < parentCustomData.length; ++i) {
                    data = parentCustomData[i];
                    if (Rectangle.contains(rectangle, data.positionCartographic)) {
                        customData.push(data);
                    }
                }

                this._frameUpdated = parent._frameUpdated;
            }
        }
    };

    defineProperties(QuadtreeTile.prototype, {
        /**
         * Gets the tiling scheme used to tile the surface.
         * @memberof QuadtreeTile.prototype
         * @type {TilingScheme}
         */
        tilingScheme : {
            get : function() {
                return this._tilingScheme;
            }
        },

        /**
         * Gets the tile X coordinate.
         * @memberof QuadtreeTile.prototype
         * @type {Number}
         */
        x : {
            get : function() {
                return this._x;
            }
        },

        /**
         * Gets the tile Y coordinate.
         * @memberof QuadtreeTile.prototype
         * @type {Number}
         */
        y : {
            get : function() {
                return this._y;
            }
        },

        /**
         * Gets the level-of-detail, where zero is the coarsest, least-detailed.
         * @memberof QuadtreeTile.prototype
         * @type {Number}
         */
        level : {
            get : function() {
                return this._level;
            }
        },

        /**
         * Gets the parent tile of this tile.
         * @memberof QuadtreeTile.prototype
         * @type {QuadtreeTile}
         */
        parent : {
            get : function() {
                return this._parent;
            }
        },

        /**
         * Gets the cartographic rectangle of the tile, with north, south, east and
         * west properties in radians.
         * @memberof QuadtreeTile.prototype
         * @type {Rectangle}
         */
        rectangle : {
            get : function() {
                return this._rectangle;
            }
        },

        /**
         * An array of tiles that is at the next level of the tile tree.
         * @memberof QuadtreeTile.prototype
         * @type {QuadtreeTile[]}
         */
        children : {
            get : function() {
                return [this.northwestChild, this.northeastChild, this.southwestChild, this.southeastChild];
            }
        },

        /**
         * Gets the southwest child tile.
         * @type {QuadtreeTile}
         */
        southwestChild : {
            get : function() {
                if (!defined(this._southwestChild)) {
                    this._southwestChild = new QuadtreeTile({
                        tilingScheme : this.tilingScheme,
                        x : this.x * 2,
                        y : this.y * 2 + 1,
                        level : this.level + 1,
                        parent : this
                    });
                }
                return this._southwestChild;
            }
        },

        /**
         * Gets the southeast child tile.
         * @type {QuadtreeTile}
         */
        southeastChild : {
            get : function() {
                if (!defined(this._southeastChild)) {
                    this._southeastChild = new QuadtreeTile({
                        tilingScheme : this.tilingScheme,
                        x : this.x * 2 + 1,
                        y : this.y * 2 + 1,
                        level : this.level + 1,
                        parent : this
                    });
                }
                return this._southeastChild;
            }
        },

        /**
         * Gets the northwest child tile.
         * @type {QuadtreeTile}
         */
        northwestChild : {
            get : function() {
                if (!defined(this._northwestChild)) {
                    this._northwestChild = new QuadtreeTile({
                        tilingScheme : this.tilingScheme,
                        x : this.x * 2,
                        y : this.y * 2,
                        level : this.level + 1,
                        parent : this
                    });
                }
                return this._northwestChild;
            }
        },

        /**
         * Gets the northeast child tile.
         * @type {QuadtreeTile}
         */
        northeastChild : {
            get : function() {
                if (!defined(this._northeastChild)) {
                    this._northeastChild = new QuadtreeTile({
                        tilingScheme : this.tilingScheme,
                        x : this.x * 2 + 1,
                        y : this.y * 2,
                        level : this.level + 1,
                        parent : this
                    });
                }
                return this._northeastChild;
            }
        },

        /**
         * An array of objects associated with this tile.
         * @memberof QuadtreeTile.prototype
         * @type {Array}
         */
        customData : {
            get : function() {
                return this._customData;
            }
        },

        /**
         * Gets a value indicating whether or not this tile needs further loading.
         * This property will return true if the {@link QuadtreeTile#state} is
         * START or LOADING.
         * @memberof QuadtreeTile.prototype
         * @type {Boolean}
         */
        needsLoading : {
            get : function() {
                return this.state < QuadtreeTileLoadState.DONE;
            }
        },

        /**
         * Gets a value indicating whether or not this tile is eligible to be unloaded.
         * Typically, a tile is ineligible to be unloaded while an asynchronous operation,
         * such as a request for data, is in progress on it.  A tile will never be
         * unloaded while it is needed for rendering, regardless of the value of this
         * property.  If {@link QuadtreeTile#data} is defined and has an
         * eligibleForUnloading property, the value of that property is returned.
         * Otherwise, this property returns true.
         * @memberof QuadtreeTile.prototype
         * @type {Boolean}
         */
        eligibleForUnloading : {
            get : function() {
                var result = true;

                if (defined(this.data)) {
                    result = this.data.eligibleForUnloading;
                    if (!defined(result)) {
                        result = true;
                    }
                }

                return result;
            }
        }
    });

    /**
     * Frees the resources associated with this tile and returns it to the START
     * {@link QuadtreeTileLoadState}.  If the {@link QuadtreeTile#data} property is defined and it
     * has a freeResources method, the method will be invoked.
     *
     * @memberof QuadtreeTile
     */
    QuadtreeTile.prototype.freeResources = function() {
        this.state = QuadtreeTileLoadState.START;
        this.renderable = false;
        this.upsampledFromParent = false;

        if (defined(this.data) && defined(this.data.freeResources)) {
            this.data.freeResources();
        }

        freeTile(this._southwestChild);
        this._southwestChild = undefined;
        freeTile(this._southeastChild);
        this._southeastChild = undefined;
        freeTile(this._northwestChild);
        this._northwestChild = undefined;
        freeTile(this._northeastChild);
        this._northeastChild = undefined;
    };

    function freeTile(tile) {
        if (defined(tile)) {
            tile.freeResources();
        }
    }

    return QuadtreeTile;
});
<|endoftext|>