<commit_before>app.controller('state', function ($scope, $timeout, $filter,sharedData) {
	var name=sharedData.getName();
	$scope.header=name.name;
	console.log(name);
	$scope.ok="OK";
	var asciiToHex = function(ascii){
		return ascii.charCodeAt(0).toString(16);
	};

	var hexToascii = function(hex){
		return String.fromCharCode(parseInt(n, 16));
	};

	var hexTobinary = function(hex){
		hex = "0x"+hex;
		return (Number(hex).toString(2));
	};

	var binaryTodecimal = function(bin){
		return parseInt(bin,2);
	};

	var decimalTohex = function(num){
		return num.toString(16);
	};

	$scope.m = [];
	$scope.mShift = [];
	$scope.mSbox = [];

	$scope.process = function(){
		$scope.m = [];
		m = $scope.message.split('');
		for(i=0;i<$scope.message.length;i++){
			$scope.m.push({ascii:m[i],hex:asciiToHex(m[i])});
		}
		sharedData.setMessage($scope.m);
		$scope.mShift = shiftRows($scope.m);
		$scope.mSbox = subBytes($scope.mShift);
	}

	console.log($scope.m);

	var toDecimal = function(hex){
		if(hex=="a"){
			hex="10";
		}
		if(hex=="b"){
			hex="11";
		}
		if(hex=="c"){
			hex="12";
		}
		if(hex=="d"){
			hex="13";
		}
		if(hex=="e"){
			hex="14";
		}
		if(hex=="f"){
			hex="15";
		}
		return hex;
	}
	var subByte = function(byte){
		var byteArray = byte.split('');
		var x = toDecimal(byteArray[0]);
		var y = toDecimal(byteArray[1]);
		var sBox = [
		["63","7c","77","7b","f2","6b","6f","c5","30","01","67","2b","fe","d7","ab","76"],
		["ca","82","c9","7d","fa","59","47","f0","ad","d4","a2","af","9c","a4","72","c0"],
		["b7","fd","93","26","36","3f","f7","cc","34","a5","e5","f1","71","d8","31","15"],
		["04","c7","23","c3","18","96","05","9a","07","12","80","e2","eb","27","b2","75"],
		["09","83","2c","1a","1b","6e","5a","a0","52","3b","d6","b3","29","e3","2f","84"],
		["53","d1","00","ed","20","fc","b1","5b","6a","cb","be","39","4a","4c","58","cf"],
		["d0","ef","aa","fb","43","4d","33","85","45","f9","02","7f","50","3c","9f","a8"],
		["51","a3","40","8f","92","9d","38","f5","bc","b6","da","21","10","ff","f3","d2"],
		["cd","0c","13","ec","5f","97","44","17","c4","a7","7e","3d","64","5d","19","73"],
		["60","81","4f","dc","22","2a","90","88","46","ee","b8","14","de","5e","0b","db"],
		["e0","32","3a","0a","49","06","24","5c","c2","d3","ac","62","91","95","e4","79"],
		["e7","c8","37","6d","8d","d5","4e","a9","6c","56","f4","ea","65","7a","ae","08"],
		["ba","78","25","2e","1c","a6","b4","c6","e8","dd","74","1f","4b","bd","8b","8a"],
		["70","3e","b5","66","48","03","f6","0e","61","35","57","b9","86","c1","1d","9e"],
		["e1","f8","98","11","69","d9","8e","94","9b","1e","87","e9","ce","55","28","df"],
		["8c","a1","89","0d","bf","e6","42","68","41","99","2d","0f","b0","54","bb","16"]];

		return sBox[x][y];

	};
	var subBytes = function(array){
		var result = [];
		for(i=0;i<array.length;i++){
			result.push({ascii:array[i].ascii,hex:subByte(array[i].hex)});
		}
		return result;
	};
	var shiftForward = function(originalArray,cut,paste){
		//copy original array to the variable we're going to manipulate
		var array = [];
		for (var i=0;i<originalArray.length;i++){
			array[i]=originalArray[i];
		}
		var arrayLength = array.length;
		var a= array.splice(cut,1);
		var b=[];
		//rebuild original array before splice, place the cut value into the paste position in this new array
		for(i=0;i<arrayLength;i++){
			if(i<paste){
				b[i]=array[i];
			}else if (i==paste){
				b[i]=a[0];
			}else{
				b[i]=array[i-1];
			}
		}
		return b;
	};

	var shiftRows = function(state){
		var matrix=[];
		for(i=0;i<state.length;i++){
			matrix[i]=state[i];
		}
		/* increment every five cells, shifting forward every fifth cell forward once and wrapping
		 * around it every five cells. thus turning something that looks like this:
		 * ["00", "01", "02", "03", "10", "11", "12", "13", "20", "21", "22", "23", "30", "31", "32", "33"]
		 * into this:
		 * ["00", "01", "02", "03", "11", "12", "13", "10", "22", "23", "20", "21", "33", "30", "31", "32"]
		 */
		for(i=0,j=0;i<matrix.length;i=i+4,j++){
				a=matrix;
			for(k=0;k<j;k++){
				a=shiftForward(a,i,i+3);
			}
				matrix=a;
		}
		return matrix;
	};

	var eightBit = function(bin){
		//makes sure the binary number is eight bits in length
		b = bin.split('');
		for(i=b.length;i<8;i++){
			b.unshift("0");
		}
		b = b.join("");
		return b;
	};

	var leftShift = function(bin){
		//shifts the binary number one forward, adding a zero to the end;
		b = bin.split('');
		b.push('0');
		b.shift();
		b = b.join("");
		return b;
	};

	var rightShift = function(bin){
		//shifts the binary number one backward, adding a zero to the beginning;
		b = bin.split('');
		b.unshift('0');
		b.splice(-1,1);
		return b.join("");
	};

	var bitMultX = function(hex){
		/*
		 * multiplication of a value by x (ie. by 02) can be implemented as a 1-bit left shift
		 * followed by a conditional bitwise XOR with (0001 1011) if the leftmost bit of the
		 * original value (before the shift) is 1.
		 */
		left = hexTobinary(hex);
		if(left[0]==1){
			x = hexTobinary(decimalTohex(binaryTodecimal(leftShift(eightBit(hexTobinary(hex)))) ^ 27));
		}
		return x;
	};

	//galois field mulitiplation

	var galois = {
		addition:function(bina,binb){
			bina=eightBit(bina).split('');
			binb=eightBit(binb).split('');
			binc=[];
			for(i=0;i<bina.length;i++){
				binc[i]=parseInt(bina[i] ^ binb[i],2);
			}
			return binc;
		},
		multiplication : function(bina, binb) {
			bina = eightBit(bina);
			binb = eightBit(binb);
			p = 0;
			for (i = 0; i < 8; i++) {

				if (binb[7] == 1) {
					p = p ^ (binaryTodecimal(bina));
				}
				highBit = bina[0];
				bina = (leftShift(bina));

				if (highBit == 1) {
					hexA = binaryTodecimal(bina);
					xor = hexA ^ 27;
					bina = (hexTobinary(decimalTohex(xor)));
				}

				binb = (rightShift(binb));

				if (binaryTodecimal(binb) == 0) {
					break;
				}

			}
			return eightBit(hexTobinary(decimalTohex(p)));
		}
	};

	row = [["02","d4"],["03","bf"],["01","5d"],["01","30"]];
	var computeRow = function(row){
		sum = 0;
		for(m=0;m<row.length;m++){
			product = galois.multiplication(hexTobinary(row[m][0]),hexTobinary(row[m][1]));
			product = binaryTodecimal(product);
			sum = sum ^ product;
		}
		return decimalTohex(sum);
	};
});<commit_msg>created MixColumns function<commit_after>app.controller('state', function ($scope, $timeout, $filter,sharedData) {
	var name=sharedData.getName();
	$scope.header=name.name;
	console.log(name);
	$scope.ok="OK";
	var asciiToHex = function(ascii){
		return ascii.charCodeAt(0).toString(16);
	};

	var hexToascii = function(hex){
		return String.fromCharCode(parseInt(n, 16));
	};

	var hexTobinary = function(hex){
		hex = "0x"+hex;
		return (Number(hex).toString(2));
	};

	var binaryTodecimal = function(bin){
		return parseInt(bin,2);
	};

	var decimalTohex = function(num){
		return num.toString(16);
	};

	$scope.m = [];
	$scope.mShift = [];
	$scope.mSbox = [];
	$scope.mMix = [];

	$scope.process = function(){
		$scope.m = [];
		$scope.mShift = [];
		$scope.mSbox = [];
		$scope.mMix = [];
		m = $scope.message.split('');
		for(i=0;i<$scope.message.length;i++){
			$scope.m.push({ascii:m[i],hex:asciiToHex(m[i])});
		}
		sharedData.setMessage($scope.m);
		$scope.mShift = shiftRows($scope.m);
		$scope.mSbox = subBytes($scope.mShift);
		$scope.mMix = mixColumns($scope.mSbox);
	}

	console.log($scope.m);

	var toDecimal = function(hex){
		if(hex=="a"){
			hex="10";
		}
		if(hex=="b"){
			hex="11";
		}
		if(hex=="c"){
			hex="12";
		}
		if(hex=="d"){
			hex="13";
		}
		if(hex=="e"){
			hex="14";
		}
		if(hex=="f"){
			hex="15";
		}
		return hex;
	}
	var subByte = function(byte){
		var byteArray = byte.split('');
		var x = toDecimal(byteArray[0]);
		var y = toDecimal(byteArray[1]);
		var sBox = [
		["63","7c","77","7b","f2","6b","6f","c5","30","01","67","2b","fe","d7","ab","76"],
		["ca","82","c9","7d","fa","59","47","f0","ad","d4","a2","af","9c","a4","72","c0"],
		["b7","fd","93","26","36","3f","f7","cc","34","a5","e5","f1","71","d8","31","15"],
		["04","c7","23","c3","18","96","05","9a","07","12","80","e2","eb","27","b2","75"],
		["09","83","2c","1a","1b","6e","5a","a0","52","3b","d6","b3","29","e3","2f","84"],
		["53","d1","00","ed","20","fc","b1","5b","6a","cb","be","39","4a","4c","58","cf"],
		["d0","ef","aa","fb","43","4d","33","85","45","f9","02","7f","50","3c","9f","a8"],
		["51","a3","40","8f","92","9d","38","f5","bc","b6","da","21","10","ff","f3","d2"],
		["cd","0c","13","ec","5f","97","44","17","c4","a7","7e","3d","64","5d","19","73"],
		["60","81","4f","dc","22","2a","90","88","46","ee","b8","14","de","5e","0b","db"],
		["e0","32","3a","0a","49","06","24","5c","c2","d3","ac","62","91","95","e4","79"],
		["e7","c8","37","6d","8d","d5","4e","a9","6c","56","f4","ea","65","7a","ae","08"],
		["ba","78","25","2e","1c","a6","b4","c6","e8","dd","74","1f","4b","bd","8b","8a"],
		["70","3e","b5","66","48","03","f6","0e","61","35","57","b9","86","c1","1d","9e"],
		["e1","f8","98","11","69","d9","8e","94","9b","1e","87","e9","ce","55","28","df"],
		["8c","a1","89","0d","bf","e6","42","68","41","99","2d","0f","b0","54","bb","16"]];

		return sBox[x][y];

	};
	var subBytes = function(array){
		var result = [];
		for(i=0;i<array.length;i++){
			result.push({ascii:array[i].ascii,hex:subByte(array[i].hex)});
		}
		return result;
	};
	var shiftForward = function(originalArray,cut,paste){
		//copy original array to the variable we're going to manipulate
		var array = [];
		for (var i=0;i<originalArray.length;i++){
			array[i]=originalArray[i];
		}
		var arrayLength = array.length;
		var a= array.splice(cut,1);
		var b=[];
		//rebuild original array before splice, place the cut value into the paste position in this new array
		for(i=0;i<arrayLength;i++){
			if(i<paste){
				b[i]=array[i];
			}else if (i==paste){
				b[i]=a[0];
			}else{
				b[i]=array[i-1];
			}
		}
		return b;
	};

	var shiftRows = function(state){
		var matrix=[];
		for(i=0;i<state.length;i++){
			matrix[i]=state[i];
		}
		/* increment every five cells, shifting forward every fifth cell forward once and wrapping
		 * around it every five cells. thus turning something that looks like this:
		 * ["00", "01", "02", "03", "10", "11", "12", "13", "20", "21", "22", "23", "30", "31", "32", "33"]
		 * into this:
		 * ["00", "01", "02", "03", "11", "12", "13", "10", "22", "23", "20", "21", "33", "30", "31", "32"]
		 */
		for(i=0,j=0;i<matrix.length;i=i+4,j++){
				a=matrix;
			for(k=0;k<j;k++){
				a=shiftForward(a,i,i+3);
			}
				matrix=a;
		}
		return matrix;
	};

	var eightBit = function(bin){
		//makes sure the binary number is eight bits in length
		b = bin.split('');
		for(i=b.length;i<8;i++){
			b.unshift("0");
		}
		b = b.join("");
		return b;
	};

	var leftShift = function(bin){
		//shifts the binary number one forward, adding a zero to the end;
		b = bin.split('');
		b.push('0');
		b.shift();
		b = b.join("");
		return b;
	};

	var rightShift = function(bin){
		//shifts the binary number one backward, adding a zero to the beginning;
		b = bin.split('');
		b.unshift('0');
		b.splice(-1,1);
		return b.join("");
	};

	var bitMultX = function(hex){
		/*
		 * multiplication of a value by x (ie. by 02) can be implemented as a 1-bit left shift
		 * followed by a conditional bitwise XOR with (0001 1011) if the leftmost bit of the
		 * original value (before the shift) is 1.
		 */
		left = hexTobinary(hex);
		if(left[0]==1){
			x = hexTobinary(decimalTohex(binaryTodecimal(leftShift(eightBit(hexTobinary(hex)))) ^ 27));
		}
		return x;
	};

	//galois field mulitiplation

	var galois = {
		addition:function(bina,binb){
			bina=eightBit(bina).split('');
			binb=eightBit(binb).split('');
			binc=[];
			for(i=0;i<bina.length;i++){
				binc[i]=parseInt(bina[i] ^ binb[i],2);
			}
			return binc;
		},
		multiplication : function(bina, binb) {
			bina = eightBit(bina);
			binb = eightBit(binb);
			p = 0;
			for (i = 0; i < 8; i++) {

				if (binb[7] == 1) {
					p = p ^ (binaryTodecimal(bina));
				}
				highBit = bina[0];
				bina = (leftShift(bina));

				if (highBit == 1) {
					hexA = binaryTodecimal(bina);
					xor = hexA ^ 27;
					bina = (hexTobinary(decimalTohex(xor)));
				}

				binb = (rightShift(binb));

				if (binaryTodecimal(binb) == 0) {
					break;
				}

			}
			return eightBit(hexTobinary(decimalTohex(p)));
		}
	};

	row = [["02","d4"],["03","bf"],["01","5d"],["01","30"]];
	var computeRow = function(row){
		sum = 0;
		for(m=0;m<row.length;m++){
			product = galois.multiplication(hexTobinary(row[m][0]),hexTobinary(row[m][1]));
			product = binaryTodecimal(product);
			sum = sum ^ product;
		}
		return decimalTohex(sum);
	};

	var mixColumns = function(m){
		var idMatrix = [2,3,1,1,1,2,3,1,1,1,2,3,3,1,1,2];
		//var m = [{ascii:"a", hex:"61", $$hashKey:"3XU"}, {ascii:"b", hex:"d4", $$hashKey:"3XX"}, {ascii:"c", hex:"63", $$hashKey:"3Y0"}, {ascii:"d", hex:"64", $$hashKey:"3Y3"}, {ascii:"e", hex:"65", $$hashKey:"3Y6"}, {ascii:"f", hex:"bf", $$hashKey:"3Y9"}, {ascii:"g", hex:"67", $$hashKey:"3YC"}, {ascii:"h", hex:"68", $$hashKey:"3YF"}, {ascii:"i", hex:"69", $$hashKey:"3YI"}, {ascii:"j", hex:"5d", $$hashKey:"3YL"}, {ascii:"k", hex:"6b", $$hashKey:"3YO"}, {ascii:"l", hex:"6c", $$hashKey:"3YR"}, {ascii:"m", hex:"6d", $$hashKey:"3YU"}, {ascii:"n", hex:"30", $$hashKey:"3YX"}, {ascii:"o", hex:"6f", $$hashKey:"3Z0"}, {ascii:"p", hex:"70", $$hashKey:"3Z3"}];
		var n = [];
		for(copy=0;copy<m.length;copy++){
			n[copy]=m[copy];
		}
		for(w=0;w<4;w++){
			var c = ["d4","bf","5d","30"];
			c[0]=m[w+0].hex;
			c[1]=m[w+4].hex;
			c[2]=m[w+8].hex;
			c[3]=m[w+12].hex;
			var result = [];
			for(x=0;x<16;x=4+x){
				column = [["",""],["",""],["",""],["",""]];
				for (y=0;y<c.length;y++){
					column[y][0]=idMatrix[y+x];
					column[y][1]=c[y];
				}

				result[x/4]=computeRow(column);

			}
			n[w+0].hex=result[0];
			n[w+4].hex=result[1];
			n[w+8].hex=result[2];
			n[w+12].he=result[3];
		}
		return n;
	};
});<|endoftext|>


