<commit_before>'use strict';

var store1 = new SalmonStore('1st and Pike', 23, 65, 6.3);
var store2 = new SalmonStore('SeaTac Airport', 3, 24, 1.2);
var store3 = new SalmonStore('Seattle Center', 11, 38, 3.7);
var store4 = new SalmonStore('Capitol Hill', 20, 38, 2.3);
var store5 = new SalmonStore('Alki', 2, 16, 4.6);

var storeList = [store1, store2, store3, store4, store5];
console.log('there are ' + storeList.length + ' stores');

function SalmonStore(storeName, minCust, maxCust, custAv) {
  //var this = {};
  this.name = storeName;
  this.minCust = minCust;
  this.maxCust = maxCust;
  this.custAv = custAv;
  this.hoursOpen = ['6am', '7am', '8am', '9am', '10am', '11am','12pm', '1pm', '2pm','3pm', '4pm', '5pm', '6pm', '7pm', '8pm' ];
  this.cookiesNeeded = [];
  this.totalCookiesNeededForStore = 0;
  //return this;
}

// Practice constructor to verify I know what the hell I am about to try. in console. store1.logStoreName(); works!
SalmonStore.prototype.logStoreName = function() {
  console.log(this.name);
};

//constructor 1
SalmonStore.prototype.randCustPerHour = function() {
  var min = Math.ceil(this.minCust);
  var max = Math.floor(this.maxCust);
  return Math.floor(Math.random() * (max - min)) + min;
};
console.log(store1.randCustPerHour());

//constructor 2
SalmonStore.prototype.cookiesNeededPerHour = function() {
  var needed = this.randCustPerHour() * this.custAv;
  return needed;
};
console.log(store1.cookiesNeededPerHour());

//constructor 3
SalmonStore.prototype.cookiesNeededEveryHour = function() {
  for (var i = 0; i < this.hoursOpen.length; i++) {
    this.cookiesNeeded.push(this.cookiesNeededPerHour());
    //this.push(cookiesNeededPerHour());
  }
  return this.cookiesNeeded;
};
console.log(store1.cookiesNeededEveryHour());

//constructor 4
SalmonStore.prototype.totalCookiesNeeded = function() {
  //console.log(this.cookiesNeeded);
  for (var j = 0; j < this.hoursOpen.length; j++) {
    this.totalCookiesNeededForStore = this.totalCookiesNeededForStore + this.cookiesNeeded[j];
  }
  return this.totalCookiesNeededForStore;
};
//console.log(store1.totalCookiesNeeded());

//constructor 5 --- Do Not reference this one
SalmonStore.prototype.storeCookieProjections = function() {
  var contentArea = document.getElementById('content_area');
  var ul = document.createElement('ul');
  var li;
  this.cookiesNeededEveryHour(); /// should I remove this?
  for (var i = 0; i <= this.hoursOpen.length; i++) {
    li = document.createElement('li');
    li.textContent = this.hoursOpen[i] + ': ' + Math.round(this.cookiesNeeded[i]) + ' cookies';
    ul.appendChild(li);
  }
  li.textContent = 'Total: ' + Math.round(this.totalCookiesNeeded()) + ' cookies';
  ul.appendChild(li);
  contentArea.appendChild(ul);
};

//constructor 6 --- Table Row Data
SalmonStore.prototype.tableRowData = function() {
  var storeTable = document.getElementById('store_table');
  var tableRow = document.createElement('tr');
  var blankTableHeader = document.createElement('th');
  var totalTableHeader = document.createElement('th');
  var hourlyTableHeader;

  tableRow.appendChild(blankTableHeader);

  for (var i = 0; i < store1.hoursOpen.length; i++) {
    this.randCustPerHour();
    this.cookiesNeededPerHour();
    this.cookiesNeededEveryHour();
    //this.totalCookiesNeeded();
    hourlyTableHeader = document.createElement('th');
    hourlyTableHeader.textContent = Math.round(this.cookiesNeeded[i]) ;
    tableRow.appendChild(hourlyTableHeader);
  }
  blankTableHeader.textContent = this.name;
  this.totalCookiesNeeded();
  totalTableHeader.textContent = Math.round(this.totalCookiesNeededForStore);
  tableRow.appendChild(totalTableHeader);

  storeTable.appendChild(tableRow);
};

function renderHeaderRow() {
  var storeTable = document.getElementById('store_table');
  var tableRow = document.createElement('tr');
  var blankTableHeader = document.createElement('th');
  var totalTableHeader = document.createElement('th');
  var hourlyTableHeader;

  tableRow.appendChild(blankTableHeader);

  for (var i = 0; i < store1.hoursOpen.length; i++) {
    hourlyTableHeader = document.createElement('th');
    hourlyTableHeader.textContent = store1.hoursOpen[i];
    tableRow.appendChild(hourlyTableHeader);
  }

  totalTableHeader.textContent = '';
  tableRow.appendChild(totalTableHeader);

  storeTable.appendChild(tableRow);
}

function renderFooterRow() {
  var storeTable = document.getElementById('store_table');
  var tableRow = document.createElement('tr');
  var blankTableHeader = document.createElement('th');
  var totalTableHeader = document.createElement('th');
  var hourlyTableHeader;

  tableRow.appendChild(blankTableHeader);

  for (var i = 0; i < store1.hoursOpen.length; i++) {
    hourlyTableHeader = document.createElement('th');
    hourlyTableHeader.textContent = Math.round(store1.cookiesNeeded[0]);
    tableRow.appendChild(hourlyTableHeader);
  }
  blankTableHeader.textContent = 'Total';
  totalTableHeader.textContent = 'Grand Total Here';
  tableRow.appendChild(totalTableHeader);

  storeTable.appendChild(tableRow);
}

//store1.totalCookiesNeeded();

renderHeaderRow();
for (var i = 0; i < storeList.length; i++) {
  var storeNum = storeList[i];
  storeNum.tableRowData();
}

//store1.tableRowData();
//store2.tableRowData();
//store3.tableRowData();
//store4.tableRowData();
//store5.tableRowData();
renderFooterRow();

/*
//--------Beginning of Sample Code from Erin on Tuesday
function CookieStore(storeName, minCust, maxCust, avgCookies) {
  //var this = {};
  this.name = storeName;
  this.minCust = minCust;
  this.maxCust = maxCust;
  this.avgCookies = avgCookies;
  this.hours = ['9am', '10am', '11am'];

  //return this;
}

CookieStore.prototype.logStoreName = function() {
  console.log(this.name);
}

CookieStore.prototype.toHtml = function() {
  var storeTable = document.getElementById('store_table');
  var tableRow = document.createElement('tr');
  var nameTableHeader = document.createElement('th');
  var totalTableData = document.createElement('td');
  var hourlyTableData;

  nameTableHeader.textContent = this.name;
  tableRow.appendChild(nameTableHeader);

  for (var i = 0; i < this.hours.length; i++) {
    hourlyTableData = document.createElement('td');
    hourlyTableData.textContent = 5;
    tableRow.appendChild(hourlyTableData);
  }

  totalTableData.textContent = 15;
  tableRow.appendChild(totalTableData);

  console.log(tableRow, storeTable)

  storeTable.appendChild(tableRow);
}
/// -- End of Sample Code
*/
<commit_msg>stores dynamic<commit_after>'use strict';

//var store1 = new SalmonStore('1st and Pike', 23, 65, 6.3);
//var store2 = new SalmonStore('SeaTac Airport', 3, 24, 1.2);
//var store3 = new SalmonStore('Seattle Center', 11, 38, 3.7);
//var store4 = new SalmonStore('Capitol Hill', 20, 38, 2.3);
//var store5 = new SalmonStore('Alki', 2, 16, 4.6);

var storeList = [
  new SalmonStore('1st and Pike', 23, 65, 6.3),
  new SalmonStore('SeaTac Airport', 3, 24, 1.2),
  new SalmonStore('Seattle Center', 11, 38, 3.7),
  new SalmonStore('Capitol Hill', 20, 38, 2.3),
  new SalmonStore('Alki', 2, 16, 4.6)
];
console.log('there are ' + storeList.length + ' stores');

function SalmonStore(storeName, minCust, maxCust, custAv) {
  //var this = {};
  this.name = storeName;
  this.minCust = minCust;
  this.maxCust = maxCust;
  this.custAv = custAv;
  this.hoursOpen = ['6am', '7am', '8am', '9am', '10am', '11am','12pm', '1pm', '2pm','3pm', '4pm', '5pm', '6pm', '7pm', '8pm' ];
  this.cookiesNeeded = [];
  this.totalCookiesNeededForStore = 0;
  //return this;
}

// Practice constructor to verify I know what the hell I am about to try. in console. store1.logStoreName(); works!
SalmonStore.prototype.logStoreName = function() {
  console.log(this.name);
};

//constructor 1
SalmonStore.prototype.randCustPerHour = function() {
  var min = Math.ceil(this.minCust);
  var max = Math.floor(this.maxCust);
  return Math.floor(Math.random() * (max - min)) + min;
};
//console.log(storeList[0].randCustPerHour());

//constructor 2
SalmonStore.prototype.cookiesNeededPerHour = function() {
  var needed = this.randCustPerHour() * this.custAv;
  return needed;
};
//console.log(storeList[0].cookiesNeededPerHour());

//constructor 3
SalmonStore.prototype.cookiesNeededEveryHour = function() {
  for (var i = 0; i < this.hoursOpen.length; i++) {
    this.cookiesNeeded.push(this.cookiesNeededPerHour());
    //this.push(cookiesNeededPerHour());
  }
  return this.cookiesNeeded;
};
//console.log(storeList[0].cookiesNeededEveryHour());

//constructor 4
SalmonStore.prototype.totalCookiesNeeded = function() {
  //console.log(this.cookiesNeeded);
  for (var j = 0; j < this.hoursOpen.length; j++) {
    this.totalCookiesNeededForStore = this.totalCookiesNeededForStore + this.cookiesNeeded[j];
  }
  return this.totalCookiesNeededForStore;
};
//console.log(storeList[0].totalCookiesNeeded());

//constructor 5 --- Do Not reference this one
SalmonStore.prototype.storeCookieProjections = function() {
  var contentArea = document.getElementById('content_area');
  var ul = document.createElement('ul');
  var li;
  this.cookiesNeededEveryHour(); /// should I remove this?
  for (var i = 0; i <= this.hoursOpen.length; i++) {
    li = document.createElement('li');
    li.textContent = this.hoursOpen[i] + ': ' + Math.round(this.cookiesNeeded[i]) + ' cookies';
    ul.appendChild(li);
  }
  li.textContent = 'Total: ' + Math.round(this.totalCookiesNeeded()) + ' cookies';
  ul.appendChild(li);
  contentArea.appendChild(ul);
};

//constructor 6 --- Table Row Data
SalmonStore.prototype.tableRowData = function() {
  var storeTable = document.getElementById('store_table');
  var tableRow = document.createElement('tr');
  var blankTableHeader = document.createElement('th');
  var totalTableHeader = document.createElement('th');
  var hourlyTableHeader;

  tableRow.appendChild(blankTableHeader);

  for (var i = 0; i < storeList[0].hoursOpen.length; i++) {
    this.randCustPerHour();
    this.cookiesNeededPerHour();
    this.cookiesNeededEveryHour();
    //this.totalCookiesNeeded();
    hourlyTableHeader = document.createElement('th');
    hourlyTableHeader.textContent = Math.round(this.cookiesNeeded[i]) ;
    tableRow.appendChild(hourlyTableHeader);
  }
  blankTableHeader.textContent = this.name;
  this.totalCookiesNeeded();
  totalTableHeader.textContent = Math.round(this.totalCookiesNeededForStore);
  tableRow.appendChild(totalTableHeader);

  storeTable.appendChild(tableRow);
};

