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

var resize = require('./demo.js').resize;
var scrollToElement = require('./demo.js').scrollToElement;
const StateManager = require('./state.js');
var getRandomInt = require('./demo.js').getRandomInt;
var { renderBoxes } = require('./image-boxes.jsx');
var { classifyScoreTable, customClassifyScoreTable } = require('./classresults.jsx');
var jpath = require('jpath-query');

var errorMessages = {
  ERROR_PROCESSING_REQUEST: 'Oops! The system encoutered an error. Try again.',
  LIMIT_FILE_SIZE: 'Ensure the image is under 2mb',
  URL_FETCH_PROBLEM: 'This is an invalid image URL.',
  TOO_MANY_REQUESTS: 'You have entered too many requests at once. Please try again later.',
  SITE_IS_DOWN: 'We are working to get Visual Recognition up and running shortly!'
};

var lockState = {};

function lock(lockName) {
  if (lockState[lockName] === 1) {
    return false;
  } else {
    lockState[lockName] = 1;
    return true;
  }
}

function unlock(lockName) {
  lockState[lockName] = 0;
}

/*
 * Setups the "Try Out" and "Test" panels.
 * It connects listeners to the DOM elements in the panel to allow
 * users to select an existing image or upload a file.
 * @param params.panel {String} The panel name that will be use to locate the DOM elements.
 */

