<commit_before>var enableDebugging = true;

var currentScale = 0;
var scalingFactor = 1.0;
var dragOffsetX = 0;
var dragOffsetY = 0;

var renderingComplete = false;

function init() {
    $(document).ready(function() {
        if (enableDebugging) {
            $("#logitem").show();
            $("#logtab").show();
        }
        else {
            $("#logitem").hide();
            $("#logtab").hide();
        }

        log("Initializing pussycat UI...");
        // create tabs
        $("#browsertabs").tabs({
                                   show:tabShow,
                                   selected:1
                               });

        // resize the page so it fills window
        resizeDisplay();
        // register resize handler so that the page resizes when the window size changes
        $(window).resize(function() {
            resizeDisplay();
        });

        $("#retrybutton").button();
        $("#retrybutton").click(renderDiagram);

        // bind mousewheel event handler
        $('#diagramareacontent').mousewheel(function(event, delta) {
            if (delta > 0) {
                currentScale++;
                zoomIn();
            }
            else if (delta < 0) {
                currentScale--;
                zoomOut();
            }
        });

        // bind drag handler
        $('#diagramareacontent')
                .drag(function(ev, dd) {
                          $("#xOffset").html(dd.deltaX);
                          $("#yOffset").html(dd.deltaY);
                          pan(dd.deltaX, dd.deltaY);
                      }, {relative:true})
                .drag("end", function(ev, dd) {
                          updateOffset();
                      }
        );
    });
    log("Pussycat UI initialized OK");
}

function resizeDisplay() {
    // get sizes of borders around body and page_wrapper element
    var bodyMargin = parseInt($('body').css('margin-top')) + parseInt($('body').css('margin-bottom'));
    var pageMargin = parseInt($("#page_wrapper").css('margin-top')) + parseInt($("#page_wrapper").css('margin-bottom'));
    var pageBorder = parseInt($("#page_wrapper").css('border-top-width')) +
            parseInt($("#page_wrapper").css('border-bottom-width'));
    var offset = bodyMargin + pageMargin + pageBorder;

    // update the size of the page_wrapper to fill document
    $("#page_wrapper").height($(window).height() - offset);

    // update the size of divtable to fill the available space
    $("#divtable").height($("#page_wrapper").height() - $("#divtable").offset().top);

    // update the size of tabs to fill the available space
    $("#tabs").height($("#page_wrapper").height() - $("#tabs").offset().top);

    // update the size of tabcontent to match the size of max tabs child
    var maxPadding = 0;
    $('.tabcontent').each(function(i) {
        var padding = parseFloat($(this).css("padding-top")) + parseFloat($(this).css("padding-bottom"));
        if (padding > maxPadding) {
            maxPadding = padding;
        }
    });
    $(".tabcontent").height($("#tabs").height() - maxPadding);

    // update the diagramarea to fill the available space
    $("#diagramarea").height($("#diagramtab").height() -
                                     ($("#diagramarea").position().top - $("#diagramtab").position().top));
    log("Window resized: new size = " + $("#page_wrapper").height() + ", " + $("#page_wrapper").width());

    // update the svg size to fill the space available in the diagram area
    try {
        var width = $("#diagramarea").width();
        var height = $("#diagramarea").height();
        $("#goci-svg").attr("width", width);
        $("#goci-svg").attr("height", height);
        // update the svg viewBox to match width and height adjusting for scaling
        var newWidth = width * scalingFactor;
        var newHeight = height * scalingFactor;
        var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
        var elements = viewBox.split(' ');
        var newViewBox = elements[0] + " " + elements[1] + " " + newWidth + " " + newHeight;
//        document.getElementById('goci-svg').setAttribute("viewBox", newViewBox);
        $("#goci-svg").attr("viewBox", newViewBox);
        log("Adjusted SVG dimensions - SVG now width = " + width + ", height = " + height + ", viewBox = " +
                    newViewBox);
    }
    catch (ex) {
        log("Failed to adjust SVG dimensions: " + ex);
    }
}

function tabShow(event, ui) {
    if (ui.panel.id == "diagramtab") {
        renderDiagram();
    }
}

function renderDiagram() {
    if (!renderingComplete) {
        // call to api/views/gwasdiagram to get required svg
        log("Rendering GWAS diagram - calling api/views/gwasdiagram...");
        $.ajax({
                   url:'api/views/gwasdiagram',
                   dataType:'html',
                   success:insertSVG,
                   error:serverCommunicationFail
               });
    }
    else {
        log("Rendering already complete, update request not sent");
    }
}

function insertSVG(svg) {
    // update diagramarea div with returned SVG
    log("Obtained SVG from server OK, rendering...");
    $("#diagramareaerror").css({"display":"none"});
    $("#diagramareacontent").html(svg);
    resizeDisplay();
    renderingComplete = true;
    log("Diagram area div updated with SVG content OK");
}

function serverCommunicationFail(jqXHR, textStatus, errorThrown) {
    // show diagram area error div
    $("#diagramareaerror").css({"display":"block"});
    $("#diagramareaerrortext").html(jqXHR.responseText);
    log("Failed to acquire SVG from server - " + jqXHR.responseText);
}

function zoomIn() {
    var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
    var elements = viewBox.split(' ');
    // get width and height
    var width = elements[2];
    var height = elements[3];
    // update scaling factor
    scalingFactor = scalingFactor * 0.95;
    $("#scale").html(scalingFactor);
    // calculate new sizes
    // transform width and height by multiplying by 0.95
    var newWidth = width * 0.95;
    var newHeight = height * 0.95;
    var newViewBox = elements[0] + " " + elements[1] + " " + newWidth + " " + newHeight;
    document.getElementById('goci-svg').setAttribute("viewBox", newViewBox);
    log("Zoom in over SVG event.  New zoom level: " + scalingFactor + ", viewBox now set to " + newViewBox);
}

function zoomOut() {
    var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
    var elements = viewBox.split(' ');
    // get width and height
    var width = elements[2];
    var height = elements[3];
    // update scaling factor
    scalingFactor = scalingFactor * 1.05;
    $("#scale").html(scalingFactor);
    // calculate new sizes
    // transform width and height by multiplying by 1.05
    var newWidth = width * 1.05;
    var newHeight = height * 1.05;
    var newViewBox = elements[0] + " " + elements[1] + " " + newWidth + " " + newHeight;
    document.getElementById('goci-svg').setAttribute("viewBox", newViewBox);
    log("Zoom out over SVG event.  New zoom level: " + scalingFactor + ", viewBox now set to " + newViewBox);
}

function pan(deltaX, deltaY) {
    var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
    var elements = viewBox.split(' ');
    var newX = (parseFloat(-deltaX) * scalingFactor) + parseFloat(dragOffsetX);
    var newY = (parseFloat(-deltaY) * scalingFactor) + parseFloat(dragOffsetY);
    $("#xOffsetSVG").html(newX);
    $("#yOffsetSVG").html(newY);
    // get width and height
    var newViewBox = newX + " " + newY + " " + elements[2] + " " + elements[3];
    document.getElementById('goci-svg').setAttribute("viewBox", newViewBox);
    log("Pan over SVG event.  ViewBox now set to " + newViewBox);
}

function updateOffset() {
    var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
    var elements = viewBox.split(' ');
    dragOffsetX = elements[0];
    dragOffsetY = elements[1];
}