function renderHeaderRow() {
  var storeTable = document.getElementById('store_table');
  var tableRow = document.createElement('tr');
  var blankTableHeader = document.createElement('th');
  var totalTableHeader = document.createElement('th');
  var hourlyTableHeader;

  tableRow.appendChild(blankTableHeader);

  for (var i = 0; i < storeList[0].hoursOpen.length; i++) {
    hourlyTableHeader = document.createElement('th');
    hourlyTableHeader.textContent = storeList[0].hoursOpen[i];
    tableRow.appendChild(hourlyTableHeader);
  }

  totalTableHeader.textContent = '';
  tableRow.appendChild(totalTableHeader);

  storeTable.appendChild(tableRow);
}

function renderFooterRow() {
  var storeTable = document.getElementById('store_table');
  var tableRow = document.createElement('tr');
  var blankTableHeader = document.createElement('th');
  var totalTableHeader = document.createElement('th');
  var hourlyTableHeader;

  tableRow.appendChild(blankTableHeader);

  for (var i = 0; i < storeList[0].hoursOpen.length; i++) {
    hourlyTableHeader = document.createElement('th');
    hourlyTableHeader.textContent = Math.round(storeList[0].cookiesNeeded[0]);
    tableRow.appendChild(hourlyTableHeader);
  }
  blankTableHeader.textContent = 'Total';
  totalTableHeader.textContent = 'Grand Total Here';
  tableRow.appendChild(totalTableHeader);

  storeTable.appendChild(tableRow);
}

//store1.totalCookiesNeeded();

renderHeaderRow();
for (var i = 0; i < storeList.length; i++) {
  //var storeNum = storeList[i];
  //storeNum.tableRowData();
  storeList[i].tableRowData();
}

//store1.tableRowData();
//store2.tableRowData();
//store3.tableRowData();
//store4.tableRowData();
//store5.tableRowData();
renderFooterRow();

/*
//--------Beginning of Sample Code from Erin on Tuesday
function CookieStore(storeName, minCust, maxCust, avgCookies) {
  //var this = {};
  this.name = storeName;
  this.minCust = minCust;
  this.maxCust = maxCust;
  this.avgCookies = avgCookies;
  this.hours = ['9am', '10am', '11am'];

  //return this;
}

CookieStore.prototype.logStoreName = function() {
  console.log(this.name);
}

CookieStore.prototype.toHtml = function() {
  var storeTable = document.getElementById('store_table');
  var tableRow = document.createElement('tr');
  var nameTableHeader = document.createElement('th');
  var totalTableData = document.createElement('td');
  var hourlyTableData;

  nameTableHeader.textContent = this.name;
  tableRow.appendChild(nameTableHeader);

  for (var i = 0; i < this.hours.length; i++) {
    hourlyTableData = document.createElement('td');
    hourlyTableData.textContent = 5;
    tableRow.appendChild(hourlyTableData);
  }

  totalTableData.textContent = 15;
  tableRow.appendChild(totalTableData);

  console.log(tableRow, storeTable)

  storeTable.appendChild(tableRow);
}
/// -- End of Sample Code
*/
<|endoftext|>


<commit_before>annotools.prototype.generateGeoTemplate = function () {

  var case_id = this.iid
  var subject_id = case_id.substr(0,12);
  if(subject_id.substr(0,4) != "TCGA"){
    subject_id = "";
  }

  var execution_id = annotool.execution_id ;

  var geoJSONTemplate = {
    'type': 'Feature',
    'parent_id': 'self',
    'randval': Math.random(),
    'geometry': {
      'type': 'Polygon',
      'coordinates': []
    },
    'normalized': true,
    'object_type': 'annotation', // nucleus?
    'properties': {
      'scalar_features': [],
      'annotations': []
    },
    'footprint': 10000,
    'provenance': {
      'analysis': {
        'execution_id': execution_id,
        'study_id': "",
        'source': "human",
        'computation': 'segmentation'
      },
      'image': {
        'case_id': case_id,
        'subject_id': subject_id
      }
    },
    'date': Date.now()
  }
  return geoJSONTemplate
}

annotools.prototype.convertRectToGeo = function (annotation) {

  var origin = new OpenSeadragon.Point(this.imagingHelper.physicalToDataX(annotation.x), this.imagingHelper.physicalToDataY(annotation.y))
  var max = new OpenSeadragon.Point(this.imagingHelper.physicalToDataX(annotation.x + annotation.w), this.imagingHelper.physicalToDataY(annotation.y + annotation.h))

  /* Compute footprint(area)*/
  var physicalX1 = this.imagingHelper.logicalToPhysicalX(annotation.x);
  var physicalY1 = this.imagingHelper.logicalToPhysicalY(annotation.y);
  var physicalX2 = this.imagingHelper.logicalToPhysicalX(annotation.x + annotation.w);
  var physicalY2 = this.imagingHelper.logicalToPhysicalY(annotation.y + annotation.h);

  var helper = this.imagingHelper;
  var dataX1 = helper.physicalToDataX(physicalX1);
  var dataY1 = helper.physicalToDataY(physicalY1);
  var dataX2 = helper.physicalToDataX(physicalX2);
  var dataY2 = helper.physicalToDataY(physicalY2);

  var area = (dataX2 - dataX1)*(dataY2-dataY1);

  var coordinates = []
  var x = annotation.x
  var y = annotation.y
  var w = annotation.w
  var h = annotation.h
  var geoAnnot = this.generateGeoTemplate()
  coordinates.push([])
  // coordinates[0].push([])
  coordinates[0].push([x, y])
  coordinates[0].push([x + w, y])
  coordinates[0].push([x + w, y + h])
  coordinates[0].push([x, y + h])
  //create a closed loop polygon
  coordinates[0].push([x, y])
  geoAnnot.x = x
  geoAnnot.y = y;
  geoAnnot.footprint = area;
  geoAnnot.geometry.coordinates = coordinates

  return geoAnnot
}

annotools.prototype.convertPencilToGeo = function (annotation) {
  var origin = new OpenSeadragon.Point(this.imagingHelper.physicalToDataX(annotation.x), this.imagingHelper.physicalToDataY(annotation.y))
  var max = new OpenSeadragon.Point(this.imagingHelper.physicalToDataX(annotation.x + annotation.w), this.imagingHelper.physicalToDataY(annotation.y + annotation.h))

  /* Compute footprint(area)*/
  var physicalX1 = this.imagingHelper.logicalToPhysicalX(annotation.x);
  var physicalY1 = this.imagingHelper.logicalToPhysicalY(annotation.y);
  var physicalX2 = this.imagingHelper.logicalToPhysicalX(annotation.x + annotation.w);
  var physicalY2 = this.imagingHelper.logicalToPhysicalY(annotation.y + annotation.h);

  var helper = this.imagingHelper;
  var dataX1 = helper.physicalToDataX(physicalX1);
  var dataY1 = helper.physicalToDataY(physicalY1);
  var dataX2 = helper.physicalToDataX(physicalX2);
  var dataY2 = helper.physicalToDataY(physicalY2);

  var area = Math.abs((dataX2 - dataX1))*Math.abs((dataY2-dataY1));
  console.log(area)

  var points = annotation.points
  var p = points.split(' ')
  var geocoords = []

  for (var i = 0; i < p.length; i++) {
    var pt = p[i].split(',')
    var ptx = +pt[0]
    var pty = +pt[1]
    geocoords.push([ptx, pty])
  }

  var geojson = this.generateGeoTemplate()
  var coordinates = []
  coordinates.push([])
  geojson.geometry = {}
  geojson.geometry.coordinates = [geocoords]
  geojson.footprint = area;
  // set x, y and width and height
  geojson.x = annotation.x
  geojson.y = annotation.y
  // geojson.w = annotation.w
  // geojson.h = annotation.h

  // console.log(geojson)
  return geojson
}

function endProfile (startTime) {
  // nvar t2 = performance.now()
  // console.log(startTime)
  // console.log(t2)
  // console.log("Total time: "+ (t2-startTime))
}

annotools.prototype.generateCanvas = function (annotations) {
  // console.log(annotation)
  // var annotation = annotations[ii]
  var annotations = this.annotations
  if (annotations) {
    var markup_svg = document.getElementById('markups')
    if (markup_svg) {
      // console.log("destroying")
      markup_svg.destroy()
    }
    // console.log(annotations.length)
    // console.log(this.canvas)
    var container = document.getElementsByClassName(this.canvas)[0].childNodes[0] // Get The Canvas Container
    // console.log(container)
    var context = container.getContext('2d')
    context.fillStyle = '#f00'
    // console.log(nativepoints)
    // var container = document.getElementsByClassName(this.cavas)[0]
    // console.log(container)
    var width = parseInt(container.offsetWidth)
    var height = parseInt(container.offsetHeight)
    /* Why is there an ellipse in the center? */
    /*
    var svgHtml = '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + 'px" height="' + height + 'px" version="1.1" id="markups">'
        svgHtml += '<g id="groupcenter"/>'
        svgHtml += '<g id="origin">'
        var origin = viewer.viewport.pixelFromPoint(new OpenSeadragon.Point(.5,.5))
        svgHtml += '<ellipse id="originpt" cx="' + origin.x + '" cy="' + origin.y + '" rx="' + 4 + '" ry="' + 4  + '" style="display: none"/>'
        svgHtml += '</g>'
        svgHtml += '<g id="viewport" transform="translate(0,0)">'
    */
    for (var i = 0; i < annotations.length; i++) {
      var annotation = annotations[i]
      var nativepoints = annotation.geometry.coordinates[0]

      var offset = OpenSeadragon.getElementOffset(viewer.canvas)
      // svgHtml += '<polygon id="'+Math.random()+'" points="'
      // var polySVG = ""

      if (nativepoints.length > 2) {
        context.beginPath()
        var x0 = this.imagingHelper.logicalToPhysicalX(nativepoints[0][0])
        var x1 = this.imagingHelper.logicalToPhysicalY(nativepoints[0][1])
        context.moveTo(x0, x1)
      }
      for (var k = 1; k < nativepoints.length; k++) {
        var px = this.imagingHelper.logicalToPhysicalX(nativepoints[k][0])
        var py = this.imagingHelper.logicalToPhysicalY(nativepoints[k][1])

        context.lineTo(px, py)
      }
      context.strokeStyle = 'blue'
      context.stokeWidth = 6
      context.closePath()
      context.stroke()
    }
  }
}