<commit_before>import { highlight } from 'highlight.js';

export default function highlightCode(text) {
  try {
    const doc = new DOMParser().parseFromString(text, 'text/html');

    [].forEach.call(doc.querySelectorAll('code'), (el) => {
      el.classList.add('hljs');
      if (el.dataset.language && !el.dataset.highlighted) {
        try {
          el.innerHTML = highlight(el.dataset.language, el.innerText).value;
          el.dataset.highlighted = true;
        } catch(e) {
          // unsupported syntax for highlight.js
        }
      }
    });

    return doc.body.innerHTML;
  } catch(e) {
    return text;
  }
};
<commit_msg>Fix code highlighter not to delete line breaks for Firefox<commit_after>import { highlight } from 'highlight.js';

export default function highlightCode(text) {
  try {
    const doc = new DOMParser().parseFromString(text, 'text/html');

    [].forEach.call(doc.querySelectorAll('code'), (el) => {
      el.classList.add('hljs');
      if (el.dataset.language && !el.dataset.highlighted) {
        try {
          el.innerHTML = highlight(el.dataset.language, getTextContent(el)).value;
          el.dataset.highlighted = true;
        } catch(e) {
          // unsupported syntax for highlight.js
        }
      }
    });

    return doc.body.innerHTML;
  } catch(e) {
    return text;
  }
};

function getTextContent(el) {
  const contentEl = document.createElement('div');
  contentEl.innerHTML = el.innerHTML.replace(/<br\s*\/?>/g, "\n");
  return contentEl.textContent;
}
<|endoftext|>


<commit_before>var querystring = require("querystring"),
    fs = require("fs"),
    formidable = require("formidable");

function start(response, request) {
    var body = '' +
        '' +
        '<meta http-equiv="Content-Type" ' +
        'content="text/html; charset=UTF-8" />' +
        '' +
        '' +
        '<form action="/upload" enctype="multipart/form-data" ' +
        'method="post">' +
        '<input type="file" name="upload">' +
        '<input type="submit" value="Upload file" />' +
        '</form>' +
        '' +
        '';
    response.writeHead(200, {
        "Content-Type": "text/html"
    });
    response.write(body);
    response.end();
}