function log(msg) {
    $('#log').append('
' + msg + '
');
}
<commit_msg>goci-pussycat: SVG zoom enhancement<commit_after>var enableDebugging = true;

var currentScale = 0;
var scalingFactor = 1.0;
var dragOffsetX = 0;
var dragOffsetY = 0;

var renderingComplete = false;

function init() {
    $(document).ready(function() {
        if (enableDebugging) {
            $("#logitem").show();
            $("#logtab").show();
        }
        else {
            $("#logitem").hide();
            $("#logtab").hide();
        }

        log("Initializing pussycat UI...");
        // create tabs
        $("#browsertabs").tabs({
                                   show:tabShow,
                                   selected:1
                               });

        // resize the page so it fills window
        resizeDisplay();
        // register resize handler so that the page resizes when the window size changes
        $(window).resize(function() {
            resizeDisplay();
        });

        $("#retrybutton").button();
        $("#retrybutton").click(renderDiagram);

        // bind mousewheel event handler
        $('#diagramareacontent').mousewheel(function(event, delta) {
            if (delta > 0) {
                currentScale++;
                zoomIn();
            }
            else if (delta < 0) {
                currentScale--;
                zoomOut();
            }
        });

        // bind drag handler
        $('#diagramareacontent')
                .drag(function(ev, dd) {
                          $("#xOffset").html(dd.deltaX);
                          $("#yOffset").html(dd.deltaY);
                          pan(dd.deltaX, dd.deltaY);
                      }, {relative:true})
                .drag("end", function(ev, dd) {
                          updateOffset();
                      }
        );
    });
    log("Pussycat UI initialized OK");
}

function resizeDisplay() {
    // get sizes of borders around body and page_wrapper element
    var bodyMargin = parseInt($('body').css('margin-top')) + parseInt($('body').css('margin-bottom'));
    var pageMargin = parseInt($("#page_wrapper").css('margin-top')) + parseInt($("#page_wrapper").css('margin-bottom'));
    var pageBorder = parseInt($("#page_wrapper").css('border-top-width')) +
            parseInt($("#page_wrapper").css('border-bottom-width'));
    var offset = bodyMargin + pageMargin + pageBorder;

    // update the size of the page_wrapper to fill document
    $("#page_wrapper").height($(window).height() - offset);

    // update the size of divtable to fill the available space
    $("#divtable").height($("#page_wrapper").height() - $("#divtable").offset().top);

    // update the size of tabs to fill the available space
    $("#tabs").height($("#page_wrapper").height() - $("#tabs").offset().top);

    // update the size of tabcontent to match the size of max tabs child
    var maxPadding = 0;
    $('.tabcontent').each(function(i) {
        var padding = parseFloat($(this).css("padding-top")) + parseFloat($(this).css("padding-bottom"));
        if (padding > maxPadding) {
            maxPadding = padding;
        }
    });
    $(".tabcontent").height($("#tabs").height() - maxPadding);

    // update the diagramarea to fill the available space
    $("#diagramarea").height($("#diagramtab").height() -
                                     ($("#diagramarea").position().top - $("#diagramtab").position().top));
    log("Window resized: new size = " + $("#page_wrapper").height() + ", " + $("#page_wrapper").width());

    // update the svg size to fill the space available in the diagram area
    try {
        var width = $("#diagramarea").width();
        var height = $("#diagramarea").height();
        $("#goci-svg").attr("width", width);
        $("#goci-svg").attr("height", height);
        // update the svg viewBox to match width and height adjusting for scaling
        var newWidth = width * scalingFactor;
        var newHeight = height * scalingFactor;
        var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
        var elements = viewBox.split(' ');
        var newViewBox = elements[0] + " " + elements[1] + " " + newWidth + " " + newHeight;
//        document.getElementById('goci-svg').setAttribute("viewBox", newViewBox); // this uses server SVG size.  If commented out, default view is zoomed
        log("Adjusted SVG dimensions - SVG now width = " + width + ", height = " + height + ", viewBox = " +
                    newViewBox);
    }
    catch (ex) {
        log("Failed to adjust SVG dimensions: " + ex);
    }
}

function tabShow(event, ui) {
    if (ui.panel.id == "diagramtab") {
        renderDiagram();
    }
}

function renderDiagram() {
    if (!renderingComplete) {
        // call to api/views/gwasdiagram to get required svg
        log("Rendering GWAS diagram - calling api/views/gwasdiagram...");
        $.ajax({
                   url:'api/views/gwasdiagram',
                   dataType:'html',
                   success:insertSVG,
                   error:serverCommunicationFail
               });
    }
    else {
        log("Rendering already complete, update request not sent");
    }
}

function insertSVG(svg) {
    // update diagramarea div with returned SVG
    log("Obtained SVG from server OK, rendering...");
    $("#diagramareaerror").css({"display":"none"});
    $("#diagramareacontent").html(svg);
    resizeDisplay();
    renderingComplete = true;
    log("Diagram area div updated with SVG content OK");
}

function serverCommunicationFail(jqXHR, textStatus, errorThrown) {
    // show diagram area error div
    $("#diagramareaerror").css({"display":"block"});
    $("#diagramareaerrortext").html(jqXHR.responseText);
    log("Failed to acquire SVG from server - " + jqXHR.responseText);
}

function zoomIn() {
    var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
    var elements = viewBox.split(' ');
    // get width and height
    var width = elements[2];
    var height = elements[3];
    // update scaling factor
    scalingFactor = scalingFactor * 0.95;
    $("#scale").html(scalingFactor);
    // calculate new sizes
    // transform width and height by multiplying by 0.95
    var newWidth = width * 0.95;
    var newHeight = height * 0.95;
    var newViewBox = elements[0] + " " + elements[1] + " " + newWidth + " " + newHeight;
    document.getElementById('goci-svg').setAttribute("viewBox", newViewBox);
    log("Zoom in over SVG event.  New zoom level: " + scalingFactor + ", viewBox now set to " + newViewBox);
}

function zoomOut() {
    var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
    var elements = viewBox.split(' ');
    // get width and height
    var width = elements[2];
    var height = elements[3];
    // update scaling factor
    scalingFactor = scalingFactor * 1.05;
    $("#scale").html(scalingFactor);
    // calculate new sizes
    // transform width and height by multiplying by 1.05
    var newWidth = width * 1.05;
    var newHeight = height * 1.05;
    var newViewBox = elements[0] + " " + elements[1] + " " + newWidth + " " + newHeight;
    document.getElementById('goci-svg').setAttribute("viewBox", newViewBox);
    log("Zoom out over SVG event.  New zoom level: " + scalingFactor + ", viewBox now set to " + newViewBox);
}

function pan(deltaX, deltaY) {
    var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
    var elements = viewBox.split(' ');
    var newX = (parseFloat(-deltaX) * scalingFactor) + parseFloat(dragOffsetX);
    var newY = (parseFloat(-deltaY) * scalingFactor) + parseFloat(dragOffsetY);
    $("#xOffsetSVG").html(newX);
    $("#yOffsetSVG").html(newY);
    // get width and height
    var newViewBox = newX + " " + newY + " " + elements[2] + " " + elements[3];
    document.getElementById('goci-svg').setAttribute("viewBox", newViewBox);
    log("Pan over SVG event.  ViewBox now set to " + newViewBox);
}

function updateOffset() {
    var viewBox = document.getElementById('goci-svg').getAttribute("viewBox");
    var elements = viewBox.split(' ');
    dragOffsetX = elements[0];
    dragOffsetY = elements[1];
}

function log(msg) {
    $('#log').append('
' + msg + '
');
}
<|endoftext|>


<commit_before>/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

export function missingFieldArgMessage(fieldName, argName, typeName) {
  return `Field ${fieldName} argument ${argName} of type ${typeName} is ` +
    `required but not provided.`;
}

export function missingDirectiveArgMessage(directiveName, argName, typeName) {
  return `Directive @${directiveName} argument ${argName} of type ` +
    `${typeName} is required but not provided.`;
}

export function badValueMessage(argName, typeName, value) {
  return `Argument ${argName} expected type ${typeName} but got: ${value}.`;
}

export function defaultForNonNullArgMessage(varName, typeName, guessTypeName) {
  return `Variable $${varName} of type ${typeName} ` +
    `is required and will never use the default value. ` +
    `Perhaps you meant to use type ${guessTypeName}.`;
}

export function badValueForDefaultArgMessage(varName, typeName, value) {
  return `Variable $${varName} of type ${typeName} has invalid default ` +
    `value: ${value}.`;
}

export function undefinedFieldMessage(field, type) {
  return 'Cannot query field ' + field + ' on ' + type;
}

export function fragmentOnNonCompositeErrorMessage(fragName, typeName) {
  return `Fragment "${fragName}" cannot condition on non composite ` +
    `type "${typeName}".`;
}

export function inlineFragmentOnNonCompositeErrorMessage(typeName) {
  return `Fragment cannot condition on non composite type "${typeName}".`;
}

export function unknownArgMessage(argName, fieldName, typeName) {
  return `Unknown argument ${argName} on field ${fieldName} ` +
    `of type ${typeName}.`;
}

export function unknownTypeMessage(typeName) {
  return `Unknown type ${typeName}.`;
}

export function undefinedVarMessage(varName) {
  return `Variable $${varName} is not defined.`;
}

export function undefinedVarByOpMessage(varName, opName) {
  return `Variable $${varName} is not defined by operation ${opName}.`;
}

export function unusedFragMessage(fragName) {
  return `Fragment ${fragName} is not used.`;
}

export function unusedVariableMessage(varName) {
  return `Variable $${varName} is not used.`;
}

export function typeIncompatibleSpreadMessage(fragName, parentType, fragType) {
  return `Fragment "${fragName}" cannot be spread here as objects of ` +
    `type "${parentType}" can never be of type "${fragType}".`;
}

export function typeIncompatibleAnonSpreadMessage(parentType, fragType) {
  return `Fragment cannot be spread here as objects of ` +
    `type "${parentType}" can never be of type "${fragType}".`;
}

export function noSubselectionAllowedMessage(field, type) {
  return `Field "${field}" of type ${type} must not have a sub selection.`;
}

export function requiredSubselectionMessage(field, type) {
  return `Field "${field}" of type ${type} must have a sub selection.`;
}

export function nonInputTypeOnVarMessage(variableName, typeName) {
  return `Variable $${variableName} cannot be non ` +
    `input type ${typeName}.`;
}

export function cycleErrorMessage(fragmentName, spreadNames) {
  return `Cannot spread fragment ${fragmentName} within itself` +
  (spreadNames.length ? ` via ${spreadNames.join(', ')}.` : '.');
}

export function unknownDirectiveMessage(directiveName) {
  return `Unknown directive ${directiveName}.`;
}

export function misplacedDirectiveMessage(directiveName, placement) {
  return `Directive ${directiveName} may not be used on ${placement}.`;
}

export function missingDirectiveValueMessage(directiveName, typeName) {
  return `Directive ${directiveName} expects a value of type ${typeName}.`;
}

export function noDirectiveValueMessage(directiveName) {
  return `Directive ${directiveName} expects no value.`;
}

export function badDirectiveValueMessage(directiveName, typeName, value) {
  return `Directive ${directiveName} expected type ${typeName} but ` +
    `got: ${value}.`;
}

export function badVarPosMessage(varName, varType, expectedType) {
  return `Variable $${varName} of type ${varType} used in position expecting ` +
    `type ${expectedType}.`;
}

export function fieldsConflictMessage(responseName, reason) {
  return `Fields ${responseName} conflict because ${reasonMessage(reason)}.`;
}

function reasonMessage(reason) {
  if (Array.isArray(reason)) {
    return reason.map(([responseName, subreason]) =>
      `subfields ${responseName} conflict because ${reasonMessage(subreason)}`
    ).join(' and ');
  }
  return reason;
}
<commit_msg>rm dead code<commit_after>/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

export function missingFieldArgMessage(fieldName, argName, typeName) {
  return `Field ${fieldName} argument ${argName} of type ${typeName} is ` +
    `required but not provided.`;
}

export function missingDirectiveArgMessage(directiveName, argName, typeName) {
  return `Directive @${directiveName} argument ${argName} of type ` +
    `${typeName} is required but not provided.`;
}

export function badValueMessage(argName, typeName, value) {
  return `Argument ${argName} expected type ${typeName} but got: ${value}.`;
}

export function defaultForNonNullArgMessage(varName, typeName, guessTypeName) {
  return `Variable $${varName} of type ${typeName} ` +
    `is required and will never use the default value. ` +
    `Perhaps you meant to use type ${guessTypeName}.`;
}

export function badValueForDefaultArgMessage(varName, typeName, value) {
  return `Variable $${varName} of type ${typeName} has invalid default ` +
    `value: ${value}.`;
}

export function undefinedFieldMessage(field, type) {
  return 'Cannot query field ' + field + ' on ' + type;
}

export function fragmentOnNonCompositeErrorMessage(fragName, typeName) {
  return `Fragment "${fragName}" cannot condition on non composite ` +
    `type "${typeName}".`;
}

export function inlineFragmentOnNonCompositeErrorMessage(typeName) {
  return `Fragment cannot condition on non composite type "${typeName}".`;
}

export function unknownArgMessage(argName, fieldName, typeName) {
  return `Unknown argument ${argName} on field ${fieldName} ` +
    `of type ${typeName}.`;
}

export function unknownTypeMessage(typeName) {
  return `Unknown type ${typeName}.`;
}

export function undefinedVarMessage(varName) {
  return `Variable $${varName} is not defined.`;
}

export function undefinedVarByOpMessage(varName, opName) {
  return `Variable $${varName} is not defined by operation ${opName}.`;
}

export function unusedFragMessage(fragName) {
  return `Fragment ${fragName} is not used.`;
}

export function unusedVariableMessage(varName) {
  return `Variable $${varName} is not used.`;
}

export function typeIncompatibleSpreadMessage(fragName, parentType, fragType) {
  return `Fragment "${fragName}" cannot be spread here as objects of ` +
    `type "${parentType}" can never be of type "${fragType}".`;
}

export function typeIncompatibleAnonSpreadMessage(parentType, fragType) {
  return `Fragment cannot be spread here as objects of ` +
    `type "${parentType}" can never be of type "${fragType}".`;
}

export function noSubselectionAllowedMessage(field, type) {
  return `Field "${field}" of type ${type} must not have a sub selection.`;
}

export function requiredSubselectionMessage(field, type) {
  return `Field "${field}" of type ${type} must have a sub selection.`;
}

export function nonInputTypeOnVarMessage(variableName, typeName) {
  return `Variable $${variableName} cannot be non ` +
    `input type ${typeName}.`;
}

export function cycleErrorMessage(fragmentName, spreadNames) {
  return `Cannot spread fragment ${fragmentName} within itself` +
  (spreadNames.length ? ` via ${spreadNames.join(', ')}.` : '.');
}

export function unknownDirectiveMessage(directiveName) {
  return `Unknown directive ${directiveName}.`;
}

export function misplacedDirectiveMessage(directiveName, placement) {
  return `Directive ${directiveName} may not be used on ${placement}.`;
}

export function badVarPosMessage(varName, varType, expectedType) {
  return `Variable $${varName} of type ${varType} used in position expecting ` +
    `type ${expectedType}.`;
}

export function fieldsConflictMessage(responseName, reason) {
  return `Fields ${responseName} conflict because ${reasonMessage(reason)}.`;
}

function reasonMessage(reason) {
  if (Array.isArray(reason)) {
    return reason.map(([responseName, subreason]) =>
      `subfields ${responseName} conflict because ${reasonMessage(subreason)}`
    ).join(' and ');
  }
  return reason;
}
<|endoftext|>


<commit_before>var CentsGauge = (function (containerID) {

  function CentsGauge() {

    ViewContextAndStyle.apply(this,arguments);

    var _width  = 400
      , _height = 200
      , twoPI   = 2 * Math.PI
      , mxCents = 50
      ;

    this.cvs.width  = _width;
    this.cvs.height = _height;

    this.cvs.style.position = "absolute";
    this.cvs.style.zIndex = 1;

    var centerX       = this.cvs.width / 2
      , centerY       = this.cvs.height - 10
      , radius        = 160
      , circumference = twoPI * radius
      , quadrantArc   = circumference / 4
      , tickWidth     = 2
      , maxTickWidth  = quadrantArc / mxCents
      , tickLength    = 16
      , markStep      = 10
      , _bgCVS        = document.createElement("canvas")
      , _bgCTX        = _bgCVS.getContext("2d")
      , dCents        = 0
      , totalSteps    = 30
      , animStep      = 1
      , dt            = 1 /* seconds */
      , mxSteps       = dt * totalSteps
      ;

      _bgCVS.id = "gtunerViewBg";

      document.getElementById(containerID).appendChild(_bgCVS);

      _bgCVS.width  = _width;
      _bgCVS.height = _height;

      _bgCVS.style.position = "absolute";
      _bgCVS.style.zIndex   = 0;

      _bgCTX.fillStyle   = this.color;
      _bgCTX.strokeStyle = this.color;

    Object.defineProperties(this, {
        "width" : {
          enumerable   : true
        , configurable : false
        , get : function () {
            return _width;
          }
        , set : function (val) {
            _width = val;

            this.cvs.width = _width;
            centerX  = this.cvs.width / 2;

            this.background();
          }
        }
      , "height" : {
          enumerable   : true
        , configurable : false
        , get : function () {
            return _height;
          }
        , set : function (val) {
            _height = val;

            this.cvs.height = _height;
            centerY         = this.cvs.height;

            this.background();
          }
        }
      , "tickWidth" : {
           configurable : false
         , enumerable   : true
         , get : function () {
             return tickWidth;
           }
         , set : function (val) {

             if (val <= maxTickWidth) {
               tickWidth = val;
             }

             this.background();
           }
        }
      , "tickLength" : {
           configurable : false
         , enumerable   : true
         , get : function () {
             return tickLength;
         }
         , set : function (val) {
             tickLength = val;

             this.background();
           }
        }
      , "markStep" : {
          configurable : false
        , enumerable   : true
        , get : function () {
            return markStep;
          }
        , set : function (val) {
            markStep = val;

            this.background();
          }
        }
      , "radius" : {
          enumerable   : true
        , configurable : false
        , get          : function () {
            return radius;
          }
        , set : function (r) {
            radius = r;

            circumference = twoPI * radius;
            quadrantArc   = circumference / 4;
            maxTickWidth  = quadrantArc / mxCents

            this.background();
        }
      }
    });

    Object.defineProperties(CentsGauge.prototype, {
        "background" : {
          value : function() {

            var unitStep = quadrantArc / mxCents
              , arc      = quadrantArc - unitStep
              , halfMark = markStep / 2
              , c        = mxCents - 1
              , currentTickLength = tickLength
              , alfa
              , y0
              , y1
              , xc0
              , xc1
              , yc0
              , yc1
              , unitTickLength = tickLength / 3
              , halfTickLength = tickLength / 1.5
              , sTickLength    = currentTickLength / 2
              ;

            _bgCTX.beginPath();
            _bgCTX.arc(centerX,centerY,10,0,twoPI,false);
            _bgCTX.fill();

            _bgCTX.beginPath();
            _bgCTX.lineWidth = tickWidth;

            _bgCTX.moveTo(centerX, centerY - radius - sTickLength);
            _bgCTX.lineTo(centerX, centerY - radius + sTickLength);

            while (c >= 0) {

              if (c % markStep == 0) {
                currentTickLength = tickLength;
              } else if (c % halfMark == 0) {
                currentTickLength = halfTickLength;
              } else {
                currentTickLength = unitTickLength;
              }

              alfa = arc / radius;

              sTickLength = currentTickLength / 2;

              xc0 = (radius + sTickLength) * Math.cos(alfa);
              xc1 = (radius - sTickLength) * Math.cos(alfa);
              yc0 = (radius + sTickLength) * Math.sin(alfa);
              yc1 = (radius - sTickLength) * Math.sin(alfa);

              y0 = centerY - yc0;
              y1 = centerY - yc1;

              _bgCTX.moveTo(centerX - xc0, y0);
              _bgCTX.lineTo(centerX - xc1, y1);

              _bgCTX.moveTo(centerX + xc0, y0);
              _bgCTX.lineTo(centerX + xc1, y1);

              arc -= unitStep;
              c   -= 1;
            }

            _bgCTX.stroke();
          }
        , enumerable   : false
        , configurable : false
        , writable     : false
        }
      , "run" : {
          value : function() {

            var arc  = quadrantArc - (dCents * quadrantArc / mxCents)
              , alfa = arc / radius
              , x = centerX + radius * Math.cos(alfa)
              , y = centerY - radius * Math.sin(alfa)
              ;

            this.ctx.clearRect(0,0,this.cvs.width,this.cvs.height);

            this.ctx.font      = this.noteFont;
            this.ctx.fillStyle = this.color;

            this.ctx.fillText(this.peek.note.name,20,50);

            this.ctx.font = this.freqFont;
            this.ctx.fillText(this.peek.frequency.toFixed(2) + " Hz",this.cvs.width-110,40);

            this.ctx.beginPath();
            this.ctx.moveTo(centerX,centerY);
            this.ctx.lineTo(x,y);
            this.ctx.strokeStyle = this.color;
            this.ctx.stroke();

            if (dCents > this.peek.cents) {
              dCents -= animStep;

              if (dCents < this.peek.cents) {
                dCents = this.peek.cents;
              }
            } else {
              dCents += animStep;

              if (dCents > this.peek.cents) {
                dCents = this.peek.cents;
              }
            }
            window.requestAnimationFrame(this.run.bind(this));
          }
        , enumerable   : false
        , configurable : false
        , writable     : false
        }
      , "update" : {
          value : function (element) {

            if (element.peek) {

            this.peek = element.peek;

            tmpAnimStep = Math.abs(dCents - Math.abs(this.peek.cents)) / mxSteps;

            animStep = tmpAnimStep > 1 ? tmpAnimStep : 1 ;
            }

            if (element.updateTime) {
              dt = element.updateTime;
              mxSteps = dt * totalSteps;
            }
          }
        , enumerable   : false
        , configurable : false
        , writable     : false
        }
      }
    );

    this.background();
  };

  if (!this.instance) {
    this.instance = new CentsGauge(containerID);
  } else {
    console.log("An instance of CentsGauge already exists.");
  }

  return this.instance;

});
<commit_msg>Make radius and font size scale with canvas height and width.<commit_after>var CentsGauge = (function (containerID) {

  function CentsGauge() {

    ViewContextAndStyle.apply(this,arguments);

    var _width  = 360
      , _height = 180
      , pradius = _width / 2 - 20
      , twoPI   = 2 * Math.PI
      , mxCents = 50
      ;

    this.cvs.width  = _width;
    this.cvs.height = _height;

    this.cvs.style.position = "absolute";
    this.cvs.style.zIndex   = 1;

    this.ctx.textAlign   = "center";
    this.ctx.fillStyle   = this.color;
    this.ctx.strokeStyle = this.color;

    var centerX       = this.cvs.width / 2
      , centerY       = this.cvs.height
      , radius        = pradius < _height - 10 ? pradius : _height - 10
      , circumference = twoPI * radius
      , quadrantArc   = circumference / 4
      , tickWidth     = 2
      , maxTickWidth  = quadrantArc / mxCents
      , tickLength    = 16
      , markStep      = 10
      , _bgCVS        = document.createElement("canvas")
      , _bgCTX        = _bgCVS.getContext("2d")
      , dCents        = 0
      , totalSteps    = 30
      , animStep      = 1
      , dt            = 1 /* seconds */
      , mxSteps       = dt * totalSteps
      ;

      this.noteFontSize = _width / 8;
      this.freqFontSize = _width / 16;

      _bgCVS.id = "gtunerViewBg";

      document.getElementById(containerID).appendChild(_bgCVS);

      _bgCVS.width  = _width;
      _bgCVS.height = _height;

      _bgCVS.style.position = "absolute";
      _bgCVS.style.zIndex   = 0;

      _bgCTX.fillStyle   = this.color;
      _bgCTX.strokeStyle = this.color;
      _bgCTX.textAlign   = "center";

    Object.defineProperties(this, {
        "width" : {
          enumerable   : true
        , configurable : false
        , get : function () {
            return _width;
          }
        , set : function (val) {
            _width = val;

            this.cvs.width = _width;
            this.noteFontSize = _width / 8;

            centerX  = this.cvs.width / 2;
            pradius  = _width / 2 - 20;
            radius   = pradius < _height - 10 ? pradius : _height - 10;
            this.noteFontSize = _width / 8;

          }
        }
      , "height" : {
          enumerable   : true
        , configurable : false
        , get : function () {
            return _height;
          }
        , set : function (val) {
            _height = val;

            this.cvs.height = _height;
            centerY         = this.cvs.height;

            pradius  = _width / 2 - 20;
            radius   = pradius < _height - 10 ? pradius : _height - 10;

            this.background();
          }
        }
      , "tickWidth" : {
           configurable : false
         , enumerable   : true
         , get : function () {
             return tickWidth;
           }
         , set : function (val) {

             if (val <= maxTickWidth) {
               tickWidth = val;
             }

             this.background();
           }
        }
      , "tickLength" : {
           configurable : false
         , enumerable   : true
         , get : function () {
             return tickLength;
         }
         , set : function (val) {
             tickLength = val;

             this.background();
           }
        }
      , "markStep" : {
          configurable : false
        , enumerable   : true
        , get : function () {
            return markStep;
          }
        , set : function (val) {
            markStep = val;

            this.background();
          }
        }
      , "radius" : {
          enumerable   : true
        , configurable : false
        , get          : function () {
            return radius;
          }
        , set : function (r) {
            radius = r;

            circumference = twoPI * radius;
            quadrantArc   = circumference / 4;
            maxTickWidth  = quadrantArc / mxCents

            this.background();
        }
      }
    });

    Object.defineProperties(CentsGauge.prototype, {
        "background" : {
          value : function() {

            var unitStep = quadrantArc / mxCents
              , arc      = quadrantArc - unitStep
              , halfMark = markStep / 2
              , c        = mxCents - 1
              , currentTickLength = tickLength
              , alfa
              , y0
              , y1
              , xc0
              , xc1
              , yc0
              , yc1
              , unitTickLength = tickLength / 3
              , halfTickLength = tickLength / 1.5
              , sTickLength    = currentTickLength / 2
              ;

            _bgCTX.beginPath();
            _bgCTX.arc(centerX,centerY,10,0,twoPI,false);
            _bgCTX.fill();

            _bgCTX.beginPath();
            _bgCTX.lineWidth = tickWidth;

            _bgCTX.moveTo(centerX, centerY - radius - sTickLength);
            _bgCTX.lineTo(centerX, centerY - radius + sTickLength);

            while (c >= 0) {

              if (c % markStep == 0) {
                currentTickLength = tickLength;
              } else if (c % halfMark == 0) {
                currentTickLength = halfTickLength;
              } else {
                currentTickLength = unitTickLength;
              }

              alfa = arc / radius;

              sTickLength = currentTickLength / 2;

              xc0 = (radius + sTickLength) * Math.cos(alfa);
              xc1 = (radius - sTickLength) * Math.cos(alfa);
              yc0 = (radius + sTickLength) * Math.sin(alfa);
              yc1 = (radius - sTickLength) * Math.sin(alfa);

              y0 = centerY - yc0;
              y1 = centerY - yc1;

              _bgCTX.moveTo(centerX - xc0, y0);
              _bgCTX.lineTo(centerX - xc1, y1);

              _bgCTX.moveTo(centerX + xc0, y0);
              _bgCTX.lineTo(centerX + xc1, y1);

              arc -= unitStep;
              c   -= 1;
            }

            _bgCTX.stroke();
          }
        , enumerable   : false
        , configurable : false
        , writable     : false
        }
      , "run" : {
          value : function() {

            var arc  = quadrantArc - (dCents * quadrantArc / mxCents)
              , alfa = arc / radius
              , x = centerX + radius * Math.cos(alfa)
              , y = centerY - radius * Math.sin(alfa)
              , noteTextWidth
              , frequencyTextWidth
              ;

            this.ctx.clearRect(0,0,this.cvs.width,this.cvs.height);

            this.ctx.font      = this.noteFont;

            this.ctx.fillText(this.peek.note.name,centerX, centerY - radius / 1.5);

            this.ctx.font = this.freqFont;
            this.ctx.fillText(this.peek.frequency.toFixed(2) + " Hz",centerX, centerY - radius / 2.5);

            this.ctx.beginPath();
            this.ctx.moveTo(centerX,centerY);
            this.ctx.lineTo(x,y);
            this.ctx.stroke();

            if (dCents > this.peek.cents) {
              dCents -= animStep;

              if (dCents < this.peek.cents) {
                dCents = this.peek.cents;
              }
            } else {
              dCents += animStep;

              if (dCents > this.peek.cents) {
                dCents = this.peek.cents;
              }
            }
            window.requestAnimationFrame(this.run.bind(this));
          }
        , enumerable   : false
        , configurable : false
        , writable     : false
        }
      , "update" : {
          value : function (element) {

            if (element.peek) {

            this.peek = element.peek;

            tmpAnimStep = Math.abs(dCents - Math.abs(this.peek.cents)) / mxSteps;

            animStep = tmpAnimStep > 1 ? tmpAnimStep : 1 ;
            }

            if (element.updateTime) {
              dt = element.updateTime;
              mxSteps = dt * totalSteps;
            }
          }
        , enumerable   : false
        , configurable : false
        , writable     : false
        }
      }
    );

    this.background();
  };

  if (!this.instance) {
    this.instance = new CentsGauge(containerID);
  } else {
    console.log("An instance of CentsGauge already exists.");
  }

  return this.instance;

});
<|endoftext|>


<commit_before>/* This file is part of Indico.
 * Copyright (C) 2002 - 2018 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

import {createSelector} from 'reselect';
import {RequestState} from 'indico/utils/redux';
import {selectors as roomsSelectors} from '../rooms';

export function roomSearchSelectorFactory(namespace) {
    const getFilters = ({[namespace]: stateSlice}) => stateSlice.filters;
    const getSearchResultIds = ({[namespace]: stateSlice}) => stateSlice.search.results.rooms;
    const getTotalResultCount = ({[namespace]: stateSlice}) => stateSlice.search.results.total;
    const getAvailabilityDateRange = ({[namespace]: stateSlice}) => stateSlice.search.results.availabilityDays;
    const isSearching = ({[namespace]: stateSlice}) => stateSlice.search.request.state === RequestState.STARTED;
    const isSearchFinished = ({[namespace]: stateSlice}) => stateSlice.search.request.state === RequestState.SUCCESS;
    const hasUnavailableRooms = createSelector(
        getSearchResultIds,
        getTotalResultCount,
        (available, totalCount) => available.length !== totalCount
    );

    const getSearchResults = createSelector(
        getSearchResultIds,
        roomsSelectors.getAllRooms,
        (roomIds, allRooms) => roomIds.map(id => allRooms[id])
    );

    const getSearchResultsForMap = createSelector(
        getSearchResults,
        rooms => rooms.map(room => ({
            id: room.id,
            name: room.full_name,
            lat: room.latitude,
            lng: room.longitude,
        }))
    );

    return {
        getFilters,
        isSearching,
        isSearchFinished,
        getSearchResults,
        getSearchResultIds,
        getTotalResultCount,
        getSearchResultsForMap,
        getAvailabilityDateRange,
        hasUnavailableRooms,
    };
}
<commit_msg>Do not pass rooms without coordinates to map<commit_after>/* This file is part of Indico.
 * Copyright (C) 2002 - 2018 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

import {createSelector} from 'reselect';
import {RequestState} from 'indico/utils/redux';
import {selectors as roomsSelectors} from '../rooms';

export function roomSearchSelectorFactory(namespace) {
    const getFilters = ({[namespace]: stateSlice}) => stateSlice.filters;
    const getSearchResultIds = ({[namespace]: stateSlice}) => stateSlice.search.results.rooms;
    const getTotalResultCount = ({[namespace]: stateSlice}) => stateSlice.search.results.total;
    const getAvailabilityDateRange = ({[namespace]: stateSlice}) => stateSlice.search.results.availabilityDays;
    const isSearching = ({[namespace]: stateSlice}) => stateSlice.search.request.state === RequestState.STARTED;
    const isSearchFinished = ({[namespace]: stateSlice}) => stateSlice.search.request.state === RequestState.SUCCESS;
    const hasUnavailableRooms = createSelector(
        getSearchResultIds,
        getTotalResultCount,
        (available, totalCount) => available.length !== totalCount
    );

    const getSearchResults = createSelector(
        getSearchResultIds,
        roomsSelectors.getAllRooms,
        (roomIds, allRooms) => roomIds.map(id => allRooms[id])
    );

    const getSearchResultsForMap = createSelector(
        getSearchResults,
        rooms => rooms
            .filter(room => room.latitude !== null && room.longitude !== null)
            .map(room => ({
                id: room.id,
                name: room.full_name,
                lat: room.latitude,
                lng: room.longitude,
            }))
    );

    return {
        getFilters,
        isSearching,
        isSearchFinished,
        getSearchResults,
        getSearchResultIds,
        getTotalResultCount,
        getSearchResultsForMap,
        getAvailabilityDateRange,
        hasUnavailableRooms,
    };
}
<|endoftext|>


<commit_before><commit_msg>Get Programming<commit_after><|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

/* global variables used throughout creation phase. */
let userEmail = null;

let userId = 0;

let keywordCampaignId = null;

// number of locations
let locationCount = 1;

/*
 * Submission form only requires 2 decimals, this function enforces that rule
 */
function setTwoNumberDecimal() {
  this.value = parseFloat(this.value).toFixed(2);
}

/*
 * verifyLoginStatus() is ran on page load
 * and obtains the user email associated with the user.
 */
function verifyLoginStatus() {
  fetch('/userapi').then(response => response.json()).then(loginStatus => {
    userEmail = loginStatus.Email;
    userId = loginStatus.id;
    if (!loginStatus.isLoggedIn) {
      window.location.replace("../index.html");
    }
    return loginStatus.isLoggedIn;
  });

  return false;
}

/*
 * This function allows preset data to be saved while the form is being
 * filled out. Alerts the user of the status of their saved preset.
 */
function submitPresetData() {
  let xmlhttp= window.XMLHttpRequest ?
    new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.status === 0) {
      alert("Preset saved!");
    }
    else if ((xmlhttp.status < 200) && (xmlhttp.status >= 400)) {
      alert("Error: Preset cannot be saved. Please try again later.");
    }
  }

  // error handling for the preset nickname prompt. JS prompt sticks until valid name input.
  var presetName;
  while (true) {
    presetName = prompt("What would you like to call the preset?", "");

    // user clicked cancel
    if (presetName == null) {
      return;
    }

    // start error handling.
    if (presetName != "") {
      // TODO: verify that the preset nickname does not already exist.
      break;
    }
    else {
      alert("Preset name is not valid or already exists! Please pick another name.");
    }
  }

  // dynamically build a URI string with form elements
  var keyval_pairs = [];

  // Encode email, user ID, and preset ID into POST URI string.
  keyval_pairs.push(encodeURIComponent("userEmail") + "=" + encodeURIComponent(userEmail));
  keyval_pairs.push(encodeURIComponent("userId") + "=" + encodeURIComponent(userId));
  keyval_pairs.push(encodeURIComponent("presetId") + "=" + encodeURIComponent(presetName));

  var form = document.getElementById('campaign-form'); // get the comment form
  for (var i = 0; i < form.elements.length; i++) {
    // stop preset process if parameter is empty.
    if ((form.elements[i].value === null) || (form.elements[i].value === "")) {
      alert("Not all the settings are filled out!");
      return;
    }
    var curr_element = form.elements[i];
    keyval_pairs.push(encodeURIComponent(curr_element.name) + "=" + encodeURIComponent(curr_element.value));
  }

  // divide each parameter with '&'
  var queryString = keyval_pairs.join("&");

  xmlhttp.open("POST", '/preset', true);
  xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  alert(queryString);
  xmlhttp.send(queryString);
}