annotools.prototype.generateSVG = function (annotations) {
  // console.log(annotation)
  // var annotation = annotations[ii]
  var case_id = this.iid;
  var cancerType = this.cancerType ;

  var self =this;
  var annotations = this.annotations
  if (annotations) {
    var markup_svg = document.getElementById('markups')
    if (markup_svg) {
      // console.log("destroying")
      markup_svg.destroy()
    }

    // console.log(annotations.length)
    var container = document.getElementsByClassName(this.canvas)[0] // Get The Canvas Container
    // console.log(nativepoints)
    // var container = document.getElementsByClassName(this.cavas)[0]
    // console.log(container)
    var width = parseInt(container.offsetWidth)
    var height = parseInt(container.offsetHeight)
    /* Why is there an ellipse in the center? */
    var svgHtml = '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + 'px" height="' + height + 'px" version="1.1" id="markups">'
    svgHtml += '<g id="groupcenter"/>'
    svgHtml += '<g id="origin">'
    var origin = viewer.viewport.pixelFromPoint(new OpenSeadragon.Point(.5, .5))
    svgHtml += '<ellipse id="originpt" cx="' + origin.x + '" cy="' + origin.y + '" rx="' + 4 + '" ry="' + 4 + '" style="display: none"/>'
    svgHtml += '</g>'
    svgHtml += '<g id="viewport" transform="translate(0,0)">'

    for (var i = 0; i < annotations.length; i++) {
      var annotation = annotations[i]
      var id = '';

      if (annotation['_id'])
        id = annotation['_id']['$oid']
	    //console.log("inside of generateSVG func.");
        //console.log(id);
		//console.log(annotation);
      var nativepoints = annotation.geometry.coordinates[0]

      // var offset = OpenSeadragon.getElementOffset(viewer.canvas)
      var algorithm_id = annotation.provenance.analysis.execution_id
      var color = algorithm_color[algorithm_id];
	  //use the same color as algorithm does
	  if(annotation.color)
		   color =annotation.color;

	 // var svg =
     if(annotation.provenance.analysis.source=="computer"){
		  svgHtml += '<polygon  class="nucleussvg" id="' + id + '" points="'
	  }else
	  {  svgHtml += '<polygon  class="annotationsvg" id="' + id + '" points="' };

       //svgHtml += '<polygon  class="annotationsvg" id="' + id + '" points="'

      // svgHtml += '<polygon onclick="clickSVG(event)" class="annotationsvg" id="'+"poly"+i+'" points="'
      var polySVG = ''
      for (var k = 0; k < nativepoints.length; k++) {
        var polyPixelX = this.imagingHelper.logicalToPhysicalX(nativepoints[k][0])
        var polyPixelY = this.imagingHelper.logicalToPhysicalY(nativepoints[k][1])
        // svgHtml += nativepoints[k][0] + ',' + nativepoints[k][1] + ' '
        // polySVG += nativepoints[k][0] + ',' + nativepoints[k][1] + ' '
        svgHtml += polyPixelX + ',' + polyPixelY + ' '
      }

      //svgHtml += '" style="fill: transparent; stroke: lime; stroke-width:2.5"/>'
      if(color === undefined)
        color = 'lime'
      svgHtml += '" style="fill:transparent; stroke:'+color+ '; stroke-width:2.5"/>'
    }

    this.svg = new Element('div', {
      styles: {
        position: 'absolute',
        left: 0,
        top: 0,
        width: '100%',
        height: '100%'
      },
      html: svgHtml
    }).inject(container)
  }

  var ctrl = false;
  var alt = false;
  jQuery(document).keydown(function(event){
    //console.log("control");
    //console.log(event);
    if(event.which == 17 || event.which == 91)//Ctrl key and left window key
      ctrl = true;
   else if (event.which == 18 || event.which == 92)//Alt key and right window key
	  alt = true;
  });

  jQuery(document).keyup(function(){
        ctrl = false;
		alt = false;
  });

  jQuery('.annotationsvg').mousedown(function (event) {
        //console.log(event.which);
        if(ctrl){
          //console.log("double clicked");
          event.preventDefault();
          event.stopPropagation();
          event.stopImmediatePropagation();
          //return false;
        } else if (alt){
          //console.log("double clicked");
          event.preventDefault();
          event.stopPropagation();
          event.stopImmediatePropagation();
          //return false;
        }
		else {
          return;
        }

        var panel = jQuery('#panel').show('slide')
        panel.html('');
        jQuery(".annotationsvg").css("opacity", 0.5);
        jQuery("#"+event.target.id).css("opacity", 1);
        var id = event.target.id
        var url = "api/Data/getROI.php?id="+id;
        console.log("id:"+url);
        console.log("id:"+id);
        var content = "

Annotation Details

"
        + "
";

        jQuery.get(url, function(d){
          var data = {}
          try{
            data = JSON.parse(d)[0];
          } catch(e){
            console.log(e);
          }

      // console.log(data);

          var features = [];
          var properties = {};
		      var algorithm="";
		      var coordinates=[];

          try {
            if(data.properties.scalar_features)
              features=  data.properties.scalar_features[0].nv;

            properties = data.properties.annotations;
			algorithm = data.algorithm;
			coordinates= data.geometry.coordinates[0];

          } catch(e){
            console.log(e);
          }

          for(var i in properties){
            if(i == "secret"){

            } else {
              var line = "
"+i+": " + properties[i]+"
";
              content+=line;
            }
          }

          for(var i =0; i< features.length; i++){
            var feature = features[i];
            var line = "

"+feature.name +"

"+feature.value+"

";
            content+=line;
          }

		  if(ctrl)
             content += "<button class='btn-danger btn' id='deleteAnnot'>Delete</button>";
		  else if (alt)
			       content += "<button class='btn-danger btn' id='featureScape'>FeatureScape</button>";

             content += "<button class='btn' id='cancelPanel'>Cancel</button>";
            content +="
";

          var cancel = function () {
            jQuery('#panel').hide('slide')
          }

          panel.html(content);

          jQuery("#cancelPanel").click(function(){cancel();});

        if(ctrl){ // Ctrl key for deletion of human generated annotation
          jQuery("#deleteAnnot").click(function(e) {
            //$("#confirmDelete").css(
            console.log("data.provenance.analysis.source is :"+data.provenance.analysis.source);

            if(data.provenance.analysis.source == "human"){
              jQuery("#confirmDeleteButton").click(function(){
                var secret = jQuery("#deleteSecret").val();
                var payload = {
                  "id": id,
                  "secret": secret
                }

                jQuery.ajax({
                  url: 'api/Data/getPropertiesClone.php?id='+id,
                  type: 'DELETE',
                  data:(payload),
                  success: function(data){
                    console.log(data);
                    jQuery("#panel").hide("slide");
                    self.getMultiAnnot();
                  }
                });
              });
            } else {
              alert("Can't delete computer generated segments");
            }
          })
		 };

		if (alt){  //new code go here to handle Alt key for featureScape view
		   if(data.provenance.analysis.source == "human"){
              jQuery("#featureScape").click(function(){

              var execution_id= algorithm;
			  var this_case_id = case_id;
              var this_cancerType = cancerType ;
			  var x_min= coordinates[0][0];
			  var y_min= coordinates[0][1];
			  var x_max= coordinates[2][0];
			  var y_max= coordinates[2][1];
			  var randval = Math.random();

			  var featureScape_url="";

			  //var github_url="http://sbu-bmi.github.io/FeatureScapeApps/featurescape/?";
			  //var osprey_url="http://osprey.bmi.stonybrook.edu:3000?";

			  var webhost_url="/featurescapeapps/featurescape/?";
			 // var findAPI_host="http://129.49.249.191";
			  //var findAPI_port="4500";
			  //var findAPI_url=findAPI_host+":"+findAPI_port+"?";
			   var findAPI_url=findAPIConfig.findAPI+":"+findAPIConfig.port+"?";

			  var mongodb_query="limit=1000&find={";
			      mongodb_query+="\"provenance.analysis.source\":\"computer\",";
			      mongodb_query+="\"randval\":{\"$gte\":"+randval+"},";
				  mongodb_query+="\"provenance.analysis.execution_id\":\""+execution_id+"\",";
			      mongodb_query+="\"provenance.image.case_id\":\""+this_case_id+"\",";
			      mongodb_query+="\"x\":{\"$gte\":"+x_min+",\"$lte\":"+x_max+"},";
			      mongodb_query+="\"y\":{\"$gte\":"+y_min+",\"$lte\":"+y_max+"}";
			      mongodb_query+="}&db=quip"+"&c="+this_cancerType;

			  //featureScape_url=github_url+osprey_url+mongodb_query;
			  featureScape_url=webhost_url+findAPI_url+mongodb_query;

			  console.log(featureScape_url);
			  window.open(featureScape_url,'_blank');
              jQuery("#panel").hide("slide");
              self.getMultiAnnot();

              });
            } else {
              alert("Can't view the featureScape of computer generated segments");
            }
		 } ;//end of new code

        });

  })

}

annotools.prototype.displayGeoAnnots = function () {
  var geoJSONs = this.annotations
  if (this.annotVisible) {
    // var renderStartTime = performance.now()
    this.generateSVG(geoJSONs)
    // var renderEndTime = performance.now()
    var renderStartTime = 9
    var renderEndTime = 23
  }
}
<commit_msg>Delete geoJSONHandler_sc.js<commit_after><|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */

CLASS({
  package: 'com.google.ow.ui',
  name: 'UpdateStreamView',
  extends: 'foam.u2.View',

  requires: [
    'com.google.ow.model.Envelope',
  ],
  imports: [
    'envelope',
    'streamDAO',
  ],

  properties: [
    {
      model_: 'ArrayProperty',
      name: 'versions',
      lazyFactory: function() { return []; },
    },
  ],

  methods: [
    function init() {
      var substreams = this.envelope.substreams;
      for ( var i = 0; i < substreams.length; ++i ) {
        var substream = substreams[i];
        // TODO(markdittmer): Add date-based ordering and limit(1).
        this.streamDAO.where(EQ(this.Envelope.SID, substream)).select({
          put: function(o) {
            var arr = this.versions.slice();
            arr.push(o);
            this.versions = arr;
          }.bind(this),
        })(nop);
      }
      this.SUPER();
    },
  ],
});
<commit_msg>OW UpdateStreamView: Bug fix: Use select-then-listen, not just select.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */

CLASS({
  package: 'com.google.ow.ui',
  name: 'UpdateStreamView',
  extends: 'foam.u2.View',

  requires: [
    'com.google.ow.model.Envelope',
  ],
  imports: [
    'envelope',
    'streamDAO',
  ],

  properties: [
    {
      model_: 'ArrayProperty',
      name: 'versions',
      lazyFactory: function() { return []; },
    },
  ],

  methods: [
    function init() {
      var substreams = this.envelope.substreams;
      var sink = {
        put: function(o) {
          var arr = this.versions.slice();
          arr.push(o);
          this.versions = arr;
        }.bind(this),
      };
      for ( var i = 0; i < substreams.length; ++i ) {
        var substream = substreams[i];
        var filteredDAO = this.streamDAO.where(EQ(this.Envelope.SID, substream));
        // TODO(markdittmer): Add date-based ordering and limit(1).
        filteredDAO.select(sink)(function() { filteredDAO.listen(sink); });
      }
      this.SUPER();
    },
  ],
});
<|endoftext|>


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

/**
 * A Node that contains a section of text and icons for a KeyboardHelpDialog. Typically multiple KeyboardHelpSecctions
 * are assembled to describe the keyboard interactions for the sim. Takes a heading string for the section label and
 * an array of contents for rows of labels and icons.
 *
 * This type has many static functions for creating and laying out rows of content.
 * Default values for spacing and fonts are also available through statics if necessary.
 *
 * Help sections are aligned with two groups. Text labels are aligned in one VBox, icons are aligned in another. To
 * structure the accessible content, we chose to instrument a11y on the icons in the section. To label content
 * in your own KeyboardHelpSection, instrument the icons not the label Text so a11y content is placed correctly in the DOM.
 * KeyboardHelpSections are generally a list of items, so each icon has a  tagName of 'li' be default.
 *
 * @author Jesse Greenberg
 */

import inherit from '../../../../phet-core/js/inherit.js';
import merge from '../../../../phet-core/js/merge.js';
import StringUtils from '../../../../phetcommon/js/util/StringUtils.js';
import AlignGroup from '../../../../scenery/js/nodes/AlignGroup.js';
import HBox from '../../../../scenery/js/nodes/HBox.js';
import RichText from '../../../../scenery/js/nodes/RichText.js';
import Text from '../../../../scenery/js/nodes/Text.js';
import VBox from '../../../../scenery/js/nodes/VBox.js';
import PhetFont from '../../PhetFont.js';
import sceneryPhetStrings from '../../scenery-phet-strings.js';
import sceneryPhet from '../../sceneryPhet.js';
import EnterKeyNode from '../EnterKeyNode.js';
import LetterKeyNode from '../LetterKeyNode.js';
import SpaceKeyNode from '../SpaceKeyNode.js';
import KeyboardHelpIconFactory from './KeyboardHelpIconFactory.js';

// constants
const keyboardHelpDialogGrabOrReleaseHeadingPatternString = sceneryPhetStrings.keyboardHelpDialog.grabOrReleaseHeadingPattern;
const keyboardHelpDialogGrabOrReleaseLabelPatternString = sceneryPhetStrings.keyboardHelpDialog.grabOrReleaseLabelPattern;
const keyboardHelpDialogOrString = sceneryPhetStrings.keyboardHelpDialog.or;
const grabOrReleaseDescriptionPatternString = sceneryPhetStrings.a11y.keyboardHelpDialog.grabOrReleaseDescriptionPattern;

// heading defaults
const DEFAULT_HEADING_CONTENT_SPACING = 10; // spacing between h
const DEFAULT_HEADING_FONT = new PhetFont( { size: 16, weight: 'bold' } );

// Content spacing and alignment
const DEFAULT_ALIGN = 'left'; // default alignment for the content and title
const DEFAULT_LABEL_ICON_SPACING = 20; // spacing between
const DEFAULT_VERTICAL_ICON_SPACING = 10;