function upload(response, request) {
    console.log("upload!");
    var form = formidable.IncomingForm();
    console.log("about to parese");
    form.parse(request, function(err, fieds, files) {
        console.log("parse done");
        console.log(files);
        fs.renameSync(files.upload.path, "./tmp/test.png");
            response.writeHead(200, {"Content-Type": "text/html"});
    response.write("received image:
");
    response.write("");
    response.end();

    });

}

function show(response, request) {
    console.log("Request handler 'show' was called.");
    fs.readFile("./tmp/test.png", "binary", function(error, file) {
        if (error) {
            response.writeHead(500, {
                "Content-Type": "text/plain"
            });
            response.write(error + "\n");
            response.end();
        } else {
            response.writeHead(200, {
                "Content-Type": "image/png"
            });
            response.write(file, "binary");
            response.end();
        }
    });
}
exports.start = start;
exports.upload = upload;
exports.show = show;<commit_msg>File upload is ok.<commit_after>var querystring = require("querystring"),
    fs = require("fs"),
    formidable = require("formidable"),
    util = require("util");

function start(response, request) {
    var body = '' +
        '' +
        '<meta http-equiv="Content-Type" ' +
        'content="text/html; charset=UTF-8" />' +
        '' +
        '' +
        '<form action="/upload" enctype="multipart/form-data" ' +
        'method="post">' +
        '<input type="file" name="upload">' +
        '<input type="submit" value="Upload file" />' +
        '</form>' +
        '' +
        '';
    response.writeHead(200, {
        "Content-Type": "text/html"
    });
    response.write(body);
    response.end();
}

function upload(response, request) {
    console.log("upload!");
    var form = formidable.IncomingForm();
    console.log("about to parese");
    form.parse(request, function(err, fieds, files) {
        console.log("parse done");
        console.log(files);
        var readStream = fs.createReadStream(files.upload.path);

        var writeStream = fs.createWriteStream("./tmp/test.png");

        util.pump(readStream, writeStream, function() {

            fs.unlinkSync(files.upload.path);

        });
      //  fs.renameSync(files.upload.path, "./tmp/test.png");
        response.writeHead(200, {
            "Content-Type": "text/html"
        });
        response.write("received image:
");
        response.write("");
        response.end();

    });

}

function show(response, request) {
    console.log("Request handler 'show' was called.");
    fs.readFile("./tmp/test.png", "binary", function(error, file) {
        if (error) {
            response.writeHead(500, {
                "Content-Type": "text/plain"
            });
            response.write(error + "\n");
            response.end();
        } else {
            response.writeHead(200, {
                "Content-Type": "image/png"
            });
            response.write(file, "binary");
            response.end();
        }
    });
}
exports.start = start;
exports.upload = upload;
exports.show = show;<|endoftext|>


<commit_before>import { findByBossId } from 'raids';

export const PHASE_START_EVENT_TYPE = 'phasestart';
export const PHASE_END_EVENT_TYPE = 'phaseend';

export const abilityFilter = { //filter used for abilities that don't use the default "ability" filter
  "interrupt": "stoppedability",
  "dispel": "stoppedability",
};

/**
 * Creates Phase filter events as defined in boss configs via phase.filter.type
 * All filters can be passed a filter.offset attribute that shifts how many milliseconds after the specicifed event the phase starts (can be negative)
 * Supported filter event types:
 *
 * Buff filters:
 *  - removebuff
 *  - applybuff
 *  - removedebuff
 *  - applydebuff
 *
 *  Buff filters require filter.ability.id to be set for the Ability to search for
 *
 * Cast filters:
 *  - begincast
 *  - cast
 *  - interrupt
 *  - dispel
 *
 * Cast filters require filter.ability.id to be set for the Ability to search for
 *
 * Time filters:
 * - time
 *
 * Time filters require filter.time to be set as the milliseconds from combat begin to set the phase event at
 *
 * Health filters:
 * - health
 *
 * Health filters require filter.guid for the mob to check as well as filter.health for the health percentage threshold that triggers this transition (currently only works for phase starting when boss drops BELOW this percentage)
 */
export function fabricateBossPhaseEvents(events, report, fight) {
  const bossConfig = findByBossId(fight.boss);
  const fightDifficulty = fight.difficulty;

  const phaseEvents = [];
  const bossPhaseEvents = [];
  const phaseInstances = {};
  if (bossConfig && bossConfig.fight && bossConfig.fight.phases && bossConfig.fight.phases.length !== 0) {
    const phasesKeys = Object.keys(bossConfig.fight.phases).filter(key => bossConfig.fight.phases[key].difficulties.includes(fightDifficulty));

    if (phasesKeys && phasesKeys.length !== 0) {
      phaseEvents.push({
        key: phasesKeys[0],
        phase: bossConfig.fight.phases[phasesKeys[0]],
        start: fight.start_time,
        end: null,
      });

      phasesKeys.forEach(key => {
        const phase = bossConfig.fight.phases[key];
        if (phase.filter && phase.filter.type) {
          switch (phase.filter.type) {
            case 'removebuff':
            case 'applybuff':
            case 'removedebuff':
            case 'applydebuff':
            case 'begincast':
            case 'interrupt':
            case 'dispel':
            case 'cast': {
              let bossEvents = events.filter(e => e.type === phase.filter.type && (e.ability.guid === phase.filter.ability.id || e.extraAbility.guid === phase.filter.ability.id));
              if (phase.filter.eventInstance !== undefined && phase.filter.eventInstance >= 0 && !phase.multiple) {
                if (bossEvents.length >= (phase.filter.eventInstance + 1)) {
                  // If the instance exists, only that specific instance is relevant
                  bossEvents = [bossEvents[phase.filter.eventInstance]];
                } else {
                  // Otherwise the phase does not exist (it was probably a wipe)
                  console.warn('Phase not found:', phase);
                  break;
                }
              }
              bossEvents.forEach(bossEvent => {
                phaseEvents.push({
                  key: key,
                  phase: phase,
                  start: bossEvent.timestamp + (phase.filter.offset || 0),
                  end: null,
                });
              });
              break;
            }
            case 'time': {
              phaseEvents.push({
                key: key,
                phase: phase,
                start: fight.start_time + (phase.filter.time || 0) + (phase.filter.offset || 0),
                end: null,
              });
              break;
            }
            case 'health': {
              const enemy = report.enemies.find(enemy => enemy.guid === phase.filter.guid);
              if(enemy){
                const healthEvent = events.find(e => e.targetID === enemy.id && (e.hitPoints / e.maxHitPoints) <= phase.filter.health);
                if(healthEvent){
                  phaseEvents.push({
                    key: key,
                    phase: phase,
                    start: healthEvent.timestamp + (phase.filter.offset || 0),
                    end: null,
                  });
                }
              }
              break;
            }
            case 'adds': {
              const enemy = report.enemies.find(enemy => enemy.guid === phase.filter.guid);
              if (enemy) {
                const addEvents = events.filter(e => e.sourceID === enemy.id || e.targetID === enemy.id);

                const spawns = addEvents.filter(e => e.type !== 'death');
                const firstSpawnOfWaveEvents = spawns.reduce((addWaves, spawn) => {
                  const instance = (spawn.targetID === enemy.id ? spawn.targetInstance : spawn.sourceInstance) - 1;
                  const wave = Math.floor(instance / phase.filter.addCount);
                  if (!addWaves[wave]) {
                    addWaves[wave] = spawn.timestamp;
                  }
                  return addWaves;
                }, []);

                firstSpawnOfWaveEvents.forEach((timestamp, index) => {
                  phaseEvents.push({
                    key: `${key}_${index}`,
                    phase: phase,
                    start: timestamp + (phase.filter.offset || 0),
                    end: null,
                  });
                });

                const deaths = addEvents.filter(e => e.type === 'death').reverse();
                const lastDeathOfWaveEvents = deaths.reduce((addWaves, death) => {
                  const instance = (death.targetID === enemy.id ? death.targetInstance : death.sourceInstance) - 1;
                  const wave = Math.floor(instance / phase.filter.addCount);
                  if (!addWaves[wave]) {
                    addWaves[wave] = death.timestamp;
                  }
                  return addWaves;
                }, []);

                lastDeathOfWaveEvents.forEach((timestamp, index) => {
                  const startIndex = phaseEvents.findIndex(event => event.key === `${key}_${index}`);
                  phaseEvents[startIndex].end = timestamp;
                });
              }
              break;
            }
            default:
              break;
          }
        }
      });
    }
  }

  phaseEvents.sort((a, b) => a.start - b.start);
  phaseEvents.filter((event, index, array) => {
    return index === 0 || event.key !== array[index - 1].key; //only keep events that arent preceded by another start event of the same phase
  }).forEach((event, _, array) => {
    if (!event.end) {
      const nextMainPhase = array.find(next => !next.end && next.key !== event.key);
      if (nextMainPhase) {
        event.end = nextMainPhase.start;
      } else {
        event.end = fight.end_time;
      }
    }
    if(!phaseInstances[event.key]){
      phaseInstances[event.key] = 0;
    }
    createPhaseStartEvent(event.start, {key: event.key, instance: phaseInstances[event.key], ...event.phase}, bossPhaseEvents);
    createPhaseEndEvent(event.end, {key: event.key, instance: phaseInstances[event.key], ...event.phase}, bossPhaseEvents);
    phaseInstances[event.key] += 1;
  });
  return bossPhaseEvents;
}

function createPhaseStartEvent(timestamp, phase, events) {
  const phaseStartEvent = {
    timestamp: timestamp,
    phase: phase,
    type: PHASE_START_EVENT_TYPE,
    __fabricated: true,
  };

  events.push(phaseStartEvent);
}

function createPhaseEndEvent(timestamp, phase, events) {
  const phaseEndEvent = {
    timestamp: timestamp,
    phase: phase,
    type: PHASE_END_EVENT_TYPE,
    __fabricated: true,
  };

  events.push(phaseEndEvent);
}
<commit_msg>oops<commit_after>import { findByBossId } from 'raids';

export const PHASE_START_EVENT_TYPE = 'phasestart';
export const PHASE_END_EVENT_TYPE = 'phaseend';

export const abilityFilter = { //filter used for abilities that don't use the default "ability" filter
  "interrupt": "stoppedability",
  "dispel": "stoppedability",
};

/**
 * Creates Phase filter events as defined in boss configs via phase.filter.type
 * All filters can be passed a filter.offset attribute that shifts how many milliseconds after the specicifed event the phase starts (can be negative)
 * Supported filter event types:
 *
 * Buff filters:
 *  - removebuff
 *  - applybuff
 *  - removedebuff
 *  - applydebuff
 *
 *  Buff filters require filter.ability.id to be set for the Ability to search for
 *
 * Cast filters:
 *  - begincast
 *  - cast
 *  - interrupt
 *  - dispel
 *
 * Cast filters require filter.ability.id to be set for the Ability to search for
 *
 * Time filters:
 * - time
 *
 * Time filters require filter.time to be set as the milliseconds from combat begin to set the phase event at
 *
 * Health filters:
 * - health
 *
 * Health filters require filter.guid for the mob to check as well as filter.health for the health percentage threshold that triggers this transition (currently only works for phase starting when boss drops BELOW this percentage)
 */
export function fabricateBossPhaseEvents(events, report, fight) {
  const bossConfig = findByBossId(fight.boss);
  const fightDifficulty = fight.difficulty;

  const phaseEvents = [];
  const bossPhaseEvents = [];
  const phaseInstances = {};
  if (bossConfig && bossConfig.fight && bossConfig.fight.phases && bossConfig.fight.phases.length !== 0) {
    const phasesKeys = Object.keys(bossConfig.fight.phases).filter(key => bossConfig.fight.phases[key].difficulties.includes(fightDifficulty));

    if (phasesKeys && phasesKeys.length !== 0) {
      phaseEvents.push({
        key: phasesKeys[0],
        phase: bossConfig.fight.phases[phasesKeys[0]],
        start: fight.start_time,
        end: null,
      });

      phasesKeys.forEach(key => {
        const phase = bossConfig.fight.phases[key];
        if (phase.filter && phase.filter.type) {
          switch (phase.filter.type) {
            case 'removebuff':
            case 'applybuff':
            case 'removedebuff':
            case 'applydebuff':
            case 'begincast':
            case 'interrupt':
            case 'dispel':
            case 'cast': {
              let bossEvents = events.filter(e => e.type === phase.filter.type && (e.ability.guid === phase.filter.ability.id || e.extraAbility.guid === phase.filter.ability.id));
              if (phase.filter.eventInstance !== undefined && phase.filter.eventInstance >= 0 && !phase.multiple) {
                if (bossEvents.length >= (phase.filter.eventInstance + 1)) {
                  // If the instance exists, only that specific instance is relevant
                  bossEvents = [bossEvents[phase.filter.eventInstance]];
                } else {
                  // Otherwise the phase does not exist (it was probably a wipe)
                  console.warn('Phase not found:', phase);
                  break;
                }
              }
              bossEvents.forEach(bossEvent => {
                phaseEvents.push({
                  key: key,
                  phase: phase,
                  start: bossEvent.timestamp + (phase.filter.offset || 0),
                  end: null,
                });
              });
              break;
            }
            case 'time': {
              phaseEvents.push({
                key: key,
                phase: phase,
                start: fight.start_time + (phase.filter.time || 0) + (phase.filter.offset || 0),
                end: null,
              });
              break;
            }
            case 'health': {
              const enemy = report.enemies.find(enemy => enemy.guid === phase.filter.guid);
              if(enemy){
                const healthEvent = events.find(e => e.targetID === enemy.id && (100 * e.hitPoints / e.maxHitPoints) <= phase.filter.health);
                if(healthEvent){
                  phaseEvents.push({
                    key: key,
                    phase: phase,
                    start: healthEvent.timestamp + (phase.filter.offset || 0),
                    end: null,
                  });
                }
              }
              break;
            }
            case 'adds': {
              const enemy = report.enemies.find(enemy => enemy.guid === phase.filter.guid);
              if (enemy) {
                const addEvents = events.filter(e => e.sourceID === enemy.id || e.targetID === enemy.id);

                const spawns = addEvents.filter(e => e.type !== 'death');
                const firstSpawnOfWaveEvents = spawns.reduce((addWaves, spawn) => {
                  const instance = (spawn.targetID === enemy.id ? spawn.targetInstance : spawn.sourceInstance) - 1;
                  const wave = Math.floor(instance / phase.filter.addCount);
                  if (!addWaves[wave]) {
                    addWaves[wave] = spawn.timestamp;
                  }
                  return addWaves;
                }, []);

                firstSpawnOfWaveEvents.forEach((timestamp, index) => {
                  phaseEvents.push({
                    key: `${key}_${index}`,
                    phase: phase,
                    start: timestamp + (phase.filter.offset || 0),
                    end: null,
                  });
                });

                const deaths = addEvents.filter(e => e.type === 'death').reverse();
                const lastDeathOfWaveEvents = deaths.reduce((addWaves, death) => {
                  const instance = (death.targetID === enemy.id ? death.targetInstance : death.sourceInstance) - 1;
                  const wave = Math.floor(instance / phase.filter.addCount);
                  if (!addWaves[wave]) {
                    addWaves[wave] = death.timestamp;
                  }
                  return addWaves;
                }, []);

                lastDeathOfWaveEvents.forEach((timestamp, index) => {
                  const startIndex = phaseEvents.findIndex(event => event.key === `${key}_${index}`);
                  phaseEvents[startIndex].end = timestamp;
                });
              }
              break;
            }
            default:
              break;
          }
        }
      });
    }
  }

  phaseEvents.sort((a, b) => a.start - b.start);
  phaseEvents.filter((event, index, array) => {
    return index === 0 || event.key !== array[index - 1].key; //only keep events that arent preceded by another start event of the same phase
  }).forEach((event, _, array) => {
    if (!event.end) {
      const nextMainPhase = array.find(next => !next.end && next.key !== event.key);
      if (nextMainPhase) {
        event.end = nextMainPhase.start;
      } else {
        event.end = fight.end_time;
      }
    }
    if(!phaseInstances[event.key]){
      phaseInstances[event.key] = 0;
    }
    createPhaseStartEvent(event.start, {key: event.key, instance: phaseInstances[event.key], ...event.phase}, bossPhaseEvents);
    createPhaseEndEvent(event.end, {key: event.key, instance: phaseInstances[event.key], ...event.phase}, bossPhaseEvents);
    phaseInstances[event.key] += 1;
  });
  return bossPhaseEvents;
}

function createPhaseStartEvent(timestamp, phase, events) {
  const phaseStartEvent = {
    timestamp: timestamp,
    phase: phase,
    type: PHASE_START_EVENT_TYPE,
    __fabricated: true,
  };

  events.push(phaseStartEvent);
}

function createPhaseEndEvent(timestamp, phase, events) {
  const phaseEndEvent = {
    timestamp: timestamp,
    phase: phase,
    type: PHASE_END_EVENT_TYPE,
    __fabricated: true,
  };

  events.push(phaseEndEvent);
}
<|endoftext|>


<commit_before>// +----------------------------------------------------------------------+
// | Bitflux Editor                                                       |
// +----------------------------------------------------------------------+
// | Copyright (c) 2003 Bitflux GmbH                                      |
// +----------------------------------------------------------------------+
// | This software is published under the terms of the Apache Software    |
// | License a copy of which has been included with this distribution in  |
// | the LICENSE file and is available through the web at                 |
// | http://bitflux.ch/editor/license.html                                |
// +----------------------------------------------------------------------+
// | Author: Christian Stocker <chregu@bitflux.ch>                        |
// +----------------------------------------------------------------------+
//
// $Id: bxeLoader.js,v 1.246 2004/02/26 17:43:25 chregu Exp $

const BXE_VERSION = "0.9.3";
const BXE_BUILD = "200402261654"

const E_FATAL = 1;

if (window.location.protocol == "file:" || window.location.host.match(/localhost.*/)) {
	var DebugOutput = false;
} else {
	var DebugOutput = false;
}

mozile_js_files = new Array();

mozile_js_files.push("mozile/mozWrappers.js");
mozile_js_files.push("js/widget.js");

mozile_js_files.push("mozile/eDOM.js");
mozile_js_files.push("js/bxeConfig.js");
mozile_js_files.push("mozile/eDOMXHTML.js");
mozile_js_files.push("js/bxeXMLNode.js");
mozile_js_files.push("js/bxeNodeElements.js");
mozile_js_files.push("js/bxeXMLDocument.js");
mozile_js_files.push("mozile/mozileTransportDriver.js");
mozile_js_files.push("mozile/td/http.js");
mozile_js_files.push("mozile/domlevel3.js");
mozile_js_files.push("mozile/mozCE.js");
mozile_js_files.push("mozile/mozIECE.js");
//mozile_js_files.push("mozile/mozileModify.js");
mozile_js_files.push("js/eDOMEvents.js");
mozile_js_files.push("js/bxeFunctions.js");

var mozile_root_dir = "./";

// some global vars, no need to change them
var mozile_corescript_loaded = 0;
var mozile_script_loaded = 0;
var bxe_config = new Object();
var bxe_about_box = null;
var bxe_format_list = null;
var bxe_context_menu = null;
var bxe_delayedUpdate = false;
var eDOM_bxe_mode = true;
var bxe_editable_page = true;

var startTimer = new Date();

function bxe_start(config_file,fromUrl, configArray) {

	/*if (! (BX_checkUnsupportedBrowsers())) {
		return false;
	}*/

	if((navigator.product == 'Gecko') && (navigator.userAgent.indexOf("Safari") == -1))
	{
		// navigator.productSub > '20020801' (test to see what the date should be)

		// POST04: if document.documentElement != HTML then ... or no "head" ...
		var head = document.getElementsByTagName("head")[0];
		bxe_config.file = config_file;
		bxe_config.fromUrl = fromUrl;
		bxe_config.configArray = configArray;
		if(head)
		{
			// get the location of this script and reuse it for the others
			for(var i=0; i<head.childNodes.length; i++)
			{
				var mozileLoaderRE = /(.*)bxeLoader.js$/;
				if(head.childNodes[i].localName == "SCRIPT")
				{
					var src = head.childNodes[i].src;
					var result = mozileLoaderRE.exec(src);
					if(result)
					{
						mozile_root_dir = result[1];
						break;
					}
				}
			}
			for (var i=0; i < mozile_js_files.length; i++)
			{
				var scr = document.createElementNS("http://www.w3.org/1999/xhtml","script");
				var src = mozile_root_dir + mozile_js_files[i];
				if (mozile_js_files[i] == "js/widget.js") {
					scr.onload = widget_loaded;
				} else {
					scr.onload = corescript_loaded;
				}
				scr.setAttribute("src", src);

				scr.setAttribute("language","JavaScript");
				head.appendChild(scr);
			}
			//when last include src is loaded, call onload handler

		}
		else {
			alert("*** ALERT: MozileLoader only works in (X)HTML - load Mozile JS explicitly in XML files");
		}
	}

}

function bxe_load_xml (xmlfile) {

	var td = new mozileTransportDriver("webdav");
	function callback (e) {
		if (e.isError) {
			alert("Error loading xml file \n"+e.statusText);
			return false;
		}
		var xmldoc =  e.document;
		bxe_config.xmldoc = xmldoc;
		xmldoc.init();
		if (bxe_config.xhtmlfile) {
			xmldoc.importXHTMLDocument(bxe_config.xhtmlfile)
		} else if (bxe_config.xslfile) {
			xmldoc.transformToXPathMode(bxe_config.xslfile)
		} else {
			xmldoc.insertIntoHTMLDocument();
			xml_loaded(xmldoc);
		}

	}
	td.load(xmlfile,callback);

	return true;
}

function widget_loaded(e) {
	bxe_about_box = new Widget_AboutBox();
	bxe_about_box.draw();
	bxe_about_box.setText("Loading files ...");
	corescript_loaded(e);

}

function corescript_loaded(e) {
	mozile_corescript_loaded++;
	debug("from core script " + mozile_corescript_loaded + " of " + mozile_js_files.length  + "loaded: " + e.currentTarget.src);
	if ( mozile_js_files.length == mozile_corescript_loaded) {
		debug("call mozile_core_loaded()");
		mozile_core_loaded();
	} else {
		if (bxe_about_box) {
			bxe_about_box.addText(mozile_corescript_loaded );
		}
	}
}

function script_loaded(e) {
	mozile_script_loaded++;
	debug("from config script " + mozile_script_loaded + " loaded: " + e.currentTarget.src );
	if ( bxe_config.scriptfiles.length == mozile_script_loaded ) {
		debug("call mozile_loaded()");
		mozile_loaded();
	} else {
		bxe_about_box.addText(mozile_script_loaded );
	}
}

function mozile_core_loaded() {
	bxe_about_box.addText("Scripts loaded ...");
	bxe_about_box.addText("Load Config ");
	try {
		bxe_config = new bxeConfig(bxe_config.file, bxe_config.fromUrl, bxe_config.configArray);
	} catch (e) {
		bxe_catch_alert(e);
	}
}

function mozile_loaded() {
	defaultContainerName = "p";

	bxe_about_box.addText("Load XML ...");
	bxe_load_xml(bxe_config.xmlfile);

}

function xml_loaded(xmldoc) {
	bxe_about_box.addText("Load RelaxNG " + bxe_config.validationfile + " ");
	if (!(bxe_config.validationfile && xmldoc.XMLNode.loadSchema(bxe_config.validationfile,validation_loaded))) {
		bxe_about_box.addText("RelaxNG File was not found");
	}
	bxe_history_snapshot();
	document.eDOMaddEventListener("toggleSourceMode",bxe_toggleSourceMode,false);
	document.eDOMaddEventListener("toggleTagMode",bxe_toggleTagMode,false);
	document.eDOMaddEventListener("toggleNormalMode",bxe_toggleNormalMode,false);
	document.eDOMaddEventListener("DocumentSave",__bxeSave,false);
	document.eDOMaddEventListener("ToggleTextClass",bxe_toggleTextClass,false);
	document.eDOMaddEventListener("appendNode",bxe_appendNode,false);
	document.eDOMaddEventListener("appendChildNode",bxe_appendChildNode,false);
	document.eDOMaddEventListener("InsertLink",bxe_InsertLink,false);
	document.eDOMaddEventListener("DeleteLink",bxe_DeleteLink,false);
	document.eDOMaddEventListener("CleanInline",bxe_CleanInline,false);
	document.eDOMaddEventListener("InsertTable",bxe_InsertTable,false);
	document.eDOMaddEventListener("InsertImage",bxe_InsertObject,false);
	document.eDOMaddEventListener("InsertAsset",bxe_InsertAsset,false);
	document.eDOMaddEventListener("OrderedList",bxe_OrderedList,false);
	document.eDOMaddEventListener("UnorderedList",bxe_UnorderedList,false);

	document.eDOMaddEventListener("changeLinesContainer",bxe_changeLinesContainer,false);
	document.eDOMaddEventListener("Exit",bxe_exit,false);

	document.eDOMaddEventListener("ClipboardCopy",function(e) { window.getSelection().copy()},false);
	document.eDOMaddEventListener("ClipboardPaste",function(e) { window.getSelection().paste()},false);
	document.eDOMaddEventListener("ClipboardCut",function(e) { window.getSelection().cut()},false);

	document.eDOMaddEventListener("Undo",function(e) { bxe_history_undo()}, false);
	document.eDOMaddEventListener("Redo",function(e) { bxe_history_redo()}, false);

	//document.eDOMaddEventListener("NodeInsertedParent",bxe_NodeInsertedParent, false);
	document.eDOMaddEventListener("NodeInsertedBefore",bxe_NodeInsertedBefore,false);
	document.eDOMaddEventListener("NodeBeforeDelete",bxe_NodeBeforeDelete,false);
	/*document.eDOMaddEventListener("NodeChanged",bxe_NodeChanged,false);

	document.eDOMaddEventListener("NodeRemovedChildOnly",bxe_NodeRemovedChildOnly,false);
	*/
	//document.eDOMaddEventListener("NodeAppendedChild",bxe_NodeAppendedChild,false);
	document.eDOMaddEventListener("NodePositionChanged",bxe_NodePositionChanged,false);

	document.eDOMaddEventListener("ContextPopup",bxe_ContextPopup,false);
	document.addEventListener("contextmenu",bxe_ContextMenuEvent, false);

	bxe_context_menu = new Widget_ContextMenu();
}

function validation_loaded(vdom) {
	bxe_about_box.addText("Validation Loaded ...");

	var vali = bxe_config.xmldoc.XMLNode.validateDocument();
	if (!vali) {
		bxe_about_box.addText("Document is *not* valid.");
		//alert(vali.getErrorMessagesAsText());
		bxe_disableEditablePage();
	}
	else {
		bxe_about_box.addText("<br/>Document is valid.");

	}
	var endTimer = new Date();
	debug ("startTime: " + startTimer);
	debug ("endTime  : " + endTimer);
	dump ("Total Start Time: " + (endTimer - startTimer)/1000 + " sec\n");
}

function config_loaded(bxe_config_in) {
	bxe_about_box.addText("& Parsed ...");

	bxe_config = bxe_config_in;
	var head = document.getElementsByTagName("head")[0];
	for (var i=0; i < bxe_config.cssfiles.length; i++)
	{
		var scr = document.createElementNS(XHTMLNS,"link");
		scr.setAttribute("type","text/css");
		scr.setAttribute("rel","stylesheet");
		scr.setAttribute("href",bxe_config.cssfiles[i]);
		head.appendChild(scr);
	}

	for (var i=0; i < bxe_config.scriptfiles.length; i++)
	{
		var scr = document.createElementNS("http://www.w3.org/1999/xhtml","script");
		var src = mozile_root_dir + bxe_config.scriptfiles[i];
		scr.onload = script_loaded;
		scr.setAttribute("src", src);
		scr.setAttribute("language","JavaScript");
		head.appendChild(scr);
	}

}

debug = function (text, options) {

	if (DebugOutput) {

		fn =  bxe_getCaller(debug);

		dump( fn.getName() +  ": " + text + "\n");
		if (options) {

			if (options.evalArguments) {
				var fname= fn.getName();
					dump("  Arguments: (");
					for (var i = 0; i < fn.arguments.length; i++) {
						dump("[" + typeof fn.arguments[i] + "] ");
						switch( typeof( fn.arguments[i] )) {
							case "function":
								dump (fn.arguments[i].getName());
								break;
							case "string":
								dump('"' + fn.arguments[i] + '"');
								break;
							default:
								dump(fn.arguments[i]);
						}
						if (i < fn.arguments.length - 1 ) {
							dump(", ");
						}
					}
					dump (")\n");

			}

			if (options.callstack) {
				var callstack =  new Array();
				dump ("  Callstack: \n");
				dump ("  ----------\n");
				while (fn) {

					callstack.unshift(fn.getName() + "\n");
					var newfn = JsUtil_getCaller(fn);
					if (fn == newfn) {
						callstack.unshift ("  [javascript recursion]\n");
						break;
					}
					fn = newfn;
				}
				for (var i = 0; i < callstack.length; i++) {
					dump("  " +i +": " +callstack[i]);
				}
				dump ("  ----------\n");

			}

		}

	}
}

Function.prototype.getName = function () {
	var r = /function (\w*)([^\{\}]*\))/
	var s = new String( this);
	var m = s.match( r );
	if (m) {
		var f = "";
		if (m[1]) {
			f = m[1];
		} else {
			r = /var id = "(.+)"/;
			var n = s.match(r);
			if (n && n[1]) {
				f = n[1];
			} else {
				f = "anoynmous function";
			}
		}
		var args = m[2];
		return (f+args);
	} else {
		return "anonymous function";
	}
}

function bxe_getCaller( fn )
{
	switch( typeof( fn ))
	{
		case "undefined":
			return bxe_getCaller( bxe_getCaller );

		case "function":
			if( fn.caller )
				return fn.caller;
			if( fn.arguments && fn.arguments.caller )
				return fn.arguments.caller;
	}
	return undefined;
}

<commit_msg>bump up version number<commit_after>// +----------------------------------------------------------------------+
// | Bitflux Editor                                                       |
// +----------------------------------------------------------------------+
// | Copyright (c) 2003 Bitflux GmbH                                      |
// +----------------------------------------------------------------------+
// | This software is published under the terms of the Apache Software    |
// | License a copy of which has been included with this distribution in  |
// | the LICENSE file and is available through the web at                 |
// | http://bitflux.ch/editor/license.html                                |
// +----------------------------------------------------------------------+
// | Author: Christian Stocker <chregu@bitflux.ch>                        |
// +----------------------------------------------------------------------+
//
// $Id: bxeLoader.js,v 1.247 2004/02/26 17:52:55 chregu Exp $

const BXE_VERSION = "0.9.4-dev";
const BXE_BUILD = "200402261654"

const E_FATAL = 1;

if (window.location.protocol == "file:" || window.location.host.match(/localhost.*/)) {
	var DebugOutput = false;
} else {
	var DebugOutput = false;
}

mozile_js_files = new Array();

mozile_js_files.push("mozile/mozWrappers.js");
mozile_js_files.push("js/widget.js");

mozile_js_files.push("mozile/eDOM.js");
mozile_js_files.push("js/bxeConfig.js");
mozile_js_files.push("mozile/eDOMXHTML.js");
mozile_js_files.push("js/bxeXMLNode.js");
mozile_js_files.push("js/bxeNodeElements.js");
mozile_js_files.push("js/bxeXMLDocument.js");
mozile_js_files.push("mozile/mozileTransportDriver.js");
mozile_js_files.push("mozile/td/http.js");
mozile_js_files.push("mozile/domlevel3.js");
mozile_js_files.push("mozile/mozCE.js");
mozile_js_files.push("mozile/mozIECE.js");
//mozile_js_files.push("mozile/mozileModify.js");
mozile_js_files.push("js/eDOMEvents.js");
mozile_js_files.push("js/bxeFunctions.js");

var mozile_root_dir = "./";

// some global vars, no need to change them
var mozile_corescript_loaded = 0;
var mozile_script_loaded = 0;
var bxe_config = new Object();
var bxe_about_box = null;
var bxe_format_list = null;
var bxe_context_menu = null;
var bxe_delayedUpdate = false;
var eDOM_bxe_mode = true;
var bxe_editable_page = true;

var startTimer = new Date();

function bxe_start(config_file,fromUrl, configArray) {

	/*if (! (BX_checkUnsupportedBrowsers())) {
		return false;
	}*/

	if((navigator.product == 'Gecko') && (navigator.userAgent.indexOf("Safari") == -1))
	{
		// navigator.productSub > '20020801' (test to see what the date should be)

		// POST04: if document.documentElement != HTML then ... or no "head" ...
		var head = document.getElementsByTagName("head")[0];
		bxe_config.file = config_file;
		bxe_config.fromUrl = fromUrl;
		bxe_config.configArray = configArray;
		if(head)
		{
			// get the location of this script and reuse it for the others
			for(var i=0; i<head.childNodes.length; i++)
			{
				var mozileLoaderRE = /(.*)bxeLoader.js$/;
				if(head.childNodes[i].localName == "SCRIPT")
				{
					var src = head.childNodes[i].src;
					var result = mozileLoaderRE.exec(src);
					if(result)
					{
						mozile_root_dir = result[1];
						break;
					}
				}
			}
			for (var i=0; i < mozile_js_files.length; i++)
			{
				var scr = document.createElementNS("http://www.w3.org/1999/xhtml","script");
				var src = mozile_root_dir + mozile_js_files[i];
				if (mozile_js_files[i] == "js/widget.js") {
					scr.onload = widget_loaded;
				} else {
					scr.onload = corescript_loaded;
				}
				scr.setAttribute("src", src);

				scr.setAttribute("language","JavaScript");
				head.appendChild(scr);
			}
			//when last include src is loaded, call onload handler

		}
		else {
			alert("*** ALERT: MozileLoader only works in (X)HTML - load Mozile JS explicitly in XML files");
		}
	}

}

function bxe_load_xml (xmlfile) {

	var td = new mozileTransportDriver("webdav");
	function callback (e) {
		if (e.isError) {
			alert("Error loading xml file \n"+e.statusText);
			return false;
		}
		var xmldoc =  e.document;
		bxe_config.xmldoc = xmldoc;
		xmldoc.init();
		if (bxe_config.xhtmlfile) {
			xmldoc.importXHTMLDocument(bxe_config.xhtmlfile)
		} else if (bxe_config.xslfile) {
			xmldoc.transformToXPathMode(bxe_config.xslfile)
		} else {
			xmldoc.insertIntoHTMLDocument();
			xml_loaded(xmldoc);
		}

	}
	td.load(xmlfile,callback);

	return true;
}

function widget_loaded(e) {
	bxe_about_box = new Widget_AboutBox();
	bxe_about_box.draw();
	bxe_about_box.setText("Loading files ...");
	corescript_loaded(e);

}

function corescript_loaded(e) {
	mozile_corescript_loaded++;
	debug("from core script " + mozile_corescript_loaded + " of " + mozile_js_files.length  + "loaded: " + e.currentTarget.src);
	if ( mozile_js_files.length == mozile_corescript_loaded) {
		debug("call mozile_core_loaded()");
		mozile_core_loaded();
	} else {
		if (bxe_about_box) {
			bxe_about_box.addText(mozile_corescript_loaded );
		}
	}
}

function script_loaded(e) {
	mozile_script_loaded++;
	debug("from config script " + mozile_script_loaded + " loaded: " + e.currentTarget.src );
	if ( bxe_config.scriptfiles.length == mozile_script_loaded ) {
		debug("call mozile_loaded()");
		mozile_loaded();
	} else {
		bxe_about_box.addText(mozile_script_loaded );
	}
}

function mozile_core_loaded() {
	bxe_about_box.addText("Scripts loaded ...");
	bxe_about_box.addText("Load Config ");
	try {
		bxe_config = new bxeConfig(bxe_config.file, bxe_config.fromUrl, bxe_config.configArray);
	} catch (e) {
		bxe_catch_alert(e);
	}
}

function mozile_loaded() {
	defaultContainerName = "p";

	bxe_about_box.addText("Load XML ...");
	bxe_load_xml(bxe_config.xmlfile);

}

function xml_loaded(xmldoc) {
	bxe_about_box.addText("Load RelaxNG " + bxe_config.validationfile + " ");
	if (!(bxe_config.validationfile && xmldoc.XMLNode.loadSchema(bxe_config.validationfile,validation_loaded))) {
		bxe_about_box.addText("RelaxNG File was not found");
	}
	bxe_history_snapshot();
	document.eDOMaddEventListener("toggleSourceMode",bxe_toggleSourceMode,false);
	document.eDOMaddEventListener("toggleTagMode",bxe_toggleTagMode,false);
	document.eDOMaddEventListener("toggleNormalMode",bxe_toggleNormalMode,false);
	document.eDOMaddEventListener("DocumentSave",__bxeSave,false);
	document.eDOMaddEventListener("ToggleTextClass",bxe_toggleTextClass,false);
	document.eDOMaddEventListener("appendNode",bxe_appendNode,false);
	document.eDOMaddEventListener("appendChildNode",bxe_appendChildNode,false);
	document.eDOMaddEventListener("InsertLink",bxe_InsertLink,false);
	document.eDOMaddEventListener("DeleteLink",bxe_DeleteLink,false);
	document.eDOMaddEventListener("CleanInline",bxe_CleanInline,false);
	document.eDOMaddEventListener("InsertTable",bxe_InsertTable,false);
	document.eDOMaddEventListener("InsertImage",bxe_InsertObject,false);
	document.eDOMaddEventListener("InsertAsset",bxe_InsertAsset,false);
	document.eDOMaddEventListener("OrderedList",bxe_OrderedList,false);
	document.eDOMaddEventListener("UnorderedList",bxe_UnorderedList,false);

	document.eDOMaddEventListener("changeLinesContainer",bxe_changeLinesContainer,false);
	document.eDOMaddEventListener("Exit",bxe_exit,false);

	document.eDOMaddEventListener("ClipboardCopy",function(e) { window.getSelection().copy()},false);
	document.eDOMaddEventListener("ClipboardPaste",function(e) { window.getSelection().paste()},false);
	document.eDOMaddEventListener("ClipboardCut",function(e) { window.getSelection().cut()},false);

	document.eDOMaddEventListener("Undo",function(e) { bxe_history_undo()}, false);
	document.eDOMaddEventListener("Redo",function(e) { bxe_history_redo()}, false);

	//document.eDOMaddEventListener("NodeInsertedParent",bxe_NodeInsertedParent, false);
	document.eDOMaddEventListener("NodeInsertedBefore",bxe_NodeInsertedBefore,false);
	document.eDOMaddEventListener("NodeBeforeDelete",bxe_NodeBeforeDelete,false);
	/*document.eDOMaddEventListener("NodeChanged",bxe_NodeChanged,false);

	document.eDOMaddEventListener("NodeRemovedChildOnly",bxe_NodeRemovedChildOnly,false);
	*/
	//document.eDOMaddEventListener("NodeAppendedChild",bxe_NodeAppendedChild,false);
	document.eDOMaddEventListener("NodePositionChanged",bxe_NodePositionChanged,false);

	document.eDOMaddEventListener("ContextPopup",bxe_ContextPopup,false);
	document.addEventListener("contextmenu",bxe_ContextMenuEvent, false);

	bxe_context_menu = new Widget_ContextMenu();
}

function validation_loaded(vdom) {
	bxe_about_box.addText("Validation Loaded ...");

	var vali = bxe_config.xmldoc.XMLNode.validateDocument();
	if (!vali) {
		bxe_about_box.addText("Document is *not* valid.");
		//alert(vali.getErrorMessagesAsText());
		bxe_disableEditablePage();
	}
	else {
		bxe_about_box.addText("<br/>Document is valid.");

	}
	var endTimer = new Date();
	debug ("startTime: " + startTimer);
	debug ("endTime  : " + endTimer);
	dump ("Total Start Time: " + (endTimer - startTimer)/1000 + " sec\n");
}

function config_loaded(bxe_config_in) {
	bxe_about_box.addText("& Parsed ...");

	bxe_config = bxe_config_in;
	var head = document.getElementsByTagName("head")[0];
	for (var i=0; i < bxe_config.cssfiles.length; i++)
	{
		var scr = document.createElementNS(XHTMLNS,"link");
		scr.setAttribute("type","text/css");
		scr.setAttribute("rel","stylesheet");
		scr.setAttribute("href",bxe_config.cssfiles[i]);
		head.appendChild(scr);
	}

	for (var i=0; i < bxe_config.scriptfiles.length; i++)
	{
		var scr = document.createElementNS("http://www.w3.org/1999/xhtml","script");
		var src = mozile_root_dir + bxe_config.scriptfiles[i];
		scr.onload = script_loaded;
		scr.setAttribute("src", src);
		scr.setAttribute("language","JavaScript");
		head.appendChild(scr);
	}

}

debug = function (text, options) {

	if (DebugOutput) {

		fn =  bxe_getCaller(debug);

		dump( fn.getName() +  ": " + text + "\n");
		if (options) {

			if (options.evalArguments) {
				var fname= fn.getName();
					dump("  Arguments: (");
					for (var i = 0; i < fn.arguments.length; i++) {
						dump("[" + typeof fn.arguments[i] + "] ");
						switch( typeof( fn.arguments[i] )) {
							case "function":
								dump (fn.arguments[i].getName());
								break;
							case "string":
								dump('"' + fn.arguments[i] + '"');
								break;
							default:
								dump(fn.arguments[i]);
						}
						if (i < fn.arguments.length - 1 ) {
							dump(", ");
						}
					}
					dump (")\n");

			}

			if (options.callstack) {
				var callstack =  new Array();
				dump ("  Callstack: \n");
				dump ("  ----------\n");
				while (fn) {

					callstack.unshift(fn.getName() + "\n");
					var newfn = JsUtil_getCaller(fn);
					if (fn == newfn) {
						callstack.unshift ("  [javascript recursion]\n");
						break;
					}
					fn = newfn;
				}
				for (var i = 0; i < callstack.length; i++) {
					dump("  " +i +": " +callstack[i]);
				}
				dump ("  ----------\n");

			}

		}

	}
}

Function.prototype.getName = function () {
	var r = /function (\w*)([^\{\}]*\))/
	var s = new String( this);
	var m = s.match( r );
	if (m) {
		var f = "";
		if (m[1]) {
			f = m[1];
		} else {
			r = /var id = "(.+)"/;
			var n = s.match(r);
			if (n && n[1]) {
				f = n[1];
			} else {
				f = "anoynmous function";
			}
		}
		var args = m[2];
		return (f+args);
	} else {
		return "anonymous function";
	}
}