/*
 * updatePresetData takes in a presetId and userId and sends a GET request
 * to '/preset'. Once received, the preset data in the comment form is updated
 * with all updated links.
 */
function updatePresetData() {
  // TODO: implement
}

/**
 * sendFormData is called on submission button click and creates a query
 * string appending the parameter values from form. Once query string is
 * built, it is sent to '/DSA-campaigns' to be processed and analyzed. If
 * error occurs at any point during creation phase, then the function terminates.
 */
function sendFormData() {
  let xmlhttp= window.XMLHttpRequest ?
    new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.status === 0) {
      // once form is submitted, redirect to home page.
      window.location.href("../Home/home.html");
    }
    else if ((xmlhttp.status < 200) && (xmlhttp.status >= 400)) {
      alert("Could not submit form, please try again.")
    }
  }

  // dynamically build a URI string with form elements
  var keyval_pairs = [];

  // verify that user is logged in before submission.
  if (userId == 0) {
    alert("Login before submitting!");
    return;
  }
  keyval_pairs.push(encodeURIComponent("userId") + "=" + encodeURIComponent(userId));

  // verify that keywordCampaignId is set.
  if (keywordCampaignId == null) {
    alert("Select a keyword campaign before submitting!");
    return;
  }
  keyval_pairs.push(encodeURIComponent("keywordCampaignId") + "=" + encodeURIComponent(keywordCampaignId));

  // default values for variables (not applicable to creation phase) sent to servlet
  keyval_pairs.push(encodeURIComponent("DSACampaignId") + "=" + encodeURIComponent("0"));
  keyval_pairs.push(encodeURIComponent("campaignStatus") + "=" + encodeURIComponent("pending"));
  keyval_pairs.push(encodeURIComponent("clicks") + "=" + encodeURIComponent("0"));
  keyval_pairs.push(encodeURIComponent("cost") + "=" + encodeURIComponent("0"));
  keyval_pairs.push(encodeURIComponent("impressions") + "=" + encodeURIComponent("0"));

  // represents campaign location - built during runtime
  let location = "";

  var form = document.getElementById('campaign-form'); // get the comment form
  for (var i = 0; i < form.elements.length; i++) {
    // Form contains buttons that are irrelevant to input - need to filter out only input
    if (form.elements[i].nodeName === "BUTTON") {
      continue;
    }

    // stop submission process if parameter is empty.
    if ((form.elements[i].value === null) || (form.elements[i].value === "")) {
      console.log(form.elements[i].nodeName);
      alert("Not all the settings are filled out!");
      return;
    }

    // build location string 'Region, Country' - country occurs in the form first.
    if (form.elements[i].name.includes("region")) {
      location = form.elements[i].value + "," + location;
      keyval_pairs.push(encodeURIComponent("locations") + "=" + encodeURIComponent(location));
    }
    else if (form.elements[i].name.includes("country")) {
      location = form.elements[i].value;
    }
    else {
      console.log(`value ${form.elements[i].name} ${form.elements[i].value}`);
      keyval_pairs.push(encodeURIComponent(form.elements[i].name) + "=" + encodeURIComponent(form.elements[i].value));
    }
  }

  // separate each entity in keyval_pairs with '&' for query string
  var queryString = keyval_pairs.join("&");

  xmlhttp.open("POST", '/DSA-campaigns', true);
  xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  console.log(queryString);
  xmlhttp.send(queryString);
}