// text fonts and max widths
const LABEL_FONT = new PhetFont( 12 );
const OR_TEXT_MAX_WIDTH = 12;
const DEFAULT_LABEL_MAX_WIDTH = 175;
const DEFAULT_HEADING_MAX_WIDTH = 250;

/**
 * @constructor
 *
 * @param {string} headingString - the translatable label for this content
 * @param {Array.<HelpSectionRow>} content -  icons and labels are each placed in their own VBox, and these layout
 *                                            boxes are aligned horizontally. It is assumed that label and icon have
 *                                            identical bounds so that each row of content can be aligned by
 *                                            KeyboardHelpSection. Static functions in this file use AlignGroup to acheive
 *                                            this. For examples, see labelWithIcon() and labelWithIconList().
 * @param {Object} [options]
 */
function KeyboardHelpSection( headingString, content, options ) {

  options = merge( {

    // heading options
    headingContentSpacing: DEFAULT_HEADING_CONTENT_SPACING,
    headingFont: DEFAULT_HEADING_FONT,
    headingMaxWidth: DEFAULT_HEADING_MAX_WIDTH,

    // {number} The max width for all labels in the KeyboardHelpSection. Used as the base sizing to layout the rest
    // of the KeyboardHelpSection.
    labelMaxWidth: DEFAULT_LABEL_MAX_WIDTH,

    // VBox options
    align: DEFAULT_ALIGN,

    // a11y - tag name for the entire content, usually content is a list of items
    a11yContentTagName: 'ul'
  }, options );

  // create the heading
  const headingText = new Text( headingString, {
    font: options.headingFont,
    maxWidth: options.headingMaxWidth,

    // a11y
    tagName: 'h2',
    innerContent: headingString
  } );

  // place icons in labels in unique layout boxes for alignment
  const icons = [];
  const labels = [];
  for ( let i = 0; i < content.length; i++ ) {
    const helpSectionRow = content[ i ];

    assert && assert( helpSectionRow.text.maxWidth === null, 'KeyboardHelpSection sets maxWidth for children' );
    helpSectionRow.text.maxWidth = options.labelMaxWidth;

    icons.push( helpSectionRow.icon );
    labels.push( helpSectionRow.label );
  }

  const vBoxOptions = { align: 'left', spacing: DEFAULT_VERTICAL_ICON_SPACING };

  // @private - to adjust spacing if necessary for alignment
  this.labelVBox = new VBox( merge( {
    children: labels
  }, vBoxOptions ) );

  // @private - parent for all icons, instance variable to adjust spacing if necessary
  this.iconVBox = new VBox( merge( {
    children: icons,

    // a11y
    tagName: options.a11yContentTagName
  }, vBoxOptions ) );

  // @private - labels and icons horizontally aligned, instance variable to adjust spacing if necessary
  this.contentHBox = new HBox( {
    children: [ this.labelVBox, this.iconVBox ],
    spacing: DEFAULT_LABEL_ICON_SPACING
  } );

  // heading and content aligned in a VBox
  VBox.call( this, {
    children: [ headingText, this.contentHBox ],
    align: options.align,
    spacing: options.headingContentSpacing
  } );
}

sceneryPhet.register( 'KeyboardHelpSection', KeyboardHelpSection );

inherit( VBox, KeyboardHelpSection, {}, {

  /**
   * Horizontally align a label and an icon, with the label on the left and the icon on the right. AlignGroup is used
   * to give the label and icon identical dimensions for easy layout in KeyboardHelpSection.
   * @public
   * @static
   *
   * @param {string} labelString - string for the label Text
   * @param {Node} icon
   * @param {string} [labelInnerContent] - required to have the PDOM description of this row in the dialog
   * @param {Object} [options]
   * @returns {HelpSectionRow} - so KeyboardHelpSection can layout content groups
   */
  labelWithIcon: function( labelString, icon, labelInnerContent, options ) {
    assert && assert( typeof labelString === 'string', 'labelWithIcon creates Text label from string.' );

    options = merge( {

      // options passed for layout, passed to AlignGroup
      spacing: DEFAULT_LABEL_ICON_SPACING,
      align: 'center',
      matchHorizontal: false,

      // options for the AlignBox surrounding the icon
      iconOptions: {}
    }, options );
    assert && assert( !options.children, 'children are not optional' );

    const labelText = new RichText( labelString, { font: LABEL_FONT } );

    if ( labelInnerContent ) {
      assert && assert( !options.iconOptions.innerContent, 'should be specified as an argument' );
      options.iconOptions = merge( {
        tagName: 'li',
        innerContent: labelInnerContent
      }, options.iconOptions );
    }

    // make the label and icon the same height so that they will align when we assemble help section group
    const labelIconGroup = new AlignGroup( options );
    const labelBox = labelIconGroup.createBox( labelText );
    const iconBox = labelIconGroup.createBox( icon, options.iconOptions );

    return new HelpSectionRow( labelText, labelBox, iconBox );
  },

  /**
   * Create a label with a list of icons. The icons will be vertically aligned, each separated by 'or' text. The
   * label will be vertically centered with the first item in the list of icons. To vertically align the label
   * with the first icon, AlignGroup is used. Finally, an AlignGroup is used to make the label
   * content match height with the entire icon list. When assembled, the label with icon list will look like:
   *
   * This is the label: Icon1 or
   *                    Icon2 or
   *                    Icon3
   *
   * @param {string} labelString - string for the visible label RichText
   * @param {Node[]} icons
   * @param {string} labelInnerContent - content for the parallel DOM, read by a screen reader
   * @param {Object} [options] - cannot pass in children
   *
   * @returns {HelpSectionRow} -  so KeyboardHelpSection can layout content groups
   */
  labelWithIconList: function( labelString, icons, labelInnerContent, options ) {
    assert && assert( typeof labelString === 'string', 'labelWithIcon creates Text label from string.' );

    options = merge( {
      iconsVBoxOptions: {} // options for the iconsVBox, extended below
    }, options );
    assert && assert( !options.children, 'labelWithIconList adds its own children' );

    assert && assert( !options.iconsVBoxOptions.innerContent, 'should be specified as an argument' );
    options.iconsVBoxOptions = merge( {
      spacing: DEFAULT_VERTICAL_ICON_SPACING * .75, // less than the normal vertical icon spacing since it is a group
      align: 'left',
      tagName: 'li',
      innerContent: labelInnerContent
    }, options.iconsVBoxOptions );

    const labelText = new RichText( labelString, { font: LABEL_FONT } );

    // horizontally align the label with the first item in the list of icons, guarantees that the label and first
    // icon have identical heights
    const labelFirstIconGroup = new AlignGroup( { matchHorizontal: false } );
    labelFirstIconGroup.createBox( icons[ 0 ] ); // create the box to restrain bounds, but a reference isn't necessary
    const labelBox = labelFirstIconGroup.createBox( labelText );

    // for each of the icons (excluding the last one, add a vertically aligned 'or' text to the right
    const iconsWithOrText = [];
    for ( let i = 0; i < icons.length - 1; i++ ) {
      const orText = new Text( keyboardHelpDialogOrString, {
        font: LABEL_FONT,
        maxWidth: OR_TEXT_MAX_WIDTH
      } );

      // place orText with the icon in an HBox
      iconsWithOrText.push( new HBox( {
        children: [ icons[ i ], orText ],
        spacing: KeyboardHelpIconFactory.DEFAULT_ICON_SPACING
      } ) );
    }
    iconsWithOrText.push( icons[ icons.length - 1 ] );

    // place icons in a VBox, passing through optional spacing and a11y representation
    const iconsVBox = new VBox( merge( {
      children: iconsWithOrText
    }, options.iconsVBoxOptions ) );

    // make the label the same height as the icon list by aligning them in a box that matches height
    const groupOptions = { yAlign: 'top' };
    const labelIconListGroup = new AlignGroup( { matchHorizontal: false } );
    const iconsBox = labelIconListGroup.createBox( iconsVBox, groupOptions ); // create the box to match height, but reference not necessary
    const labelWithHeightBox = labelIconListGroup.createBox( labelBox, groupOptions );

    return new HelpSectionRow( labelText, labelWithHeightBox, iconsBox );
  },

  /**
   * Create an entry for the dialog that looks horizontally aligns a letter key with a 'J' key separated by a plus
   * sign, with a descriptive label. Something like:   * "J + S jumps close to sweater"
   *
   * @param {string} keyString - the letter name that will come after 'J', note this can be hard coded, no need for i18n.
   * @param {string} labelString - visual label
   * @param {string} labelInnerContent - PDOM description
   * @returns {HBox}
   */
  createJumpKeyRow: function( keyString, labelString, labelInnerContent ) {

    // Not translated because it maps directly to a specific key code.
    const jKey = new LetterKeyNode( 'J' );
    const otherKey = new LetterKeyNode( keyString );

    const jPlusOtherKey = KeyboardHelpIconFactory.iconPlusIcon( jKey, otherKey );
    return KeyboardHelpSection.labelWithIcon( labelString, jPlusOtherKey, labelInnerContent );
  },

  /**
   * Vertically align icons for a number of different KeyboardHelpSections. Useful when two KeyboardHelpSection
   * sections are stacked vertically in a Dialog. Loops through sectionArray and finds the max x value of the left
   * edge of the icon VBox. Then increases spacing of all other content HBoxes accordingly.
   *
   * @param {KeyboardHelpSection[]} sectionArray
   */
  alignHelpSectionIcons: function( sectionArray ) {

    // left edge of icons farthest to the right in the array of KeyboardHelpSection
    const maxLeftEdge = _.maxBy( sectionArray, function( section ) { return section.iconVBox.left; } ).iconVBox.left;

    // adjust the spacing of all section HBoxes so that they align
    sectionArray.forEach( function( section ) {
      section.contentHBox.spacing = section.contentHBox.spacing + maxLeftEdge - section.iconVBox.left;
    } );
  },

  /**
   * Convenience method to construct a KeyboardHelpSection for describing the grab button interaction
   * @param {string} thingAsTitle - the item being grabbed, capitalized as a title
   * @param {string} thingAsLowerCase - the item being grabbed, lower case as used in a sentence.
   * @param {Object} [options]
   * @static
   * @returns {KeyboardHelpSection}
   */
  getGrabReleaseHelpSection: function( thingAsTitle, thingAsLowerCase, options ) {

    options = merge( {

      // just a paragraph for this section, no list
      a11yContentTagName: null
    }, options );

    // the visible heading string
    const heading = StringUtils.fillIn( keyboardHelpDialogGrabOrReleaseHeadingPatternString, {
      thing: thingAsTitle
    } );

    // the visible label string
    const labelString = StringUtils.fillIn( keyboardHelpDialogGrabOrReleaseLabelPatternString, {
      thing: thingAsLowerCase
    } );

    // the string for the PDOM
    const descriptionString = StringUtils.fillIn( grabOrReleaseDescriptionPatternString, {
      thing: thingAsLowerCase
    } );

    const spaceKeyNode = new SpaceKeyNode();
    const enterKeyNode = new EnterKeyNode();
    const icons = KeyboardHelpIconFactory.iconOrIcon( spaceKeyNode, enterKeyNode );
    const labelWithContentRow = KeyboardHelpSection.labelWithIcon( labelString, icons, descriptionString, {
      iconOptions: {
        tagName: 'p' // it is the only item so it is a p rather than an li
      }
    } );

    return new KeyboardHelpSection( heading, [ labelWithContentRow ], options );
  }
} );

/**
 * A row of KeyboardHelpSection, containing the label, icon, and text. Many of the static functions of KeyboardHelpSection
 * will return a HelpSectionRow. The label and icon are often grouped in an AlignGroup for easy positioning
 * in KeyboardHelpSection. This cannot be done in KeyboardHelpSection directly because different labels and icons will have
 * varying layout. For instance, see labelWithIcon vs labelWithIconList.
 *
 * Includes a reference to the Text because KeyboardHelpSection will constrain the width of all text in its
 * HelpSectionRows for i18n.
 */
class HelpSectionRow {