function bxe_getCaller( fn )
{
	switch( typeof( fn ))
	{
		case "undefined":
			return bxe_getCaller( bxe_getCaller );

		case "function":
			if( fn.caller )
				return fn.caller;
			if( fn.arguments && fn.arguments.caller )
				return fn.arguments.caller;
	}
	return undefined;
}

<|endoftext|>


<commit_before>javascript:str="RecMessages
~~~~~~~~~Start~~~~~~~~~~~
";
links=document.getElementById("webMessengerRecentMessages").getElementsByTagName("p");
for(i in links){str=str.concat("
".concat(links[i].innerHTML))};
myWin = window.open("", "MsgWindow", "width=500, height=700");
myWin.document.write(str);
myWin.document.write("
~~~~~~~~~End~~~~~~~~~~~
")
//this snippet outputs all loaded messages from a facebook conversation to a new msgBox
<commit_msg>Layout changes<commit_after>//This script works for the url that is in the following form: facebook.com/messages
//javascript:
str=window.location.href;
str= str.concat("
RecMessages

~~~~~~~~~Start~~~~~~~~~~~
");
links=document.getElementById("webMessengerRecentMessages").getElementsByTagName("p");
for(i in links){str=str.concat("
".concat(links[i].innerHTML))};
myWin = window.open("", "MsgWindow", "width=500, height=700");
myWin.document.write(str);
myWin.document.write("
~~~~~~~~~End~~~~~~~~~~~
");
//this snippet outputs all loaded messages from a facebook conversation to a new msgBox
<|endoftext|>


<commit_before>const { cleanJSON } = require("./src/utils");

var isNode = typeof window == "undefined";

/**
 * Expects a Fabric.js Canvas
 */
function ImageMarkupBuilder(fabricCanvas) {
  var Fabric = require("fabric").fabric;

  var Shapes = {
    Rectangle: require("./shapes/rectangle").klass,
    Circle: require("./shapes/circle").klass,
    Line: require("./shapes/line").klass,
    Arrow: require("./shapes/arrow").klass,
    Gap: require("./shapes/gap").klass,
  };

  var colorValues = {
    red: "#C1280B",
    orange: "#FF9024",
    yellow: "#F3E00E",
    green: "#16DC81",
    lightBlue: "#1BB1E9",
    blue: "#2343E8",
    darkBlue: "#2343E8",
    violet: "#DC54B7",
    pink: "#DC54B7",
    black: "#000000",
  };

  // Reference to the json object from processJSON
  var innerJSON;

  var imageOffset;
  var resizeRatio = 1;
  var finalWidth = 0;
  var markupObjects = [];

  var strokeWidth = null;
  var crop = null;

  /**
   * Array of delegate functions to draw a proper shape.
   */
  var addShapeDelegate = {
    arrow: function (data) {
      return drawLineBasedShape(Shapes.Arrow, data);
    },
    gap: function (data) {
      return drawLineBasedShape(Shapes.Gap, data);
    },
    line: function (data) {
      return drawLineBasedShape(Shapes.Line, data);
    },
    circle: drawCircle,
    rectangle: function (data) {
      // Because fabric considers top/left as the center of the rectangle.
      data.from.x -= data.size.width / 2;
      data.from.y -= data.size.height / 2;

      return drawRectangle(data);
    },
  };

  /**
   * Returns true if the given object is off the edge of the fabric canvas.
   * This is defined as whether the color stroke would be the only part of
   * the shape still visible at those coordinates.
   */
  fabricCanvas.isOffScreen = function (object) {
    var rect = object.getBoundingRect();
    return (
      rect.left + rect.width - strokeWidth * 2 < 0 ||
      rect.left + strokeWidth * 2 > this.width ||
      rect.top + rect.height - strokeWidth * 2 < 0 ||
      rect.top + strokeWidth * 2 > this.height
    );
  };

  function applyBackground(callback) {
    if (!isNode) {
      //Listen for shapes falling off the edge and delete
      fabricCanvas.on({
        "object:modified": function (e) {
          var shape = e.target;

          if (fabricCanvas.isOffScreen(shape)) remove(shape);
        }.bind(this),
      });
    }

    //Disable drag selection on canvas
    fabricCanvas.selection = false;

    if (!innerJSON.sourceFile) {
      if (!innerJSON.finalDimensions) {
        var msg = "Need source file or final dimensions to create canvas";
        throw new Error(msg);
      }

      //Apply markup to blank canvas
      applyMarkup(callback);
    } else {
      finalWidth = innerJSON.finalDimensions.width;
      if (isNode) {
        fabricCanvas.setBackgroundColor("#FFFFFF");
        require("fs").readFile(innerJSON.sourceFile, function (err, blob) {
          if (err) throw err;

          var dimensions = innerJSON.dimensions;
          var img = {
            width: dimensions.width,
            height: dimensions.height,
            originX: "left",
            originY: "top",
            src: blob,
          };

          Fabric.Image.fromObject(img, function (fimg) {
            var top = -imageOffset.y;
            if (top % 1 != 0) {
              top -= 0.5;
            }
            var left = -imageOffset.x;
            if (left % 1 != 0) {
              left -= 0.5;
            }

            fabricCanvas.add(fimg.set("top", top).set("left", left));
            applyMarkup(callback);
          });
        });
      } else {
        throw new Error("Source files not supported on frontend");
      }
    }
  }

  /**
   * Applies the given markup instructions to the fabric canvas. If this is
   * server-side, call writeCanvas() to write the results to a file. If
   * client-side, call the client-provided callback directly.
   */
  function applyMarkup(callback) {
    // strokeWidth needs to be caught now, since if it's lower in the JSON
    // it will not get picked up until it's too late.
    if (innerJSON.instructions.strokeWidth) {
      strokeWidth = innerJSON.instructions.strokeWidth;
    }

    for (var instruction in innerJSON.instructions) {
      switch (instruction) {
        case "draw":
          innerJSON.instructions.draw.forEach(function (e) {
            for (var shapeName in e) {
              var shape = e[shapeName];
              shape.shapeName = shapeName;

              switch (shapeName) {
                case "rectangle":
                  drawRectangle(shape);
                  break;
                case "circle":
                  drawCircle(shape);
                  break;
                case "line":
                  drawLineBasedShape(Shapes.Line, shape);
                  break;
                case "gap":
                  drawLineBasedShape(Shapes.Gap, shape);
                  break;
                case "arrow":
                  drawLineBasedShape(Shapes.Arrow, shape);
                  break;
                default:
                  console.error("Unsupported Shape: " + shapeName);
              }
            }
          });
          break;

        //These should already be taken care of
        case "crop":
        case "strokeWidth":
          break;
        default:
          console.error("Unsupported Instruction: " + instruction);
      }
    }

    if (isNode) {
      writeCanvas(callback);
    } else {
      callback(fabricCanvas);
    }
  }

  /**
   * Writes the processed canvas to a file.
   */
  function writeCanvas(callback) {
    fabricCanvas.renderAll();
    var outstream = require("fs").createWriteStream(innerJSON.destinationFile),
      stream = fabricCanvas.createJPEGStream({
        quality: 93,
        progressive: true,
      });

    stream.on("data", function (chunk) {
      outstream.write(chunk);
    });
    stream.on("end", function () {
      callback(fabricCanvas);
    });
  }

  /**
   * Returns a strokeWidth calculated based on the dimensions of the canvas.
   */
  function getStrokeWidth(finalWidth) {
    return strokeWidth != null
      ? strokeWidth
      : Math.max(Math.round((finalWidth / 300) * 2), 4);
  }

  function drawRectangle(shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var rect = {
      left: shape.from.x - imageOffset.x,
      top: shape.from.y - imageOffset.y,
      width: shape.size.width,
      height: shape.size.height,
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };

    //Fabric調整
    rect.top *= resizeRatio;
    rect.left *= resizeRatio;
    rect.width *= resizeRatio;
    rect.height *= resizeRatio;

    var fabricRect = new Shapes.Rectangle(rect);

    markupObjects.push(fabricRect);
    fabricCanvas.add(fabricRect);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricRect);
    }

    return fabricRect;
  }

  function drawLineBasedShape(klass, shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var line = {
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };

    var points = [
      (shape.from.x - imageOffset.x) * resizeRatio,
      (shape.from.y - imageOffset.y) * resizeRatio,
      (shape.to.x - imageOffset.x) * resizeRatio,
      (shape.to.y - imageOffset.y) * resizeRatio,
    ];

    var fabricLine = new klass(points, line);
    fabricLine.color = shape.color;

    markupObjects.push(fabricLine);
    fabricCanvas.add(fabricLine);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricLine);
    }

    return fabricLine;
  }

  function drawCircle(shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var circle = {
      left: shape.from.x - imageOffset.x,
      top: shape.from.y - imageOffset.y,
      radius: shape.radius,
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };
    circle.left *= resizeRatio;
    circle.top *= resizeRatio;
    circle.radius *= resizeRatio;

    var fabricCircle = new Shapes.Circle(circle);
    markupObjects.push(fabricCircle);
    fabricCanvas.add(fabricCircle);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricCircle);
    }

    return fabricCircle;
  }

  function locate(shape) {
    for (var i = 0; i < markupObjects.length; ++i) {
      if (markupObjects[i] === shape) {
        return i;
      }
    }

    return null;
  }

  function remove(shape) {
    var index = locate(shape);
    if (index != null) {
      fabricCanvas.remove(shape);
      markupObjects.splice(index, 1);
    }
  }

  /**
   * Performs ShapeData.type-agnostic attribute checks to ensure that the
   * necessary attributes are set in the given ShapeData object. Specifically,
   * the following operations are performed:
   *
   * - If data.x or data.y are not present, both are set to the center of
   *   the canvas. If both are present, they are scaled to the preview size
   *   of the canvas (if given).
   * - if data.color is not present, it is set to "red".
   */
  function normalizeShapeData(data) {
    data.from.x /= resizeRatio;
    data.from.y /= resizeRatio;
    data.from.x += imageOffset.x;
    data.from.y += imageOffset.y;

    if (data.to) {
      data.to.x /= resizeRatio;
      data.to.y /= resizeRatio;
      data.to.x += imageOffset.x;
      data.to.y += imageOffset.y;
    }

    if (!data.color) {
      data.color = "red";
    }
  }

  var publicInterface = {
    fabricCanvas: fabricCanvas,

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. The act of drawing is delegated to the proper
     * draw method based on its "type" attribute.
     */
    addShape: function addShape(data) {
      if (!data.type) throw "ShapeData.type is not defined.";

      normalizeShapeData(data);
      return addShapeDelegate[data.type](data);
    },

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. This ignores the "type" attribute of the object
     * and just adds a circle. To be deprecated by addShape().
     *
     * @return a reference to the tracked shape.
     */
    addCircle: function addCircle(data) {
      console.warn("Deprecated function: addCircle(). Use addShape() instead.");

      if (data.type !== "circle") data.type = "circle";
      return this.addShape(data);
    },

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. This ignores the "type" attribute of the object
     * and just adds a rectangle. To be deprecated by addShape().
     *
     * @return a reference to the tracked shape.
     */
    addRectangle: function addRectangle(data) {
      console.warn(
        "Deprecated function: addRectangle(). Use addShape() instead."
      );

      if (data.type !== "rectangle") data.type = "rectangle";
      return this.addShape(data);
    },

    /**
     * Sets the color of a tracked shape.
     */
    setColor: function setColor(shape, colorName) {
      shape.stroke = colorValues[colorName];
      shape.color = colorName;

      if (!isNode) fabricCanvas.renderAll();
    },

    /**
     * Returns an array of all objects in the fabric canvas, whether
     * tracked by the Builder or not.
     */
    getShapes: function getShapes() {
      return fabricCanvas._objects;
    },

    /**
     * Returns a reference to the currently selected shape.
     *
     * @return a reference to the currently selected shape.
     */
    getActiveShape: function getActiveShape() {
      return fabricCanvas.getActiveObject();
    },

    /**
     * Removes a specific tracked shape from the fabric canvas.
     */
    removeShape: function removeShape(shape) {
      remove(shape);
    },

    /**
     * Removes all tracked shapes from the fabric canvas.
     */
    removeShapes: function removeShapes() {
      for (var i = 0; i < markupObjects.length; ++i) {
        var shape = markupObjects[i];
        fabricCanvas.remove(shape);
      }

      markupObjects = [];
    },

    /**
     * Grows or shrinks the given shape depending on the given key
     * by the given number of pixels.
     *
     * @param shape The fabric shape to resize.
     * @param increment The number of pixels (negative or positive) to
     *  resize the shape.
     */
    incrementSize: function incrementSize(shape, increment) {
      if (!(typeof increment === "number")) {
        console.error("increment must be a number");
        return;
      }

      shape._withSizeLimitations(function () {
        shape.incrementSize(increment);
        if (fabricCanvas.isOffScreen(shape)) {
          shape.incrementSize(increment * -1);
        }
      }, true);

      shape.setCoords();
      fabricCanvas.renderAll();
    },

    /**
     * Takes a Builder-schema JSON object and performs the operations
     * listed therein, then calls the callback function.
     */
    processJSON: function processJSON(json, callback) {
      //Make sure not to render every addition on server end
      fabricCanvas.renderOnAddition = !isNode;
      fabricCanvas.uniScaleTransform = true;

      cleanJSON(json);

      innerJSON = json;

      crop = innerJSON.instructions.crop;
      imageOffset =
        typeof crop != "undefined"
          ? {
              x: crop.from.x,
              y: crop.from.y,
            }
          : {
              x: 0,
              y: 0,
            };

      if (innerJSON.resizeRatio) {
        resizeRatio = innerJSON.resizeRatio;
      }

      applyBackground(callback);
    },

    getMarkupObjects: function getMarkupObjects() {
      return markupObjects;
    },

    /**
     * Returns a markup string which can be used with ImageMarkupCall
     * to produce the same markup results as represented in this builder.
     */
    getMarkupString: function getMarkupString() {
      /**
       * Translate RGB value to applicable color string. Unknown
       * RGB values will will become black and a console.error message
       * will be written.
       */
      function translateRGBtoColorString(rgb) {
        for (var colorString in colorValues) {
          if (rgb == colorValues[colorString]) {
            return colorString;
          }
        }
        console.error("No mapping from RGB to Color String Found: " + rgb);
        return "black";
      }

      var markupString = ";";

      if (crop) {
        // Cut out cases where crop is used to offset non-4:3
        // images
        markupString +=
          "crop," +
          crop.from.x +
          "x" +
          crop.from.y +
          "," +
          crop.size.width +
          "x" +
          crop.size.height +
          ";";
      }

      for (var i = 0; i < markupObjects.length; ++i) {
        var object = markupObjects[i];

        switch (object.shapeName) {
          case "circle":
            var from = {
              x: Math.round(object.left / resizeRatio) + imageOffset.x,
              y: Math.round(object.top / resizeRatio) + imageOffset.y,
            };
            var radius = Math.round(object.getRadiusX() / resizeRatio);
            var color = translateRGBtoColorString(object.stroke);

            markupString +=
              "circle," +
              from.x +
              "x" +
              from.y +
              "," +
              radius +
              "," +
              color +
              ";";
            break;
          case "rectangle":
            var from = {
              x: Math.round(object.left / resizeRatio) + imageOffset.x,
              y: Math.round(object.top / resizeRatio) + imageOffset.y,
            };
            var size = {
              width: Math.round(object.width / resizeRatio),
              height: Math.round(object.height / resizeRatio),
            };
            var color = translateRGBtoColorString(object.stroke);

            markupString +=
              "rectangle," +
              from.x +
              "x" +
              from.y +
              "," +
              size.width +
              "x" +
              size.height +
              "," +
              color +
              ";";
            break;
          case "gap":
          case "arrow":
          case "line":
            markupString += object.toMarkup(resizeRatio, imageOffset);
            break;
          default:
            console.error("Unexpected object name: " + object.shapeName);
        }
      }

      return markupString;
    },
  };

  // Setup drag-to-draw for this canvas
  require("./src/drag_to_create").setup(publicInterface);

  return publicInterface;
}

module.exports.Builder = ImageMarkupBuilder;
<commit_msg>Make `from` variable name unique in Builder<commit_after>const { cleanJSON } = require("./src/utils");

var isNode = typeof window == "undefined";

/**
 * Expects a Fabric.js Canvas
 */
function ImageMarkupBuilder(fabricCanvas) {
  var Fabric = require("fabric").fabric;

  var Shapes = {
    Rectangle: require("./shapes/rectangle").klass,
    Circle: require("./shapes/circle").klass,
    Line: require("./shapes/line").klass,
    Arrow: require("./shapes/arrow").klass,
    Gap: require("./shapes/gap").klass,
  };

  var colorValues = {
    red: "#C1280B",
    orange: "#FF9024",
    yellow: "#F3E00E",
    green: "#16DC81",
    lightBlue: "#1BB1E9",
    blue: "#2343E8",
    darkBlue: "#2343E8",
    violet: "#DC54B7",
    pink: "#DC54B7",
    black: "#000000",
  };

  // Reference to the json object from processJSON
  var innerJSON;

  var imageOffset;
  var resizeRatio = 1;
  var finalWidth = 0;
  var markupObjects = [];

  var strokeWidth = null;
  var crop = null;

  /**
   * Array of delegate functions to draw a proper shape.
   */
  var addShapeDelegate = {
    arrow: function (data) {
      return drawLineBasedShape(Shapes.Arrow, data);
    },
    gap: function (data) {
      return drawLineBasedShape(Shapes.Gap, data);
    },
    line: function (data) {
      return drawLineBasedShape(Shapes.Line, data);
    },
    circle: drawCircle,
    rectangle: function (data) {
      // Because fabric considers top/left as the center of the rectangle.
      data.from.x -= data.size.width / 2;
      data.from.y -= data.size.height / 2;

      return drawRectangle(data);
    },
  };

  /**
   * Returns true if the given object is off the edge of the fabric canvas.
   * This is defined as whether the color stroke would be the only part of
   * the shape still visible at those coordinates.
   */
  fabricCanvas.isOffScreen = function (object) {
    var rect = object.getBoundingRect();
    return (
      rect.left + rect.width - strokeWidth * 2 < 0 ||
      rect.left + strokeWidth * 2 > this.width ||
      rect.top + rect.height - strokeWidth * 2 < 0 ||
      rect.top + strokeWidth * 2 > this.height
    );
  };

  function applyBackground(callback) {
    if (!isNode) {
      //Listen for shapes falling off the edge and delete
      fabricCanvas.on({
        "object:modified": function (e) {
          var shape = e.target;

          if (fabricCanvas.isOffScreen(shape)) remove(shape);
        }.bind(this),
      });
    }

    //Disable drag selection on canvas
    fabricCanvas.selection = false;

    if (!innerJSON.sourceFile) {
      if (!innerJSON.finalDimensions) {
        var msg = "Need source file or final dimensions to create canvas";
        throw new Error(msg);
      }

      //Apply markup to blank canvas
      applyMarkup(callback);
    } else {
      finalWidth = innerJSON.finalDimensions.width;
      if (isNode) {
        fabricCanvas.setBackgroundColor("#FFFFFF");
        require("fs").readFile(innerJSON.sourceFile, function (err, blob) {
          if (err) throw err;

          var dimensions = innerJSON.dimensions;
          var img = {
            width: dimensions.width,
            height: dimensions.height,
            originX: "left",
            originY: "top",
            src: blob,
          };

          Fabric.Image.fromObject(img, function (fimg) {
            var top = -imageOffset.y;
            if (top % 1 != 0) {
              top -= 0.5;
            }
            var left = -imageOffset.x;
            if (left % 1 != 0) {
              left -= 0.5;
            }

            fabricCanvas.add(fimg.set("top", top).set("left", left));
            applyMarkup(callback);
          });
        });
      } else {
        throw new Error("Source files not supported on frontend");
      }
    }
  }

  /**
   * Applies the given markup instructions to the fabric canvas. If this is
   * server-side, call writeCanvas() to write the results to a file. If
   * client-side, call the client-provided callback directly.
   */
  function applyMarkup(callback) {
    // strokeWidth needs to be caught now, since if it's lower in the JSON
    // it will not get picked up until it's too late.
    if (innerJSON.instructions.strokeWidth) {
      strokeWidth = innerJSON.instructions.strokeWidth;
    }

    for (var instruction in innerJSON.instructions) {
      switch (instruction) {
        case "draw":
          innerJSON.instructions.draw.forEach(function (e) {
            for (var shapeName in e) {
              var shape = e[shapeName];
              shape.shapeName = shapeName;

              switch (shapeName) {
                case "rectangle":
                  drawRectangle(shape);
                  break;
                case "circle":
                  drawCircle(shape);
                  break;
                case "line":
                  drawLineBasedShape(Shapes.Line, shape);
                  break;
                case "gap":
                  drawLineBasedShape(Shapes.Gap, shape);
                  break;
                case "arrow":
                  drawLineBasedShape(Shapes.Arrow, shape);
                  break;
                default:
                  console.error("Unsupported Shape: " + shapeName);
              }
            }
          });
          break;

        //These should already be taken care of
        case "crop":
        case "strokeWidth":
          break;
        default:
          console.error("Unsupported Instruction: " + instruction);
      }
    }

    if (isNode) {
      writeCanvas(callback);
    } else {
      callback(fabricCanvas);
    }
  }

  /**
   * Writes the processed canvas to a file.
   */
  function writeCanvas(callback) {
    fabricCanvas.renderAll();
    var outstream = require("fs").createWriteStream(innerJSON.destinationFile),
      stream = fabricCanvas.createJPEGStream({
        quality: 93,
        progressive: true,
      });

    stream.on("data", function (chunk) {
      outstream.write(chunk);
    });
    stream.on("end", function () {
      callback(fabricCanvas);
    });
  }

  /**
   * Returns a strokeWidth calculated based on the dimensions of the canvas.
   */
  function getStrokeWidth(finalWidth) {
    return strokeWidth != null
      ? strokeWidth
      : Math.max(Math.round((finalWidth / 300) * 2), 4);
  }

  function drawRectangle(shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var rect = {
      left: shape.from.x - imageOffset.x,
      top: shape.from.y - imageOffset.y,
      width: shape.size.width,
      height: shape.size.height,
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };

    //Fabric調整
    rect.top *= resizeRatio;
    rect.left *= resizeRatio;
    rect.width *= resizeRatio;
    rect.height *= resizeRatio;

    var fabricRect = new Shapes.Rectangle(rect);

    markupObjects.push(fabricRect);
    fabricCanvas.add(fabricRect);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricRect);
    }

    return fabricRect;
  }

  function drawLineBasedShape(klass, shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var line = {
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };

    var points = [
      (shape.from.x - imageOffset.x) * resizeRatio,
      (shape.from.y - imageOffset.y) * resizeRatio,
      (shape.to.x - imageOffset.x) * resizeRatio,
      (shape.to.y - imageOffset.y) * resizeRatio,
    ];

    var fabricLine = new klass(points, line);
    fabricLine.color = shape.color;

    markupObjects.push(fabricLine);
    fabricCanvas.add(fabricLine);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricLine);
    }

    return fabricLine;
  }

  function drawCircle(shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var circle = {
      left: shape.from.x - imageOffset.x,
      top: shape.from.y - imageOffset.y,
      radius: shape.radius,
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };
    circle.left *= resizeRatio;
    circle.top *= resizeRatio;
    circle.radius *= resizeRatio;

    var fabricCircle = new Shapes.Circle(circle);
    markupObjects.push(fabricCircle);
    fabricCanvas.add(fabricCircle);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricCircle);
    }

    return fabricCircle;
  }

  function locate(shape) {
    for (var i = 0; i < markupObjects.length; ++i) {
      if (markupObjects[i] === shape) {
        return i;
      }
    }

    return null;
  }

  function remove(shape) {
    var index = locate(shape);
    if (index != null) {
      fabricCanvas.remove(shape);
      markupObjects.splice(index, 1);
    }
  }

  /**
   * Performs ShapeData.type-agnostic attribute checks to ensure that the
   * necessary attributes are set in the given ShapeData object. Specifically,
   * the following operations are performed:
   *
   * - If data.x or data.y are not present, both are set to the center of
   *   the canvas. If both are present, they are scaled to the preview size
   *   of the canvas (if given).
   * - if data.color is not present, it is set to "red".
   */
  function normalizeShapeData(data) {
    data.from.x /= resizeRatio;
    data.from.y /= resizeRatio;
    data.from.x += imageOffset.x;
    data.from.y += imageOffset.y;

    if (data.to) {
      data.to.x /= resizeRatio;
      data.to.y /= resizeRatio;
      data.to.x += imageOffset.x;
      data.to.y += imageOffset.y;
    }

    if (!data.color) {
      data.color = "red";
    }
  }

  var publicInterface = {
    fabricCanvas: fabricCanvas,

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. The act of drawing is delegated to the proper
     * draw method based on its "type" attribute.
     */
    addShape: function addShape(data) {
      if (!data.type) throw "ShapeData.type is not defined.";

      normalizeShapeData(data);
      return addShapeDelegate[data.type](data);
    },

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. This ignores the "type" attribute of the object
     * and just adds a circle. To be deprecated by addShape().
     *
     * @return a reference to the tracked shape.
     */
    addCircle: function addCircle(data) {
      console.warn("Deprecated function: addCircle(). Use addShape() instead.");

      if (data.type !== "circle") data.type = "circle";
      return this.addShape(data);
    },

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. This ignores the "type" attribute of the object
     * and just adds a rectangle. To be deprecated by addShape().
     *
     * @return a reference to the tracked shape.
     */
    addRectangle: function addRectangle(data) {
      console.warn(
        "Deprecated function: addRectangle(). Use addShape() instead."
      );

      if (data.type !== "rectangle") data.type = "rectangle";
      return this.addShape(data);
    },

    /**
     * Sets the color of a tracked shape.
     */
    setColor: function setColor(shape, colorName) {
      shape.stroke = colorValues[colorName];
      shape.color = colorName;

      if (!isNode) fabricCanvas.renderAll();
    },

    /**
     * Returns an array of all objects in the fabric canvas, whether
     * tracked by the Builder or not.
     */
    getShapes: function getShapes() {
      return fabricCanvas._objects;
    },

    /**
     * Returns a reference to the currently selected shape.
     *
     * @return a reference to the currently selected shape.
     */
    getActiveShape: function getActiveShape() {
      return fabricCanvas.getActiveObject();
    },

    /**
     * Removes a specific tracked shape from the fabric canvas.
     */
    removeShape: function removeShape(shape) {
      remove(shape);
    },

    /**
     * Removes all tracked shapes from the fabric canvas.
     */
    removeShapes: function removeShapes() {
      for (var i = 0; i < markupObjects.length; ++i) {
        var shape = markupObjects[i];
        fabricCanvas.remove(shape);
      }

      markupObjects = [];
    },

    /**
     * Grows or shrinks the given shape depending on the given key
     * by the given number of pixels.
     *
     * @param shape The fabric shape to resize.
     * @param increment The number of pixels (negative or positive) to
     *  resize the shape.
     */
    incrementSize: function incrementSize(shape, increment) {
      if (!(typeof increment === "number")) {
        console.error("increment must be a number");
        return;
      }

      shape._withSizeLimitations(function () {
        shape.incrementSize(increment);
        if (fabricCanvas.isOffScreen(shape)) {
          shape.incrementSize(increment * -1);
        }
      }, true);

      shape.setCoords();
      fabricCanvas.renderAll();
    },

    /**
     * Takes a Builder-schema JSON object and performs the operations
     * listed therein, then calls the callback function.
     */
    processJSON: function processJSON(json, callback) {
      //Make sure not to render every addition on server end
      fabricCanvas.renderOnAddition = !isNode;
      fabricCanvas.uniScaleTransform = true;

      cleanJSON(json);

      innerJSON = json;

      crop = innerJSON.instructions.crop;
      imageOffset =
        typeof crop != "undefined"
          ? {
              x: crop.from.x,
              y: crop.from.y,
            }
          : {
              x: 0,
              y: 0,
            };

      if (innerJSON.resizeRatio) {
        resizeRatio = innerJSON.resizeRatio;
      }

      applyBackground(callback);
    },

    getMarkupObjects: function getMarkupObjects() {
      return markupObjects;
    },

    /**
     * Returns a markup string which can be used with ImageMarkupCall
     * to produce the same markup results as represented in this builder.
     */
    getMarkupString: function getMarkupString() {
      /**
       * Translate RGB value to applicable color string. Unknown
       * RGB values will will become black and a console.error message
       * will be written.
       */
      function translateRGBtoColorString(rgb) {
        for (var colorString in colorValues) {
          if (rgb == colorValues[colorString]) {
            return colorString;
          }
        }
        console.error("No mapping from RGB to Color String Found: " + rgb);
        return "black";
      }

      var markupString = ";";

      if (crop) {
        // Cut out cases where crop is used to offset non-4:3
        // images
        markupString +=
          "crop," +
          crop.from.x +
          "x" +
          crop.from.y +
          "," +
          crop.size.width +
          "x" +
          crop.size.height +
          ";";
      }

      for (var i = 0; i < markupObjects.length; ++i) {
        var object = markupObjects[i];

        switch (object.shapeName) {
          case "circle":
            var circleFrom = {
              x: Math.round(object.left / resizeRatio) + imageOffset.x,
              y: Math.round(object.top / resizeRatio) + imageOffset.y,
            };
            var radius = Math.round(object.getRadiusX() / resizeRatio);
            var color = translateRGBtoColorString(object.stroke);

            markupString +=
              "circle," +
              circleFrom.x +
              "x" +
              circleFrom.y +
              "," +
              radius +
              "," +
              color +
              ";";
            break;
          case "rectangle":
            var rectFrom = {
              x: Math.round(object.left / resizeRatio) + imageOffset.x,
              y: Math.round(object.top / resizeRatio) + imageOffset.y,
            };
            var size = {
              width: Math.round(object.width / resizeRatio),
              height: Math.round(object.height / resizeRatio),
            };
            var color = translateRGBtoColorString(object.stroke);

            markupString +=
              "rectangle," +
              rectFrom.x +
              "x" +
              rectFrom.y +
              "," +
              size.width +
              "x" +
              size.height +
              "," +
              color +
              ";";
            break;
          case "gap":
          case "arrow":
          case "line":
            markupString += object.toMarkup(resizeRatio, imageOffset);
            break;
          default:
            console.error("Unexpected object name: " + object.shapeName);
        }
      }

      return markupString;
    },
  };

  // Setup drag-to-draw for this canvas
  require("./src/drag_to_create").setup(publicInterface);

  return publicInterface;
}

module.exports.Builder = ImageMarkupBuilder;
<|endoftext|>