/**
 * processes each onchange event associated with the keyword-campaigns
 * select element. If element value is 0 (no selected element) then form
 * does not show. If not 0 (selected element), then form shows.
 */
function keywordSelection() {
  var select_element_value = document.getElementById("keyword-campaigns").value;
  if (select_element_value == 0) {
    document.getElementById("campaign-form").style.display = "none";
    document.getElementById("buttons").style.display = "none";
    keywordCampaignId = null;
  }
  else {
    document.getElementById("campaign-form").style.display = "block";
    document.getElementById("buttons").style.display = "inline";
    keywordCampaignId = select_element_value;
  }
}

// TODO: Fix additional dropdown menu not showing elements.
function addRegion() {
  var tempCount = 1;
  // verify that all existing locations specified before creating new input
  while (tempCount <= locationCount) {
    if (document.getElementById("country" + tempCount).value == "") {
      alert("Specify country " + tempCount + " first!");
      return;
    }
    else if (document.getElementById("gds-cr-" + tempCount).value == "") {
      alert("Specify region " + tempCount + " first!");
      return;
    }
    tempCount++;
  }

  locationCount++;

  // create the location input HTML elements
  var locationDiv = document.createElement('div');
  locationDiv.className = 'form-group';

  var countryLabel = document.createElement('label');
  countryLabel.className = 'control-label';
  countryLabel.innerText = `Country ${locationCount}`;

  var countrySelect = document.createElement('select');
  countrySelect.className = 'form-control gds-cr';
  countrySelect.setAttribute('country-data-region-id',`gds-cr-${locationCount}`);
  countrySelect.setAttribute('id', `country${locationCount}`);
  countrySelect.setAttribute('data-language', 'en');
  countrySelect.setAttribute('name', `country${locationCount}`);

  locationDiv.appendChild(countryLabel);
  locationDiv.appendChild(countrySelect);
  locationDiv.appendChild(document.createElement('br'));

  var regionLabel = document.createElement('label');
  regionLabel.className = 'control-label';
  regionLabel.innerText = `Region ${locationCount}`;
  regionLabel.setAttribute('for', `gds-cr-${locationCount}`);

  var regionSelect = document.createElement('select');
  regionSelect.className = 'form-control';
  regionSelect.setAttribute('id', `gds-cr-${locationCount}`);
  regionSelect.setAttribute('name', `region${locationCount}`);
  locationDiv.appendChild(regionLabel);
  locationDiv.appendChild(regionSelect);
  locationDiv.appendChild(document.createElement('br'));

  var locations = document.getElementById("locations");
  locations.appendChild(locationDiv);
}