  /**
   * @param {Text|RichText} text - must be a child of the "label" Node, KeyboardHelpSection
   * @param {Node} label
   * @param {Node} icon
   */
  constructor( text, label, icon ) {
    assert && assert( text instanceof Text || text instanceof RichText, 'unsupported label type: ' + text );

    // @public (read-only)
    this.label = label;
    this.icon = icon;
    this.text = text;
  }
}

export default KeyboardHelpSection;<commit_msg>make labelInnerContent a required parameter, https://github.com/phetsims/scenery-phet/issues/590<commit_after>// Copyright 2017-2020, University of Colorado Boulder

/**
 * A Node that contains a section of text and icons for a KeyboardHelpDialog. Typically multiple KeyboardHelpSecctions
 * are assembled to describe the keyboard interactions for the sim. Takes a heading string for the section label and
 * an array of contents for rows of labels and icons.
 *
 * This type has many static functions for creating and laying out rows of content.
 * Default values for spacing and fonts are also available through statics if necessary.
 *
 * Help sections are aligned with two groups. Text labels are aligned in one VBox, icons are aligned in another. To
 * structure the accessible content, we chose to instrument a11y on the icons in the section. To label content
 * in your own KeyboardHelpSection, instrument the icons not the label Text so a11y content is placed correctly in the DOM.
 * KeyboardHelpSections are generally a list of items, so each icon has a  tagName of 'li' be default.
 *
 * @author Jesse Greenberg
 */

import inherit from '../../../../phet-core/js/inherit.js';
import merge from '../../../../phet-core/js/merge.js';
import StringUtils from '../../../../phetcommon/js/util/StringUtils.js';
import AlignGroup from '../../../../scenery/js/nodes/AlignGroup.js';
import HBox from '../../../../scenery/js/nodes/HBox.js';
import RichText from '../../../../scenery/js/nodes/RichText.js';
import Text from '../../../../scenery/js/nodes/Text.js';
import VBox from '../../../../scenery/js/nodes/VBox.js';
import PhetFont from '../../PhetFont.js';
import sceneryPhetStrings from '../../scenery-phet-strings.js';
import sceneryPhet from '../../sceneryPhet.js';
import EnterKeyNode from '../EnterKeyNode.js';
import LetterKeyNode from '../LetterKeyNode.js';
import SpaceKeyNode from '../SpaceKeyNode.js';
import KeyboardHelpIconFactory from './KeyboardHelpIconFactory.js';

// constants
const keyboardHelpDialogGrabOrReleaseHeadingPatternString = sceneryPhetStrings.keyboardHelpDialog.grabOrReleaseHeadingPattern;
const keyboardHelpDialogGrabOrReleaseLabelPatternString = sceneryPhetStrings.keyboardHelpDialog.grabOrReleaseLabelPattern;
const keyboardHelpDialogOrString = sceneryPhetStrings.keyboardHelpDialog.or;
const grabOrReleaseDescriptionPatternString = sceneryPhetStrings.a11y.keyboardHelpDialog.grabOrReleaseDescriptionPattern;

// heading defaults
const DEFAULT_HEADING_CONTENT_SPACING = 10; // spacing between h
const DEFAULT_HEADING_FONT = new PhetFont( { size: 16, weight: 'bold' } );

// Content spacing and alignment
const DEFAULT_ALIGN = 'left'; // default alignment for the content and title
const DEFAULT_LABEL_ICON_SPACING = 20; // spacing between
const DEFAULT_VERTICAL_ICON_SPACING = 10;

// text fonts and max widths
const LABEL_FONT = new PhetFont( 12 );
const OR_TEXT_MAX_WIDTH = 12;
const DEFAULT_LABEL_MAX_WIDTH = 175;
const DEFAULT_HEADING_MAX_WIDTH = 250;

/**
 * @constructor
 *
 * @param {string} headingString - the translatable label for this content
 * @param {Array.<HelpSectionRow>} content -  icons and labels are each placed in their own VBox, and these layout
 *                                            boxes are aligned horizontally. It is assumed that label and icon have
 *                                            identical bounds so that each row of content can be aligned by
 *                                            KeyboardHelpSection. Static functions in this file use AlignGroup to acheive
 *                                            this. For examples, see labelWithIcon() and labelWithIconList().
 * @param {Object} [options]
 */
function KeyboardHelpSection( headingString, content, options ) {

  options = merge( {

    // heading options
    headingContentSpacing: DEFAULT_HEADING_CONTENT_SPACING,
    headingFont: DEFAULT_HEADING_FONT,
    headingMaxWidth: DEFAULT_HEADING_MAX_WIDTH,

    // {number} The max width for all labels in the KeyboardHelpSection. Used as the base sizing to layout the rest
    // of the KeyboardHelpSection.
    labelMaxWidth: DEFAULT_LABEL_MAX_WIDTH,

    // VBox options
    align: DEFAULT_ALIGN,

    // a11y - tag name for the entire content, usually content is a list of items
    a11yContentTagName: 'ul'
  }, options );

  // create the heading
  const headingText = new Text( headingString, {
    font: options.headingFont,
    maxWidth: options.headingMaxWidth,

    // a11y
    tagName: 'h2',
    innerContent: headingString
  } );

  // place icons in labels in unique layout boxes for alignment
  const icons = [];
  const labels = [];
  for ( let i = 0; i < content.length; i++ ) {
    const helpSectionRow = content[ i ];

    assert && assert( helpSectionRow.text.maxWidth === null, 'KeyboardHelpSection sets maxWidth for children' );
    helpSectionRow.text.maxWidth = options.labelMaxWidth;

    icons.push( helpSectionRow.icon );
    labels.push( helpSectionRow.label );
  }

  const vBoxOptions = { align: 'left', spacing: DEFAULT_VERTICAL_ICON_SPACING };

  // @private - to adjust spacing if necessary for alignment
  this.labelVBox = new VBox( merge( {
    children: labels
  }, vBoxOptions ) );

  // @private - parent for all icons, instance variable to adjust spacing if necessary
  this.iconVBox = new VBox( merge( {
    children: icons,

    // a11y
    tagName: options.a11yContentTagName
  }, vBoxOptions ) );

  // @private - labels and icons horizontally aligned, instance variable to adjust spacing if necessary
  this.contentHBox = new HBox( {
    children: [ this.labelVBox, this.iconVBox ],
    spacing: DEFAULT_LABEL_ICON_SPACING
  } );

  // heading and content aligned in a VBox
  VBox.call( this, {
    children: [ headingText, this.contentHBox ],
    align: options.align,
    spacing: options.headingContentSpacing
  } );
}

sceneryPhet.register( 'KeyboardHelpSection', KeyboardHelpSection );

inherit( VBox, KeyboardHelpSection, {}, {

  /**
   * Horizontally align a label and an icon, with the label on the left and the icon on the right. AlignGroup is used
   * to give the label and icon identical dimensions for easy layout in KeyboardHelpSection.
   * @public
   * @static
   *
   * @param {string} labelString - string for the label Text
   * @param {Node} icon
   * @param {string} labelInnerContent - required to have the PDOM description of this row in the dialog
   * @param {Object} [options]
   * @returns {HelpSectionRow} - so KeyboardHelpSection can layout content groups
   */
  labelWithIcon: function( labelString, icon, labelInnerContent, options ) {
    assert && assert( typeof labelString === 'string', 'labelWithIcon creates Text label from string.' );
    assert && assert( typeof labelInnerContent === 'string', 'labelInnerContent should be a string.' );

    options = merge( {

      // options passed for layout, passed to AlignGroup
      spacing: DEFAULT_LABEL_ICON_SPACING,
      align: 'center',
      matchHorizontal: false,

      // options for the AlignBox surrounding the icon
      iconOptions: {
        tagName: 'li'
      }
    }, options );
    assert && assert( !options.children, 'children are not optional' );
    assert && assert( !options.iconOptions.innerContent, 'should be specified as an parameter, see labelInnerContent' );

    options.iconOptions.innerContent = labelInnerContent;

    const labelText = new RichText( labelString, { font: LABEL_FONT } );

    // make the label and icon the same height so that they will align when we assemble help section group
    const labelIconGroup = new AlignGroup( options );
    const labelBox = labelIconGroup.createBox( labelText );
    const iconBox = labelIconGroup.createBox( icon, options.iconOptions );

    return new HelpSectionRow( labelText, labelBox, iconBox );
  },

  /**
   * Create a label with a list of icons. The icons will be vertically aligned, each separated by 'or' text. The
   * label will be vertically centered with the first item in the list of icons. To vertically align the label
   * with the first icon, AlignGroup is used. Finally, an AlignGroup is used to make the label
   * content match height with the entire icon list. When assembled, the label with icon list will look like:
   *
   * This is the label: Icon1 or
   *                    Icon2 or
   *                    Icon3
   *
   * @param {string} labelString - string for the visible label RichText
   * @param {Node[]} icons
   * @param {string} labelInnerContent - content for the parallel DOM, read by a screen reader
   * @param {Object} [options] - cannot pass in children
   *
   * @returns {HelpSectionRow} -  so KeyboardHelpSection can layout content groups
   */
  labelWithIconList: function( labelString, icons, labelInnerContent, options ) {
    assert && assert( typeof labelString === 'string', 'labelWithIcon creates Text label from string.' );

    options = merge( {
      iconsVBoxOptions: {} // options for the iconsVBox, extended below
    }, options );
    assert && assert( !options.children, 'labelWithIconList adds its own children' );

    assert && assert( !options.iconsVBoxOptions.innerContent, 'should be specified as an argument' );
    options.iconsVBoxOptions = merge( {
      spacing: DEFAULT_VERTICAL_ICON_SPACING * .75, // less than the normal vertical icon spacing since it is a group
      align: 'left',
      tagName: 'li',
      innerContent: labelInnerContent
    }, options.iconsVBoxOptions );

    const labelText = new RichText( labelString, { font: LABEL_FONT } );

    // horizontally align the label with the first item in the list of icons, guarantees that the label and first
    // icon have identical heights
    const labelFirstIconGroup = new AlignGroup( { matchHorizontal: false } );
    labelFirstIconGroup.createBox( icons[ 0 ] ); // create the box to restrain bounds, but a reference isn't necessary
    const labelBox = labelFirstIconGroup.createBox( labelText );

    // for each of the icons (excluding the last one, add a vertically aligned 'or' text to the right
    const iconsWithOrText = [];
    for ( let i = 0; i < icons.length - 1; i++ ) {
      const orText = new Text( keyboardHelpDialogOrString, {
        font: LABEL_FONT,
        maxWidth: OR_TEXT_MAX_WIDTH
      } );

      // place orText with the icon in an HBox
      iconsWithOrText.push( new HBox( {
        children: [ icons[ i ], orText ],
        spacing: KeyboardHelpIconFactory.DEFAULT_ICON_SPACING
      } ) );
    }
    iconsWithOrText.push( icons[ icons.length - 1 ] );

    // place icons in a VBox, passing through optional spacing and a11y representation
    const iconsVBox = new VBox( merge( {
      children: iconsWithOrText
    }, options.iconsVBoxOptions ) );

    // make the label the same height as the icon list by aligning them in a box that matches height
    const groupOptions = { yAlign: 'top' };
    const labelIconListGroup = new AlignGroup( { matchHorizontal: false } );
    const iconsBox = labelIconListGroup.createBox( iconsVBox, groupOptions ); // create the box to match height, but reference not necessary
    const labelWithHeightBox = labelIconListGroup.createBox( labelBox, groupOptions );

    return new HelpSectionRow( labelText, labelWithHeightBox, iconsBox );
  },

  /**
   * Create an entry for the dialog that looks horizontally aligns a letter key with a 'J' key separated by a plus
   * sign, with a descriptive label. Something like:   * "J + S jumps close to sweater"
   *
   * @param {string} keyString - the letter name that will come after 'J', note this can be hard coded, no need for i18n.
   * @param {string} labelString - visual label
   * @param {string} labelInnerContent - PDOM description
   * @returns {HBox}
   */
  createJumpKeyRow: function( keyString, labelString, labelInnerContent ) {

    // Not translated because it maps directly to a specific key code.
    const jKey = new LetterKeyNode( 'J' );
    const otherKey = new LetterKeyNode( keyString );

    const jPlusOtherKey = KeyboardHelpIconFactory.iconPlusIcon( jKey, otherKey );
    return KeyboardHelpSection.labelWithIcon( labelString, jPlusOtherKey, labelInnerContent );
  },

  /**
   * Vertically align icons for a number of different KeyboardHelpSections. Useful when two KeyboardHelpSection
   * sections are stacked vertically in a Dialog. Loops through sectionArray and finds the max x value of the left
   * edge of the icon VBox. Then increases spacing of all other content HBoxes accordingly.
   *
   * @param {KeyboardHelpSection[]} sectionArray
   */
  alignHelpSectionIcons: function( sectionArray ) {

    // left edge of icons farthest to the right in the array of KeyboardHelpSection
    const maxLeftEdge = _.maxBy( sectionArray, function( section ) { return section.iconVBox.left; } ).iconVBox.left;

    // adjust the spacing of all section HBoxes so that they align
    sectionArray.forEach( function( section ) {
      section.contentHBox.spacing = section.contentHBox.spacing + maxLeftEdge - section.iconVBox.left;
    } );
  },

  /**
   * Convenience method to construct a KeyboardHelpSection for describing the grab button interaction
   * @param {string} thingAsTitle - the item being grabbed, capitalized as a title
   * @param {string} thingAsLowerCase - the item being grabbed, lower case as used in a sentence.
   * @param {Object} [options]
   * @static
   * @returns {KeyboardHelpSection}
   */
  getGrabReleaseHelpSection: function( thingAsTitle, thingAsLowerCase, options ) {

    options = merge( {

      // just a paragraph for this section, no list
      a11yContentTagName: null
    }, options );

    // the visible heading string
    const heading = StringUtils.fillIn( keyboardHelpDialogGrabOrReleaseHeadingPatternString, {
      thing: thingAsTitle
    } );

    // the visible label string
    const labelString = StringUtils.fillIn( keyboardHelpDialogGrabOrReleaseLabelPatternString, {
      thing: thingAsLowerCase
    } );

    // the string for the PDOM
    const descriptionString = StringUtils.fillIn( grabOrReleaseDescriptionPatternString, {
      thing: thingAsLowerCase
    } );

    const spaceKeyNode = new SpaceKeyNode();
    const enterKeyNode = new EnterKeyNode();
    const icons = KeyboardHelpIconFactory.iconOrIcon( spaceKeyNode, enterKeyNode );
    const labelWithContentRow = KeyboardHelpSection.labelWithIcon( labelString, icons, descriptionString, {
      iconOptions: {
        tagName: 'p' // it is the only item so it is a p rather than an li
      }
    } );

    return new KeyboardHelpSection( heading, [ labelWithContentRow ], options );
  }
} );