function setupUse(params) {

  $('.hideload').css('opacity',1.0);
  var panel = params.panel || 'use';
  console.log('setupUse()', panel);

  // panel ids
  var pclass = '.' + panel + '--';
  var pid = '#' + panel + '--';

  // jquery elements we are using
  var $loading = $(pclass + 'loading');
  var $result = $(pclass + 'output');
  var $error = $(pclass + 'error');
  var $errorMsg = $(pclass + 'error-message');
  var $tbody = $(pclass + 'output-tbody');
  var $image = $(pclass + 'output-image');
  var $urlInput = $(pclass + 'url-input');
  var $imageDataInput = $(pclass + 'image-data-input');
  var $radioImages = $(pclass + 'example-radio');
  var $invalidImageUrl = $(pclass + 'invalid-image-url').hide();
  var $invalidUrl = $(pclass + 'invalid-url').show();
  var $dropzone = $(pclass + 'dropzone');
  var $fileupload = $(pid + 'fileupload');
  var $outputData = $(pclass + 'output-data');
  var $boxes = $('.boxes');
  var $randomImage = $(pclass + 'random-test-image');

  /*
   * Resets the panel
   */
  function reset() {
    $loading.hide();
    $result.hide();
    $error.hide();
    resetPasteUrl();
    $urlInput.val('');
    $tbody.empty();
    $outputData.empty();
    $('.dragover').removeClass('dragover');
    $boxes.empty();
  }

  // init reset
  reset();

  function processImage() {
    reset();
    $loading.show();
    scrollToElement($loading);
  }

  /*
   * Shows the result from classifing an image
   */
  function showResult(results) {
    $loading.hide();
    $error.hide();

    if (!results || !results.images || !results.images[0]) {
      showError(errorMessages.ERROR_PROCESSING_REQUEST);
      return;
    }

    if (results.images[0].error) {
      var error = results.images[0].error;
      if (error.description && error.description.indexOf('limit exceeded') != -1) {
        showError(errorMessages.LIMIT_FILE_SIZE);
        return;
      } else if (results.images[0].error.error_id === 'input_error') {
        showError(errorMessages.URL_FETCH_PROBLEM);
        return;
      }
    }
    // populate table
    renderTable(results,null);
    $result.show();

    setTimeout(function () {
      renderEntities(results);
    }, 100);
    $(window).resize(function () {
      $boxes.empty();
      renderEntities(results);
    });

    // check if there are results or not
    if ($outputData.html() === '') {
      $outputData.after(
          $('
')
              .html('No matching classifiers found.'));
    }

    var outputImage = document.querySelector('.use--output-image');
    if (outputImage && (outputImage.height >= outputImage.width)) {
      $(outputImage).addClass('landscape');
    }
    scrollToElement($result);
  }

  function showError(message) {
    $error.show();
    $errorMsg.html(message);
    console.log($error, $errorMsg);
  }

  function _error(xhr, responseMessage) {
    $loading.hide();
    var message = responseMessage || 'Error classifying the image';
    if (xhr && xhr.responseJSON) {
      message = xhr.responseJSON.error;
    }
    showError(message);
  }

  /*
   * submit event
   */
  function classifyImage(imgPath, imageData, beforeFunction, afterFunction) {
    if (!lock('classify')) {
      return;
    }

    beforeFunction ? beforeFunction() : false;

    processImage();
    if (imgPath !== '') {
      $image.attr('src', imgPath);
      $urlInput.val(imgPath);
    }

    $imageDataInput.val(imageData);

    let formData = $(pclass + 'form').serialize();
    // Grab all form data
    $.post('/api/classify', formData)
        .done(showResult)
        .error(function (error) {
          $loading.hide();
          console.log(error);

          if (error.status === 429) {
            showError(errorMessages.TOO_MANY_REQUESTS);
          } else if (error.responseJSON && error.responseJSON.error) {
            showError('We had a problem classifying that image because ' + jpath.jpath('/responseJSON/error/description',error,' of an unknown error'));
          } else {
            showError(errorMessages.SITE_IS_DOWN);
          }
        }).always(function () {
      afterFunction ? afterFunction() : false;
      unlock('classify');
    });
  }

  /*
   * Prevent default form submission
   */
  $fileupload.submit(false);

  /*
   * Radio image submission
   */
  $radioImages.click(function () {
    var rI = $(this);
    var imgPath = rI.next('label').find('img').attr('src');
    $urlInput.hide();
    classifyImage(imgPath, null, function () {
      $('input[type=radio][name=use--example-images]').prop('disabled', true);
      resetPasteUrl();
      rI.parent().find('label').addClass('dim');
      rI.parent().find('label[for=use--file]').removeClass('dim')
      rI.parent().find('label[for=' + rI.attr('id') + ']').removeClass('dim');
    }, function () {
      $urlInput.val('');
      $urlInput.show();
      $('input[type=radio][name=use--example-images]').prop('disabled', false);
    });
  });

  /*
   * Random image submission
   */
  $randomImage.click(function (e) {
    e.preventDefault();
    resetPasteUrl();
    var kind = StateManager.getState().kind;
    var path = kind === 'user' ? '/samples/' : '/bundles/' + kind + '/test/';
    classifyImage('images' + path + getRandomInt(1, 5) + '.jpg', true);
    $urlInput.val('');
  });

  /*
   * Image url submission
   */
  $urlInput.keypress(function (e) {
    var url = $(this).val();
    var self = $(this);

    if (e.keyCode === 13) {
      $invalidUrl.hide();
      $invalidImageUrl.hide();
      resetPasteUrl();
      classifyImage(url);
      self.blur();
    }

    $(self).focus();
  });

  function resetPasteUrl() {
    $urlInput.removeClass(panel + '--url-input_error');
    $invalidUrl.hide();
    $invalidImageUrl.hide();
  }

  /**
   * Jquery file upload configuration
   * See details: https://github.com/blueimp/jQuery-File-Upload
   */
  $fileupload.fileupload({
    dataType: 'json',
    dropZone: $dropzone,
    acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
    add: function (e, data) {
      data.url = '/api/classify';
      if (data.files && data.files[0]) {
        $error.hide();

        processImage();
        var reader = new FileReader();
        reader.onload = function () {
          var image = new Image();
          image.src = reader.result;
          image.onload = function () {
            var resizedImage = (data.files[0]['size'] > 2000000) ? resize(image, 800) : this.src;
            $image.attr('src', resizedImage);
            classifyImage('', resizedImage);
          };
          image.onerror = function () {
            _error(null, 'Error loading the image file. I can only work with images.');
          };
        };
        reader.readAsDataURL(data.files[0]);
      }
    },
    error: _error,
    done: function (e, data) {
      showResult(data.result);
    }
  });

  $(document).on('dragover', function () {
    $(pclass + 'dropzone label').addClass('dragover');
    $('form#use--fileupload').addClass('dragover');
  });

  $(document).on('dragleave', function () {
    $(pclass + 'dropzone label').removeClass('dragover');
    $('form#use--fileupload').removeClass('dragover');
  });

  // need to add on resize event listener for faces
  // need to offset and position itself and scale properly with physical image location
  // need to calculate ratio of image
  // get ratio
  // get starting image position
  // get transformed positions of face
  //  = ratio * original positions + offset
  function renderEntities(results) {
    var faces = jpath.jpath('/images/0/faces', results, []);
    var faceLocations = faces.map(function (face) {
      return transformBoxLocations(face.face_location, document.querySelector('.use--output-image'));
    });

    var words = jpath.jpath('/images/0/words', results, []);
    var wordsLocations = words.map(function (word) {
      return transformBoxLocations(word.location, document.querySelector('.use--output-image'));
    });

    renderBoxes('box', wordsLocations.concat(faceLocations));
  }

  function transformBoxLocations(faceLocation, image) {
    var newFaceLocation = faceLocation;
    var ratio = image.getBoundingClientRect().width / image.naturalWidth;
    var coordinates = getCoords(image);
    newFaceLocation = {
      width: faceLocation.width * ratio,
      height: faceLocation.height * ratio,
      top: coordinates.top + faceLocation.top * ratio,
      left: coordinates.left + faceLocation.left * ratio
    };
    return newFaceLocation;
  }

  /*
   * Solution found here:
   * http://stackoverflow.com/questions/5598743/finding-elements-position-relative-to-the-document#answer-26230989
   */
  function getCoords(elem) { // crossbrowser version
    var box = elem.getBoundingClientRect();

    var body = document.body;
    var docEl = document.documentElement;

    var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
    var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;

    var clientTop = docEl.clientTop || body.clientTop || 0;
    var clientLeft = docEl.clientLeft || body.clientLeft || 0;

    var top = box.top + scrollTop - clientTop;
    var left = box.left + scrollLeft - clientLeft;

    return {top: Math.round(top), left: Math.round(left)};
  }

  function renderTable(results) {
    $('.' + panel + '--mismatch').remove();
    let resolved_url = jpath.jpath('/images/0/resolved_url',results);
    if (resolved_url) {
      $image.attr('src', resolved_url);
    }

    if (results.classifier_ids && results.classifier_ids.split(",").filter(function(item) { return item !== 'default'; }).length > 0) {
      customClassifyScoreTable(results, $outputData[0]);
    } else {
      classifyScoreTable(results,$outputData[0]);
    }
  }
}

module.exports = setupUse;
<commit_msg>Add default error message if nothing else is caught<commit_after>/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* eslint no-unused-vars: "warn"*/
'use strict';

var resize = require('./demo.js').resize;
var scrollToElement = require('./demo.js').scrollToElement;
const StateManager = require('./state.js');
var getRandomInt = require('./demo.js').getRandomInt;
var { renderBoxes } = require('./image-boxes.jsx');
var { classifyScoreTable, customClassifyScoreTable } = require('./classresults.jsx');
var jpath = require('jpath-query');

var errorMessages = {
  ERROR_PROCESSING_REQUEST: 'Oops! The system encoutered an error. Try again.',
  LIMIT_FILE_SIZE: 'Ensure the image is under 2mb',
  URL_FETCH_PROBLEM: 'This is an invalid image URL.',
  TOO_MANY_REQUESTS: 'You have entered too many requests at once. Please try again later.',
  SITE_IS_DOWN: 'We are working to get Visual Recognition up and running shortly!',
  UNKNOWN_ERROR: 'An unknown error has occured.'
};

var lockState = {};

function lock(lockName) {
  if (lockState[lockName] === 1) {
    return false;
  } else {
    lockState[lockName] = 1;
    return true;
  }
}

function unlock(lockName) {
  lockState[lockName] = 0;
}

/*
 * Setups the "Try Out" and "Test" panels.
 * It connects listeners to the DOM elements in the panel to allow
 * users to select an existing image or upload a file.
 * @param params.panel {String} The panel name that will be use to locate the DOM elements.
 */

function setupUse(params) {

  $('.hideload').css('opacity',1.0);
  var panel = params.panel || 'use';
  console.log('setupUse()', panel);

  // panel ids
  var pclass = '.' + panel + '--';
  var pid = '#' + panel + '--';

  // jquery elements we are using
  var $loading = $(pclass + 'loading');
  var $result = $(pclass + 'output');
  var $error = $(pclass + 'error');
  var $errorMsg = $(pclass + 'error-message');
  var $tbody = $(pclass + 'output-tbody');
  var $image = $(pclass + 'output-image');
  var $urlInput = $(pclass + 'url-input');
  var $imageDataInput = $(pclass + 'image-data-input');
  var $radioImages = $(pclass + 'example-radio');
  var $invalidImageUrl = $(pclass + 'invalid-image-url').hide();
  var $invalidUrl = $(pclass + 'invalid-url').show();
  var $dropzone = $(pclass + 'dropzone');
  var $fileupload = $(pid + 'fileupload');
  var $outputData = $(pclass + 'output-data');
  var $boxes = $('.boxes');
  var $randomImage = $(pclass + 'random-test-image');

  /*
   * Resets the panel
   */
  function reset() {
    $loading.hide();
    $result.hide();
    $error.hide();
    resetPasteUrl();
    $urlInput.val('');
    $tbody.empty();
    $outputData.empty();
    $('.dragover').removeClass('dragover');
    $boxes.empty();
  }

  // init reset
  reset();

  function processImage() {
    reset();
    $loading.show();
    scrollToElement($loading);
  }

  /*
   * Shows the result from classifing an image
   */
  function showResult(results) {
    $loading.hide();
    $error.hide();

    if (!results || !results.images || !results.images[0]) {
      showError(errorMessages.ERROR_PROCESSING_REQUEST);
      return;
    }

    if (results.images[0].error) {
      var error = results.images[0].error;
      if (error.description && error.description.indexOf('limit exceeded') != -1) {
        showError(errorMessages.LIMIT_FILE_SIZE);
        return;
      } else if (results.images[0].error.error_id === 'input_error') {
        showError(errorMessages.URL_FETCH_PROBLEM);
        return;
      } else {
        showError(errorMessages.UNKNOWN_ERROR);
      }
    }
    // populate table
    renderTable(results,null);
    $result.show();

    setTimeout(function () {
      renderEntities(results);
    }, 100);
    $(window).resize(function () {
      $boxes.empty();
      renderEntities(results);
    });

    // check if there are results or not
    if ($outputData.html() === '') {
      $outputData.after(
          $('
')
              .html('No matching classifiers found.'));
    }

    var outputImage = document.querySelector('.use--output-image');
    if (outputImage && (outputImage.height >= outputImage.width)) {
      $(outputImage).addClass('landscape');
    }
    scrollToElement($result);
  }

  function showError(message) {
    $error.show();
    $errorMsg.html(message);
    console.log($error, $errorMsg);
  }

  function _error(xhr, responseMessage) {
    $loading.hide();
    var message = responseMessage || 'Error classifying the image';
    if (xhr && xhr.responseJSON) {
      message = xhr.responseJSON.error;
    }
    showError(message);
  }

  /*
   * submit event
   */
  function classifyImage(imgPath, imageData, beforeFunction, afterFunction) {
    if (!lock('classify')) {
      return;
    }

    beforeFunction ? beforeFunction() : false;

    processImage();
    if (imgPath !== '') {
      $image.attr('src', imgPath);
      $urlInput.val(imgPath);
    }

    $imageDataInput.val(imageData);

    let formData = $(pclass + 'form').serialize();
    // Grab all form data
    $.post('/api/classify', formData)
        .done(showResult)
        .error(function (error) {
          $loading.hide();
          console.log(error);

          if (error.status === 429) {
            showError(errorMessages.TOO_MANY_REQUESTS);
          } else if (error.responseJSON && error.responseJSON.error) {
            showError('We had a problem classifying that image because ' + jpath.jpath('/responseJSON/error/description',error,' of an unknown error'));
          } else {
            showError(errorMessages.SITE_IS_DOWN);
          }
        }).always(function () {
      afterFunction ? afterFunction() : false;
      unlock('classify');
    });
  }

  /*
   * Prevent default form submission
   */
  $fileupload.submit(false);

  /*
   * Radio image submission
   */
  $radioImages.click(function () {
    var rI = $(this);
    var imgPath = rI.next('label').find('img').attr('src');
    $urlInput.hide();
    classifyImage(imgPath, null, function () {
      $('input[type=radio][name=use--example-images]').prop('disabled', true);
      resetPasteUrl();
      rI.parent().find('label').addClass('dim');
      rI.parent().find('label[for=use--file]').removeClass('dim')
      rI.parent().find('label[for=' + rI.attr('id') + ']').removeClass('dim');
    }, function () {
      $urlInput.val('');
      $urlInput.show();
      $('input[type=radio][name=use--example-images]').prop('disabled', false);
    });
  });

  /*
   * Random image submission
   */
  $randomImage.click(function (e) {
    e.preventDefault();
    resetPasteUrl();
    var kind = StateManager.getState().kind;
    var path = kind === 'user' ? '/samples/' : '/bundles/' + kind + '/test/';
    classifyImage('images' + path + getRandomInt(1, 5) + '.jpg', true);
    $urlInput.val('');
  });

  /*
   * Image url submission
   */
  $urlInput.keypress(function (e) {
    var url = $(this).val();
    var self = $(this);

    if (e.keyCode === 13) {
      $invalidUrl.hide();
      $invalidImageUrl.hide();
      resetPasteUrl();
      classifyImage(url);
      self.blur();
    }

    $(self).focus();
  });

  function resetPasteUrl() {
    $urlInput.removeClass(panel + '--url-input_error');
    $invalidUrl.hide();
    $invalidImageUrl.hide();
  }

  /**
   * Jquery file upload configuration
   * See details: https://github.com/blueimp/jQuery-File-Upload
   */
  $fileupload.fileupload({
    dataType: 'json',
    dropZone: $dropzone,
    acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
    add: function (e, data) {
      data.url = '/api/classify';
      if (data.files && data.files[0]) {
        $error.hide();

        processImage();
        var reader = new FileReader();
        reader.onload = function () {
          var image = new Image();
          image.src = reader.result;
          image.onload = function () {
            var resizedImage = (data.files[0]['size'] > 2000000) ? resize(image, 800) : this.src;
            $image.attr('src', resizedImage);
            classifyImage('', resizedImage);
          };
          image.onerror = function () {
            _error(null, 'Error loading the image file. I can only work with images.');
          };
        };
        reader.readAsDataURL(data.files[0]);
      }
    },
    error: _error,
    done: function (e, data) {
      showResult(data.result);
    }
  });

  $(document).on('dragover', function () {
    $(pclass + 'dropzone label').addClass('dragover');
    $('form#use--fileupload').addClass('dragover');
  });

  $(document).on('dragleave', function () {
    $(pclass + 'dropzone label').removeClass('dragover');
    $('form#use--fileupload').removeClass('dragover');
  });

  // need to add on resize event listener for faces
  // need to offset and position itself and scale properly with physical image location
  // need to calculate ratio of image
  // get ratio
  // get starting image position
  // get transformed positions of face
  //  = ratio * original positions + offset
  function renderEntities(results) {
    var faces = jpath.jpath('/images/0/faces', results, []);
    var faceLocations = faces.map(function (face) {
      return transformBoxLocations(face.face_location, document.querySelector('.use--output-image'));
    });

    var words = jpath.jpath('/images/0/words', results, []);
    var wordsLocations = words.map(function (word) {
      return transformBoxLocations(word.location, document.querySelector('.use--output-image'));
    });

    renderBoxes('box', wordsLocations.concat(faceLocations));
  }

  function transformBoxLocations(faceLocation, image) {
    var newFaceLocation = faceLocation;
    var ratio = image.getBoundingClientRect().width / image.naturalWidth;
    var coordinates = getCoords(image);
    newFaceLocation = {
      width: faceLocation.width * ratio,
      height: faceLocation.height * ratio,
      top: coordinates.top + faceLocation.top * ratio,
      left: coordinates.left + faceLocation.left * ratio
    };
    return newFaceLocation;
  }

  /*
   * Solution found here:
   * http://stackoverflow.com/questions/5598743/finding-elements-position-relative-to-the-document#answer-26230989
   */
  function getCoords(elem) { // crossbrowser version
    var box = elem.getBoundingClientRect();

    var body = document.body;
    var docEl = document.documentElement;

    var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
    var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;

    var clientTop = docEl.clientTop || body.clientTop || 0;
    var clientLeft = docEl.clientLeft || body.clientLeft || 0;

    var top = box.top + scrollTop - clientTop;
    var left = box.left + scrollLeft - clientLeft;

    return {top: Math.round(top), left: Math.round(left)};
  }

  function renderTable(results) {
    $('.' + panel + '--mismatch').remove();
    let resolved_url = jpath.jpath('/images/0/resolved_url',results);
    if (resolved_url) {
      $image.attr('src', resolved_url);
    }

    if (results.classifier_ids && results.classifier_ids.split(",").filter(function(item) { return item !== 'default'; }).length > 0) {
      customClassifyScoreTable(results, $outputData[0]);
    } else {
      classifyScoreTable(results,$outputData[0]);
    }
  }
}

module.exports = setupUse;
<|endoftext|>


<commit_before>// CallPro script library (C)opyright 2014 Calway Nederland b.v.
// v20140612
// interface:
//
// getFieldValue(field) 		- return a value for the field parameter
// setFieldValue(field,value) 	- set the fieldparameter
// hookField(source,target) 	- hooks value of source to target element
// Log(text)					- simple log function, a element with id logpart is needed
//
(function( window, document, undefined ) {

	var initialized = false;
	var goCallProObject = undefined;

	// wait for dom-model to be loaded and ready before exectuting the initializer
	if ( !!(window.addEventListener) )
		window.addEventListener("DOMContentLoaded", initCallPro)
	else // MSIE
		window.attachEvent("onload", initCallPro)

	insertVB = function() {
		var head = document.getElementsByTagName("head")[0];
		var script = document.createElement("script");
		script.type = "text/vbscript";
		script.text = ' Sub SetCallPro(ByVal toObject)	\r\n \tMsgBox("vb injected") \r\n End Sub ';
		head.appendChild(script);
	} ();

	Initialized = function () {
		return initialized;
	}

	// initializer function, called once on load
	var retryCount = 0;
	function initCallPro() {

		// alert("start initializing");

		// wait for the goCallPro object to be assigned
		// tood: strategy what to do when not initialized in time
		if (goCallProObject === undefined) {
			Log("callpro waiting..." + retryCount++);
			if (retryCount < 10) {
				setTimeout(function() { initCallPro(); },500);
				return;
			}
		}

		if (goCallPro !== undefined) {

			var loScript = goCallPro.GetScript();
			var loEntry = loScript.GetEntry();
			var loDialer = loScript.GetDialer();
			var loAgent = loScript.GetAgent();
			var loCampaign = loScript.GetCampaign();

			if (loEntry != null && loEntry !== undefined) {

				scriptFields.push(new scriptField("CLEntryID",loEntry.CLEntryID));
				scriptFields.push(new scriptField("AgentName",loAgent.ResName));
				scriptFields.push(new scriptField("CampaignID",loCampaign.ResID));

				for(var i=1; i<= loEntry.ScriptFieldsCount; i++) {
					var loField = loEntry.ScriptFields(i);
					var loValue = loField.Value;

					if (!loValue || loValue === undefined || loValue  === "null" || loValue == null || loValue  === "NULL")
						loValue = "";

					scriptFields.push(new scriptField(loField.FName, loValue));
				}

			}
		}
		initialized = true;
	};

	// CallPro scriptfield collection
	scriptFields = [];
	function scriptField(fieldName, fieldValue) {
		this.fieldName = fieldName;
		this.fieldValue = fieldValue;
	}

	updateFromDOM = function() {
		for (var i = 0; i < scriptFields.length; i++) {
			sf = scriptFields[i];
			var val = getFieldValue("script_" + sf.fieldName);
			if (val !== undefined) {
				// alert(sf.fieldName + " updated from " + sf.fieldValue + " to " + val);
				sf.fieldValue = val;
			}
		}
	}

	getCachedValue = function(fieldName) {
		for (var i = 0; i < scriptFields.length; i++) {
			sf = scriptFields[i];
			if (sf.fieldName.toUpperCase() === fieldName.toUpperCase()
				|| "SCRIPT_" + sf.fieldName.toUpperCase() === fieldName.toUpperCase())
				return sf.fieldValue;
		}
		return undefined;
	}

	// isCollection
	// parameters: field
	// returns: true if field is a collection of elements, false otherwise
	var isCollection = function(field) {
		return (field.nodeName === undefined && field.length >= 1);
	}

	// elementType
	// return the elementType of field (object), compatible with IE7 and higher
	var elementType = function(field) {
		return field.nodeName;
		// Object.prototype.toString.call(field);
	}

	// isInputElement
	// return true if field is an input field, false otherwise
	var isInputElement = function(field) {
		var tp = elementType(field);
		return (tp === "INPUT" || tp === "SELECT" || tp === "TEXTAREA");
	}

	// string2Object
	// parameters: field - string, id or name of a element
	// returns: element-object for field, or undefined if not existing
	var string2Object = function(field) {
		if (typeof field === "string") {
			var toObj = document.getElementsByName(field);
			if (toObj.length === 0) {
				// maybe field is by Id
				toObj = document.getElementById(field);
			} else
			if (toObj.length === 1) {
				// collection of one, get the first one
				toObj = toObj[0];
			}

			if (toObj)
				return toObj;
			else
				return undefined;
		}

		// already an object
		return field;
	}

	// simple log function, depends on an element with ID "logpart" (otherwise no logging)
	// parameters: msg - line of text to write to log
	Log = function(msg) {
		var logpart = document.getElementById("logpart");
		// !! casts the expression to a boolean
		if (!!logpart)
			logpart.innerHTML = logpart.innerHTML + msg + "
";
	};

	// goCallPro object
	// get and set to manipulate (future) calls to set and get
	// WARNING: Only works with IE8 and up (NOT ie7 and lower!)
	Object.defineProperty(this, "goCallPro", {
		get: function() {
				if (goCallProObject === undefined)
					alert("goCallPro not properly initialized");
				return goCallProObject;
			},
		set: function(obj) {
				goCallProObject = obj;
			}
	});

	// hooks the inputfield obj to labelfield objcopy (can be every type)
	// parameters: source - source field (copy from)
	//             target - target field (retrieves value of source)
	hookField = function (source, target) {
		if (!initialized){
			// wait for everything to be fired up
			Log("waiting...");
			setTimeout(function() { hookField(source,target); },500);
		} else
		{
			source = string2Object(source);
			target = string2Object(target);

			if (source && target) {
				setFieldValue(target,getFieldValue(source));
				if (isCollection(source)) {
					for(i=0;i<source.length;i++) {
						// probably checkbox or radio, so also include mouse up. Wait 100ms to allow the system to set the value
						source[i].onmouseup = source[i].onkeyup = function() { setTimeout(function() {
							setFieldValue(target,getFieldValue(source)); }, 100);
					} }
				} else {
					source.onkeyup = function() {
					setFieldValue(target,getFieldValue(source));
					}
				}
			} else
				Log("unknown or undefined field " + source + " or " + target);
		}
	};

	// getFieldValue - tries to return the value of field.
	// parameters: field - can be either an object (by name or by id) or a stringref.
	// returns: string value or undefined if non-existing
	getFieldValue = function (field) {
		if (typeof field === "string")
			field = string2Object(field);

		if (field === undefined)
			return undefined;

		if (isCollection(field)){
			var result = undefined;
			for(var i=0;i<field.length;i++) {
				var itemValue = getFieldValue(field[i]);
				if (elementType(field[i]) === "INPUT" && field[i].type === "checkbox") {
					// checkbox = multiselect, seperate values with "+"
					if (itemValue != "") {
						if (result === undefined)
							result = ""
						result += ((result!="")?"+":"") + itemValue;
					}
				} else {
					// allother elements (input, label etc...)

					if (isInputElement(field[i])) {
						// input elements
						if (field[i].type === "radio") {
							if (itemValue != "")
								result = itemValue;
						}
						else
							result = itemValue;
					}
					else {
						// non input elements (labels...)
						if (itemValue != "") {
							if (result === undefined)
								result = itemValue;
						}
					}
				}
			}
			return (result===undefined)?"":result;
		} else
		if (typeof field === "object") {
			var fieldType = "";
			var objType = elementType(field);

			switch (objType)
			{
				case "INPUT":
					fieldType = field.type;
					break;
				case "SELECT":
					fieldType = "select";
					break;
				case "LABEL":
					fieldType = "label";
					break;
				case "TEXTAREA":
					fieldType = "textarea";
					break;
				default:
					fieldType = "unknown";
					break;
			}

			switch (fieldType)
			{
				case "text":
				case "password":
				case "hidden":
				case "button":
					return field.value;
				case "radio":
				case "checkbox":
					return (field.checked)?field.value:"";
				case "label":
				case "textarea":
					return field.innerHTML;
				case "select":
					return field.value;
				case "unknown":
					//Log("Unknown control " + objType);
					return field.innerHTML;
			}
		}
		else
			return undefined;
	};

	// setFieldValue - tries to returns the value of field.
	// parameters: field - can be either an object (by name or by id) or a stringref.
	//             value - the new value for the field
	// returns: true on succeed, false otherwise
	setFieldValue = function(field, value) {
		if (typeof field === "string")
			field = string2Object(field);

		if (field === undefined)
			return false;

		value = String(value);

		if (isCollection(field)){
			var result = true;
			for(i=0;i<field.length;i++)
				result = result && setFieldValue(field[i],value);
			return result;
		} else
		if (typeof field === "object") {
			var fieldType = "";
			var objType = elementType(field);

			switch (objType)
			{
				case "INPUT":
					fieldType = field.type;
					break;
				case "SELECT":
					fieldType = "select";
					break;
				case "LABEL":
					fieldType = "label";
					break;
				case "TEXTAREA":
					fieldType = "textarea";
					break;
				default:
					fieldType = "unknown";
					break;
			}
			switch (fieldType)
			{
				case "text":
				case "password":
				case "hidden":
				case "button":
					field.value = value;
					return true;
				case "radio":
				case "checkbox":
					field.checked = (value.indexOf(field.value) !== -1);
					return true;
				case "label":
				case "textarea":
					field.innerHTML = value;
					break;
				case "select":
					field.value = value;
					return true;
				case "unknown":
					// Log("Unknown control " + objType);
					field.innerHTML = value;
					return false;
			}
		}

		return false;
	}

	// setFieldReadOnly - makes the field readonly
	// parameters: field - can be either an object (by name or by id) or a stringref.
	//             value - true (readonly) or false (!readonly)
	// returns: boolean, succeeded (true) or failed(false)
	setFieldReadOnly = function (field, value) {
		if (typeof field === "string")
			field = string2Object(field);

		if (field === undefined)
			return false;

		if (isCollection(field)){
			var result = true;
			for(i=0;i<field.length;i++) {
				result = result && setFieldReadOnly(field[i], value);
			}
			return result;
		} else
		if (typeof field === "object") {
			var fieldType = "";
			var objType = elementType(field);

			switch (objType)
			{
				case "INPUT":
					fieldType = field.type;
					break;
				case "SELECT":
					fieldType = "select";
					break;
				case "LABEL":
					fieldType = "label";
					break;
				case "TEXTAREA":
					fieldType = "textarea";
					break;
				default:
					fieldType = "unknown";
					break;
			}
			switch (fieldType)
			{
				case "text":
				case "password":
				case "hidden":
				case "textarea":
					field.readOnly = value;
					return true;
				case "button":
				case "radio":
				case "checkbox":
					field.disabled = value;
					return true;
				case "label":
					field.disabled = value;
					return true;
				case "select":
					field.disabled = value;
					return true;
				case "unknown":
					//Log("Unknown control " + objType);
					return false;
			}
		}
		else
			return false;
	};

	// setFieldHighlight - set color of field
	// parameters: field - can be either an object (by name or by id) or a stringref.
	//             forecolor - foreground (text) color
	//             backcolor - background color
	// returns: boolean, succeeded (true) or failed(false)
	setFieldHighlight = function (field, forecolor, backcolor) {
		if (typeof field === "string")
			field = string2Object(field);

		if (field === undefined)
			return false;

		if (isCollection(field)){
			var result = true;
			for(i=0;i<field.length;i++) {
				result = result && setFieldHighlight(field[i], forecolor, backcolor);
			}
			return result;
		} else
		if (typeof field === "object") {
			field.style.color = forecolor;
			field.style.backgroundColor = backcolor;

			return true;
		}
		else
			return false;
	};

	niceDate = function(dateObject) {
		var d = new Date(dateObject);
		var day = d.getDate();
		var month = d.getMonth() + 1;
		var year = d.getFullYear();
		if (day < 10) {
			day = "0" + day;
		}
		if (month < 10) {
			month = "0" + month;
		}
		var date = year + "/" + month + "/" + day;

		return date;
	};

	// Backwards compatibility
	GetFieldValue = getFieldValue;
	SetFieldValue = setFieldValue;
	SetFieldReadOnly = setFieldReadOnly;
	SetFieldHighlight = setFieldHighlight;

	// set object to root
	window.CallPro = window.$ = this;

	return this;
})( window, document );

// moet helaas buiten de "class" ivm CallPro oproep
function SetCallPro(obj) {
	try {

		if (CallPro === undefined) {
			Log("Spawn SetCallPro");
			setTimeout(function() { SetCallPro(obj); },500);
			return;
		}

		CallPro.goCallPro = obj;

		Log("SetCallPro executed");
	}
	catch(e) {
		alert("Error setting goCallPro object " + e);
	}
};

<commit_msg>Update Callpro.Scripting.js<commit_after>// CallPro script library (C)opyright 2014-2015 Calway Nederland b.v.
// v20140612
// interface:
//
// getFieldValue(field) 		- return a value for the field parameter
// setFieldValue(field,value) 	- set the fieldparameter
// hookField(source,target) 	- hooks value of source to target element
// Log(text)					- simple log function, a element with id logpart is needed
//
(function( window, document, undefined ) {

	var initialized = false;
	var goCallProObject = undefined;

	// wait for dom-model to be loaded and ready before exectuting the initializer
	if ( !!(window.addEventListener) )
		window.addEventListener("DOMContentLoaded", initCallPro)
	else // MSIE
		window.attachEvent("onload", initCallPro)

	insertVB = function() {
		var head = document.getElementsByTagName("head")[0];
		var script = document.createElement("script");
		script.type = "text/vbscript";
		script.text = ' Sub SetCallPro(ByVal toObject)	\r\n \tMsgBox("vb injected") \r\n End Sub ';
		head.appendChild(script);
	} ();

	Initialized = function () {
		return initialized;
	}

	// initializer function, called once on load
	var retryCount = 0;
	function initCallPro() {

		// alert("start initializing");

		// wait for the goCallPro object to be assigned
		// tood: strategy what to do when not initialized in time
		if (goCallProObject === undefined) {
			Log("callpro waiting..." + retryCount++);
			if (retryCount < 10) {
				setTimeout(function() { initCallPro(); },500);
				return;
			}
		}

		if (goCallPro !== undefined) {

			var loScript = goCallPro.GetScript();
			var loEntry = loScript.GetEntry();
			var loDialer = loScript.GetDialer();
			var loAgent = loScript.GetAgent();
			var loCampaign = loScript.GetCampaign();

			if (loEntry != null && loEntry !== undefined) {

				scriptFields.push(new scriptField("CLEntryID",loEntry.CLEntryID));
				scriptFields.push(new scriptField("AgentName",loAgent.ResName));
				scriptFields.push(new scriptField("CampaignID",loCampaign.ResID));

				for(var i=1; i<= loEntry.ScriptFieldsCount; i++) {
					var loField = loEntry.ScriptFields(i);
					var loValue = loField.Value;

					if (!loValue || loValue === undefined || loValue  === "null" || loValue == null || loValue  === "NULL")
						loValue = "";

					scriptFields.push(new scriptField(loField.FName, loValue));
				}

			}
		}
		initialized = true;
	};

	// CallPro scriptfield collection
	scriptFields = [];
	function scriptField(fieldName, fieldValue) {
		this.fieldName = fieldName;
		this.fieldValue = fieldValue;
	}

	updateFromDOM = function() {
		for (var i = 0; i < scriptFields.length; i++) {
			sf = scriptFields[i];
			var val = getFieldValue("script_" + sf.fieldName);
			if (val !== undefined) {
				// alert(sf.fieldName + " updated from " + sf.fieldValue + " to " + val);
				sf.fieldValue = val;
			}
		}
	}

	getCachedValue = function(fieldName) {
		for (var i = 0; i < scriptFields.length; i++) {
			sf = scriptFields[i];
			if (sf.fieldName.toUpperCase() === fieldName.toUpperCase()
				|| "SCRIPT_" + sf.fieldName.toUpperCase() === fieldName.toUpperCase())
				return sf.fieldValue;
		}
		return undefined;
	}

	// isCollection
	// parameters: field
	// returns: true if field is a collection of elements, false otherwise
	var isCollection = function(field) {
		return (field.nodeName === undefined && field.length >= 1);
	}

	// elementType
	// return the elementType of field (object), compatible with IE7 and higher
	var elementType = function(field) {
		return field.nodeName;
		// Object.prototype.toString.call(field);
	}

	// isInputElement
	// return true if field is an input field, false otherwise
	var isInputElement = function(field) {
		var tp = elementType(field);
		return (tp === "INPUT" || tp === "SELECT" || tp === "TEXTAREA");
	}

	// string2Object
	// parameters: field - string, id or name of a element
	// returns: element-object for field, or undefined if not existing
	var string2Object = function(field) {
		if (typeof field === "string") {
			var toObj = document.getElementsByName(field);
			if (toObj.length === 0) {
				// maybe field is by Id
				toObj = document.getElementById(field);
			} else
			if (toObj.length === 1) {
				// collection of one, get the first one
				toObj = toObj[0];
			}

			if (toObj)
				return toObj;
			else
				return undefined;
		}

		// already an object
		return field;
	}

	// simple log function, depends on an element with ID "logpart" (otherwise no logging)
	// parameters: msg - line of text to write to log
	Log = function(msg) {
		var logpart = document.getElementById("logpart");
		// !! casts the expression to a boolean
		if (!!logpart)
			logpart.innerHTML = logpart.innerHTML + msg + "
";
	};

	// goCallPro object
	// get and set to manipulate (future) calls to set and get
	// WARNING: Only works with IE8 and up (NOT ie7 and lower!)
	Object.defineProperty(this, "goCallPro", {
		get: function() {
				if (goCallProObject === undefined)
					alert("goCallPro not properly initialized");
				return goCallProObject;
			},
		set: function(obj) {
				goCallProObject = obj;
			}
	});

	// hooks the inputfield obj to labelfield objcopy (can be every type)
	// parameters: source - source field (copy from)
	//             target - target field (retrieves value of source)
	hookField = function (source, target) {
		if (!initialized){
			// wait for everything to be fired up
			Log("waiting...");
			setTimeout(function() { hookField(source,target); },500);
		} else
		{
			source = string2Object(source);
			target = string2Object(target);

			if (source && target) {
				setFieldValue(target,getFieldValue(source));
				if (isCollection(source)) {
					for(i=0;i<source.length;i++) {
						// probably checkbox or radio, so also include mouse up. Wait 100ms to allow the system to set the value
						source[i].onmouseup = source[i].onkeyup = function() { setTimeout(function() {
							setFieldValue(target,getFieldValue(source)); }, 100);
					} }
				} else {
					source.onkeyup = function() {
					setFieldValue(target,getFieldValue(source));
					}
				}
			} else
				Log("unknown or undefined field " + source + " or " + target);
		}
	};

	// getFieldValue - tries to return the value of field.
	// parameters: field - can be either an object (by name or by id) or a stringref.
	// returns: string value or undefined if non-existing
	getFieldValue = function (field) {
		if (typeof field === "string")
			field = string2Object(field);

		if (field === undefined)
			return undefined;

		if (isCollection(field)){
			var result = undefined;
			for(var i=0;i<field.length;i++) {
				var itemValue = getFieldValue(field[i]);
				if (elementType(field[i]) === "INPUT" && field[i].type === "checkbox") {
					// checkbox = multiselect, seperate values with "+"
					if (itemValue != "") {
						if (result === undefined)
							result = ""
						result += ((result!="")?"+":"") + itemValue;
					}
				} else {
					// allother elements (input, label etc...)

					if (isInputElement(field[i])) {
						// input elements
						if (field[i].type === "radio") {
							if (itemValue != "")
								result = itemValue;
						}
						else
							result = itemValue;
					}
					else {
						// non input elements (labels...)
						if (itemValue != "") {
							if (result === undefined)
								result = itemValue;
						}
					}
				}
			}
			return (result===undefined)?"":result;
		} else
		if (typeof field === "object") {
			var fieldType = "";
			var objType = elementType(field);

			switch (objType)
			{
				case "INPUT":
					fieldType = field.type;
					break;
				case "SELECT":
					fieldType = "select";
					break;
				case "LABEL":
					fieldType = "label";
					break;
				case "TEXTAREA":
					fieldType = "textarea";
					break;
				default:
					fieldType = "unknown";
					break;
			}

			switch (fieldType)
			{
				case "text":
				case "password":
				case "hidden":
				case "button":
					return field.value;
				case "radio":
				case "checkbox":
					return (field.checked)?field.value:"";
				case "label":
				case "textarea":
					return field.innerHTML;
				case "select":
					return field.value;
				case "unknown":
					//Log("Unknown control " + objType);
					return field.innerHTML;
			}
		}
		else
			return undefined;
	};

	// setFieldValue - tries to returns the value of field.
	// parameters: field - can be either an object (by name or by id) or a stringref.
	//             value - the new value for the field
	// returns: true on succeed, false otherwise
	setFieldValue = function(field, value) {
		if (typeof field === "string")
			field = string2Object(field);

		if (field === undefined)
			return false;

		value = String(value);

		if (isCollection(field)){
			var result = true;
			for(i=0;i<field.length;i++)
				result = result && setFieldValue(field[i],value);
			return result;
		} else
		if (typeof field === "object") {
			var fieldType = "";
			var objType = elementType(field);

			switch (objType)
			{
				case "INPUT":
					fieldType = field.type;
					break;
				case "SELECT":
					fieldType = "select";
					break;
				case "LABEL":
					fieldType = "label";
					break;
				case "TEXTAREA":
					fieldType = "textarea";
					break;
				default:
					fieldType = "unknown";
					break;
			}
			switch (fieldType)
			{
				case "text":
				case "password":
				case "hidden":
				case "button":
					field.value = value;
					return true;
				case "radio":
				case "checkbox":
					field.checked = (value.indexOf(field.value) !== -1);
					return true;
				case "label":
				case "textarea":
					field.innerHTML = value;
					break;
				case "select":
					field.value = value;
					return true;
				case "unknown":
					// Log("Unknown control " + objType);
					field.innerHTML = value;
					return false;
			}
		}

		return false;
	}

	// setFieldReadOnly - makes the field readonly
	// parameters: field - can be either an object (by name or by id) or a stringref.
	//             value - true (readonly) or false (!readonly)
	// returns: boolean, succeeded (true) or failed(false)
	setFieldReadOnly = function (field, value) {
		if (typeof field === "string")
			field = string2Object(field);

		if (field === undefined)
			return false;

		if (isCollection(field)){
			var result = true;
			for(i=0;i<field.length;i++) {
				result = result && setFieldReadOnly(field[i], value);
			}
			return result;
		} else
		if (typeof field === "object") {
			var fieldType = "";
			var objType = elementType(field);

			switch (objType)
			{
				case "INPUT":
					fieldType = field.type;
					break;
				case "SELECT":
					fieldType = "select";
					break;
				case "LABEL":
					fieldType = "label";
					break;
				case "TEXTAREA":
					fieldType = "textarea";
					break;
				default:
					fieldType = "unknown";
					break;
			}
			switch (fieldType)
			{
				case "text":
				case "password":
				case "hidden":
				case "textarea":
					field.readOnly = value;
					return true;
				case "button":
				case "radio":
				case "checkbox":
					field.disabled = value;
					return true;
				case "label":
					field.disabled = value;
					return true;
				case "select":
					field.disabled = value;
					return true;
				case "unknown":
					//Log("Unknown control " + objType);
					return false;
			}
		}
		else
			return false;
	};

	// setFieldHighlight - set color of field
	// parameters: field - can be either an object (by name or by id) or a stringref.
	//             forecolor - foreground (text) color
	//             backcolor - background color
	// returns: boolean, succeeded (true) or failed(false)
	setFieldHighlight = function (field, forecolor, backcolor) {
		if (typeof field === "string")
			field = string2Object(field);

		if (field === undefined)
			return false;

		if (isCollection(field)){
			var result = true;
			for(i=0;i<field.length;i++) {
				result = result && setFieldHighlight(field[i], forecolor, backcolor);
			}
			return result;
		} else
		if (typeof field === "object") {
			field.style.color = forecolor;
			field.style.backgroundColor = backcolor;

			return true;
		}
		else
			return false;
	};

	niceDate = function(dateObject) {
		var d = new Date(dateObject);
		var day = d.getDate();
		var month = d.getMonth() + 1;
		var year = d.getFullYear();
		if (day < 10) {
			day = "0" + day;
		}
		if (month < 10) {
			month = "0" + month;
		}
		var date = year + "/" + month + "/" + day;

		return date;
	};

	// Backwards compatibility
	GetFieldValue = getFieldValue;
	SetFieldValue = setFieldValue;
	SetFieldReadOnly = setFieldReadOnly;
	SetFieldHighlight = setFieldHighlight;

	// set object to root
	window.CallPro = window.$ = this;

	return this;
})( window, document );

// moet helaas buiten de "class" ivm CallPro oproep
function SetCallPro(obj) {
	try {

		if (CallPro === undefined) {
			Log("Spawn SetCallPro");
			setTimeout(function() { SetCallPro(obj); },500);
			return;
		}

		CallPro.goCallPro = obj;

		Log("SetCallPro executed");
	}
	catch(e) {
		alert("Error setting goCallPro object " + e);
	}
};

<|endoftext|>


<commit_before>// Copyright 2019-2020, University of Colorado Boulder

/**
 * This class renders DynamicSeries data in a ScrollingChartNode and is tightly coupled with ScrollingChartNode
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

import Shape from '../../kite/js/Shape.js';
import Circle from '../../scenery/js/nodes/Circle.js';
import Node from '../../scenery/js/nodes/Node.js';
import Path from '../../scenery/js/nodes/Path.js';
import griddle from './griddle.js';

class DynamicSeriesNode extends Node {

  /**
   * @param {DynamicSeries} dynamicSeries - the series of data to render
   * @param {number} plotWidth - the horizontal size of the plot, up to the center of the pen
   * @param {Bounds2} bounds - bounds for rendering, includes area to the right of the pens
   * @param {number} maxTime - Set the range by incorporating the model's time units, so it will match with the timer.
   * @param {Property.<number>} timeProperty
   * @param {ModelViewTransform2} modelViewTransform
   */
  constructor( dynamicSeries, plotWidth, bounds, maxTime, timeProperty, modelViewTransform ) {

    // For the initial point or when there has been NaN data, the next call should be moveTo() instead of lineTo()
    let moveToNextPoint = true;

    // Create the pen which draws the data at the right side of the graph
    const penNode = new Circle( 4.5, {
      fill: dynamicSeries.color,
      centerX: plotWidth,
      centerY: bounds.height / 2
    } );
    const pathNode = new Path( new Shape(), {
      stroke: dynamicSeries.color,
      lineWidth: dynamicSeries.lineWidth
    } );

    super( {
      children: [ penNode, pathNode ]
    } );

    // prevent bounds computations during main loop
    pathNode.computeShapeBounds = () => bounds;

    const dynamicSeriesListener = () => {

      // Draw the graph with line segments
      const dynamicSeriesPathShape = new Shape();
      for ( let i = 0; i < dynamicSeries.getLength(); i++ ) {
        const dataPoint = dynamicSeries.getDataPoint( i );
        if ( isNaN( dataPoint.y ) ) {
          moveToNextPoint = true;

          // Center the pen when data is NaN
          penNode.centerY = modelViewTransform.modelToViewY( 0 );
        }
        else {
          const point = modelViewTransform.modelToViewPosition( dataPoint );
          if ( moveToNextPoint ) {
            dynamicSeriesPathShape.moveToPoint( point );
          }
          else {
            dynamicSeriesPathShape.lineToPoint( point );
          }

          if ( i === dynamicSeries.getLength() - 1 ) {
            penNode.centerY = point.y;
          }
          moveToNextPoint = false;
        }
      }
      pathNode.shape = dynamicSeriesPathShape;
    };
    dynamicSeries.addDynamicSeriesListener( dynamicSeriesListener );
    modelViewTransform.on( 'change', dynamicSeriesListener );
    this.disposeDynamicSeriesNode = () => {
      dynamicSeries.removeDynamicSeriesListener( dynamicSeriesListener );
      modelViewTransform.off( 'change', dynamicSeriesListener );
    };
  }

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

griddle.register( 'DynamicSeriesNode', DynamicSeriesNode );
export default DynamicSeriesNode;<commit_msg>Initial work for removing Events .on()/.onStatic()/.off()/.offStatic(), see https://github.com/phetsims/scenery/issues/490<commit_after>// Copyright 2019-2020, University of Colorado Boulder

/**
 * This class renders DynamicSeries data in a ScrollingChartNode and is tightly coupled with ScrollingChartNode
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

import Shape from '../../kite/js/Shape.js';
import Circle from '../../scenery/js/nodes/Circle.js';
import Node from '../../scenery/js/nodes/Node.js';
import Path from '../../scenery/js/nodes/Path.js';
import griddle from './griddle.js';

class DynamicSeriesNode extends Node {

  /**
   * @param {DynamicSeries} dynamicSeries - the series of data to render
   * @param {number} plotWidth - the horizontal size of the plot, up to the center of the pen
   * @param {Bounds2} bounds - bounds for rendering, includes area to the right of the pens
   * @param {number} maxTime - Set the range by incorporating the model's time units, so it will match with the timer.
   * @param {Property.<number>} timeProperty
   * @param {ModelViewTransform2} modelViewTransform
   */
  constructor( dynamicSeries, plotWidth, bounds, maxTime, timeProperty, modelViewTransform ) {

    // For the initial point or when there has been NaN data, the next call should be moveTo() instead of lineTo()
    let moveToNextPoint = true;

    // Create the pen which draws the data at the right side of the graph
    const penNode = new Circle( 4.5, {
      fill: dynamicSeries.color,
      centerX: plotWidth,
      centerY: bounds.height / 2
    } );
    const pathNode = new Path( new Shape(), {
      stroke: dynamicSeries.color,
      lineWidth: dynamicSeries.lineWidth
    } );

    super( {
      children: [ penNode, pathNode ]
    } );

    // prevent bounds computations during main loop
    pathNode.computeShapeBounds = () => bounds;

    const dynamicSeriesListener = () => {

      // Draw the graph with line segments
      const dynamicSeriesPathShape = new Shape();
      for ( let i = 0; i < dynamicSeries.getLength(); i++ ) {
        const dataPoint = dynamicSeries.getDataPoint( i );
        if ( isNaN( dataPoint.y ) ) {
          moveToNextPoint = true;

          // Center the pen when data is NaN
          penNode.centerY = modelViewTransform.modelToViewY( 0 );
        }
        else {
          const point = modelViewTransform.modelToViewPosition( dataPoint );
          if ( moveToNextPoint ) {
            dynamicSeriesPathShape.moveToPoint( point );
          }
          else {
            dynamicSeriesPathShape.lineToPoint( point );
          }

          if ( i === dynamicSeries.getLength() - 1 ) {
            penNode.centerY = point.y;
          }
          moveToNextPoint = false;
        }
      }
      pathNode.shape = dynamicSeriesPathShape;
    };
    dynamicSeries.addDynamicSeriesListener( dynamicSeriesListener );
    modelViewTransform.changeEmitter.addListener( dynamicSeriesListener );
    this.disposeDynamicSeriesNode = () => {
      dynamicSeries.removeDynamicSeriesListener( dynamicSeriesListener );
      modelViewTransform.changeEmitter.removeListener( dynamicSeriesListener );
    };
  }

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

griddle.register( 'DynamicSeriesNode', DynamicSeriesNode );
export default DynamicSeriesNode;<|endoftext|>


<commit_before>/**
 * @desc        backbone router for pushState page routing
 */

define(
  [
    'app',
    'jquery',
    'underscore',
    'backbone',
    'socket',
    'bootstrap',

    'views/MainView',
    'views/YoutubeView',
    'views/GoogleView',
    'views/HeaderView',
    'views/SettingsView',
    'views/SearchBarView',
    'views/TwitterView',
    'views/SpotifyView',
    'views/FacebookView',
    'views/RssView',
], function(app, $, _, Backbone, Socket, bootstrap, MainView, YoutubeView, GoogleView, HeaderView, SettingsView, SearchBarView,TwitterView, SpotifyView, FacebookView, RssView){

    var WebRouter = Backbone.Router.extend({

      routes: {
        "":     "index"
      },

      initialize: function(options) {

        this.remoteSocket = io.connect('http://192.168.2.11:3000');
        this.screenSocket = io.connect('http://127.0.0.1:3000');
        this.isMobile = this.checkForMobile();
        var selector;
        var _this = this;

        if(this.isMobile){
          this.socket = this.remoteSocket;
          this.state = "mobile";
          this.remoteSocket.emit('remote');
        }
        else {
          this.socket = this.screenSocket;
          this.state = "tv";
          this.socket.on('connect', function(data){
            _this.socket.emit('screen');
          });
        }

        //Socket listeners for the screen
        //may move these to their respective views

        //takes the selection data
        this.socket.on('controlling', function(data){
           _this.onRenderSelection(data);
        });

        //handles which search bar to render
        this.socket.on('controlling-data', function(data){
          $('#searchBar').val(data);
        });

        //handles recieving youtube player commands
        this.socket.on('youtube-control', function(data){
        });

        this.socket.on('twitter-login', function(data){
          console.log("login!");
          alert("Please log in with the settings page");
        });
        this.socket.on('facebook-login', function(data){
          console.log("login!");
          alert("Please log in with the settings page");
        });

        this.socket.on('log-out-facebook', function(data){

           $('.fb-feed').html('

');

         });

         this.socket.on('log-out-twitter', function(data){

           $('.tw-feed').html('

');

         });

        this.socket.on('render-twitter', function(data){
          console.log("twitter render");

          _this.twitterView.getTwitterFeed();
        });

        this.views = [];
        this.$body = $("body");
        this.$header = $("header");

        this.mainView = new MainView({socket: this.socket, mobile: this.isMobile });
        this.mainView.on('renderSelection',this.onRenderSelection,this);
        this.views.push(this.mainView);

        this.settingsView = new SettingsView({socket: this.socket});
        this.settingsView.on('refresh',this.refresh,this);
        this.settingsView.$el.hide();
        this.$body.prepend(this.settingsView.render().$el);
        this.views.push(this.settingsView);

        this.spotifyView = new SpotifyView({ socket: this.remoteSocket, mobile: this.isMobile });
        this.spotifyView.$el.hide();
        this.$body.prepend(this.spotifyView.render().$el);
        this.views.push(this.spotifyView);

        this.rssView = new RssView({socket: this.socket, mobile: this.isMobile});
        this.rssView.on('renderSelection',this.onRenderSelection,this);
        this.rssView.on('goHome',this.onGoBack,this);
        this.rssView.$el.hide();
        this.$body.prepend(this.rssView.render(this.isMobile).$el);
        this.views.push(this.rssView);

        this.$body.prepend(this.mainView.render(this.state).$el);

        if(!this.isMobile){
          this.$twitter = $('.tw-feed');
          this.twitterView = new TwitterView({socket: this.socket});
          this.twitterView.$el.show();
          this.$twitter.append(this.twitterView.render().$el);

          this.$rss = $('.rss-feed');
          this.$rss.append(this.rssView.render(this.isMobile).$el);

          this.$facebook = $('.fb-feed');
          this.facebookView = new FacebookView({socket: this.socket});
          this.facebookView.$el.show();
          this.$facebook.append(this.facebookView.render().$el);
        }

        this.socket.on('ss-log-in-facebook', function(data){

           window.location = "/auth/facebook";

        });

        $('#searchBar').bind('input', function(e) {
            var searchBarData = $('#searchBar').val();
            _this.sendSearchBoxData(searchBarData);
        });

        this.socket.on('rss-control', function(data){

          _this.rssView.$el.show();

          _this.rssView.renderFeed(data);

        });
      },

      sendSearchBoxData: function(data) {
        this.remoteSocket.emit('send-data',{key: data});
      },

      refresh: function() {
        this.remoteSocket.emit("refresh", "test");
      },

      onRenderSelection: function(chosenSelection) {

        //only render views on mobile
        if(this.isMobile){

            this.searchBarView = new SearchBarView({socket: this.socket});
            this.searchBarView.on('onCloseSearchBar',this.closeSearchBar,this);
            this.searchBarView.on('onSearchSubmit',this.submitSearchBar,this);
            this.searchBarView.$el.hide();
            this.$body.prepend(this.searchBarView.render().$el);
            this.views.push(this.searchBarView);
        }

          //attempt to render the views as needed to increase speed
        if(chosenSelection == "youtube"){

          if(this.isMobile){
              this.youtubeView = new YoutubeView({socket: this.socket, mobile: this.isMobile});
              this.youtubeView.on('renderSelection',this.onRenderSelection,this);
              this.youtubeView.on('goHome',this.onGoBack,this);
              this.youtubeView.$el.hide();
              this.$body.prepend(this.youtubeView.render().$el);
              this.views.push(this.youtubeView);
              this.currentSearchBarView = "youtube";
              this.showSearchBar(chosenSelection);
          }
          else {
            screenSelector = $('#youtube');
            this.mainView.mouseovercard(screenSelector);

          }

        }
        if(chosenSelection == "chrome"){

          if(this.isMobile){
              this.googleView = new GoogleView();
              this.googleView.$el.hide();
              this.$body.prepend(this.googleView.render().$el);
              this.views.push(this.googleView);
              this.currentSearchBarView = "google";
              this.showSearchBar(chosenSelection);
          }
          else {
            screenSelector = $('#chrome');
            this.mainView.mouseovercard(screenSelector);
          }

        }
        if(chosenSelection == "settings"){
          this.socket.emit('get-twitter-status'); //Check if logged in or not
          this.socket.emit('get-facebook-status'); //Check if logged in or not

          if(!this.isMobile){
            screenSelector = $('#settings');
            this.mainView.mouseovercard(screenSelector);
          } else {
            this.showModal(chosenSelection);
          }
        }
        if(chosenSelection == "music"){
          if(!this.isMobile){
            screenSelector = $('#music');
            this.mainView.mouseovercard(screenSelector);
          } else {
            this.showModal(chosenSelection);
          }
        }
        if(chosenSelection == "facebook"){
          if(!this.isMobile){
            this.showOnlyFeed("facebook");

            this.socket.emit('on-click-facebook');
            screenSelector = $('#facebook');
            this.mainView.mouseovercard(screenSelector);
          } else {

          }
        }
        if(chosenSelection == "twitter"){
          if(!this.isMobile){

            this.socket.emit('on-click-twitter');
            this.showOnlyFeed("twitter");

            screenSelector = $('#twitter');
            this.mainView.mouseovercard(screenSelector);

          } else {

          }
        }
        if(chosenSelection == "news"){
          if(!this.isMobile){
            this.showOnlyFeed("news");
            screenSelector = $('#news');
            this.mainView.mouseovercard(screenSelector);
          } else {

            this.currentSearchBarView = "news";
            this.showSearchBar(chosenSelection);

          }
        }
        if(chosenSelection == "home"){
          if(!this.isMobile){
            screenSelector = $('#home');
            this.mainView.mouseovercard(screenSelector);
          } else {

          }
        }
      },

      showModal: function(modal) {
        if(this.isMobile){
          if(modal == "settings") {
            this.settingsView.$el.show();
            $('#settingsModal').modal('show');
          } else if ( modal == "music") {
            this.spotifyView.$el.show();
            $('#spotifyModal').modal('show');
          }
        }
      },

      showSearchBar: function(chosenSelection) {
        this.searchBarView.changeHeader(chosenSelection);
        this.searchBarView.$el.show();
        $('#myModal').modal('show');
      },

      closeSearchBar: function(){
        $('#myModal').modal('hide');
        this.remoteSocket.emit('modal-control',{action: 'close'});
      },

      submitSearchBar: function(data) {
        if(this.isMobile){
          var data = $('#searchBar').val();
          $('#myModal').modal('hide');
          this.remoteSocket.emit('modal-control',{action: 'submit', data: data, view: this.currentSearchBarView});
          var currentView = this.getCurrentSearchView(this.currentSearchBarView);
          currentView.newSearch(data);
          this.showOnly(currentView);
        }
      },

      onGoBack: function() {
        this.showOnly(this.mainView);
      },

      showOnly: function(view){
        if (view != this.currentView) {
          _.each(this.views, function (view) { view.$el.hide(); } );

          if (view.show) {
            view.show();
          }
          else {
            view.$el.show();
          }
          this.currentView = view;
          document.documentElement.scrollTop = document.documentElement.scrollTop = 0;
        }
      },

      showOnlyFeed: function(feed){

        this.twitterView.$el.hide();
        this.rssView.$el.hide();
        this.facebookView.$el.hide();

        if(feed == "twitter"){
          this.twitterView.$el.show();
        }
        else if(feed == "facebook"){
          this.facebookView.$el.show();
        }
        else if(feed == "news"){
          this.rssView.$el.show();
        }

      },

      checkForMobile: function() {
        if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
         return true;
        } else {
          return false;
        }
      },

      getCurrentSearchView: function(view){
        if(view == "youtube"){
          return this.youtubeView;
        }
        else if(view == "google"){
          return this.googleView;
        }
        else if(view == "news"){
          return this.rssView;
        }
      }
    });

    return WebRouter;
});
<commit_msg>set rasp pi<commit_after>/**
 * @desc        backbone router for pushState page routing
 */

define(
  [
    'app',
    'jquery',
    'underscore',
    'backbone',
    'socket',
    'bootstrap',

    'views/MainView',
    'views/YoutubeView',
    'views/GoogleView',
    'views/HeaderView',
    'views/SettingsView',
    'views/SearchBarView',
    'views/TwitterView',
    'views/SpotifyView',
    'views/FacebookView',
    'views/RssView',
], function(app, $, _, Backbone, Socket, bootstrap, MainView, YoutubeView, GoogleView, HeaderView, SettingsView, SearchBarView,TwitterView, SpotifyView, FacebookView, RssView){

    var WebRouter = Backbone.Router.extend({

      routes: {
        "":     "index"
      },

      initialize: function(options) {

        this.remoteSocket = io.connect('http://192.168.2.12:3000');
        this.screenSocket = io.connect('http://127.0.0.1:3000');
        this.isMobile = this.checkForMobile();
        var selector;
        var _this = this;

        if(this.isMobile){
          this.socket = this.remoteSocket;
          this.state = "mobile";
          this.remoteSocket.emit('remote');
        }
        else {
          this.socket = this.screenSocket;
          this.state = "tv";
          this.socket.on('connect', function(data){
            _this.socket.emit('screen');
          });
        }

        //Socket listeners for the screen
        //may move these to their respective views

        //takes the selection data
        this.socket.on('controlling', function(data){
           _this.onRenderSelection(data);
        });

        //handles which search bar to render
        this.socket.on('controlling-data', function(data){
          $('#searchBar').val(data);
        });

        //handles recieving youtube player commands
        this.socket.on('youtube-control', function(data){
        });

        this.socket.on('twitter-login', function(data){
          console.log("login!");
          alert("Please log in with the settings page");
        });
        this.socket.on('facebook-login', function(data){
          console.log("login!");
          alert("Please log in with the settings page");
        });

        this.socket.on('log-out-facebook', function(data){

           $('.fb-feed').html('

');

         });

         this.socket.on('log-out-twitter', function(data){

           $('.tw-feed').html('

');

         });

        this.socket.on('render-twitter', function(data){
          console.log("twitter render");

          _this.twitterView.getTwitterFeed();
        });

        this.views = [];
        this.$body = $("body");
        this.$header = $("header");

        this.mainView = new MainView({socket: this.socket, mobile: this.isMobile });
        this.mainView.on('renderSelection',this.onRenderSelection,this);
        this.views.push(this.mainView);

        this.settingsView = new SettingsView({socket: this.socket});
        this.settingsView.on('refresh',this.refresh,this);
        this.settingsView.$el.hide();
        this.$body.prepend(this.settingsView.render().$el);
        this.views.push(this.settingsView);

        this.spotifyView = new SpotifyView({ socket: this.remoteSocket, mobile: this.isMobile });
        this.spotifyView.$el.hide();
        this.$body.prepend(this.spotifyView.render().$el);
        this.views.push(this.spotifyView);

        this.rssView = new RssView({socket: this.socket, mobile: this.isMobile});
        this.rssView.on('renderSelection',this.onRenderSelection,this);
        this.rssView.on('goHome',this.onGoBack,this);
        this.rssView.$el.hide();
        this.$body.prepend(this.rssView.render(this.isMobile).$el);
        this.views.push(this.rssView);

        this.$body.prepend(this.mainView.render(this.state).$el);

        if(!this.isMobile){
          this.$twitter = $('.tw-feed');
          this.twitterView = new TwitterView({socket: this.socket});
          this.twitterView.$el.show();
          this.$twitter.append(this.twitterView.render().$el);

          this.$rss = $('.rss-feed');
          this.$rss.append(this.rssView.render(this.isMobile).$el);

          this.$facebook = $('.fb-feed');
          this.facebookView = new FacebookView({socket: this.socket});
          this.facebookView.$el.show();
          this.$facebook.append(this.facebookView.render().$el);
        }

        this.socket.on('ss-log-in-facebook', function(data){

           window.location = "/auth/facebook";

        });

        $('#searchBar').bind('input', function(e) {
            var searchBarData = $('#searchBar').val();
            _this.sendSearchBoxData(searchBarData);
        });

        this.socket.on('rss-control', function(data){

          _this.rssView.$el.show();

          _this.rssView.renderFeed(data);

        });
      },

      sendSearchBoxData: function(data) {
        this.remoteSocket.emit('send-data',{key: data});
      },

      refresh: function() {
        this.remoteSocket.emit("refresh", "test");
      },

      onRenderSelection: function(chosenSelection) {

        //only render views on mobile
        if(this.isMobile){

            this.searchBarView = new SearchBarView({socket: this.socket});
            this.searchBarView.on('onCloseSearchBar',this.closeSearchBar,this);
            this.searchBarView.on('onSearchSubmit',this.submitSearchBar,this);
            this.searchBarView.$el.hide();
            this.$body.prepend(this.searchBarView.render().$el);
            this.views.push(this.searchBarView);
        }

          //attempt to render the views as needed to increase speed
        if(chosenSelection == "youtube"){

          if(this.isMobile){
              this.youtubeView = new YoutubeView({socket: this.socket, mobile: this.isMobile});
              this.youtubeView.on('renderSelection',this.onRenderSelection,this);
              this.youtubeView.on('goHome',this.onGoBack,this);
              this.youtubeView.$el.hide();
              this.$body.prepend(this.youtubeView.render().$el);
              this.views.push(this.youtubeView);
              this.currentSearchBarView = "youtube";
              this.showSearchBar(chosenSelection);
          }
          else {
            screenSelector = $('#youtube');
            this.mainView.mouseovercard(screenSelector);

          }

        }
        if(chosenSelection == "chrome"){

          if(this.isMobile){
              this.googleView = new GoogleView();
              this.googleView.$el.hide();
              this.$body.prepend(this.googleView.render().$el);
              this.views.push(this.googleView);
              this.currentSearchBarView = "google";
              this.showSearchBar(chosenSelection);
          }
          else {
            screenSelector = $('#chrome');
            this.mainView.mouseovercard(screenSelector);
          }

        }
        if(chosenSelection == "settings"){
          this.socket.emit('get-twitter-status'); //Check if logged in or not
          this.socket.emit('get-facebook-status'); //Check if logged in or not

          if(!this.isMobile){
            screenSelector = $('#settings');
            this.mainView.mouseovercard(screenSelector);
          } else {
            this.showModal(chosenSelection);
          }
        }
        if(chosenSelection == "music"){
          if(!this.isMobile){
            screenSelector = $('#music');
            this.mainView.mouseovercard(screenSelector);
          } else {
            this.showModal(chosenSelection);
          }
        }
        if(chosenSelection == "facebook"){
          if(!this.isMobile){
            this.showOnlyFeed("facebook");

            this.socket.emit('on-click-facebook');
            screenSelector = $('#facebook');
            this.mainView.mouseovercard(screenSelector);
          } else {

          }
        }
        if(chosenSelection == "twitter"){
          if(!this.isMobile){

            this.socket.emit('on-click-twitter');
            this.showOnlyFeed("twitter");

            screenSelector = $('#twitter');
            this.mainView.mouseovercard(screenSelector);

          } else {

          }
        }
        if(chosenSelection == "news"){
          if(!this.isMobile){
            this.showOnlyFeed("news");
            screenSelector = $('#news');
            this.mainView.mouseovercard(screenSelector);
          } else {

            this.currentSearchBarView = "news";
            this.showSearchBar(chosenSelection);

          }
        }
        if(chosenSelection == "home"){
          if(!this.isMobile){
            screenSelector = $('#home');
            this.mainView.mouseovercard(screenSelector);
          } else {

          }
        }
      },

      showModal: function(modal) {
        if(this.isMobile){
          if(modal == "settings") {
            this.settingsView.$el.show();
            $('#settingsModal').modal('show');
          } else if ( modal == "music") {
            this.spotifyView.$el.show();
            $('#spotifyModal').modal('show');
          }
        }
      },

      showSearchBar: function(chosenSelection) {
        this.searchBarView.changeHeader(chosenSelection);
        this.searchBarView.$el.show();
        $('#myModal').modal('show');
      },

      closeSearchBar: function(){
        $('#myModal').modal('hide');
        this.remoteSocket.emit('modal-control',{action: 'close'});
      },

      submitSearchBar: function(data) {
        if(this.isMobile){
          var data = $('#searchBar').val();
          $('#myModal').modal('hide');
          this.remoteSocket.emit('modal-control',{action: 'submit', data: data, view: this.currentSearchBarView});
          var currentView = this.getCurrentSearchView(this.currentSearchBarView);
          currentView.newSearch(data);
          this.showOnly(currentView);
        }
      },

      onGoBack: function() {
        this.showOnly(this.mainView);
      },

      showOnly: function(view){
        if (view != this.currentView) {
          _.each(this.views, function (view) { view.$el.hide(); } );

          if (view.show) {
            view.show();
          }
          else {
            view.$el.show();
          }
          this.currentView = view;
          document.documentElement.scrollTop = document.documentElement.scrollTop = 0;
        }
      },

      showOnlyFeed: function(feed){

        this.twitterView.$el.hide();
        this.rssView.$el.hide();
        this.facebookView.$el.hide();

        if(feed == "twitter"){
          this.twitterView.$el.show();
        }
        else if(feed == "facebook"){
          this.facebookView.$el.show();
        }
        else if(feed == "news"){
          this.rssView.$el.show();
        }

      },

      checkForMobile: function() {
        if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
         return true;
        } else {
          return false;
        }
      },

      getCurrentSearchView: function(view){
        if(view == "youtube"){
          return this.youtubeView;
        }
        else if(view == "google"){
          return this.googleView;
        }
        else if(view == "news"){
          return this.rssView;
        }
      }
    });

    return WebRouter;
});
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

// Note that these are webpack requires, not CommonJS node requiring requires
const React = require('react')
const ImmutableComponent = require('../components/immutableComponent')
const Immutable = require('immutable')
const cx = require('../lib/classSet.js')
const appConfig = require('../constants/appConfig')
const preferenceTabs = require('../constants/preferenceTabs')
const messages = require('../constants/messages')
const settings = require('../constants/settings')
const aboutActions = require('./aboutActions')
const getSetting = require('../settings').getSetting

// TODO: Determine this from the l20n file automatically
const hintCount = 3

// Stylesheets
require('../../less/about/preferences.less')
require('../../node_modules/font-awesome/css/font-awesome.css')

const changeSetting = (key, e) => {
  if (e.target.type === 'checkbox') {
    aboutActions.changeSetting(key, e.target.checked)
  } else {
    let value = e.target.value
    if (e.target.type === 'number') {
      value = value.replace(/\D/g, '')
      value = parseInt(value, 10)
      if (Number.isNaN(value)) {
        return
      }
      value = Math.min(e.target.getAttribute('max'), Math.max(value, e.target.getAttribute('min')))
    }
    aboutActions.changeSetting(key, value)
  }
}

class SettingsList extends ImmutableComponent {
  render () {
    return

      { this.props.dataL10nId
        ?
 : null }

        {this.props.children}

  }
}

class SettingItem extends ImmutableComponent {
  render () {
    return

      {this.props.children}

  }
}

class SettingCheckbox extends ImmutableComponent {
  render () {
    return

        <input type='checkbox' id={this.props.prefKey}
          disabled={this.props.disabled}
          onChange={changeSetting.bind(null, this.props.prefKey)}
          checked={getSetting(this.props.prefKey, this.props.settings)}/>

  }
}

class GeneralTab extends ImmutableComponent {
  render () {
    return <SettingsList>
      <SettingItem dataL10nId='startsWith'>
        <select value={getSetting(settings.STARTUP_MODE, this.props.settings)}
          onChange={changeSetting.bind(null, settings.STARTUP_MODE)} >
          <option data-l10n-id='startsWithOptionLastTime' value='lastTime'/>
          <option data-l10n-id='startsWithOptionHomePage' value='homePage'/>
          <option data-l10n-id='startsWithOptionNewTabPage' value='newTabPage'/>
        </select>
      </SettingItem>
      <SettingItem dataL10nId='myHomepage'>
        <input data-l10n-id='homepageInput'
          value={getSetting(settings.HOMEPAGE, this.props.settings)}
          onChange={changeSetting.bind(null, settings.HOMEPAGE)} />
      </SettingItem>
    </SettingsList>
  }
}

class SearchTab extends ImmutableComponent {
  render () {
    return <SettingsList>
      <SettingItem dataL10nId='defaultSearchEngine'>
        <select value={getSetting(settings.DEFAULT_SEARCH_ENGINE, this.props.settings)}
          onChange={changeSetting.bind(null, settings.DEFAULT_SEARCH_ENGINE)}>
          <option value='./content/search/google.xml'>Google</option>
          <option value='./content/search/duckduckgo.xml'>DuckDuckGo</option>
        </select>
      </SettingItem>
    </SettingsList>
  }
}

class TabsTab extends ImmutableComponent {
  render () {
    return <SettingsList>
      <SettingItem dataL10nId='tabsPerTabPage'>
        <input
          type='number'
          min='3'
          max='20'
          value={getSetting(settings.TABS_PER_TAB_PAGE, this.props.settings)}
          onChange={changeSetting.bind(null, settings.TABS_PER_TAB_PAGE)} />
      </SettingItem>
      <SettingCheckbox dataL10nId='switchToNewTabs' prefKey={settings.SWITCH_TO_NEW_TABS} settings={this.props.settings}/>
      <SettingCheckbox dataL10nId='paintTabs' prefKey={settings.PAINT_TABS} settings={this.props.settings}/>
      <SettingCheckbox dataL10nId='showTabPreviews' prefKey={settings.SHOW_TAB_PREVIEWS} settings={this.props.settings}/>
    </SettingsList>
  }
}

class SyncTab extends ImmutableComponent {
  render () {
    return

      Sync settings coming soon

  }
}

class PrivacyTab extends ImmutableComponent {
  render () {
    return

      <SettingsList dataL10nId='suggestionTypes'>
        <SettingCheckbox dataL10nId='history' prefKey={settings.HISTORY_SUGGESTIONS} settings={this.props.settings}/>
        <SettingCheckbox dataL10nId='bookmarks' prefKey={settings.BOOKMARK_SUGGESTIONS} settings={this.props.settings}/>
        <SettingCheckbox dataL10nId='openedTabs' prefKey={settings.OPENED_TAB_SUGGESTIONS} settings={this.props.settings}/>
      </SettingsList>
      <SettingsList dataL10nId='advancedPrivacySettings'>
        <SettingCheckbox dataL10nId='doNotTrack' prefKey={settings.DO_NOT_TRACK} settings={this.props.settings}/>
      </SettingsList>

  }
}

class SecurityTab extends ImmutableComponent {
  render () {
    return <SettingsList>
      <SettingCheckbox disabled dataL10nId='blockAttackSites' prefKey={settings.BLOCK_REPORTED_SITES} settings={this.props.settings}/>
    </SettingsList>
  }
}

class BraveryTab extends ImmutableComponent {
  render () {
    return

      Bravery settings coming soon

  }
}

class TopBarButton extends ImmutableComponent {
  render () {
    return

      { this.props.selected
      ?

 : null }

  }
}

class TopBar extends ImmutableComponent {
  render () {
    return

      <TopBarButton icon='fa-list-alt'
        dataL10nId='general'
        onClick={this.props.changeTab.bind(null, preferenceTabs.GENERAL)}
        selected={this.props.preferenceTab === preferenceTabs.GENERAL}
      />
      <TopBarButton icon='fa-search'
        dataL10nId='search'
        onClick={this.props.changeTab.bind(null, preferenceTabs.SEARCH)}
        selected={this.props.preferenceTab === preferenceTabs.SEARCH}
      />
      <TopBarButton icon='fa-bookmark-o'
        dataL10nId='tabs'
        onClick={this.props.changeTab.bind(null, preferenceTabs.TABS)}
        selected={this.props.preferenceTab === preferenceTabs.TABS}
      />
      <TopBarButton icon='fa-refresh'
        dataL10nId='sync'
        className='notImplemented'
        onClick={this.props.changeTab.bind(null, preferenceTabs.SYNC)}
        selected={this.props.preferenceTab === preferenceTabs.SYNC}
      />
      <TopBarButton icon='fa-user'
        dataL10nId='privacy'
        onClick={this.props.changeTab.bind(null, preferenceTabs.PRIVACY)}
        selected={this.props.preferenceTab === preferenceTabs.PRIVACY}
      />
      <TopBarButton icon='fa-lock'
        dataL10nId='security'
        onClick={this.props.changeTab.bind(null, preferenceTabs.SECURITY)}
        selected={this.props.preferenceTab === preferenceTabs.SECURITY}
      />
      <TopBarButton onClick={this.props.changeTab.bind(null, preferenceTabs.BRAVERY)}
        dataL10nId='bravery'
        className='notImplemented'
        selected={this.props.preferenceTab === preferenceTabs.BRAVERY}
      />

  }
}

class HelpfulHints extends ImmutableComponent {
  render () {
    return

      <div data-l10n-id={`hint${this.props.hintNumber}`}/>

  }
}

class AboutPreferences extends React.Component {
  constructor () {
    super()
    this.state = {
      preferenceTab: preferenceTabs.GENERAL,
      hintNumber: this.getNextHintNumber(),
      settings: window.initSettings ? Immutable.fromJS(window.initSettings) : Immutable.Map()
    }
    window.addEventListener(messages.SETTINGS_UPDATED, (e) => {
      this.setState({
        settings: Immutable.fromJS(e.detail || {})
      })
    })
  }

  changeTab (preferenceTab) {
    this.setState({
      preferenceTab
    })
  }

  refreshHint () {
    this.setState({
      hintNumber: this.getNextHintNumber()
    })
  }

  getNextHintNumber () {
    // Try for a new random number at most 10 times.
    // Avoiding the same tip twice is good because people may think the
    // refresh button is broken.
    let newNumber
    for (let i = 0; i < 10; ++i) {
      newNumber = Math.random() * hintCount | 0
      if (!this.state || newNumber !== this.state.hintNumber) {
        break
      }
    }
    return newNumber
  }

  render () {
    let tab
    const settings = this.state.settings
    switch (this.state.preferenceTab) {
      case preferenceTabs.GENERAL:
        tab = <GeneralTab settings={settings}/>
        break
      case preferenceTabs.SEARCH:
        tab = <SearchTab settings={settings}/>
        break
      case preferenceTabs.TABS:
        tab = <TabsTab settings={settings}/>
        break
      case preferenceTabs.SYNC:
        tab = <SyncTab settings={settings}/>
        break
      case preferenceTabs.PRIVACY:
        tab = <PrivacyTab settings={settings}/>
        break
      case preferenceTabs.SECURITY:
        tab = <SecurityTab settings={settings}/>
        break
      case preferenceTabs.BRAVERY:
        tab = <BraveryTab settings={settings}/>
        break
    }
    return

      <TopBar preferenceTab={this.state.preferenceTab}
        changeTab={this.changeTab.bind(this)}/>

          {tab}

        <HelpfulHints hintNumber={this.state.hintNumber} refreshHint={this.refreshHint.bind(this)}/>

  }
}

module.exports = <AboutPreferences/>
<commit_msg>Fix cursor jumping to end in preferences<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

// Note that these are webpack requires, not CommonJS node requiring requires
const React = require('react')
const ImmutableComponent = require('../components/immutableComponent')
const Immutable = require('immutable')
const cx = require('../lib/classSet.js')
const appConfig = require('../constants/appConfig')
const preferenceTabs = require('../constants/preferenceTabs')
const messages = require('../constants/messages')
const settings = require('../constants/settings')
const aboutActions = require('./aboutActions')
const getSetting = require('../settings').getSetting

// TODO: Determine this from the l20n file automatically
const hintCount = 3

// Stylesheets
require('../../less/about/preferences.less')
require('../../node_modules/font-awesome/css/font-awesome.css')

const changeSetting = (cb, key, e) => {
  if (e.target.type === 'checkbox') {
    cb(key, e.target.checked)
  } else {
    let value = e.target.value
    if (e.target.type === 'number') {
      value = value.replace(/\D/g, '')
      value = parseInt(value, 10)
      if (Number.isNaN(value)) {
        return
      }
      value = Math.min(e.target.getAttribute('max'), Math.max(value, e.target.getAttribute('min')))
    }
    cb(key, value)
  }
}

class SettingsList extends ImmutableComponent {
  render () {
    return

      { this.props.dataL10nId
        ?
 : null }

        {this.props.children}

  }
}

class SettingItem extends ImmutableComponent {
  render () {
    return

      {this.props.children}

  }
}

class SettingCheckbox extends ImmutableComponent {
  render () {
    return

        <input type='checkbox' id={this.props.prefKey}
          disabled={this.props.disabled}
          onChange={changeSetting.bind(null, this.props.onChangeSetting, this.props.prefKey)}
          checked={getSetting(this.props.prefKey, this.props.settings)}/>

  }
}

class GeneralTab extends ImmutableComponent {
  render () {
    return <SettingsList>
      <SettingItem dataL10nId='startsWith'>
        <select value={getSetting(settings.STARTUP_MODE, this.props.settings)}
          onChange={changeSetting.bind(null, this.props.onChangeSetting, settings.STARTUP_MODE)} >
          <option data-l10n-id='startsWithOptionLastTime' value='lastTime'/>
          <option data-l10n-id='startsWithOptionHomePage' value='homePage'/>
          <option data-l10n-id='startsWithOptionNewTabPage' value='newTabPage'/>
        </select>
      </SettingItem>
      <SettingItem dataL10nId='myHomepage'>
        <input data-l10n-id='homepageInput'
          value={getSetting(settings.HOMEPAGE, this.props.settings)}
          onChange={changeSetting.bind(null, this.props.onChangeSetting, settings.HOMEPAGE)} />
      </SettingItem>
    </SettingsList>
  }
}

class SearchTab extends ImmutableComponent {
  render () {
    return <SettingsList>
      <SettingItem dataL10nId='defaultSearchEngine'>
        <select value={getSetting(settings.DEFAULT_SEARCH_ENGINE, this.props.settings)}
          onChange={changeSetting.bind(null, this.props.onChangeSetting, settings.DEFAULT_SEARCH_ENGINE)}>
          <option value='./content/search/google.xml'>Google</option>
          <option value='./content/search/duckduckgo.xml'>DuckDuckGo</option>
        </select>
      </SettingItem>
    </SettingsList>
  }
}

class TabsTab extends ImmutableComponent {
  render () {
    return <SettingsList>
      <SettingItem dataL10nId='tabsPerTabPage'>
        <input
          type='number'
          min='3'
          max='20'
          value={getSetting(settings.TABS_PER_TAB_PAGE, this.props.settings)}
          onChange={changeSetting.bind(null, this.props.onChangeSetting, settings.TABS_PER_TAB_PAGE)} />
      </SettingItem>
      <SettingCheckbox dataL10nId='switchToNewTabs' prefKey={settings.SWITCH_TO_NEW_TABS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting}/>
      <SettingCheckbox dataL10nId='paintTabs' prefKey={settings.PAINT_TABS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting}/>
      <SettingCheckbox dataL10nId='showTabPreviews' prefKey={settings.SHOW_TAB_PREVIEWS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting}/>
    </SettingsList>
  }
}

class SyncTab extends ImmutableComponent {
  render () {
    return

      Sync settings coming soon

  }
}

class PrivacyTab extends ImmutableComponent {
  render () {
    return

      <SettingsList dataL10nId='suggestionTypes'>
        <SettingCheckbox dataL10nId='history' prefKey={settings.HISTORY_SUGGESTIONS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting}/>
        <SettingCheckbox dataL10nId='bookmarks' prefKey={settings.BOOKMARK_SUGGESTIONS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting}/>
        <SettingCheckbox dataL10nId='openedTabs' prefKey={settings.OPENED_TAB_SUGGESTIONS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting}/>
      </SettingsList>
      <SettingsList dataL10nId='advancedPrivacySettings'>
        <SettingCheckbox dataL10nId='doNotTrack' prefKey={settings.DO_NOT_TRACK} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting}/>
      </SettingsList>

  }
}

class SecurityTab extends ImmutableComponent {
  render () {
    return <SettingsList>
      <SettingCheckbox disabled dataL10nId='blockAttackSites' prefKey={settings.BLOCK_REPORTED_SITES} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting}/>
    </SettingsList>
  }
}

class BraveryTab extends ImmutableComponent {
  render () {
    return

      Bravery settings coming soon

  }
}

class TopBarButton extends ImmutableComponent {
  render () {
    return

      { this.props.selected
      ?

 : null }

  }
}

class TopBar extends ImmutableComponent {
  render () {
    return

      <TopBarButton icon='fa-list-alt'
        dataL10nId='general'
        onClick={this.props.changeTab.bind(null, preferenceTabs.GENERAL)}
        selected={this.props.preferenceTab === preferenceTabs.GENERAL}
      />
      <TopBarButton icon='fa-search'
        dataL10nId='search'
        onClick={this.props.changeTab.bind(null, preferenceTabs.SEARCH)}
        selected={this.props.preferenceTab === preferenceTabs.SEARCH}
      />
      <TopBarButton icon='fa-bookmark-o'
        dataL10nId='tabs'
        onClick={this.props.changeTab.bind(null, preferenceTabs.TABS)}
        selected={this.props.preferenceTab === preferenceTabs.TABS}
      />
      <TopBarButton icon='fa-refresh'
        dataL10nId='sync'
        className='notImplemented'
        onClick={this.props.changeTab.bind(null, preferenceTabs.SYNC)}
        selected={this.props.preferenceTab === preferenceTabs.SYNC}
      />
      <TopBarButton icon='fa-user'
        dataL10nId='privacy'
        onClick={this.props.changeTab.bind(null, preferenceTabs.PRIVACY)}
        selected={this.props.preferenceTab === preferenceTabs.PRIVACY}
      />
      <TopBarButton icon='fa-lock'
        dataL10nId='security'
        onClick={this.props.changeTab.bind(null, preferenceTabs.SECURITY)}
        selected={this.props.preferenceTab === preferenceTabs.SECURITY}
      />
      <TopBarButton onClick={this.props.changeTab.bind(null, preferenceTabs.BRAVERY)}
        dataL10nId='bravery'
        className='notImplemented'
        selected={this.props.preferenceTab === preferenceTabs.BRAVERY}
      />

  }
}

class HelpfulHints extends ImmutableComponent {
  render () {
    return

      <div data-l10n-id={`hint${this.props.hintNumber}`}/>

  }
}

class AboutPreferences extends React.Component {
  constructor () {
    super()
    this.state = {
      preferenceTab: preferenceTabs.GENERAL,
      hintNumber: this.getNextHintNumber(),
      settings: window.initSettings ? Immutable.fromJS(window.initSettings) : Immutable.Map()
    }
    window.addEventListener(messages.SETTINGS_UPDATED, (e) => {
      this.setState({
        settings: Immutable.fromJS(e.detail || {})
      })
    })
    this.onChangeSetting = this.onChangeSetting.bind(this)
  }

  changeTab (preferenceTab) {
    this.setState({
      preferenceTab
    })
  }

  refreshHint () {
    this.setState({
      hintNumber: this.getNextHintNumber()
    })
  }

  getNextHintNumber () {
    // Try for a new random number at most 10 times.
    // Avoiding the same tip twice is good because people may think the
    // refresh button is broken.
    let newNumber
    for (let i = 0; i < 10; ++i) {
      newNumber = Math.random() * hintCount | 0
      if (!this.state || newNumber !== this.state.hintNumber) {
        break
      }
    }
    return newNumber
  }

  onChangeSetting (key, value) {
    this.setState({
      settings: this.state.settings.set(key, value)
    })
    aboutActions.changeSetting(key, value)
  }

  render () {
    let tab
    const settings = this.state.settings
    switch (this.state.preferenceTab) {
      case preferenceTabs.GENERAL:
        tab = <GeneralTab settings={settings} onChangeSetting={this.onChangeSetting}/>
        break
      case preferenceTabs.SEARCH:
        tab = <SearchTab settings={settings} onChangeSetting={this.onChangeSetting}/>
        break
      case preferenceTabs.TABS:
        tab = <TabsTab settings={settings} onChangeSetting={this.onChangeSetting}/>
        break
      case preferenceTabs.SYNC:
        tab = <SyncTab settings={settings} onChangeSetting={this.onChangeSetting}/>
        break
      case preferenceTabs.PRIVACY:
        tab = <PrivacyTab settings={settings} onChangeSetting={this.onChangeSetting}/>
        break
      case preferenceTabs.SECURITY:
        tab = <SecurityTab settings={settings} onChangeSetting={this.onChangeSetting}/>
        break
      case preferenceTabs.BRAVERY:
        tab = <BraveryTab settings={settings} onChangeSetting={this.onChangeSetting}/>
        break
    }
    return

      <TopBar preferenceTab={this.state.preferenceTab}
        changeTab={this.changeTab.bind(this)}/>

          {tab}

        <HelpfulHints hintNumber={this.state.hintNumber} refreshHint={this.refreshHint.bind(this)}/>

  }
}

module.exports = <AboutPreferences/>
<|endoftext|>