// TODO: validate DSA campaign inputs (e.g. campaign status must be "pending" or "complete")<commit_msg>Fix bug with preset settings not being recognized.<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

/* global variables used throughout creation phase. */
let userEmail = null;

let userId = 0;

let keywordCampaignId = null;

// number of locations
let locationCount = 1;

/*
 * Submission form only requires 2 decimals, this function enforces that rule
 */
function setTwoNumberDecimal() {
  this.value = parseFloat(this.value).toFixed(2);
}

/*
 * verifyLoginStatus() is ran on page load
 * and obtains the user email associated with the user.
 */
function verifyLoginStatus() {
  fetch('/userapi').then(response => response.json()).then(loginStatus => {
    userEmail = loginStatus.Email;
    userId = loginStatus.id;
    if (!loginStatus.isLoggedIn) {
      window.location.replace("../index.html");
    }
    return loginStatus.isLoggedIn;
  });

  return false;
}

/*
 * This function allows preset data to be saved while the form is being
 * filled out. Alerts the user of the status of their saved preset.
 */
function submitPresetData() {
  let xmlhttp= window.XMLHttpRequest ?
    new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.status === 0) {
      alert("Preset saved!");
    }
    else if ((xmlhttp.status < 200) && (xmlhttp.status >= 400)) {
      alert("Error: Preset cannot be saved. Please try again later.");
    }
  }

  // error handling for the preset nickname prompt. JS prompt sticks until valid name input.
  var presetName;
  while (true) {
    presetName = prompt("What would you like to call the preset?", "");

    // user clicked cancel
    if (presetName == null) {
      return;
    }

    // start error handling.
    if (presetName != "") {
      // TODO: verify that the preset nickname does not already exist.
      break;
    }
    else {
      alert("Preset name is not valid or already exists! Please pick another name.");
    }
  }

  // dynamically build a URI string with form elements
  var keyval_pairs = [];

  // Encode email, user ID, and preset ID into POST URI string.
  keyval_pairs.push(encodeURIComponent("userEmail") + "=" + encodeURIComponent(userEmail));
  keyval_pairs.push(encodeURIComponent("userId") + "=" + encodeURIComponent(userId));
  keyval_pairs.push(encodeURIComponent("presetId") + "=" + encodeURIComponent(presetName));

  var form = document.getElementById('campaign-form'); // get the comment form
  for (var i = 0; i < form.elements.length; i++) {
    if (form.elements[i].nodeName === 'BUTTON') {
      continue;
    }
    // stop preset process if parameter is empty.
    if ((form.elements[i].value === null) || (form.elements[i].value === "")) {
      alert("Not all the settings are filled out!");
      return;
    }
    var curr_element = form.elements[i];
    keyval_pairs.push(encodeURIComponent(curr_element.name) + "=" + encodeURIComponent(curr_element.value));
  }

  // divide each parameter with '&'
  var queryString = keyval_pairs.join("&");

  xmlhttp.open("POST", '/preset', true);
  xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  alert(queryString);
  xmlhttp.send(queryString);
}