/**
 * A row of KeyboardHelpSection, containing the label, icon, and text. Many of the static functions of KeyboardHelpSection
 * will return a HelpSectionRow. The label and icon are often grouped in an AlignGroup for easy positioning
 * in KeyboardHelpSection. This cannot be done in KeyboardHelpSection directly because different labels and icons will have
 * varying layout. For instance, see labelWithIcon vs labelWithIconList.
 *
 * Includes a reference to the Text because KeyboardHelpSection will constrain the width of all text in its
 * HelpSectionRows for i18n.
 */
class HelpSectionRow {

  /**
   * @param {Text|RichText} text - must be a child of the "label" Node, KeyboardHelpSection
   * @param {Node} label
   * @param {Node} icon
   */
  constructor( text, label, icon ) {
    assert && assert( text instanceof Text || text instanceof RichText, 'unsupported label type: ' + text );

    // @public (read-only)
    this.label = label;
    this.icon = icon;
    this.text = text;
  }
}

export default KeyboardHelpSection;<|endoftext|>


<commit_before>/*PathNode.js
 * path object
 * extends GeometryNode
 * node with actual path in it
 */

define([
  'underscore',
  'models/data/geometry/GeometryNode',
  'models/data/geometry/PointNode',
  'utils/TrigFunc',
  'models/data/properties/PPoint',
  'paper',
  'models/data/properties/PFloat',
  'models/data/properties/PColor'

], function(_, GeometryNode, PointNode, TrigFunc, PPoint, paper, PFloat, PColor) {
  //drawable paper.js path object that is stored in the pathnode
  var PathNode = GeometryNode.extend({

    defaults: _.extend({}, GeometryNode.prototype.defaults, {

      name: 'path',
      type: 'geometry',
      points: null,
    }),

    initialize: function() {
      GeometryNode.prototype.initialize.apply(this, arguments);
      this.set('points', []);
      this.pointsModified = false;
    },

    create: function() {
      var instance = GeometryNode.prototype.create.apply(this, arguments);
      // instance.generatePoints(instance.get('geom'));
      return instance;
    },

    deleteSelf: function() {

      GeometryNode.prototype.deleteSelf.apply(this, arguments);
    },

    toJSON: function(noUndoCache) {
      var data = GeometryNode.prototype.toJSON.call(this, noUndoCache);
      this.get('geom').data.instance = null;
      data.geom = this.get('geom').exportJSON(false);
      this.get('geom').data.instance = this;

      return data;
    },

    parseJSON: function(data, manager) {

      //if (!this.get('geom')) {
      var geom = new paper.Path();
      geom.importJSON(data.geom);
      this.changeGeomInheritance(geom);
      return GeometryNode.prototype.parseJSON.call(this, data, manager);
    },

    changeGeomInheritance: function(path) {
      GeometryNode.prototype.changeGeomInheritance.call(this, path);
      this.generatePoints(path, true);

    },

    /*normalizeGeometry
     * generates a set of transformation data based on the matrix
     * then inverts the matrix and normalizes the path based on these values
     * returns the transformation data
     */
    normalizeGeometry: function(path, matrix) {

      var data = {};
      // TODO: make some normalizations util function
      var rotationDelta;
      if (matrix.rotation < 0) {
        rotationDelta = 360 + matrix.rotation;
      } else {
        rotationDelta = matrix.rotation;
      }
      data.rotationDelta = {
        v: rotationDelta
      };

      data.scalingDelta = {
        x: matrix.scaling.x,
        y: matrix.scaling.y,
        operator: 'add'
      };

      var translationDelta = {
        x: matrix.translation.x,
        y: matrix.translation.y,
        operator: 'add'
      };
      var position = {
        x: 0,
        y: 0,
        operator: 'set'
      };

      data.translationDelta = translationDelta;
      data.position = position;

      data.rotation_origin = {
        x: 0,
        y: 0,
        operator: 'set'
      };
      data.scaling_origin = {
        x: 0,
        y: 0,
        operator: 'set'
      };
      if (path.fillColor) {
        data.fillColor = {
          r: path.fillColor.red,
          g: path.fillColor.green,
          b: path.fillColor.blue,
          h: path.fillColor.hue,
          s: path.fillColor.saturation,
          l: path.fillColor.lightness,
          operator: 'set'
        };
      } else {
        data.fillColor = {
          noColor: true
        };
      }
      if (path.strokeColor) {
        data.strokeColor = {
          r: path.strokeColor.red,
          g: path.strokeColor.green,
          b: path.strokeColor.blue,
          h: path.strokeColor.hue,
          s: path.strokeColor.saturation,
          l: path.strokeColor.lightness,
          operator: 'set'
        };
      } else {
        data.strokeColor = {
          noColor: true
        };
      }
      data.strokeWidth = {
        v: path.strokeWidth
      };

      var imatrix = matrix.inverted();
      path.transform(imatrix);

      this.set('width', path.bounds.width);
      this.set('height', path.bounds.height);

      path.visible = false;
      path.selected = false;

      this.changeGeomInheritance(path);

      this.setValue(data);
      this.get('strokeColor').setNull(false);
      this.get('fillColor').setNull(false);
      return data;
    },

    generatePoints: function(path, clear) {
      var points = this.get('points');
      if (clear) {
        points.length = 0;
      }
      if (path.segments) {
        var segments = path.segments;
        for (var j = 0; j < segments.length; j++) {
          var pointNode = new PointNode({}, {
            geometryGenerator: this.geometryGenerator
          });
          pointNode.normalizeGeometry(segments[j]);
          //this.addChildNode(pointNode);
          points.push(pointNode);
        }
      }
    },

    select: function(segments) {
      GeometryNode.prototype.select.apply(this, arguments);
      if (segments) {
        this.setSelectedSegments(segments);
      }
    },

    deselect: function() {
      GeometryNode.prototype.deselect.apply(this, arguments);
      this.deselectSegments();
    },

    //sets selection for segments
    setSelectedSegments: function(segments) {
      var proto_node = this.get('proto_node');
      if (proto_node) {
        return proto_node.setSelectedSegments(segments);
      } else {
        var points = this.get('points');
        var selectedPoints = [];
        for (var i = 0; i < segments.length; i++) {
          var index = segments[i].index;
          var point = points[index];
          point.set('selection_type', segments[i].type);
          point.get('selected').setValue(true);
          selectedPoints.push(point);
        }
        return selectedPoints;
      }
    },

    deselectSegments: function() {
      var proto_node = this.get('proto_node');
      if (proto_node) {
        proto_node.deselectSegments();
      } else {
        var points = this.get('points');
        for (var i = 0; i < points.length; i++) {
          points[i].get('selected').setValue(false);
        }
      }
    },

    inheritSelectedPoints: function() {
      var proto_node = this.get('proto_node');
      if (proto_node) {
        return proto_node.inheritSelectedPoints();
      } else {
        var points = this.get('points');
        var selected_points = points.filter(function(point) {
          return point.get('selected').getValue();
        });
        return selected_points;
      }
    },

    /* modifyPoints
     * called when segment in geometry is modified
     */
    modifyPoints: function(data, mode, modifier, exclude, registerUndo) {
      if (registerUndo) {
        this.addToUndoStack();
      }
      if (!this.pointsModified) {
        this.formerPosition = this.get('geom').position.clone();
      }
      var proto_node = this.get('proto_node');

      var geom = this.get('geom');
      var startWidth = geom.bounds.width;
      var startHeight = geom.bounds.height;

      var selectedPoints = this.inheritSelectedPoints();
      var indicies = [];
    console.log('translationDelta before',data.translationDelta);

      var delta = this.inverseTransformPoint(data.translationDelta);
      console.log('master delta', delta);

      for (var i = 0; i < selectedPoints.length; i++) {

        var selectedPoint = selectedPoints[i];
        var geomS = geom.segments[selectedPoint.get('index')];
        indicies.push({
          index: selectedPoint.get('index'),
          type: selectedPoint.get('selection_type')
        });
        switch (selectedPoint.get('selection_type')) {
          case 'segment':
          case 'curve':
            var p = selectedPoint.get('position');
            p.add(delta);
            geomS.point.x += delta.x;
            geomS.point.y += delta.y;

            break;
          case 'handle-in':
            var hi = selectedPoint.get('handle_in');
            hi.add(delta);
            geomS.handleIn.x += delta.x;
            geomS.handleIn.y += delta.y;
            break;

          case 'handle-out':
            var ho = selectedPoint.get('handle_out');
            ho.add(delta);
            geomS.handleOut.x += delta.x;
            geomS.handleOut.y += delta.y;
            break;
        }
      }
      this.pointsModified = true;

      var endWidth = geom.bounds.width;
      var endHeight = geom.bounds.height;
      var wDiff = (endWidth - startWidth) / 2;
      var hDiff = (endHeight - startHeight) / 2;

      var inheritors = this.get('inheritors').inheritors;
      console.log('translationDelta',data.translationDelta);
      var inheritor_delta = this.transformPoint(data.translationDelta);

      for (var j = 0; j < inheritors.length; j++) {

        inheritors[j].modifyPointsByIndex({
          x: inheritor_delta.x,
          y: inheritor_delta.y
        }, indicies, exclude);
      }
      if (proto_node) {
        proto_node.modifyPointsByIndex({
          x: inheritor_delta.x,
          y: inheritor_delta.y
        }, indicies, this);

      }

    },

    reset: function() {
      if (this.pointsModified) {
        var formerTdelta = this.get('translationDelta').getValue();
        this.get('translationDelta').setValue(this.get('geom').position);
        this.pointsModified = false;
        this.bboxInvalid = true;
      }
      GeometryNode.prototype.reset.call(this);
    },

    /* modifyPointsByIndex
     * called by prototpye to update inheritor points
     * note: does not actually correspond to prototypal inheritance
     * model since that would require repeatedly cloning paperjs objects
     * which would be too memory intensive. Instead just applies transformations
     * on the prototype's geometry to those of the inheritor
     */
    modifyPointsByIndex: function(initial_delta, indicies, exclude) {

      var geom = this.get('geom');

      var inheritor_delta = {
        x: initial_delta.x,
        y: initial_delta.y
      };

      var delta = this.inverseTransformPoint(initial_delta);
       console.log('delta for inheritor',delta);

      this.pointsModified = true;

      for (var i = 0; i < indicies.length; i++) {
        var geomS = geom.segments[indicies[i].index];

        switch (indicies[i].type) {
          case 'segment':
          case 'curve':
            geomS.point.x += delta.x;
            geomS.point.y += delta.y;
            break;
          case 'handle-in':
            geomS.handleIn.x += delta.x;
            geomS.handleIn.y += delta.y;
            break;
          case 'handle-out':
            geomS.handleOut.x += delta.x;
            geomS.handleOut.y += delta.y;

            break;
        }

      }

      var inheritors = this.get('inheritors').inheritors;
      for (var j = 0; j < inheritors.length; j++) {
        if (!exclude || inheritors[j] != exclude) {
          inheritors[j].modifyPointsByIndex({
            x: inheritor_delta.x,
            y: inheritor_delta.y
          }, indicies);
        }
      }

      geom.visible = true;

    },

    renderSelection: function(geom) {
      GeometryNode.prototype.renderSelection.call(this, geom);
      var proto = this.get('proto_node');
      var pointSelected = false;
      var points = proto ? proto.get('points') : this.get('points');
      for (var i = 0; i < points.length; i++) {
        var selectedPoint = points[i];
        var geomS = geom.segments[selectedPoint.get('index')];
        if (selectedPoint.get('selected').getValue()) {
          geomS.selected = true;
          pointSelected = true;
        }
      }
      if (pointSelected) {
        this.get('geom').selected = true;
        this.get('bbox').selected = false;

      }

    }

  });

  return PathNode;

});<commit_msg>modified point selection<commit_after>/*PathNode.js
 * path object
 * extends GeometryNode
 * node with actual path in it
 */

define([
  'underscore',
  'models/data/geometry/GeometryNode',
  'models/data/geometry/PointNode',
  'utils/TrigFunc',
  'models/data/properties/PPoint',
  'paper',
  'models/data/properties/PFloat',
  'models/data/properties/PColor'

], function(_, GeometryNode, PointNode, TrigFunc, PPoint, paper, PFloat, PColor) {
  //drawable paper.js path object that is stored in the pathnode
  var PathNode = GeometryNode.extend({

    defaults: _.extend({}, GeometryNode.prototype.defaults, {

      name: 'path',
      type: 'geometry',
      points: null,
    }),

    initialize: function() {
      GeometryNode.prototype.initialize.apply(this, arguments);
      this.set('points', []);
      this.pointsModified = false;
    },

    create: function() {
      var instance = GeometryNode.prototype.create.apply(this, arguments);
      // instance.generatePoints(instance.get('geom'));
      return instance;
    },

    deleteSelf: function() {

      GeometryNode.prototype.deleteSelf.apply(this, arguments);
    },

    toJSON: function(noUndoCache) {
      var data = GeometryNode.prototype.toJSON.call(this, noUndoCache);
      this.get('geom').data.instance = null;
      data.geom = this.get('geom').exportJSON(false);
      this.get('geom').data.instance = this;

      return data;
    },

    parseJSON: function(data, manager) {

      //if (!this.get('geom')) {
      var geom = new paper.Path();
      geom.importJSON(data.geom);
      this.changeGeomInheritance(geom);
      return GeometryNode.prototype.parseJSON.call(this, data, manager);
    },

    changeGeomInheritance: function(path) {
      GeometryNode.prototype.changeGeomInheritance.call(this, path);
      this.generatePoints(path, true);

    },

    /*normalizeGeometry
     * generates a set of transformation data based on the matrix
     * then inverts the matrix and normalizes the path based on these values
     * returns the transformation data
     */
    normalizeGeometry: function(path, matrix) {

      var data = {};
      // TODO: make some normalizations util function
      var rotationDelta;
      if (matrix.rotation < 0) {
        rotationDelta = 360 + matrix.rotation;
      } else {
        rotationDelta = matrix.rotation;
      }
      data.rotationDelta = {
        v: rotationDelta
      };

      data.scalingDelta = {
        x: matrix.scaling.x,
        y: matrix.scaling.y,
        operator: 'add'
      };

      var translationDelta = {
        x: matrix.translation.x,
        y: matrix.translation.y,
        operator: 'add'
      };
      var position = {
        x: 0,
        y: 0,
        operator: 'set'
      };

      data.translationDelta = translationDelta;
      data.position = position;

      data.rotation_origin = {
        x: 0,
        y: 0,
        operator: 'set'
      };
      data.scaling_origin = {
        x: 0,
        y: 0,
        operator: 'set'
      };
      if (path.fillColor) {
        data.fillColor = {
          r: path.fillColor.red,
          g: path.fillColor.green,
          b: path.fillColor.blue,
          h: path.fillColor.hue,
          s: path.fillColor.saturation,
          l: path.fillColor.lightness,
          operator: 'set'
        };
      } else {
        data.fillColor = {
          noColor: true
        };
      }
      if (path.strokeColor) {
        data.strokeColor = {
          r: path.strokeColor.red,
          g: path.strokeColor.green,
          b: path.strokeColor.blue,
          h: path.strokeColor.hue,
          s: path.strokeColor.saturation,
          l: path.strokeColor.lightness,
          operator: 'set'
        };
      } else {
        data.strokeColor = {
          noColor: true
        };
      }
      data.strokeWidth = {
        v: path.strokeWidth
      };

      var imatrix = matrix.inverted();
      path.transform(imatrix);

      this.set('width', path.bounds.width);
      this.set('height', path.bounds.height);

      path.visible = false;
      path.selected = false;

      this.changeGeomInheritance(path);

      this.setValue(data);
      this.get('strokeColor').setNull(false);
      this.get('fillColor').setNull(false);
      return data;
    },

    generatePoints: function(path, clear) {
      var points = this.get('points');
      if (clear) {
        points.length = 0;
      }
      if (path.segments) {
        var segments = path.segments;
        for (var j = 0; j < segments.length; j++) {
          var pointNode = new PointNode({}, {
            geometryGenerator: this.geometryGenerator
          });
          pointNode.normalizeGeometry(segments[j]);
          //this.addChildNode(pointNode);
          points.push(pointNode);
        }
      }
    },

    select: function(segments) {
      GeometryNode.prototype.select.apply(this, arguments);
      if (segments) {
        this.setSelectedSegments(segments);
      }
    },

    deselect: function() {
      GeometryNode.prototype.deselect.apply(this, arguments);
      this.deselectSegments();
    },

    //sets selection for segments
    setSelectedSegments: function(segments) {
      var proto_node = this.get('proto_node');
      if (proto_node) {
        return proto_node.setSelectedSegments(segments);
      } else {
        var points = this.get('points');
        var selectedPoints = [];
        for (var i = 0; i < segments.length; i++) {
          var index = segments[i].index;
          var point = points[index];
          point.set('selection_type', segments[i].type);
          point.get('selected').setValue(true);
          selectedPoints.push(point);
        }
        return selectedPoints;
      }
    },

    deselectSegments: function() {
      var proto_node = this.get('proto_node');
      if (proto_node) {
        proto_node.deselectSegments();
      } else {
        var points = this.get('points');
        for (var i = 0; i < points.length; i++) {
          points[i].get('selected').setValue(false);
        }
      }
    },

    inheritSelectedPoints: function() {
      var proto_node = this.get('proto_node');
      if (proto_node) {
        return proto_node.inheritSelectedPoints();
      } else {
        var points = this.get('points');
        var selected_points = points.filter(function(point) {
          return point.get('selected').getValue();
        });
        return selected_points;
      }
    },

    /* modifyPoints
     * called when segment in geometry is modified
     */
    modifyPoints: function(data, mode, modifier, exclude, registerUndo) {
      if (registerUndo) {
        this.addToUndoStack();
      }
      if (!this.pointsModified) {
        this.formerPosition = this.get('geom').position.clone();
      }
      var proto_node = this.get('proto_node');

      var geom = this.get('geom');
      var startWidth = geom.bounds.width;
      var startHeight = geom.bounds.height;

      var selectedPoints = this.inheritSelectedPoints();
      var indicies = [];

      var delta = this.inverseTransformPoint(data.translationDelta);
      console.log('master delta', delta);

      for (var i = 0; i < selectedPoints.length; i++) {

        var selectedPoint = selectedPoints[i];
        var geomS = geom.segments[selectedPoint.get('index')];
        indicies.push({
          index: selectedPoint.get('index'),
          type: selectedPoint.get('selection_type')
        });
        switch (selectedPoint.get('selection_type')) {
          case 'segment':
          case 'curve':
            var p = selectedPoint.get('position');
            p.add(delta);
            geomS.point.x += delta.x;
            geomS.point.y += delta.y;

            break;
          case 'handle-in':
            var hi = selectedPoint.get('handle_in');
            hi.add(delta);
            geomS.handleIn.x += delta.x;
            geomS.handleIn.y += delta.y;
            break;

          case 'handle-out':
            var ho = selectedPoint.get('handle_out');
            ho.add(delta);
            geomS.handleOut.x += delta.x;
            geomS.handleOut.y += delta.y;
            break;
        }
      }
      this.pointsModified = true;

      var endWidth = geom.bounds.width;
      var endHeight = geom.bounds.height;
      var wDiff = (endWidth - startWidth) / 2;
      var hDiff = (endHeight - startHeight) / 2;

      var inheritors = this.get('inheritors').inheritors;
      var inheritor_delta = data.translationDelta;

      for (var j = 0; j < inheritors.length; j++) {

        inheritors[j].modifyPointsByIndex({
          x: inheritor_delta.x,
          y: inheritor_delta.y
        }, indicies, exclude);
      }
      if (proto_node) {
        proto_node.modifyPointsByIndex({
          x: inheritor_delta.x,
          y: inheritor_delta.y
        }, indicies, this);

      }

    },

    reset: function() {
      if (this.pointsModified) {
        var formerTdelta = this.get('translationDelta').getValue();
        this.get('translationDelta').setValue(this.get('geom').position);
        this.pointsModified = false;
        this.bboxInvalid = true;
      }
      GeometryNode.prototype.reset.call(this);
    },

    /* modifyPointsByIndex
     * called by prototpye to update inheritor points
     * note: does not actually correspond to prototypal inheritance
     * model since that would require repeatedly cloning paperjs objects
     * which would be too memory intensive. Instead just applies transformations
     * on the prototype's geometry to those of the inheritor
     */
    modifyPointsByIndex: function(initial_delta, indicies, exclude) {

      var geom = this.get('geom');

      var inheritor_delta = {
        x: initial_delta.x,
        y: initial_delta.y
      };

      var delta = this.inverseTransformPoint(initial_delta);
       console.log('delta for inheritor',delta);

      this.pointsModified = true;

      for (var i = 0; i < indicies.length; i++) {
        var geomS = geom.segments[indicies[i].index];

        switch (indicies[i].type) {
          case 'segment':
          case 'curve':
            geomS.point.x += delta.x;
            geomS.point.y += delta.y;
            break;
          case 'handle-in':
            geomS.handleIn.x += delta.x;
            geomS.handleIn.y += delta.y;
            break;
          case 'handle-out':
            geomS.handleOut.x += delta.x;
            geomS.handleOut.y += delta.y;

            break;
        }

      }

      var inheritors = this.get('inheritors').inheritors;
      for (var j = 0; j < inheritors.length; j++) {
        if (!exclude || inheritors[j] != exclude) {
          inheritors[j].modifyPointsByIndex({
            x: inheritor_delta.x,
            y: inheritor_delta.y
          }, indicies);
        }
      }

      geom.visible = true;

    },

    renderSelection: function(geom) {
      GeometryNode.prototype.renderSelection.call(this, geom);
      var proto = this.get('proto_node');
      var pointSelected = false;
      var points = proto ? proto.get('points') : this.get('points');
      for (var i = 0; i < points.length; i++) {
        var selectedPoint = points[i];
        var geomS = geom.segments[selectedPoint.get('index')];
        if (selectedPoint.get('selected').getValue()) {
          geomS.selected = true;
          pointSelected = true;
        }
      }
      if (pointSelected) {
        this.get('geom').selected = true;
        this.get('bbox').selected = false;

      }

    }

  });

  return PathNode;

});<|endoftext|>