/*
 * updatePresetData takes in a presetId and userId and sends a GET request
 * to '/preset'. Once received, the preset data in the comment form is updated
 * with all updated links.
 */
function updatePresetData() {
  // TODO: implement
}

/**
 * sendFormData is called on submission button click and creates a query
 * string appending the parameter values from form. Once query string is
 * built, it is sent to '/DSA-campaigns' to be processed and analyzed. If
 * error occurs at any point during creation phase, then the function terminates.
 */
function sendFormData() {
  let xmlhttp= window.XMLHttpRequest ?
    new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.status === 0) {
      // once form is submitted, redirect to home page.
      window.location.href("../Home/home.html");
    }
    else if ((xmlhttp.status < 200) && (xmlhttp.status >= 400)) {
      alert("Could not submit form, please try again.")
    }
  }

  // dynamically build a URI string with form elements
  var keyval_pairs = [];

  // verify that user is logged in before submission.
  if (userId == 0) {
    alert("Login before submitting!");
    return;
  }
  keyval_pairs.push(encodeURIComponent("userId") + "=" + encodeURIComponent(userId));

  // verify that keywordCampaignId is set.
  if (keywordCampaignId == null) {
    alert("Select a keyword campaign before submitting!");
    return;
  }
  keyval_pairs.push(encodeURIComponent("keywordCampaignId") + "=" + encodeURIComponent(keywordCampaignId));

  // default values for variables (not applicable to creation phase) sent to servlet
  keyval_pairs.push(encodeURIComponent("DSACampaignId") + "=" + encodeURIComponent("0"));
  keyval_pairs.push(encodeURIComponent("campaignStatus") + "=" + encodeURIComponent("pending"));
  keyval_pairs.push(encodeURIComponent("clicks") + "=" + encodeURIComponent("0"));
  keyval_pairs.push(encodeURIComponent("cost") + "=" + encodeURIComponent("0"));
  keyval_pairs.push(encodeURIComponent("impressions") + "=" + encodeURIComponent("0"));

  // represents campaign location - built during runtime
  let location = "";

  var form = document.getElementById('campaign-form'); // get the comment form
  for (var i = 0; i < form.elements.length; i++) {
    // Form contains buttons that are irrelevant to input - need to filter out only input
    if (form.elements[i].nodeName === "BUTTON") {
      continue;
    }

    // stop submission process if parameter is empty.
    if ((form.elements[i].value === null) || (form.elements[i].value === "")) {
      console.log(form.elements[i].nodeName);
      alert("Not all the settings are filled out!");
      return;
    }

    // build location string 'Region, Country' - country occurs in the form first.
    if (form.elements[i].name.includes("region")) {
      location = form.elements[i].value + "," + location;
      keyval_pairs.push(encodeURIComponent("locations") + "=" + encodeURIComponent(location));
    }
    else if (form.elements[i].name.includes("country")) {
      location = form.elements[i].value;
    }
    else {
      console.log(`value ${form.elements[i].name} ${form.elements[i].value}`);
      keyval_pairs.push(encodeURIComponent(form.elements[i].name) + "=" + encodeURIComponent(form.elements[i].value));
    }
  }

  // separate each entity in keyval_pairs with '&' for query string
  var queryString = keyval_pairs.join("&");

  xmlhttp.open("POST", '/DSA-campaigns', true);
  xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  console.log(queryString);
  xmlhttp.send(queryString);
}

/**
 * processes each onchange event associated with the keyword-campaigns
 * select element. If element value is 0 (no selected element) then form
 * does not show. If not 0 (selected element), then form shows.
 */
function keywordSelection() {
  var select_element_value = document.getElementById("keyword-campaigns").value;
  if (select_element_value == 0) {
    document.getElementById("campaign-form").style.display = "none";
    document.getElementById("buttons").style.display = "none";
    keywordCampaignId = null;
  }
  else {
    document.getElementById("campaign-form").style.display = "block";
    document.getElementById("buttons").style.display = "inline";
    keywordCampaignId = select_element_value;
  }
}

// TODO: Fix additional dropdown menu not showing elements.
function addRegion() {
  var tempCount = 1;
  // verify that all existing locations specified before creating new input
  while (tempCount <= locationCount) {
    if (document.getElementById("country" + tempCount).value == "") {
      alert("Specify country " + tempCount + " first!");
      return;
    }
    else if (document.getElementById("gds-cr-" + tempCount).value == "") {
      alert("Specify region " + tempCount + " first!");
      return;
    }
    tempCount++;
  }

  locationCount++;

  // create the location input HTML elements
  var locationDiv = document.createElement('div');
  locationDiv.className = 'form-group';

  var countryLabel = document.createElement('label');
  countryLabel.className = 'control-label';
  countryLabel.innerText = `Country ${locationCount}`;

  var countrySelect = document.createElement('select');
  countrySelect.className = 'form-control gds-cr';
  countrySelect.setAttribute('country-data-region-id',`gds-cr-${locationCount}`);
  countrySelect.setAttribute('id', `country${locationCount}`);
  countrySelect.setAttribute('data-language', 'en');
  countrySelect.setAttribute('name', `country${locationCount}`);

  locationDiv.appendChild(countryLabel);
  locationDiv.appendChild(countrySelect);
  locationDiv.appendChild(document.createElement('br'));

  var regionLabel = document.createElement('label');
  regionLabel.className = 'control-label';
  regionLabel.innerText = `Region ${locationCount}`;
  regionLabel.setAttribute('for', `gds-cr-${locationCount}`);

  var regionSelect = document.createElement('select');
  regionSelect.className = 'form-control';
  regionSelect.setAttribute('id', `gds-cr-${locationCount}`);
  regionSelect.setAttribute('name', `region${locationCount}`);
  locationDiv.appendChild(regionLabel);
  locationDiv.appendChild(regionSelect);
  locationDiv.appendChild(document.createElement('br'));

  var locations = document.getElementById("locations");
  locations.appendChild(locationDiv);
}

// TODO: validate DSA campaign inputs (e.g. campaign status must be "pending" or "complete")<|endoftext|>