<commit_before><commit_msg>separate loader no longer needed<commit_after><|endoftext|>


<commit_before><commit_msg>More robust error checking.<commit_after><|endoftext|>


<commit_before><commit_msg>Refactoring<commit_after><|endoftext|>


<commit_before><commit_msg>Refactor literal parsing<commit_after><|endoftext|>


<commit_before>"use strict";

const fs = require("fs");
const Helper = require("../helper");
const colors = require("colors/safe");
const path = require("path");
const _ = require("lodash");
const themes = new Map();

module.exports = {
	getAll: getAll,
	getFilename: getFilename
};

fs.readdir("client/themes/", (err, builtInThemes) => {
	if (err) {
		return;
	}
	builtInThemes
		.filter((theme) => theme.endsWith(".css"))
		.map(makeLocalThemeObject)
		.forEach((theme) => themes.set(theme.name, theme));
});

fs.readdir(Helper.getPackagesPath(), (err, packages) => {
	if (err) {
		return;
	}
	packages
		.map(makePackageThemeObject)
		.forEach((theme) => {
			if (theme) {
				themes.set(theme.name, theme);
			}
		});
});

function getAll() {
	return _.sortBy(Array.from(themes.values()), "displayName");
}

function getFilename(module) {
	if (themes.has(module)) {
		return themes.get(module).filename;
	}
}

function makeLocalThemeObject(css) {
	const themeName = css.slice(0, -4);
	return {
		displayName: themeName.charAt(0).toUpperCase() + themeName.slice(1),
		filename: path.join(__dirname, "..", "client", "themes", `${themeName}.css`),
		name: themeName
	};
}

function getModuleInfo(packageName) {
	let module;
	try {
		module = require(Helper.getPackageModulePath(packageName));
	} catch (e) {
		log.warn(`Specified theme ${colors.yellow(packageName)} is not installed in packages directory`);
		return;
	}
	if (!module.thelounge) {
		log.warn(`Specified theme ${colors.yellow(packageName)} doesn't have required information.`);
		return;
	}
	return module.thelounge;
}

function makePackageThemeObject(moduleName) {
	const module = getModuleInfo(moduleName);
	if (!module || module.type !== "theme") {
		return;
	}
	const modulePath = Helper.getPackageModulePath(moduleName);
	const displayName = module.name || moduleName;
	const filename = path.join(modulePath, module.css);
	return {
		displayName: displayName,
		filename: filename,
		name: moduleName
	};
}
<commit_msg>Fix local theme folder<commit_after>"use strict";

const fs = require("fs");
const Helper = require("../helper");
const colors = require("colors/safe");
const path = require("path");
const _ = require("lodash");
const themes = new Map();

module.exports = {
	getAll: getAll,
	getFilename: getFilename
};

fs.readdir(path.join(__dirname, "..", "..", "public", "themes"), (err, builtInThemes) => {
	if (err) {
		return;
	}
	builtInThemes
		.filter((theme) => theme.endsWith(".css"))
		.map(makeLocalThemeObject)
		.forEach((theme) => themes.set(theme.name, theme));
});

fs.readdir(Helper.getPackagesPath(), (err, packages) => {
	if (err) {
		return;
	}
	packages
		.map(makePackageThemeObject)
		.forEach((theme) => {
			if (theme) {
				themes.set(theme.name, theme);
			}
		});
});

function getAll() {
	return _.sortBy(Array.from(themes.values()), "displayName");
}

function getFilename(module) {
	if (themes.has(module)) {
		return themes.get(module).filename;
	}
}

function makeLocalThemeObject(css) {
	const themeName = css.slice(0, -4);
	return {
		displayName: themeName.charAt(0).toUpperCase() + themeName.slice(1),
		filename: path.join(__dirname, "..", "..", "public", "themes", `${themeName}.css`),
		name: themeName
	};
}

function getModuleInfo(packageName) {
	let module;
	try {
		module = require(Helper.getPackageModulePath(packageName));
	} catch (e) {
		log.warn(`Specified theme ${colors.yellow(packageName)} is not installed in packages directory`);
		return;
	}
	if (!module.thelounge) {
		log.warn(`Specified theme ${colors.yellow(packageName)} doesn't have required information.`);
		return;
	}
	return module.thelounge;
}

function makePackageThemeObject(moduleName) {
	const module = getModuleInfo(moduleName);
	if (!module || module.type !== "theme") {
		return;
	}
	const modulePath = Helper.getPackageModulePath(moduleName);
	const displayName = module.name || moduleName;
	const filename = path.join(modulePath, module.css);
	return {
		displayName: displayName,
		filename: filename,
		name: moduleName
	};
}
<|endoftext|>


<commit_before><commit_msg>Init project<commit_after><|endoftext|>


<commit_before><commit_msg>prevent mobile redirect loop<commit_after><|endoftext|>


<commit_before><commit_msg>server: remove cruft<commit_after><|endoftext|>


<commit_before><commit_msg>Fast String.prototype.repeat<commit_after><|endoftext|>


<commit_before>/*
 1. Напишите функцию которая принимает 2 числа
 и возвращает массив содержащий числа между первым числом и вторым числом;
 */
//первое и второе задание слепил в кучу
 function numbersBetween(a, b) {
     var returnArray =[];
 	 if (b == undefined){//если нет 2го параметра делам 2 задание:)
		for (var i = 0; i <= a; i++){
        returnArray.push(fizzBuzz(i)); // вот тут вызывается функция для 2го задания
	 }
	 }
	 else {
	     a++; //это шоб не брало первое значание
		for (var i = a; i < b; i++){
        returnArray.push(i);
     }
	 }
     return returnArray;
 }

function fizzBuzz(num) {// функция для второго задания
  /* Ваш код здесь */
  return (num === 0 ? '0 деленный на число будет 0' :
          (((num % 3 == 0) && (num % 5 == 0)) ? 'FizzBuzz' :
            ((num % 5 == 0) ? 'Buzz' :
              (((num % 3 == 0) ? 'Fizz' :
                num)))));
	}
 console.log(numbersBetween(1, 5)); // 2,3,4
 console.log(numbersBetween(3, 6)); // 4,5
 console.log(numbersBetween(12, 15)); // 13,14
 console.log(numbersBetween(10, 15)); // 11, 12, 13, 14
 console.log(numbersBetween(100)); //Для второго задания.

/*
 2. Перепешите задачу FizzBuzz, но с использованием цикла.
 Расчет чисел должен идти до 100
 */

/*
 3. Напишите функцию которая принимает 1 аргумент - массив
 И возвращает новый массив содержащий типы значений переменных
 */

let arr = [1, null, undefined, 'str', {}, [], function() {}];

function arrayTypes(arrayT){
for (var i =0; i < arrayT.length; i++ ){
arrayT[i] = typeof(arrayT[i]);
}
return(arrayT);
}
console.log(arrayTypes(arr));
/*
 @@SUPER@@. Вам дан массив array, содержащий внутри объекты.
 Напишите функцию которая внутри цикла проходится по каждому элементу массива
 И проверяет какой тип данных содержит свойство age, если тип данных NaN,
 тогда добавляет данному объекту свойство unknownAge: true

 На основании нового массива, создайте новую функцию, которая вернет новый массив
  содержащий все объекты содержащие свойство unknownAge:true
 */

let array = Array.from({ length: 35 }).map(
  (value, index) => (index % 2 ? { age: index + 2 } : { age: NaN })
);

//console.log(array); // [ {age:NaN}, {age:3}, {age:NaN}, {age:5}, {age:NaN}, {age:7} ....]
//console.log(array.length); // 35

function solution(arr) {
    for(let i = 0; i < arr.length; i++){
    if (isNaN(arr[i].age)){
        arr[i].unknownAge = true;
    }
    }
    return arr;
}

function unknownAge(arr){
    let localArr = [];
    for(let i = 0; i < arr.length; i++){
        if (arr[i].unknownAge){
            localArr.push(arr[i]);
        }
    }
    return(localArr);
}

console.log(solution(array));
console.log(unknownAge(solution(array)));

<commit_msg>Homework4<commit_after>/*
 1. Напишите функцию которая принимает 2 числа
 и возвращает массив содержащий числа между первым числом и вторым числом;
 */
//первое и второе задание слепил в кучу
 function numbersBetween(a, b) {
     var returnArray =[];
 	 if (b == undefined){//если нет 2го параметра делам 2 задание:)
		for (var i = 0; i <= a; i++){
        returnArray.push(fizzBuzz(i)); // вот тут вызывается функция для 2го задания
	 }
	 }
	 else {
	     a++; //это шоб не брало первое значание
		for (var i = a; i < b; i++){
        returnArray.push(i);
     }
	 }
     return returnArray;
 }

function fizzBuzz(num) {// функция для второго задания
  /* Ваш код здесь */
  return (num === 0 ? '0 деленный на число будет 0' :
          (((num % 3 == 0) && (num % 5 == 0)) ? 'FizzBuzz' :
            ((num % 5 == 0) ? 'Buzz' :
              (((num % 3 == 0) ? 'Fizz' :
                num)))));
	}
 console.log(numbersBetween(1, 5)); // 2,3,4
 console.log(numbersBetween(3, 6)); // 4,5
 console.log(numbersBetween(12, 15)); // 13,14
 console.log(numbersBetween(10, 15)); // 11, 12, 13, 14
 console.log(numbersBetween(100)); //Для второго задания.

/*
 2. Перепешите задачу FizzBuzz, но с использованием цикла.
 Расчет чисел должен идти до 100
 */

/*
 3. Напишите функцию которая принимает 1 аргумент - массив
 И возвращает новый массив содержащий типы значений переменных
 */

let arr = [1, null, undefined, 'str', {}, [], function() {}];

function arrayTypes(arrayT){
    newArray =[];
for (var i =0; i < arrayT.length; i++ ){
newArray[i] = typeof(arrayT[i]);
}
return(newArray);
}
console.log(arrayTypes(arr));
/*
 @@SUPER@@. Вам дан массив array, содержащий внутри объекты.
 Напишите функцию которая внутри цикла проходится по каждому элементу массива
 И проверяет какой тип данных содержит свойство age, если тип данных NaN,
 тогда добавляет данному объекту свойство unknownAge: true

 На основании нового массива, создайте новую функцию, которая вернет новый массив
  содержащий все объекты содержащие свойство unknownAge:true
 */

let array = Array.from({ length: 35 }).map(
  (value, index) => (index % 2 ? { age: index + 2 } : { age: NaN })
);

//console.log(array); // [ {age:NaN}, {age:3}, {age:NaN}, {age:5}, {age:NaN}, {age:7} ....]
//console.log(array.length); // 35

function solution(arr) {
    for(let i = 0; i < arr.length; i++){
    if (isNaN(arr[i].age)){
        arr[i].unknownAge = true;
    }
    }
    return arr;
}

function unknownAge(arr){
    let localArr = [];
    for(let i = 0; i < arr.length; i++){
        if (arr[i].unknownAge){
            localArr.push(arr[i]);
        }
    }
    return(localArr);
}

console.log(solution(array));
console.log(unknownAge(solution(array)));

<|endoftext|>