<commit_before>backupApp.directive('sourceFolderPicker', function() {
  return {
    restrict: 'E',
    require: ['ngSources', 'ngFilters'],
    scope: {
        ngSources: '=',
        ngFilters: '=',
        ngShowHidden: '='
    },
    templateUrl: 'templates/sourcefolderpicker.html',

    controller: function($scope, $timeout, SystemInfo, AppService, AppUtils, gettextCatalog) {

        var scope = $scope;
        scope.systeminfo = SystemInfo.watch($scope);
        var sourceNodeChildren = null;

        $scope.treedata = { };

        var sourcemap = {};
        var excludemap = {};
        var defunctmap = {};
        var partialincludemap = {};
        var filterList = null;
        var displayMap = {};

        var dirsep = scope.systeminfo.DirectorySeparator || '/';

        function compareablePath(path) {
            if (path.substr(0, 1) == '%' && path.substr(path.length - 1, 1) == '%')
                path += dirsep;

            return scope.systeminfo.CaseSensitiveFilesystem ? path : path.toLowerCase();
        }

        function setIconCls(n) {
            var cp = compareablePath(n.id);

            if (cp == compareablePath('%MY_DOCUMENTS%'))
                n.iconCls = 'x-tree-icon-mydocuments';
            else if (cp == compareablePath('%MY_MUSIC%'))
                n.iconCls = 'x-tree-icon-mymusic';
            else if (cp == compareablePath('%MY_PICTURES%'))
                n.iconCls = 'x-tree-icon-mypictures';
            else if (cp == compareablePath('%DESKTOP%'))
                n.iconCls = 'x-tree-icon-desktop';
            else if (cp == compareablePath('%HOME%'))
                n.iconCls = 'x-tree-icon-home';
            else if (defunctmap[cp])
                n.iconCls = 'x-tree-icon-broken';
            else if (cp.substr(cp.length - 1, 1) != dirsep)
                n.iconCls = 'x-tree-icon-leaf';
            else if (n.id.substr(0, 8) == "%HYPERV:") {
                n.iconCls = 'x-tree-icon-hypervmachine';
                n.tooltip = gettextCatalog.getString("ID:") + " " + n.id.substring(8, n.id.length - 1);
            }

        }

        function indexOfPathInArray(array, item) {
            item = compareablePath(item);
            for(var ix in array)
                if (compareablePath(array[ix]) == item)
                    return ix;

            return -1;

        }

        function removePathFromArray(array, item) {
            var ix = indexOfPathInArray(array, item);
            if (ix >= 0) {
                array.splice(ix, 1);
                return true;
            }

            return false;
        }

        function traversenodes(m, start) {
            var root = (start || scope.treedata);
            if (!root.children)
                return;

            var work = [];
            for(var v in root.children)
                work.push([root, root.children[v]]);

            while(work.length > 0) {
                var x = work.pop();
                var r = m(x[1], x[0]);

                // false == stop
                if (r === false)
                    return;

                // true == no recurse
                if (r !== true && x[1].children) {
                    for(var v in x[1].children)
                        work.push([x[1], x[1].children[v]]);
                }
            }
        }

        function buildidlookup(sources, map) {
            var dirsep = scope.systeminfo.DirectorySeparator || '/';

            map = map || {};

            for(var n in sources) {
                var parts = compareablePath(n).split(dirsep);
                var p = [];

                for(var pi in parts) {
                    p.push(parts[pi]);
                    var r = p.join(dirsep);
                    var l = r.substr(r.length - 1, 1);
                    if (l != dirsep)
                        r += dirsep;
                    map[r] = true;
                }
            }

            return map;
        }

        function updateIncludeFlags(root, parentFlag) {
            if (root != null)
                root = {children: [root], include: parentFlag};

            traversenodes(function(n, p) {
                if (n.root)
                    return null;

                if (sourcemap[compareablePath(n.id)])
                    n.include = '+';
                else if (p != null && p.include == '+') {
                    if (filterList == null)
                        n.include = excludemap[compareablePath(n.id)] ? '-' : '+';
                    else
                        n.include = AppUtils.evalFilter(n.id, filterList) ? '+' : '-';
                }
                else if (p != null && p.include == '-')
                    n.include = '-';
                else if (partialincludemap[compareablePath(n.id)])
                    n.include = ' ';
                else
                    n.include = null;
            }, root);
        }

        function syncTreeWithLists() {
            if (scope.ngSources == null || sourceNodeChildren == null)
                return;

            dirsep = scope.systeminfo.DirectorySeparator || '/';

            sourcemap = {};
            excludemap = {};
            filterList = null;

            var anySpecials = false;

            for(var i = 0; i < (scope.ngFilters || []).length; i++) {
                var f = AppUtils.splitFilterIntoTypeAndBody(scope.ngFilters[i], dirsep);
                if (f != null) {
                    if (f[0] == '-path')
                        excludemap[compareablePath(f[1])] = true;
                    else if (f[0] == '-folder')
                        excludemap[compareablePath(f[1] + dirsep)] = true;
                    else
                        anySpecials = true;
                }
            }

            if (anySpecials)
                filterList = AppUtils.filterListToRegexps(scope.ngFilters, scope.systeminfo.CaseSensitiveFilesystem);

            sourceNodeChildren.length = 0;

            function findInList(lst, path) {
                for(var x in lst)
                    if (compareablePath(lst[x].id) == path)
                        return x;

                return false;
            }

            for(var i = 0; i < scope.ngSources.length; i++) {
                var k = compareablePath(scope.ngSources[i]);
                if (k.length == 0)
                    continue;

                sourcemap[k] = true;

                var txt = scope.ngSources[i];
                if (k.indexOf('%') == 0) {
                    var nx = k.substr(1).indexOf('%') + 2;
                    if (nx > 1) {
                        var key = compareablePath(k.substr(0, nx));
                        txt = (displayMap[compareablePath(key)] || key) + txt.substr(nx);
                    }
                }

                var n = {
                    text: txt,
                    id: scope.ngSources[i],
                    include: '+',
                    other: true,
                    leaf: true
                };

                setIconCls(n);

                sourceNodeChildren.push(n);

                if (defunctmap[k] == null) {
                    defunctmap[k] = true;

                    var p = scope.ngSources[i];
                    if (p.substr(0, 1) == '%' && p.substr(p.length - 1, 1) == '%')
                        p += dirsep;

                    AppService.post('/filesystem/validate', {path: p}).then(function(data) {
                        defunctmap[compareablePath(data.config.data.path)] = false;

                    }, function(data) {
                        var p = data.config.data.path;
                        var ix = findInList(sources, compareablePath(p));
                        if (ix != null && sources[ix].id == p)
                            sources[ix].iconCls = 'x-tree-icon-broken';
                    });
                }
            }

            partialincludemap = buildidlookup(sourcemap);

            updateIncludeFlags();
        }

        $scope.$watch('ngSources', syncTreeWithLists, true);
        $scope.$watch('ngFilters', syncTreeWithLists, true);

        function findParent(id) {
            var r = {};
            id = compareablePath(id);
            r[id] = true;

            var map = buildidlookup(r);
            var fit = null;

            traversenodes(function(n) {
                if (compareablePath(n.id) == id) {
                    fit = n;
                    return false;
                }

                if (!map[compareablePath(n.id)])
                    return true;
            });

            return fit;
        }

        $scope.toggleCheck = function(node) {
            dirsep = scope.systeminfo.DirectorySeparator || '/';

            var c = compareablePath(node.id);
            var c_is_dir = c.substr(c.length - 1, 1) == dirsep;

            if (node.include == null || node.include == ' ') {

                if (c_is_dir) {
                    for(var i = scope.ngSources.length - 1; i >= 0; i--) {
                        var s = compareablePath(scope.ngSources[i]);

                        if (s == c)
                            return;

                        if (s.substr(s.length - 1, 1) == dirsep && c.indexOf(s) == 0) {
                                return;
                        } else if (s.indexOf(c) == 0) {
                            scope.ngSources.splice(i, 1);
                        }
                    }
                }

                scope.ngSources.push(node.id);

            } else if (node.include == '+') {
                if (sourcemap[c]) {
                    removePathFromArray(scope.ngSources, node.id);

                    for(var i = scope.ngFilters.length - 1; i >= 0; i--) {
                        var n = AppUtils.splitFilterIntoTypeAndBody(scope.ngFilters[i], dirsep);
                        if (n != null) {
                            if (c_is_dir) {
                                if (n[0] == '-path' || n[0] == '-folder') {
                                    if (compareablePath(n[1] + (n[0] == '-folder' ? dirsep : '')).indexOf(c) == 0)
                                        scope.ngFilters.splice(i, 1);
                                }
                            } else {
                                if (n[0] == '-path' && compareablePath(n[1]) == c)
                                    scope.ngFilters.splice(i, 1);
                            }
                        }
                    }
                } else {
                    scope.ngFilters.push("-" + node.id);
                }
            } else if (node.include == '-') {
                removePathFromArray(scope.ngFilters, '-' + node.id);
            }
        };

        $scope.toggleExpanded = function(node) {
            node.expanded = !node.expanded;

            if (node.root || node.iconCls == 'x-tree-icon-leaf' || node.iconCls == 'x-tree-icon-locked'
                || node.iconCls == 'x-tree-icon-hyperv' || node.iconCls == 'x-tree-icon-hypervmachine')
                return;

            if (!node.children && !node.loading) {
                node.loading = true;

                AppService.post('/filesystem?onlyfolders=false&showhidden=true', {path: node.id}).then(function(data) {
                    node.children = data.data;
                    node.loading = false;

                    updateIncludeFlags(node, node.include);

                }, function() {
                    node.loading = false;
                    node.expanded = false;
                    AppUtils.connectionError.apply(AppUtils, arguments);
                });
            }
        };

        $scope.toggleSelected = function(node) {
            if (scope.selectednode != null)
                scope.selectednode.selected = false;

            scope.selectednode = node;
            scope.selectednode.selected = true;
        };

        scope.treedata.children = [];

        AppService.post('/filesystem?onlyfolders=false&showhidden=true', {path: '/'}).then(function(data) {

            var usernode = {
                text: gettextCatalog.getString('User data'),
                root: true,
                iconCls: 'x-tree-icon-userdata',
                expanded: true,
                children: []
            };
            var systemnode = {
                text: gettextCatalog.getString('Computer'),
                root: true,
                iconCls: 'x-tree-icon-computer',
                children: []
            };
            var sourcenode = {
                text: gettextCatalog.getString('Source data'),
                root: true,
                iconCls: 'x-tree-icon-others',
                expanded: true,
                children: [],
                isSourcenode: true
            };

            sourceNodeChildren = sourcenode.children;
            scope.treedata.children.push(usernode, systemnode, sourcenode);

            displayMap = {};

            for(var i = 0; i < data.data.length; i++) {
                if (data.data[i].id.indexOf('%') == 0) {
                    usernode.children.push(data.data[i]);
                    var cp = compareablePath(data.data[i].id);
                    displayMap[cp] = data.data[i].text;

                    setIconCls(data.data[i]);
                }
                else
                    systemnode.children.push(data.data[i]);
            }

            syncTreeWithLists();

        }, AppUtils.connectionError);

        AppService.get('/hyperv', {path: '/'}).then(function(data) {
            if (data.data.length > 0) {
                var hypervnode = {
                    text: gettextCatalog.getString('Hyper-V Machines'),
                    iconCls: 'x-tree-icon-hyperv',
                    id: "%HYPERV:*%",
                    children: []
                };
                var cp = compareablePath(hypervnode.id);
                displayMap[cp] = hypervnode.text;
                defunctmap[cp] = false;

                // add HyperV at the beginning
                if (scope.treedata.children.length < 1)
                    scope.treedata.children.push(hypervnode);
                else
                    scope.treedata.children = [hypervnode].concat(scope.treedata.children);

                for (var i = 0; i < data.data.length; i++) {
                    var node = {
                        iconCls: "x-tree-icon-hypervmachine",
                        leaf: true,
                        id: "%HYPERV:" + data.data[i].id + "%",
                        tooltip: gettextCatalog.getString("ID:") + " " + data.data[i].id,
                        text: data.data[i].name};

                    cp = compareablePath(node.id);
                    displayMap[cp] = data.data[i].name;
                    defunctmap[cp] = false;
                    hypervnode.children.push(node);
                }
            }
        }, AppUtils.connectionError);
    }
  }
});
<commit_msg>skip validation for hyperv<commit_after>backupApp.directive('sourceFolderPicker', function() {
  return {
    restrict: 'E',
    require: ['ngSources', 'ngFilters'],
    scope: {
        ngSources: '=',
        ngFilters: '=',
        ngShowHidden: '='
    },
    templateUrl: 'templates/sourcefolderpicker.html',

    controller: function($scope, $timeout, SystemInfo, AppService, AppUtils, gettextCatalog) {

        var scope = $scope;
        scope.systeminfo = SystemInfo.watch($scope);
        var sourceNodeChildren = null;

        $scope.treedata = { };

        var sourcemap = {};
        var excludemap = {};
        var defunctmap = {};
        var partialincludemap = {};
        var filterList = null;
        var displayMap = {};

        var dirsep = scope.systeminfo.DirectorySeparator || '/';

        function compareablePath(path) {
            if (path.substr(0, 1) == '%' && path.substr(path.length - 1, 1) == '%')
                path += dirsep;

            return scope.systeminfo.CaseSensitiveFilesystem ? path : path.toLowerCase();
        }

        function setIconCls(n) {
            var cp = compareablePath(n.id);

            if (cp == compareablePath('%MY_DOCUMENTS%'))
                n.iconCls = 'x-tree-icon-mydocuments';
            else if (cp == compareablePath('%MY_MUSIC%'))
                n.iconCls = 'x-tree-icon-mymusic';
            else if (cp == compareablePath('%MY_PICTURES%'))
                n.iconCls = 'x-tree-icon-mypictures';
            else if (cp == compareablePath('%DESKTOP%'))
                n.iconCls = 'x-tree-icon-desktop';
            else if (cp == compareablePath('%HOME%'))
                n.iconCls = 'x-tree-icon-home';
            else if (defunctmap[cp])
                n.iconCls = 'x-tree-icon-broken';
            else if (cp.substr(cp.length - 1, 1) != dirsep)
                n.iconCls = 'x-tree-icon-leaf';
            else if (n.id.substr(0, 8) == "%HYPERV:") {
                n.iconCls = 'x-tree-icon-hypervmachine';
                n.tooltip = gettextCatalog.getString("ID:") + " " + n.id.substring(8, n.id.length - 1);
            }

        }

        function indexOfPathInArray(array, item) {
            item = compareablePath(item);
            for(var ix in array)
                if (compareablePath(array[ix]) == item)
                    return ix;

            return -1;

        }

        function removePathFromArray(array, item) {
            var ix = indexOfPathInArray(array, item);
            if (ix >= 0) {
                array.splice(ix, 1);
                return true;
            }

            return false;
        }

        function traversenodes(m, start) {
            var root = (start || scope.treedata);
            if (!root.children)
                return;

            var work = [];
            for(var v in root.children)
                work.push([root, root.children[v]]);

            while(work.length > 0) {
                var x = work.pop();
                var r = m(x[1], x[0]);

                // false == stop
                if (r === false)
                    return;

                // true == no recurse
                if (r !== true && x[1].children) {
                    for(var v in x[1].children)
                        work.push([x[1], x[1].children[v]]);
                }
            }
        }

        function buildidlookup(sources, map) {
            var dirsep = scope.systeminfo.DirectorySeparator || '/';

            map = map || {};

            for(var n in sources) {
                var parts = compareablePath(n).split(dirsep);
                var p = [];

                for(var pi in parts) {
                    p.push(parts[pi]);
                    var r = p.join(dirsep);
                    var l = r.substr(r.length - 1, 1);
                    if (l != dirsep)
                        r += dirsep;
                    map[r] = true;
                }
            }

            return map;
        }

        function updateIncludeFlags(root, parentFlag) {
            if (root != null)
                root = {children: [root], include: parentFlag};

            traversenodes(function(n, p) {
                if (n.root)
                    return null;

                if (sourcemap[compareablePath(n.id)])
                    n.include = '+';
                else if (p != null && p.include == '+') {
                    if (filterList == null)
                        n.include = excludemap[compareablePath(n.id)] ? '-' : '+';
                    else
                        n.include = AppUtils.evalFilter(n.id, filterList) ? '+' : '-';
                }
                else if (p != null && p.include == '-')
                    n.include = '-';
                else if (partialincludemap[compareablePath(n.id)])
                    n.include = ' ';
                else
                    n.include = null;
            }, root);
        }

        function syncTreeWithLists() {
            if (scope.ngSources == null || sourceNodeChildren == null)
                return;

            dirsep = scope.systeminfo.DirectorySeparator || '/';

            sourcemap = {};
            excludemap = {};
            filterList = null;

            var anySpecials = false;

            for(var i = 0; i < (scope.ngFilters || []).length; i++) {
                var f = AppUtils.splitFilterIntoTypeAndBody(scope.ngFilters[i], dirsep);
                if (f != null) {
                    if (f[0] == '-path')
                        excludemap[compareablePath(f[1])] = true;
                    else if (f[0] == '-folder')
                        excludemap[compareablePath(f[1] + dirsep)] = true;
                    else
                        anySpecials = true;
                }
            }

            if (anySpecials)
                filterList = AppUtils.filterListToRegexps(scope.ngFilters, scope.systeminfo.CaseSensitiveFilesystem);

            sourceNodeChildren.length = 0;

            function findInList(lst, path) {
                for(var x in lst)
                    if (compareablePath(lst[x].id) == path)
                        return x;

                return false;
            }

            for(var i = 0; i < scope.ngSources.length; i++) {
                var k = compareablePath(scope.ngSources[i]);
                if (k.length == 0)
                    continue;

                sourcemap[k] = true;

                var txt = scope.ngSources[i];
                if (k.indexOf('%') == 0) {
                    var nx = k.substr(1).indexOf('%') + 2;
                    if (nx > 1) {
                        var key = compareablePath(k.substr(0, nx));
                        txt = (displayMap[compareablePath(key)] || key) + txt.substr(nx);
                    }
                }

                var n = {
                    text: txt,
                    id: scope.ngSources[i],
                    include: '+',
                    other: true,
                    leaf: true
                };

                setIconCls(n);

                sourceNodeChildren.push(n);

                if (defunctmap[k] == null && n.iconCls != "x-tree-icon-hyperv" && n.iconCls != "x-tree-icon-hypervmachine") {
                    defunctmap[k] = true;

                    var p = scope.ngSources[i];
                    if (p.substr(0, 1) == '%' && p.substr(p.length - 1, 1) == '%')
                        p += dirsep;

                    AppService.post('/filesystem/validate', {path: p}).then(function(data) {
                        defunctmap[compareablePath(data.config.data.path)] = false;

                    }, function(data) {
                        var p = data.config.data.path;
                        var ix = findInList(sources, compareablePath(p));
                        if (ix != null && sources[ix].id == p)
                            sources[ix].iconCls = 'x-tree-icon-broken';
                    });
                }
            }

            partialincludemap = buildidlookup(sourcemap);

            updateIncludeFlags();
        }

        $scope.$watch('ngSources', syncTreeWithLists, true);
        $scope.$watch('ngFilters', syncTreeWithLists, true);

        function findParent(id) {
            var r = {};
            id = compareablePath(id);
            r[id] = true;

            var map = buildidlookup(r);
            var fit = null;

            traversenodes(function(n) {
                if (compareablePath(n.id) == id) {
                    fit = n;
                    return false;
                }

                if (!map[compareablePath(n.id)])
                    return true;
            });

            return fit;
        }

        $scope.toggleCheck = function(node) {
            dirsep = scope.systeminfo.DirectorySeparator || '/';

            var c = compareablePath(node.id);
            var c_is_dir = c.substr(c.length - 1, 1) == dirsep;

            if (node.include == null || node.include == ' ') {

                if (c_is_dir) {
                    for(var i = scope.ngSources.length - 1; i >= 0; i--) {
                        var s = compareablePath(scope.ngSources[i]);

                        if (s == c)
                            return;

                        if (s.substr(s.length - 1, 1) == dirsep && c.indexOf(s) == 0) {
                                return;
                        } else if (s.indexOf(c) == 0) {
                            scope.ngSources.splice(i, 1);
                        }
                    }
                }

                scope.ngSources.push(node.id);

            } else if (node.include == '+') {
                if (sourcemap[c]) {
                    removePathFromArray(scope.ngSources, node.id);

                    for(var i = scope.ngFilters.length - 1; i >= 0; i--) {
                        var n = AppUtils.splitFilterIntoTypeAndBody(scope.ngFilters[i], dirsep);
                        if (n != null) {
                            if (c_is_dir) {
                                if (n[0] == '-path' || n[0] == '-folder') {
                                    if (compareablePath(n[1] + (n[0] == '-folder' ? dirsep : '')).indexOf(c) == 0)
                                        scope.ngFilters.splice(i, 1);
                                }
                            } else {
                                if (n[0] == '-path' && compareablePath(n[1]) == c)
                                    scope.ngFilters.splice(i, 1);
                            }
                        }
                    }
                } else {
                    scope.ngFilters.push("-" + node.id);
                }
            } else if (node.include == '-') {
                removePathFromArray(scope.ngFilters, '-' + node.id);
            }
        };

        $scope.toggleExpanded = function(node) {
            node.expanded = !node.expanded;

            if (node.root || node.iconCls == 'x-tree-icon-leaf' || node.iconCls == 'x-tree-icon-locked'
                || node.iconCls == 'x-tree-icon-hyperv' || node.iconCls == 'x-tree-icon-hypervmachine')
                return;

            if (!node.children && !node.loading) {
                node.loading = true;

                AppService.post('/filesystem?onlyfolders=false&showhidden=true', {path: node.id}).then(function(data) {
                    node.children = data.data;
                    node.loading = false;

                    updateIncludeFlags(node, node.include);

                }, function() {
                    node.loading = false;
                    node.expanded = false;
                    AppUtils.connectionError.apply(AppUtils, arguments);
                });
            }
        };

        $scope.toggleSelected = function(node) {
            if (scope.selectednode != null)
                scope.selectednode.selected = false;

            scope.selectednode = node;
            scope.selectednode.selected = true;
        };

        scope.treedata.children = [];

        AppService.post('/filesystem?onlyfolders=false&showhidden=true', {path: '/'}).then(function(data) {

            var usernode = {
                text: gettextCatalog.getString('User data'),
                root: true,
                iconCls: 'x-tree-icon-userdata',
                expanded: true,
                children: []
            };
            var systemnode = {
                text: gettextCatalog.getString('Computer'),
                root: true,
                iconCls: 'x-tree-icon-computer',
                children: []
            };
            var sourcenode = {
                text: gettextCatalog.getString('Source data'),
                root: true,
                iconCls: 'x-tree-icon-others',
                expanded: true,
                children: [],
                isSourcenode: true
            };

            sourceNodeChildren = sourcenode.children;
            scope.treedata.children.push(usernode, systemnode, sourcenode);

            displayMap = {};

            for(var i = 0; i < data.data.length; i++) {
                if (data.data[i].id.indexOf('%') == 0) {
                    usernode.children.push(data.data[i]);
                    var cp = compareablePath(data.data[i].id);
                    displayMap[cp] = data.data[i].text;

                    setIconCls(data.data[i]);
                }
                else
                    systemnode.children.push(data.data[i]);
            }

            syncTreeWithLists();

        }, AppUtils.connectionError);

        AppService.get('/hyperv', {path: '/'}).then(function(data) {
            if (data.data.length > 0) {
                var hypervnode = {
                    text: gettextCatalog.getString('Hyper-V Machines'),
                    iconCls: 'x-tree-icon-hyperv',
                    id: "%HYPERV:*%",
                    children: []
                };
                var cp = compareablePath(hypervnode.id);
                displayMap[cp] = hypervnode.text;

                // add HyperV at the beginning
                if (scope.treedata.children.length < 1)
                    scope.treedata.children.push(hypervnode);
                else
                    scope.treedata.children = [hypervnode].concat(scope.treedata.children);

                for (var i = 0; i < data.data.length; i++) {
                    var node = {
                        iconCls: "x-tree-icon-hypervmachine",
                        leaf: true,
                        id: "%HYPERV:" + data.data[i].id + "%",
                        tooltip: gettextCatalog.getString("ID:") + " " + data.data[i].id,
                        text: data.data[i].name};

                    cp = compareablePath(node.id);
                    displayMap[cp] = data.data[i].name;
                    hypervnode.children.push(node);
                }
            }
        }, AppUtils.connectionError);
    }
  }
});
<|endoftext|>