<commit_before>// PieceList.js v3.4.1

pzpr.classmgr.makeCommon({
//----------------------------------------------------------------------------
// ★PieceListクラス オブジェクトの配列を扱う
//---------------------------------------------------------------------------
PieceList:{
	length : 0,

	//--------------------------------------------------------------------------------
	// ☆Arrayオブジェクト関連の関数
	// list.add()      与えられたオブジェクトを配列の末尾に追加する(push()相当)
	// list.extend()   与えられたPieceListを配列の末尾に追加する
	// list.unshift()  与えられたオブジェクトを配列の先頭に入れる
	// list.pop()      配列の最後のオブジェクトを取り除いて返す
	// list.reverse()  保持している配列の順番を逆にする
	//--------------------------------------------------------------------------------
	add     : Array.prototype.push,
	extend  : function(list){
		var self = this;
		list.each(function(obj){ self.add(obj);})
	},
	unshift : Array.prototype.unshift,
	pop     : Array.prototype.pop,
	reverse : Array.prototype.reverse,

	//--------------------------------------------------------------------------------
	// ☆Arrayオブジェクトiterator関連の関数
	// list.each()     全てのオブジェクトに指定された関数を実行する
	// list.some()     条件がtrueとなるオブジェクトが存在するか判定する
	// list.include()  与えられたオブジェクトが配列に存在するか判定する
	//--------------------------------------------------------------------------------
	each    : Array.prototype.forEach,
	some    : Array.prototype.some,
	include : function(target){ return this.some(function(obj){ return (obj===target);});},

	//--------------------------------------------------------------------------------
	// list.filter()   条件がtrueとなるオブジェクトを抽出したclistを新たに作成する
	// list.notnull()  nullではないオブジェクトを抽出したclistを新たに作成する
	//--------------------------------------------------------------------------------
	/* constructorが変わってしまうので、Array.prototypeが使用できない */
	filter  : function(cond){
		var list = new this.constructor();
		for(var i=0;i<this.length;i++){ if(cond(this[i])){ list.add(this[i]);}}
		return list;
	},
	notnull : function(cond){ return this.filter(function(obj){ return !obj.isnull;});},

	//--------------------------------------------------------------------------------
	// list.map()      clistの各要素に指定された関数を適用したclistを新たに作成する
	//--------------------------------------------------------------------------------
	/* constructorが変わってしまうので、Array.prototypeが使用できない */
	map : function(cond){
		var list = new this.constructor();
		for(var i=0;i<this.length;i++){ list.add(cond(this[i]));}
		return list;
	},

	//--------------------------------------------------------------------------------
	// list.indexOf()  与えられたオブジェクトの配列上の位置を取得する
	// list.remove()   与えられたオブジェクトを配列から取り除く
	//--------------------------------------------------------------------------------
	indexOf : Array.prototype.indexOf,
	remove : function(obj){
		var idx = this.indexOf(obj);
		if(idx>=0){ Array.prototype.splice.call(this, idx, 1);}
	},

	//--------------------------------------------------------------------------------
	// list.seterr()  保持しているオブジェクトにerror値を設定する
	// list.setinfo() 保持しているオブジェクトにqinfo値を設定する
	//--------------------------------------------------------------------------------
	seterr : function(num){
		if(!this.owner.board.isenableSetError()){ return;}
		for(var i=0;i<this.length;i++){ this[i].error = num;}
	},
	setinfo : function(num){
		for(var i=0;i<this.length;i++){ this[i].qinfo = num;}
	},

	//---------------------------------------------------------------------------
	// list.allclear() 位置,描画情報以外をクリアする
	// list.ansclear() qans,anum,line,qsub,error情報をクリアする
	// list.subclear() qsub,error情報をクリアする
	// list.errclear() error情報をクリアする
	// list.propclear() 4つの共通処理
	//---------------------------------------------------------------------------
	/* undo,redo以外で盤面縮小やったときは, isrec===true */
	allclear : function(isrec){ this.propclear(this.getprop('all'), isrec);},
	ansclear : function()     { this.propclear(this.getprop('ans'), true);},
	subclear : function()     { this.propclear(this.getprop('sub'), true);},
	errclear : function()     { this.propclear(this.getprop('err'), false);},
	propclear : function(props, isrec){
		var norec = (this.length>0?this[0].propnorec:{});
		for(var i=0;i<this.length;i++){
			var obj = this[i];
			for(var j=0;j<props.length;j++){
				var pp = props[j];
				var def = obj.constructor.prototype[pp];
				if(obj[pp]!==def){
					if(isrec && !norec[pp]){ obj.addOpe(pp, obj[pp], def);}
					obj[pp] = def;
				}
			}
		}
	},

	//---------------------------------------------------------------------------
	// list.getprop() 上記の関数で使用するプロパティの配列を取得する
	//---------------------------------------------------------------------------
	getprop : function(type){
		var array = [];
		if(this.length>0){
			var level = {all:3,ans:2,sub:1,err:0}[type];
			if(level>=3){ array=array.concat(this[0].propques);}
			if(level>=2){ array=array.concat(this[0].propans);}
			if(level>=1){ array=array.concat(this[0].propsub);}
			if(level>=0){ array=array.concat(this[0].propinfo);}
		}
		return array;
	}
},

//----------------------------------------------------------------------------
// ★CellListクラス Cellの配列を扱う
//---------------------------------------------------------------------------
'CellList:PieceList':{
	//---------------------------------------------------------------------------
	// clist.getRectSize()  指定されたCellのリストの上下左右の端と、セルの数を返す
	//---------------------------------------------------------------------------
	getRectSize : function(){
		var bd = this.owner.board;
		var d = { x1:bd.maxbx+1, x2:bd.minbx-1, y1:bd.maxby+1, y2:bd.minby-1, cols:0, rows:0, cnt:0};
		for(var i=0;i<this.length;i++){
			var cell = this[i];
			if(d.x1>cell.bx){ d.x1=cell.bx;}
			if(d.x2<cell.bx){ d.x2=cell.bx;}
			if(d.y1>cell.by){ d.y1=cell.by;}
			if(d.y2<cell.by){ d.y2=cell.by;}
			d.cnt++;
		}
		d.cols = (d.x2-d.x1+2)/2;
		d.rows = (d.y2-d.y1+2)/2;
		return d;
	},

	//--------------------------------------------------------------------------------
	// clist.getQnumCell()  指定されたClistの中で一番左上にある数字のあるセルを返す
	//--------------------------------------------------------------------------------
	getQnumCell : function(){
		for(var i=0,len=this.length;i<len;i++){
			if(this[i].isNum()){ return this[i];}
		}
		return this.owner.board.emptycell;
	},

	//---------------------------------------------------------------------------
	// clist.draw()   盤面に自分の周囲を描画する
	//---------------------------------------------------------------------------
	draw : function(){
		var d = this.getRectSize();
		this.owner.painter.paintRange(d.x1-1, d.y1-1, d.x2+1, d.y2+1);
	}
},

//----------------------------------------------------------------------------
// ★CrossListクラス Crossの配列を扱う
//---------------------------------------------------------------------------
'CrossList:PieceList':{
},

//----------------------------------------------------------------------------
// ★BorderListクラス Borderの配列を扱う
//---------------------------------------------------------------------------
'BorderList:PieceList':{
	//---------------------------------------------------------------------------
	// blist.cellinside()  線が重なるセルのリストを取得する
	// blist.crossinside() 線が重なる交点のリストを取得する
	//---------------------------------------------------------------------------
	cellinside : function(){
		var clist = new this.owner.CellList(), pushed = [];
		for(var i=0;i<this.length;i++){
			var border=this[i], cell1=border.sidecell[0], cell2=border.sidecell[1];
			if(!cell1.isnull && pushed[cell1.id]!==true){ clist.add(cell1); pushed[cell1.id]=true;}
			if(!cell2.isnull && pushed[cell2.id]!==true){ clist.add(cell2); pushed[cell2.id]=true;}
		}
		return clist;
	},
	crossinside : function(){
		var clist = new this.owner.CrossList(), pushed = [];
		for(var i=0;i<this.length;i++){
			var border=this[i], cross1=border.sidecross[0], cross2=border.sidecross[1];
			if(!cross1.isnull && pushed[cross1.id]!==true){ clist.add(cross1); pushed[cross1.id]=true;}
			if(!cross2.isnull && pushed[cross2.id]!==true){ clist.add(cross2); pushed[cross2.id]=true;}
		}
		return clist;
	}
},

//----------------------------------------------------------------------------
// ★EXCellListクラス EXCellの配列を扱う
//---------------------------------------------------------------------------
'EXCellList:PieceList':{
}
});
<commit_msg>PieceList: extend, filter, map関数の処理を変更して、正答判定の速度を改善します<commit_after>// PieceList.js v3.4.1

pzpr.classmgr.makeCommon({
//----------------------------------------------------------------------------
// ★PieceListクラス オブジェクトの配列を扱う
//---------------------------------------------------------------------------
PieceList:{
	length : 0,

	//--------------------------------------------------------------------------------
	// ☆Arrayオブジェクト関連の関数
	// list.add()      与えられたオブジェクトを配列の末尾に追加する(push()相当)
	// list.extend()   与えられたPieceListを配列の末尾に追加する
	// list.unshift()  与えられたオブジェクトを配列の先頭に入れる
	// list.pop()      配列の最後のオブジェクトを取り除いて返す
	// list.reverse()  保持している配列の順番を逆にする
	//--------------------------------------------------------------------------------
	add     : Array.prototype.push,
	extend  : function(list){
		var len = list.length, n = this.length;
		this.length += len;
		for(var i=0;i<len;i++){ this[n+i] = list[i];}
	},
	unshift : Array.prototype.unshift,
	pop     : Array.prototype.pop,
	reverse : Array.prototype.reverse,

	//--------------------------------------------------------------------------------
	// ☆Arrayオブジェクトiterator関連の関数
	// list.each()     全てのオブジェクトに指定された関数を実行する
	// list.some()     条件がtrueとなるオブジェクトが存在するか判定する
	// list.include()  与えられたオブジェクトが配列に存在するか判定する
	//--------------------------------------------------------------------------------
	each    : Array.prototype.forEach,
	some    : Array.prototype.some,
	include : function(target){ return this.some(function(obj){ return (obj===target);});},

	//--------------------------------------------------------------------------------
	// list.filter()   条件がtrueとなるオブジェクトを抽出したclistを新たに作成する
	// list.notnull()  nullではないオブジェクトを抽出したclistを新たに作成する
	//--------------------------------------------------------------------------------
	/* constructorが変わってしまうので、Array.prototypeが使用できない */
	filter  : function(cond){
		var list = new this.constructor(), len = this.length, n = 0;
		for(var i=0;i<len;i++){
			if(cond(this[i])){ list[n] = this[i]; n++;}
		}
		list.length = n;
		return list;
	},
	notnull : function(cond){ return this.filter(function(obj){ return !obj.isnull;});},

	//--------------------------------------------------------------------------------
	// list.map()      clistの各要素に指定された関数を適用したclistを新たに作成する
	//--------------------------------------------------------------------------------
	/* constructorが変わってしまうので、Array.prototypeが使用できない */
	map : function(changer){
		var list = new this.constructor(), len = list.length = this.length;
		for(var i=0;i<len;i++){ list[i] = changer(this[i]);}
		return list;
	},

	//--------------------------------------------------------------------------------
	// list.indexOf()  与えられたオブジェクトの配列上の位置を取得する
	// list.remove()   与えられたオブジェクトを配列から取り除く
	//--------------------------------------------------------------------------------
	indexOf : Array.prototype.indexOf,
	remove : function(obj){
		var idx = this.indexOf(obj);
		if(idx>=0){ Array.prototype.splice.call(this, idx, 1);}
	},

	//--------------------------------------------------------------------------------
	// list.seterr()  保持しているオブジェクトにerror値を設定する
	// list.setinfo() 保持しているオブジェクトにqinfo値を設定する
	//--------------------------------------------------------------------------------
	seterr : function(num){
		if(!this.owner.board.isenableSetError()){ return;}
		for(var i=0;i<this.length;i++){ this[i].error = num;}
	},
	setinfo : function(num){
		for(var i=0;i<this.length;i++){ this[i].qinfo = num;}
	},

	//---------------------------------------------------------------------------
	// list.allclear() 位置,描画情報以外をクリアする
	// list.ansclear() qans,anum,line,qsub,error情報をクリアする
	// list.subclear() qsub,error情報をクリアする
	// list.errclear() error情報をクリアする
	// list.propclear() 4つの共通処理
	//---------------------------------------------------------------------------
	/* undo,redo以外で盤面縮小やったときは, isrec===true */
	allclear : function(isrec){ this.propclear(this.getprop('all'), isrec);},
	ansclear : function()     { this.propclear(this.getprop('ans'), true);},
	subclear : function()     { this.propclear(this.getprop('sub'), true);},
	errclear : function()     { this.propclear(this.getprop('err'), false);},
	propclear : function(props, isrec){
		var norec = (this.length>0?this[0].propnorec:{});
		for(var i=0;i<this.length;i++){
			var obj = this[i];
			for(var j=0;j<props.length;j++){
				var pp = props[j];
				var def = obj.constructor.prototype[pp];
				if(obj[pp]!==def){
					if(isrec && !norec[pp]){ obj.addOpe(pp, obj[pp], def);}
					obj[pp] = def;
				}
			}
		}
	},

	//---------------------------------------------------------------------------
	// list.getprop() 上記の関数で使用するプロパティの配列を取得する
	//---------------------------------------------------------------------------
	getprop : function(type){
		var array = [];
		if(this.length>0){
			var level = {all:3,ans:2,sub:1,err:0}[type];
			if(level>=3){ array=array.concat(this[0].propques);}
			if(level>=2){ array=array.concat(this[0].propans);}
			if(level>=1){ array=array.concat(this[0].propsub);}
			if(level>=0){ array=array.concat(this[0].propinfo);}
		}
		return array;
	}
},

//----------------------------------------------------------------------------
// ★CellListクラス Cellの配列を扱う
//---------------------------------------------------------------------------
'CellList:PieceList':{
	//---------------------------------------------------------------------------
	// clist.getRectSize()  指定されたCellのリストの上下左右の端と、セルの数を返す
	//---------------------------------------------------------------------------
	getRectSize : function(){
		var bd = this.owner.board;
		var d = { x1:bd.maxbx+1, x2:bd.minbx-1, y1:bd.maxby+1, y2:bd.minby-1, cols:0, rows:0, cnt:0};
		for(var i=0;i<this.length;i++){
			var cell = this[i];
			if(d.x1>cell.bx){ d.x1=cell.bx;}
			if(d.x2<cell.bx){ d.x2=cell.bx;}
			if(d.y1>cell.by){ d.y1=cell.by;}
			if(d.y2<cell.by){ d.y2=cell.by;}
			d.cnt++;
		}
		d.cols = (d.x2-d.x1+2)/2;
		d.rows = (d.y2-d.y1+2)/2;
		return d;
	},

	//--------------------------------------------------------------------------------
	// clist.getQnumCell()  指定されたClistの中で一番左上にある数字のあるセルを返す
	//--------------------------------------------------------------------------------
	getQnumCell : function(){
		for(var i=0,len=this.length;i<len;i++){
			if(this[i].isNum()){ return this[i];}
		}
		return this.owner.board.emptycell;
	},

	//---------------------------------------------------------------------------
	// clist.draw()   盤面に自分の周囲を描画する
	//---------------------------------------------------------------------------
	draw : function(){
		var d = this.getRectSize();
		this.owner.painter.paintRange(d.x1-1, d.y1-1, d.x2+1, d.y2+1);
	}
},

//----------------------------------------------------------------------------
// ★CrossListクラス Crossの配列を扱う
//---------------------------------------------------------------------------
'CrossList:PieceList':{
},

//----------------------------------------------------------------------------
// ★BorderListクラス Borderの配列を扱う
//---------------------------------------------------------------------------
'BorderList:PieceList':{
	//---------------------------------------------------------------------------
	// blist.cellinside()  線が重なるセルのリストを取得する
	// blist.crossinside() 線が重なる交点のリストを取得する
	//---------------------------------------------------------------------------
	cellinside : function(){
		var clist = new this.owner.CellList(), pushed = [];
		for(var i=0;i<this.length;i++){
			var border=this[i], cell1=border.sidecell[0], cell2=border.sidecell[1];
			if(!cell1.isnull && pushed[cell1.id]!==true){ clist.add(cell1); pushed[cell1.id]=true;}
			if(!cell2.isnull && pushed[cell2.id]!==true){ clist.add(cell2); pushed[cell2.id]=true;}
		}
		return clist;
	},
	crossinside : function(){
		var clist = new this.owner.CrossList(), pushed = [];
		for(var i=0;i<this.length;i++){
			var border=this[i], cross1=border.sidecross[0], cross2=border.sidecross[1];
			if(!cross1.isnull && pushed[cross1.id]!==true){ clist.add(cross1); pushed[cross1.id]=true;}
			if(!cross2.isnull && pushed[cross2.id]!==true){ clist.add(cross2); pushed[cross2.id]=true;}
		}
		return clist;
	}
},

//----------------------------------------------------------------------------
// ★EXCellListクラス EXCellの配列を扱う
//---------------------------------------------------------------------------
'EXCellList:PieceList':{
}
});
<|endoftext|>


<commit_before>/* Enigma machine emulator.
 * Author: Matheus Vieira Portela
 * GitHub: https://github.com/matheusportela/
 *
 * This is a simulator for the Enigma machine, one of the most incredible
 * applications of cryptography during World War I and II. German militaries
 * would send encrypted messages through the air using telegraphs about bombing
 * locations with security, considering one would have to know the precise
 * combination of rotor positioning, plugboard configuration, and other pieces
 * in order to decode captured messages.
 *
 * Even though several weaknesses were discovered - specially by the Allies
 * forces - allowing one to break the code, the Enigma encryption algorithm is
 * a fun way to study a little bit of cryptography.
 *
 * Of course, studying the Enigma is also a tribute to Alan Turing.
 *
 * References:
 * - Enigma machine https://en.wikipedia.org/wiki/Enigma_machine
 * - Enigma simulator http://enigma.louisedade.co.uk/howitworks.html
 */
var expect = require('chai').expect;

describe('Plugboard', function() {
    describe('addPlug', function() {
        it('expect add letter to plugs', function() {
            var plugboard = new Plugboard();
            plugboard.addPlug('A', 'B');
            expect(plugboard.plugs['A']).to.be.equal('B');
        });

        it('expect add inverse letter to plugs', function() {
            var plugboard = new Plugboard();
            plugboard.addPlug('A', 'B');
            expect(plugboard.plugs['B']).to.be.equal('A');
        });
    });

    describe('encode', function() {
        it('expect non-plugged letter encode to itself', function() {
            var plugboard = new Plugboard();
            expect(plugboard.encode('A')).to.be.equal('A');
        });

        it('expect plugged letter encode to its plug output', function() {
            var plugboard = new Plugboard();
            plugboard.addPlug('A', 'B');
            expect(plugboard.encode('A')).to.be.equal('B');
        });
    });

    describe('addPlugs', function() {
        it('expect plugged letters encode to their plugs', function() {
            var plugboard = new Plugboard();
            plugboard.addPlugs('ABC', 'DEF');
            expect(plugboard.encode('A')).to.be.equal('D');
            expect(plugboard.encode('B')).to.be.equal('E');
            expect(plugboard.encode('C')).to.be.equal('F');
        });

        it('expect non-plugged letters encode to themselves', function() {
            var plugboard = new Plugboard();
            plugboard.addPlugs('ABC', 'DEF');
            expect(plugboard.encode('G')).to.be.equal('G');
            expect(plugboard.encode('H')).to.be.equal('H');
            expect(plugboard.encode('I')).to.be.equal('I');
        });
    });
});

var Plugboard = function() {
    this.plugs = {};
};

Plugboard.prototype.addPlug = function(letter1, letter2) {
    this.plugs[letter1] = letter2;
    this.plugs[letter2] = letter1;
};

Plugboard.prototype.addPlugs = function(letters1, letters2) {
    for (var i = 0; i < letters1.length; i++) {
        this.plugs[letters1[i]] = letters2[i];
    }
}

Plugboard.prototype.encode = function(letter) {
    if (letter in this.plugs)
        return this.plugs[letter];
    return letter;
};

module.exports = Plugboard;<commit_msg>Configure Plugboard through constructor.<commit_after>/* Enigma machine emulator.
 * Author: Matheus Vieira Portela
 * GitHub: https://github.com/matheusportela/
 *
 * This is a simulator for the Enigma machine, one of the most incredible
 * applications of cryptography during World War I and II. German militaries
 * would send encrypted messages through the air using telegraphs about bombing
 * locations with security, considering one would have to know the precise
 * combination of rotor positioning, plugboard configuration, and other pieces
 * in order to decode captured messages.
 *
 * Even though several weaknesses were discovered - specially by the Allies
 * forces - allowing one to break the code, the Enigma encryption algorithm is
 * a fun way to study a little bit of cryptography.
 *
 * Of course, studying the Enigma is also a tribute to Alan Turing.
 *
 * References:
 * - Enigma machine https://en.wikipedia.org/wiki/Enigma_machine
 * - Enigma simulator http://enigma.louisedade.co.uk/howitworks.html
 */
var expect = require('chai').expect;

describe('Plugboard', function() {
    describe('addPlug', function() {
        it('expect add letter to plugs', function() {
            var plugboard = new Plugboard();
            plugboard.addPlug('A', 'B');
            expect(plugboard.plugs['A']).to.be.equal('B');
        });

        it('expect add inverse letter to plugs', function() {
            var plugboard = new Plugboard();
            plugboard.addPlug('A', 'B');
            expect(plugboard.plugs['B']).to.be.equal('A');
        });
    });

    describe('encode', function() {
        it('expect non-plugged letter encode to itself', function() {
            var plugboard = new Plugboard();
            expect(plugboard.encode('A')).to.be.equal('A');
        });

        it('expect plugged letter encode to its plug output', function() {
            var plugboard = new Plugboard();
            plugboard.addPlug('A', 'B');
            expect(plugboard.encode('A')).to.be.equal('B');
        });
    });

    describe('addPlugs', function() {
        it('expect plugged letters encode to their plugs', function() {
            var plugboard = new Plugboard();
            plugboard.addPlugs('ABC', 'DEF');
            expect(plugboard.encode('A')).to.be.equal('D');
            expect(plugboard.encode('B')).to.be.equal('E');
            expect(plugboard.encode('C')).to.be.equal('F');
        });

        it('expect non-plugged letters encode to themselves', function() {
            var plugboard = new Plugboard();
            plugboard.addPlugs('ABC', 'DEF');
            expect(plugboard.encode('G')).to.be.equal('G');
            expect(plugboard.encode('H')).to.be.equal('H');
            expect(plugboard.encode('I')).to.be.equal('I');
        });
    });

    describe('constructor', function() {
        it('expect plugboard be configured through constructor', function() {
            var plugboard = new Plugboard('ABC', 'DEF');
            expect(plugboard.encode('A')).to.be.equal('D');
            expect(plugboard.encode('B')).to.be.equal('E');
            expect(plugboard.encode('C')).to.be.equal('F');
            expect(plugboard.encode('G')).to.be.equal('G');
            expect(plugboard.encode('H')).to.be.equal('H');
            expect(plugboard.encode('I')).to.be.equal('I');
        });
    });
});

var Plugboard = function(letters1, letters2) {
    this.plugs = {};

    if (letters1 != undefined && letters2 != undefined)
        this.addPlugs(letters1, letters2);
};

Plugboard.prototype.addPlug = function(letter1, letter2) {
    this.plugs[letter1] = letter2;
    this.plugs[letter2] = letter1;
};

Plugboard.prototype.addPlugs = function(letters1, letters2) {
    for (var i = 0; i < letters1.length; i++)
        this.plugs[letters1[i]] = letters2[i];
}

Plugboard.prototype.encode = function(letter) {
    if (letter in this.plugs)
        return this.plugs[letter];
    return letter;
};

module.exports = Plugboard;<|endoftext|>


<commit_before>/*!
* jQuery Text Counter Plugin v0.3.3
* https://github.com/ractoon/jQuery-Text-Counter
*
* Copyright 2014 ractoon
* Released under the MIT license
*/
;(function($) {
  $.textcounter = function(el, options) {
    // To avoid scope issues, use 'base' instead of 'this'
    // to reference this class from internal events and functions.
    var base = this;

    // Access to jQuery and DOM versions of element
    base.$el = $(el);
    base.el = el;

    // Add a reverse reference to the DOM object
    base.$el.data("textcounter", base);

    base.init = function() {
      base.options = $.extend({}, $.textcounter.defaultOptions, options);

      // append the count element
      var counterText = base.options.countDown ? base.options.countDownText : base.options.counterText,
          counterNum = base.options.countDown ? base.options.max : 0;

      base.$el.after('<' + base.options.countContainerElement + ' class="' + base.options.countContainerClass + '">' + counterText + '' + counterNum + '</' + base.options.countContainerElement + '>');

      // bind input events
      base.$el.bind('keyup.textcounter click.textcounter blur.textcounter focus.textcounter change.textcounter paste.textcounter', base.checkLimits).trigger('click.textcounter');

      // TextCounter: init(el) Callback
      base.options.init(base.el);
    };

    base.checkLimits = function(e) {
      var $this = base.$el,
          $countEl = $this.next('.' + base.options.countContainerClass),
          $text = $this.val(),
          textCount = 0,
          textTotalCount = 0,
          eventTriggered =  e.originalEvent === undefined ? false : true;

      if (!$.isEmptyObject($text)) {
        if (base.options.type == "word") {  // word count
          textCount = $text.trim().replace(/\s+/gi, ' ').split(' ').length;
        }
        else {  // character count
          if (base.options.countSpaces) { // if need to count spaces
            textCount = $text.replace(/[^\S\n|\r|\r\n]/g, ' ').length;
          }
          else {
            textCount = $text.replace(/\s/g, '').length;
          }

          if (base.options.countExtendedCharacters) {
            var extended = $text.match(/[^\x00-\xff]/gi);

            if (extended == null) {
              textCount = $text.length;
            } else {
              textCount = $text.length + extended.length;
            }
          }
        }
      }

      // if max is auto retrieve value
      if (base.options.max == 'auto') {
        var max = base.$el.attr('maxlength');

        if (typeof max !== 'undefined' && max !== false) {
          base.options.max = max;
        }
        else {
          base.$el.next('.' + base.options.countContainerClass).text('error: [maxlength] attribute not set');
        }
      }

      // if this is a countdown counter deduct from the max characters/words
      textTotalCount = base.options.countDown ? base.options.max - textCount : textCount;

      // set the current text count
      base.setCount(textTotalCount);

      if (base.options.min > 0 && eventTriggered) {   // if a minimum value has been set
        if (textCount < base.options.min) {
          base.setErrors('min');
        }
        else if (textCount >= base.options.min) {
          // TextCounter: mincount(el) Callback
          base.options.mincount(base.el);

          base.clearErrors('min');
        }
      }

      if (base.options.max !== -1) {  // if a maximum value has been set
        if (textCount >= base.options.max && base.options.max != 0) {
          // TextCounter: maxcount(el) Callback
          base.options.maxcount(base.el);

          if (base.options.stopInputAtMaximum) {  // if the string should be trimmed at the maximum length
            var trimmedString = '';

            if (base.options.type == "word") {  // word type
              var wordArray = $text.split(/[^\S\n]/g);
              var i = 0;

              // iterate over individual words
              while (i < wordArray.length) {
                // if over the maximum words allowed break;
                if (i >= base.options.max - 1) break;

                if (wordArray[i] !== undefined) {
                  trimmedString += wordArray[i] + ' ';
                  i++;
                }
              }
            }
            else {  // character type
              if (base.options.countSpaces) {     // if spaces should be counted
                trimmedString = $text.substring(0, base.options.max);
              }
              else {
                var charArray = $text.split(''),
                    totalCharacters = charArray.length,
                    charCount = 0,
                    i = 0;

                while (charCount < base.options.max && i < totalCharacters) {
                  if (charArray[i] !== ' ') charCount++;
                  trimmedString += charArray[i++];
                }
              }
            }

            $this.val(trimmedString.trim());

            textTotalCount = base.options.countDown ? 0 : base.options.max;
            base.setCount(textTotalCount);
          } else {
            base.setErrors('max');
          }
        }
        else {
          base.clearErrors('max');
        }
      }
    };

    base.setCount = function(count) {
      var $this = base.$el,
          $countEl = $this.next('.' + base.options.countContainerClass);

      $countEl.children('.text-count').text(count);
    };

    base.setErrors = function(type) {
      var $this = base.$el,
          $countEl = $this.next('.' + base.options.countContainerClass);

      $this.addClass(base.options.inputErrorClass);
      $countEl.addClass(base.options.counterErrorClass);

      if (base.options.displayErrorText) {
        switch(type) {
          case 'min':
            errorText = base.options.minimumErrorText;
            break;
          case 'max':
            errorText = base.options.maximumErrorText;
            break;
        }

        if (!$countEl.children('.error-text-' + type).length) {
          $countEl.append('<' + base.options.errorTextElement + ' class="error-text error-text-' + type + '">' + errorText + '</' + base.options.errorTextElement + '>');
        }
      }
    };

    base.clearErrors = function(type) {
      var $this = base.$el,
          $countEl = $this.next('.' + base.options.countContainerClass);

      $countEl.children('.error-text-' + type).remove();

      if ($countEl.children('.error-text').length == 0) {
        $this.removeClass(base.options.inputErrorClass);
        $countEl.removeClass(base.options.counterErrorClass);
      }
    };

    // kick it off
    base.init();
  };

  $.textcounter.defaultOptions = {
    'type'                      : "character",              // "character" or "word"
    'min'                       : 0,                        // minimum number of characters/words
    'max'                       : 200,                      // maximum number of characters/words, -1 for unlimited, 'auto' to use maxlength attribute
    'countContainerElement'     : "div",                    // HTML element to wrap the text count in
    'countContainerClass'       : "text-count-wrapper",     // class applied to the countContainerElement
    'inputErrorClass'           : "error",                  // error class appended to the input element if error occurs
    'counterErrorClass'         : "error",                  // error class appended to the countContainerElement if error occurs
    'counterText'               : "Total Count: ",          // counter text
    'errorTextElement'          : "div",                    // error text element
    'minimumErrorText'          : "Minimum not met",        // error message for minimum not met,
    'maximumErrorText'          : "Maximum exceeded",       // error message for maximum range exceeded,
    'displayErrorText'          : true,                     // display error text messages for minimum/maximum values
    'stopInputAtMaximum'        : true,                     // stop further text input if maximum reached
    'countSpaces'               : false,                    // count spaces as character (only for "character" type)
    'countDown'                 : false,                    // if the counter should deduct from maximum characters/words rather than counting up
    'countDownText'             : "Remaining: ",            // count down text
    'countExtendedCharacters'   : false,                    // count extended UTF-8 characters as 2 bytes (such as Chinese characters)

    // Callback API
    maxcount                    : function(el){},           // Callback: function(element) - Fires when the counter hits the maximum word/character count
    mincount                    : function(el){},           // Callback: function(element) - Fires when the counter hits the minimum word/character count
    init                        : function(el){}            // Callback: function(element) - Fires after the counter is initially setup
  };

  $.fn.textcounter = function(options) {
    return this.each(function() {
      new $.textcounter(this, options);
    });
  };

})(jQuery);<commit_msg>Option to add a text count class<commit_after>/*!
* jQuery Text Counter Plugin v0.3.3
* https://github.com/ractoon/jQuery-Text-Counter
*
* Copyright 2014 ractoon
* Released under the MIT license
*/
;(function($) {
  $.textcounter = function(el, options) {
    // To avoid scope issues, use 'base' instead of 'this'
    // to reference this class from internal events and functions.
    var base = this;

    // Access to jQuery and DOM versions of element
    base.$el = $(el);
    base.el = el;

    // Add a reverse reference to the DOM object
    base.$el.data("textcounter", base);

    base.init = function() {
      base.options = $.extend({}, $.textcounter.defaultOptions, options);

      // append the count element
      var counterText = base.options.countDown ? base.options.countDownText : base.options.counterText,
          counterNum = base.options.countDown ? base.options.max : 0;

      base.$el.after('<' + base.options.countContainerElement + ' class="' + base.options.countContainerClass + '">' + counterText + '' + counterNum + '</' + base.options.countContainerElement + '>');

      // bind input events
      base.$el.bind('keyup.textcounter click.textcounter blur.textcounter focus.textcounter change.textcounter paste.textcounter', base.checkLimits).trigger('click.textcounter');

      // TextCounter: init(el) Callback
      base.options.init(base.el);
    };

    base.checkLimits = function(e) {
      var $this = base.$el,
          $countEl = $this.next('.' + base.options.countContainerClass),
          $text = $this.val(),
          textCount = 0,
          textTotalCount = 0,
          eventTriggered =  e.originalEvent === undefined ? false : true;

      if (!$.isEmptyObject($text)) {
        if (base.options.type == "word") {  // word count
          textCount = $text.trim().replace(/\s+/gi, ' ').split(' ').length;
        }
        else {  // character count
          if (base.options.countSpaces) { // if need to count spaces
            textCount = $text.replace(/[^\S\n|\r|\r\n]/g, ' ').length;
          }
          else {
            textCount = $text.replace(/\s/g, '').length;
          }

          if (base.options.countExtendedCharacters) {
            var extended = $text.match(/[^\x00-\xff]/gi);

            if (extended == null) {
              textCount = $text.length;
            } else {
              textCount = $text.length + extended.length;
            }
          }
        }
      }

      // if max is auto retrieve value
      if (base.options.max == 'auto') {
        var max = base.$el.attr('maxlength');

        if (typeof max !== 'undefined' && max !== false) {
          base.options.max = max;
        }
        else {
          base.$el.next('.' + base.options.countContainerClass).text('error: [maxlength] attribute not set');
        }
      }

      // if this is a countdown counter deduct from the max characters/words
      textTotalCount = base.options.countDown ? base.options.max - textCount : textCount;

      // set the current text count
      base.setCount(textTotalCount);

      if (base.options.min > 0 && eventTriggered) {   // if a minimum value has been set
        if (textCount < base.options.min) {
          base.setErrors('min');
        }
        else if (textCount >= base.options.min) {
          // TextCounter: mincount(el) Callback
          base.options.mincount(base.el);

          base.clearErrors('min');
        }
      }

      if (base.options.max !== -1) {  // if a maximum value has been set
        if (textCount >= base.options.max && base.options.max != 0) {
          // TextCounter: maxcount(el) Callback
          base.options.maxcount(base.el);

          if (base.options.stopInputAtMaximum) {  // if the string should be trimmed at the maximum length
            var trimmedString = '';

            if (base.options.type == "word") {  // word type
              var wordArray = $text.split(/[^\S\n]/g);
              var i = 0;

              // iterate over individual words
              while (i < wordArray.length) {
                // if over the maximum words allowed break;
                if (i >= base.options.max - 1) break;

                if (wordArray[i] !== undefined) {
                  trimmedString += wordArray[i] + ' ';
                  i++;
                }
              }
            }
            else {  // character type
              if (base.options.countSpaces) {     // if spaces should be counted
                trimmedString = $text.substring(0, base.options.max);
              }
              else {
                var charArray = $text.split(''),
                    totalCharacters = charArray.length,
                    charCount = 0,
                    i = 0;

                while (charCount < base.options.max && i < totalCharacters) {
                  if (charArray[i] !== ' ') charCount++;
                  trimmedString += charArray[i++];
                }
              }
            }

            $this.val(trimmedString.trim());

            textTotalCount = base.options.countDown ? 0 : base.options.max;
            base.setCount(textTotalCount);
          } else {
            base.setErrors('max');
          }
        }
        else {
          base.clearErrors('max');
        }
      }
    };

    base.setCount = function(count) {
      var $this = base.$el,
          $countEl = $this.next('.' + base.options.countContainerClass);

      $countEl.children('.' + base.options.textCountClass).text(count);
    };

    base.setErrors = function(type) {
      var $this = base.$el,
          $countEl = $this.next('.' + base.options.countContainerClass);

      $this.addClass(base.options.inputErrorClass);
      $countEl.addClass(base.options.counterErrorClass);

      if (base.options.displayErrorText) {
        switch(type) {
          case 'min':
            errorText = base.options.minimumErrorText;
            break;
          case 'max':
            errorText = base.options.maximumErrorText;
            break;
        }

        if (!$countEl.children('.error-text-' + type).length) {
          $countEl.append('<' + base.options.errorTextElement + ' class="error-text error-text-' + type + '">' + errorText + '</' + base.options.errorTextElement + '>');
        }
      }
    };

    base.clearErrors = function(type) {
      var $this = base.$el,
          $countEl = $this.next('.' + base.options.countContainerClass);

      $countEl.children('.error-text-' + type).remove();

      if ($countEl.children('.error-text').length == 0) {
        $this.removeClass(base.options.inputErrorClass);
        $countEl.removeClass(base.options.counterErrorClass);
      }
    };

    // kick it off
    base.init();
  };

  $.textcounter.defaultOptions = {
    'type'                      : "character",              // "character" or "word"
    'min'                       : 0,                        // minimum number of characters/words
    'max'                       : 200,                      // maximum number of characters/words, -1 for unlimited, 'auto' to use maxlength attribute
    'countContainerElement'     : "div",                    // HTML element to wrap the text count in
    'countContainerClass'       : "text-count-wrapper",     // class applied to the countContainerElement
    'textCountClass'            : "text-count",             // class applied to the counter length
    'inputErrorClass'           : "error",                  // error class appended to the input element if error occurs
    'counterErrorClass'         : "error",                  // error class appended to the countContainerElement if error occurs
    'counterText'               : "Total Count: ",          // counter text
    'errorTextElement'          : "div",                    // error text element
    'minimumErrorText'          : "Minimum not met",        // error message for minimum not met,
    'maximumErrorText'          : "Maximum exceeded",       // error message for maximum range exceeded,
    'displayErrorText'          : true,                     // display error text messages for minimum/maximum values
    'stopInputAtMaximum'        : true,                     // stop further text input if maximum reached
    'countSpaces'               : false,                    // count spaces as character (only for "character" type)
    'countDown'                 : false,                    // if the counter should deduct from maximum characters/words rather than counting up
    'countDownText'             : "Remaining: ",            // count down text
    'countExtendedCharacters'   : false,                    // count extended UTF-8 characters as 2 bytes (such as Chinese characters)

    // Callback API
    maxcount                    : function(el){},           // Callback: function(element) - Fires when the counter hits the maximum word/character count
    mincount                    : function(el){},           // Callback: function(element) - Fires when the counter hits the minimum word/character count
    init                        : function(el){}            // Callback: function(element) - Fires after the counter is initially setup
  };

  $.fn.textcounter = function(options) {
    return this.each(function() {
      new $.textcounter(this, options);
    });
  };

})(jQuery);
<|endoftext|>


<commit_before>'use strict';

(function (angular) {
  angular
    .module('skinIndexPluginWidget')
    .controller('WidgetHomeCtrl', ['$scope', 'Buildfire', 'DataStore', 'TAG_NAMES', 'STATUS_CODE', 'ViewStack', '$rootScope',
      function ($scope, Buildfire, DataStore, TAG_NAMES, STATUS_CODE, ViewStack, $rootScope) {
        var WidgetHome = this;

        $rootScope.deviceHeight = window.innerHeight;
        $rootScope.deviceWidth = window.innerWidth;
        $rootScope.itemDetailsBackgroundImage = "";
        WidgetHome.bgImage = "";
        WidgetHome.deviceHeight = window.innerHeight;
        WidgetHome.deviceWidth = window.innerWidth;
        WidgetHome.addApiKey = false;

        var init = function () {
          var success = function (result) {
              WidgetHome.data = result.data;
              if (!WidgetHome.data.design)
                WidgetHome.data.design = {};
              if (!WidgetHome.data.settings)
                WidgetHome.data.settings = {};

              if (!WidgetHome.data.design.secListBGImage) {
                $rootScope.itemListbackgroundImage = "";
              } else {
                $rootScope.itemListbackgroundImage = WidgetHome.data.design.secListBGImage;
              }
              if (!WidgetHome.data.design.secListBGImage) {
                $rootScope.itemDetailsBackgroundImage = "";
              } else {
                $rootScope.itemDetailsBackgroundImage = WidgetHome.data.design.secListBGImage;
              }
            }
            , error = function (err) {
              console.error('Error while getting data', err);
            };
          DataStore.get(TAG_NAMES.UVO_INFO).then(success, error);

        };

        init();

        WidgetHome.goToLocation = function () {
          if (WidgetHome.data.settings && WidgetHome.data.settings.apikey) {
            ViewStack.push({
              template: 'Location'
            });
          }
          else {
            WidgetHome.addApiKey = true;
            setTimeout(function () {
              WidgetHome.addApiKey = false;
              $scope.$digest();
            }, 3000);
          }
        };

        var clearOnUpdateListener = Buildfire.datastore.onUpdate(function (event) {
          if (event.tag == TAG_NAMES.UVO_INFO) {
            console.log(">>>>>>>>>>>>>>>", event.data);
            WidgetHome.data = event.data;
            WidgetHome.bgImage = event.data.design.secListBGImage;
            $rootScope.itemDetailsBackgroundImage = WidgetHome.bgImage;
            if (!$scope.$$phase)$scope.$digest();
          }
        });

      }]);
})(window.angular);<commit_msg>Fixed error message issue<commit_after>'use strict';

(function (angular) {
  angular
    .module('skinIndexPluginWidget')
    .controller('WidgetHomeCtrl', ['$scope', 'Buildfire', 'DataStore', 'TAG_NAMES', 'STATUS_CODE', 'ViewStack', '$rootScope',
      function ($scope, Buildfire, DataStore, TAG_NAMES, STATUS_CODE, ViewStack, $rootScope) {
        var WidgetHome = this;

        $rootScope.deviceHeight = window.innerHeight;
        $rootScope.deviceWidth = window.innerWidth;
        $rootScope.itemDetailsBackgroundImage = "";
        WidgetHome.bgImage = "";
        WidgetHome.deviceHeight = window.innerHeight;
        WidgetHome.deviceWidth = window.innerWidth;
        WidgetHome.addApiKey = false;

        var init = function () {
          var success = function (result) {
              WidgetHome.data = result.data;
              if (!WidgetHome.data.design)
                WidgetHome.data.design = {};
              if (!WidgetHome.data.settings)
                WidgetHome.data.settings = {};

              if (!WidgetHome.data.design.secListBGImage) {
                $rootScope.itemListbackgroundImage = "";
              } else {
                $rootScope.itemListbackgroundImage = WidgetHome.data.design.secListBGImage;
              }
              if (!WidgetHome.data.design.secListBGImage) {
                $rootScope.itemDetailsBackgroundImage = "";
              } else {
                $rootScope.itemDetailsBackgroundImage = WidgetHome.data.design.secListBGImage;
              }
            }
            , error = function (err) {
              console.error('Error while getting data', err);
            };
          DataStore.get(TAG_NAMES.UVO_INFO).then(success, error);

        };

        init();

        WidgetHome.goToLocation = function () {
          if (WidgetHome.data.settings && WidgetHome.data.settings.apiKey) {
            ViewStack.push({
              template: 'Location'
            });
          }
          else {
            WidgetHome.addApiKey = true;
            setTimeout(function () {
              WidgetHome.addApiKey = false;
              $scope.$digest();
            }, 3000);
          }
        };

        var clearOnUpdateListener = Buildfire.datastore.onUpdate(function (event) {
          if (event.tag == TAG_NAMES.UVO_INFO) {
            console.log(">>>>>>>>>>>>>>>", event.data);
            WidgetHome.data = event.data;
            WidgetHome.bgImage = event.data.design.secListBGImage;
            $rootScope.itemDetailsBackgroundImage = WidgetHome.bgImage;
            if (!$scope.$$phase)$scope.$digest();
          }
        });

      }]);
})(window.angular);<|endoftext|>


<commit_before>require([
    "util/random"
],function() {
    module("random", {
        setup: function() {
            sinon.stub(Math, "random").returns(0.85);
        },
        teardown: function() {
            Math.random.restore();
        }
    });

    test("randomBelow", function() {
        equal(Math.randomBelow(6), 5, "6");
        equal(Math.randomBelow(1), 0, "1");
        throws(function() { Math.randomBelow(0); }, "invalid argument: 0", "0");
    });
});
<commit_msg>qunit<commit_after>require([
    "util/random"
],function() {
    module("util/random", {
        setup: function() {
            sinon.stub(Math, "random").returns(0.85);
        },
        teardown: function() {
            Math.random.restore();
        }
    });

    test("randomBelow", function() {
        equal(Math.randomBelow(6), 5, "6");
        equal(Math.randomBelow(1), 0, "1");
        throws(function() { Math.randomBelow(0); }, "invalid argument: 0", "0");
    });
});
<|endoftext|>


<commit_before>/**
 * Created by stas on 28.05.16.
 */
'use strict';

playzoneControllers.directive('tournamentTable', function () {
    return {
        restrict: 'E',
        link: function(scope, element, attrs) {
            scope.beautyResult = function (result) {
                return result == 0.5 ? '½' : result;
            };

            scope.tournament.$promise.then(
                function () {
                    scope.contentUrl = 'partials/tournament_table_' + scope.tournament.tournament_params.type + '.html?v=24';
                }
            )
        },
        transclude: true,
        scope: {
            tournament: '='
        },
        template: '

'
    }
});<commit_msg>Update version of swiss table<commit_after>/**
 * Created by stas on 28.05.16.
 */
'use strict';

playzoneControllers.directive('tournamentTable', function () {
    return {
        restrict: 'E',
        link: function(scope, element, attrs) {
            scope.beautyResult = function (result) {
                return result == 0.5 ? '½' : result;
            };

            scope.tournament.$promise.then(
                function () {
                    scope.contentUrl = 'partials/tournament_table_' + scope.tournament.tournament_params.type + '.html?v=25';
                }
            )
        },
        transclude: true,
        scope: {
            tournament: '='
        },
        template: '

'
    }
});<|endoftext|>


<commit_before>'use strict';

(function (angular, window) {
  angular
    .module('loyaltyPluginWidget')
    .controller('WidgetItemCtrl', ['$scope', 'ViewStack', 'RewardCache', '$sce',
      function ($scope, ViewStack, RewardCache, $sce) {

        var WidgetItem = this;
        var currentView = ViewStack.getCurrentView();

        if (RewardCache.getReward()) {
          WidgetItem.reward = RewardCache.getReward();
        }

        WidgetItem.confirmCancel = function () {
          if (WidgetItem.reward.pointsToRedeem <= currentView.totalPoints) {
            ViewStack.push({
              template: 'Confirm_Cancel'
            });
          } else {
            alert("Not Enough Points. Please earn more points in order to redeem this reward.");
          }
        };

        WidgetItem.safeHtml = function (html) {
          if (html)
            return $sce.trustAsHtml(html);
        };
      }])
})(window.angular, window);
<commit_msg>Formatted and added comments in widget item controller<commit_after>'use strict';

(function (angular, window) {
  angular
    .module('loyaltyPluginWidget')
    .controller('WidgetItemCtrl', ['$scope', 'ViewStack', 'RewardCache', '$sce',
      function ($scope, ViewStack, RewardCache, $sce) {

        var WidgetItem = this;

        /**
         * Initialize variable with current view returned by ViewStack service. In this case it is "Item_Details" view.
         */
        var currentView = ViewStack.getCurrentView();

        /**
         * Initialize WidgetItem.reward with reward details set in home controller
         */
        if (RewardCache.getReward()) {
          WidgetItem.reward = RewardCache.getReward();
        }

        /**
         * Check if user's total loyalty points are enough to redeem the reward, if yes redirect to next page.
         */
        WidgetItem.confirmCancel = function () {
          if (WidgetItem.reward.pointsToRedeem <= currentView.totalPoints) {
            ViewStack.push({
              template: 'Confirm_Cancel'
            });
          } else {
            alert("Not Enough Points. Please earn more points in order to redeem this reward.");
          }
        };

        /**
         * To parse and show reward's description in html format
         */
        WidgetItem.safeHtml = function (html) {
          if (html)
            return $sce.trustAsHtml(html);
        };
      }])
})(window.angular, window);
<|endoftext|>


<commit_before>import { ClusterMarkers } from './ClusterMarkers';
import { STATION_ENTRANCE, FEATURE_ENTRANCE_DOTS } from '../core/constants';
import { Points, BufferGeometry, Float32BufferAttribute } from '../Three';

class Entrances extends ClusterMarkers {

	constructor ( ctx, survey ) {

		super( ctx, survey.modelLimits, 4 );

		const self = this;
		const surveyTree = survey.surveyTree;
		const entrances = survey.metadata.entrances;
		const vertices = [];

		const geometry = new BufferGeometry();

		const material = ctx.materials.getEntrancePointMaterial();

		this.entranceColor = ctx.cfg.themeColor( 'stations.entrances.marker' );

		const markers = new Points( geometry, material );

		markers.layers.set( FEATURE_ENTRANCE_DOTS );

		// remove common elements from station names if no alternatives available

		let endNode = surveyTree;

		while ( endNode.children.length === 1 ) endNode = endNode.children [ 0 ];

		// find entrances and add Markers

		surveyTree.traverse( _addEntrance );

		const l = vertices.length * 3;

		if ( l > 0 ) {

			const positions = new Float32BufferAttribute( l, 3 );
			const colors = new Float32BufferAttribute( l, 3 );

			positions.copyVector3sArray( vertices );

			geometry.setAttribute( 'position', positions );
			geometry.setAttribute( 'color', colors );

		} else {

			this.visible = false;

		}

		this.markers = markers;
		this.vertices = vertices;
		this.metadata = survey.metadata;

		// set default colors - needs to be after markers property is set
		this.setSelection( null );

		this.addStatic( markers );

		return this;

		function _addEntrance( node ) {

			if ( ! ( ( node.ownType ?? node.type ) & STATION_ENTRANCE ) ) return;

			if ( node.next ) {

				let next = node.next;

				// skip labels for all expect lowest id station
				while ( next !== node ) {

					if ( ( next.ownType & STATION_ENTRANCE ) !== 0 && Math.abs( node.id ) > Math.abs( next.id ) ) return;
					next = next.next;

				}

			}

			vertices.push( node );

			let name;

			const entranceInfo = entrances[ node.getPath() ];

			if ( entranceInfo?.name !== undefined ) {

				name = entranceInfo.name;
				if ( name === '-skip' ) return;

			} else if ( node.comment !== undefined ) {

				name = node.comment;

			} else {

				name = node.getPath( endNode );

			}

			self.addMarker( node, ' ' + name + ' ' );

		}

	}

	getStation ( index ) {

		const station = this.vertices[ index ];
		const stationName = station.getPath();

		return {
			station: station,
			name: stationName,
			info: this.metadata.entrances[ stationName ]
		};

	}

	setStation ( station, info ) {

		const metadata = this.metadata;

		metadata.entrances[ station.getPath() ] = info;

		metadata.saveLocal();

	}

	setSelection ( selection ) {

		const colors = this.markers.geometry.getAttribute( 'color' );
		const color = this.entranceColor;

		if ( colors === undefined ) return;

		if ( selection === null || selection.isEmpty() ) {

			const array = colors.array;
			const l = array.length;

			for ( let i = 0; i < l; i += 3 ) {

				color.toArray( array, i );

			}

		} else {

			const idSet = selection.getIds();

			this.vertices.forEach( function ( node, i ) {

				if ( idSet.has( node.id ) ) {

					color.toArray( colors, i * 3 );

				} else {

					colors.setXYZ( i, 0.5, 0.5, 0.5 );

				}

			} );

		}

		colors.needsUpdate = true;

	}

	forEachEntrance ( callback ) {

		this.vertices.forEach( (v, i ) => callback( v, i ) );

	}

}

export { Entrances };<commit_msg>handle linked nodes in distance determination<commit_after>import { ClusterMarkers } from './ClusterMarkers';
import { STATION_ENTRANCE, FEATURE_ENTRANCE_DOTS } from '../core/constants';
import { Points, BufferGeometry, Float32BufferAttribute } from '../Three';

class Entrances extends ClusterMarkers {

	constructor ( ctx, survey ) {

		super( ctx, survey.modelLimits, 4 );

		const self = this;
		const surveyTree = survey.surveyTree;
		const entrances = survey.metadata.entrances;
		const vertices = [];

		const geometry = new BufferGeometry();

		const material = ctx.materials.getEntrancePointMaterial();

		this.entranceColor = ctx.cfg.themeColor( 'stations.entrances.marker' );

		const markers = new Points( geometry, material );

		markers.layers.set( FEATURE_ENTRANCE_DOTS );

		// remove common elements from station names if no alternatives available

		let endNode = surveyTree;

		while ( endNode.children.length === 1 ) endNode = endNode.children [ 0 ];

		// find entrances and add Markers

		surveyTree.traverse( _addEntrance );

		const l = vertices.length * 3;

		if ( l > 0 ) {

			const positions = new Float32BufferAttribute( l, 3 );
			const colors = new Float32BufferAttribute( l, 3 );

			positions.copyVector3sArray( vertices );

			geometry.setAttribute( 'position', positions );
			geometry.setAttribute( 'color', colors );

		} else {

			this.visible = false;

		}

		this.markers = markers;
		this.vertices = vertices;
		this.metadata = survey.metadata;

		// set default colors - needs to be after markers property is set
		this.setSelection( null );

		this.addStatic( markers );

		return this;

		function _addEntrance( node ) {

			if ( ! ( ( node.ownType ?? node.type ) & STATION_ENTRANCE ) ) return;

			if ( node.next ) {

				let next = node.next;

				// skip labels for all expect lowest id station
				while ( next !== node ) {

					if ( ( next.ownType & STATION_ENTRANCE ) !== 0 && Math.abs( node.id ) > Math.abs( next.id ) ) return;
					next = next.next;

				}

			}

			vertices.push( node );

			let name;

			const entranceInfo = entrances[ node.getPath() ];

			if ( entranceInfo?.name !== undefined ) {

				name = entranceInfo.name;
				if ( name === '-skip' ) return;

			} else if ( node.comment !== undefined ) {

				name = node.comment;

			} else {

				name = node.getPath( endNode );

			}

			self.addMarker( node, ' ' + name + ' ' );

		}

	}

	getStation ( index ) {

		const station = this.vertices[ index ];
		const stationName = station.getPath();

		return {
			station: station,
			name: stationName,
			info: this.metadata.entrances[ stationName ]
		};

	}

	setStation ( station, info ) {

		const metadata = this.metadata;

		metadata.entrances[ station.getPath() ] = info;

		metadata.saveLocal();

	}

	setSelection ( selection ) {

		const colors = this.markers.geometry.getAttribute( 'color' );
		const color = this.entranceColor;

		if ( colors === undefined ) return;

		if ( selection === null || selection.isEmpty() ) {

			const array = colors.array;
			const l = array.length;

			for ( let i = 0; i < l; i += 3 ) {

				color.toArray( array, i );

			}

		} else {

			const idSet = selection.getIds();

			this.vertices.forEach( function ( node, i ) {

				if ( idSet.has( node.id ) ) {

					color.toArray( colors, i * 3 );

				} else {

					colors.setXYZ( i, 0.5, 0.5, 0.5 );

				}

			} );

		}

		colors.needsUpdate = true;

	}

	forEachEntrance ( callback ) {

		this.vertices.forEach( e => {

			callback( e );

			let next = e.next;

			while ( next !== null & next !== e ) {

				callback( next );
				next = next.next;

			}

		} );

	}

}

export { Entrances };<|endoftext|>


<commit_before>import axios from 'axios'

const logging = process.env.NODE_ENV !== 'production'

const ApiService = {
  init () {
    axios.defaults.baseURL = process.env.AXIOS_BASE_URL
    if (logging) {
      console.log('turning on interceptors')
      axios.interceptors.request.use(function (request) {
        console.log(request)
        return request
      }, function (error) {
        console.log(error)
        return Promise.reject(error)
      })
      axios.interceptors.response.use(function (response) {
        console.log(response)
        return response
      }, function (error) {
        console.log(error)
        return Promise.reject(error)
      })
    }
  },
  authHeader (prefix, token) {
    // set auth header. Expects prefix to be "Bearer", "JWT" etc.
    // deletes auth header if called without a token (useful for logging out)
    if (prefix && token) {
      axios.defaults.headers.common['Authorization'] = `${prefix} ${token}`
    } else if (axios.defaults.headers.common['Authorization']) {
      delete axios.defaults.headers.common['Authorization']
    }
  },
  query (resource, params) {
    return axios.get(resource, { params: params })
  },
  get (resource, uuid) {
    if (uuid.length && uuid.length === 36) {
      return axios.get(`${resource}/${uuid}/`)
    }
    throw new Error(`API get: UUID required to fetch ${resource}`)
  },
  post (resource, params) {
    return axios.post(resource + '/', params)
  }
}

export default ApiService
<commit_msg>support patch<commit_after>import axios from 'axios'

const logging = process.env.NODE_ENV !== 'production'

const ApiService = {
  init () {
    axios.defaults.baseURL = process.env.AXIOS_BASE_URL
    if (logging) {
      console.log('turning on interceptors')
      axios.interceptors.request.use(function (request) {
        console.log(request)
        return request
      }, function (error) {
        console.log(error)
        return Promise.reject(error)
      })
      axios.interceptors.response.use(function (response) {
        console.log(response)
        return response
      }, function (error) {
        console.log(error)
        return Promise.reject(error)
      })
    }
  },
  authHeader (prefix, token) {
    // set auth header. Expects prefix to be "Bearer", "JWT" etc.
    // deletes auth header if called without a token (useful for logging out)
    if (prefix && token) {
      axios.defaults.headers.common['Authorization'] = `${prefix} ${token}`
    } else if (axios.defaults.headers.common['Authorization']) {
      delete axios.defaults.headers.common['Authorization']
    }
  },
  query (resource, params) {
    return axios.get(resource, { params: params })
  },
  get (resource, uuid) {
    if (uuid.length && uuid.length === 36) {
      return axios.get(`${resource}/${uuid}/`)
    }
    throw new Error(`API get: UUID required to fetch ${resource}`)
  },
  post (resource, params) {
    return axios.post(resource + '/', params)
  },
  patch (resource, record, params) {
    return axios.patch(`${resource}/${record}/`, params)
  }
}

export default ApiService
<|endoftext|>


<commit_before>import React from "react";
import styled from "@emotion/styled";

const Dot = styled.div`
  display: flex;
  height: 6rem;
  width: 6rem;
  border-radius: 50%;
  margin-right: 10%;
`;

function RoundLights(props)  {
    statusColors = [];
    /*
     * Each rounds color status is appended to the status color array,
     * where green represents completed and gray represents rounds
     * that have not yet been completed.
     */
    for (let i = 0; i < props.currentRound; i++) {
      statusColors.push("#1aa260");
    }
    const remaining = tprops.totalRounds - this.props.currentRound;
    for (let i = 0; i < remaining; i++) {
      statusColors.push("#bbb");
    }

    return statusColors.map((color, i) => {
      return <Dot style={{ backgroundColor: `${color}` }} key={i}></Dot>;
    });
}

export default RoundLights;<commit_msg>Add PR Fixes<commit_after>import React from "react";
import styled from "@emotion/styled";

const Dot = styled.div`
  display: flex;
  height: 6rem;
  width: 6rem;
  border-radius: 50%;
  margin-right: 10%;
`;

function RoundLights(props)  {
    statusColors = [];
    /*
     * Each rounds color status is appended to the status color array,
     * where green represents completed and gray represents rounds
     * that have not yet been completed.
     */
    for (let i = 0; i < props.currentRound; i++) {
      statusColors.push("#1aa260");
    }
    const remaining = props.totalRounds - this.props.currentRound;
    for (let i = 0; i < remaining; i++) {
      statusColors.push("#bbb");
    }

    return statusColors.map((color, i) => {
      return <Dot style={{ backgroundColor: `${color}` }} key={i}></Dot>;
    });
}

export default RoundLights;<|endoftext|>


<commit_before>import extend from 'extend';
import Delta from 'quill-delta';
import Emitter from '../core/emitter';
import Keyboard from '../modules/keyboard';
import Theme from '../core/theme';
import ColorPicker from '../ui/color-picker';
import IconPicker from '../ui/icon-picker';
import Picker from '../ui/picker';
import Tooltip from '../ui/tooltip';

const ALIGNS = [ false, 'center', 'right', 'justify' ];

const COLORS = [
  "#000000", "#e60000", "#ff9900", "#ffff00", "#008a00", "#0066cc", "#9933ff",
  "#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff",
  "#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff",
  "#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2",
  "#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466"
];

const FONTS = [ false, 'serif', 'monospace' ];

const HEADERS = [ '1', '2', '3', false ];

const SIZES = [ 'small', false, 'large', 'huge' ];

class BaseTheme extends Theme {
  constructor(quill, options) {
    super(quill, options);
    let listener = (e) => {
      if (!document.body.contains(quill.root)) {
        return document.body.removeEventListener('click', listener);
      }
      if (this.tooltip != null && !this.tooltip.root.contains(e.target) &&
          document.activeElement !== this.tooltip.textbox && !this.quill.hasFocus()) {
        this.tooltip.hide();
      }
      if (this.pickers != null) {
        this.pickers.forEach(function(picker) {
          if (!picker.container.contains(e.target)) {
            picker.close();
          }
        });
      }
    };
    document.body.addEventListener('click', listener);
  }

  addModule(name) {
    let module = super.addModule(name);
    if (name === 'toolbar') {
      this.extendToolbar(module);
    }
    return module;
  }

  buildButtons(buttons, icons) {
    buttons.forEach((button) => {
      let className = button.getAttribute('class') || '';
      className.split(/\s+/).forEach((name) => {
        if (!name.startsWith('ql-')) return;
        name = name.slice('ql-'.length);
        if (icons[name] == null) return;
        if (name === 'direction') {
          button.innerHTML = icons[name][''] + icons[name]['rtl'];
        } else if (typeof icons[name] === 'string') {
          button.innerHTML = icons[name];
        } else {
          let value = button.value || '';
          if (value != null && icons[name][value]) {
            button.innerHTML = icons[name][value];
          }
        }
      });
    });
  }

  buildPickers(selects, icons) {
    this.pickers = selects.map((select) => {
      if (select.classList.contains('ql-align')) {
        if (select.querySelector('option') == null) {
          fillSelect(select, ALIGNS);
        }
        return new IconPicker(select, icons.align);
      } else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {
        let format = select.classList.contains('ql-background') ? 'background' : 'color';
        if (select.querySelector('option') == null) {
          fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000');
        }
        return new ColorPicker(select, icons[format]);
      } else {
        if (select.querySelector('option') == null) {
          if (select.classList.contains('ql-font')) {
            fillSelect(select, FONTS);
          } else if (select.classList.contains('ql-header')) {
            fillSelect(select, HEADERS);
          } else if (select.classList.contains('ql-size')) {
            fillSelect(select, SIZES);
          }
        }
        return new Picker(select);
      }
    });
    let update = () => {
      this.pickers.forEach(function(picker) {
        picker.update();
      });
    };
    this.quill.on(Emitter.events.EDITOR_CHANGE, update);
  }
}
BaseTheme.DEFAULTS = extend(true, {}, Theme.DEFAULTS, {
  modules: {
    toolbar: {
      handlers: {
        formula: function() {
          this.quill.theme.tooltip.edit('formula');
        },
        image: function() {
          let fileInput = this.container.querySelector('input.ql-image[type=file]');
          if (fileInput == null) {
            fileInput = document.createElement('input');
            fileInput.setAttribute('type', 'file');
            fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');
            fileInput.classList.add('ql-image');
            fileInput.addEventListener('change', () => {
              if (fileInput.files != null && fileInput.files[0] != null) {
                let reader = new FileReader();
                reader.onload = (e) => {
                  let range = this.quill.getSelection(true);
                  this.quill.updateContents(new Delta()
                    .retain(range.index)
                    .delete(range.length)
                    .insert({ image: e.target.result })
                  , Emitter.sources.USER);
                  fileInput.value = "";
                }
                reader.readAsDataURL(fileInput.files[0]);
              }
            });
            this.container.appendChild(fileInput);
          }
          fileInput.click();
        },
        video: function() {
          this.quill.theme.tooltip.edit('video');
        }
      }
    }
  }
});

class BaseTooltip extends Tooltip {
  constructor(quill, boundsContainer) {
    super(quill, boundsContainer);
    this.textbox = this.root.querySelector('input[type="text"]');
    this.listen();
  }

  listen() {
    this.textbox.addEventListener('keydown', (event) => {
      if (Keyboard.match(event, 'enter')) {
        this.save();
        event.preventDefault();
      } else if (Keyboard.match(event, 'escape')) {
        this.cancel();
        event.preventDefault();
      }
    });
  }

  cancel() {
    this.hide();
  }

  edit(mode = 'link', preview = null) {
    this.root.classList.remove('ql-hidden');
    this.root.classList.add('ql-editing');
    if (preview != null) {
      this.textbox.value = preview;
    } else if (mode !== this.root.getAttribute('data-mode')) {
      this.textbox.value = '';
    }
    this.position(this.quill.getBounds(this.quill.selection.savedRange));
    this.textbox.select();
    this.textbox.setAttribute('placeholder', this.textbox.getAttribute(`data-${mode}`) || '');
    this.root.setAttribute('data-mode', mode);
  }

  restoreFocus() {
    let scrollTop = this.quill.scrollingContainer.scrollTop;
    this.quill.focus();
    this.quill.scrollingContainer.scrollTop = scrollTop;
  }

  convertVideoUrlToEmbeded(url) {
    let match = url.match(/^(https?):\/\/(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/) ||
                url.match(/^(https?):\/\/(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/);

    if (match) {
      return match[1] + '://www.youtube.com/embed/' + match[2] + '?showinfo=0';
    }

    if (match = url.match(/^(https?):\/\/(?:www\.)?vimeo\.com\/(\d+)/)) {  // eslint-disable-line no-cond-assign
      return match[1] + '://player.vimeo.com/video/' + match[2] + '/';
    }

    return url;
  }

  save() {
    let value = this.textbox.value;
    switch(this.root.getAttribute('data-mode')) {
      case 'link': {
        let scrollTop = this.quill.root.scrollTop;
        if (this.linkRange) {
          this.quill.formatText(this.linkRange, 'link', value, Emitter.sources.USER);
          delete this.linkRange;
        } else {
          this.restoreFocus();
          this.quill.format('link', value, Emitter.sources.USER);
        }
        this.quill.root.scrollTop = scrollTop;
        break;
      }
      case 'video': {
        value = this.convertVideoUrlToEmbeded(value);
      } // eslint-disable-next-line no-fallthrough
      case 'formula': {
        if (!value) break;
        let range = this.quill.getSelection(true);
        if (range != null) {
          let index = range.index + range.length;
          this.quill.insertEmbed(index, this.root.getAttribute('data-mode'), value, Emitter.sources.USER);
          if (this.root.getAttribute('data-mode') === 'formula') {
            this.quill.insertText(index + 1, ' ', Emitter.sources.USER);
          }
          this.quill.setSelection(index + 2, Emitter.sources.USER);
        }
        break;
      }
      default:
    }
    this.textbox.value = '';
    this.hide();
  }
}

function fillSelect(select, values, defaultValue = false) {
  values.forEach(function(value) {
    let option = document.createElement('option');
    if (value === defaultValue) {
      option.setAttribute('selected', 'selected');
    } else {
      option.setAttribute('value', value);
    }
    select.appendChild(option);
  });
}

export { BaseTooltip, BaseTheme as default };
<commit_msg>no need to be class method<commit_after>import extend from 'extend';
import Delta from 'quill-delta';
import Emitter from '../core/emitter';
import Keyboard from '../modules/keyboard';
import Theme from '../core/theme';
import ColorPicker from '../ui/color-picker';
import IconPicker from '../ui/icon-picker';
import Picker from '../ui/picker';
import Tooltip from '../ui/tooltip';

const ALIGNS = [ false, 'center', 'right', 'justify' ];

const COLORS = [
  "#000000", "#e60000", "#ff9900", "#ffff00", "#008a00", "#0066cc", "#9933ff",
  "#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff",
  "#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff",
  "#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2",
  "#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466"
];

const FONTS = [ false, 'serif', 'monospace' ];

const HEADERS = [ '1', '2', '3', false ];

const SIZES = [ 'small', false, 'large', 'huge' ];

class BaseTheme extends Theme {
  constructor(quill, options) {
    super(quill, options);
    let listener = (e) => {
      if (!document.body.contains(quill.root)) {
        return document.body.removeEventListener('click', listener);
      }
      if (this.tooltip != null && !this.tooltip.root.contains(e.target) &&
          document.activeElement !== this.tooltip.textbox && !this.quill.hasFocus()) {
        this.tooltip.hide();
      }
      if (this.pickers != null) {
        this.pickers.forEach(function(picker) {
          if (!picker.container.contains(e.target)) {
            picker.close();
          }
        });
      }
    };
    document.body.addEventListener('click', listener);
  }

  addModule(name) {
    let module = super.addModule(name);
    if (name === 'toolbar') {
      this.extendToolbar(module);
    }
    return module;
  }

  buildButtons(buttons, icons) {
    buttons.forEach((button) => {
      let className = button.getAttribute('class') || '';
      className.split(/\s+/).forEach((name) => {
        if (!name.startsWith('ql-')) return;
        name = name.slice('ql-'.length);
        if (icons[name] == null) return;
        if (name === 'direction') {
          button.innerHTML = icons[name][''] + icons[name]['rtl'];
        } else if (typeof icons[name] === 'string') {
          button.innerHTML = icons[name];
        } else {
          let value = button.value || '';
          if (value != null && icons[name][value]) {
            button.innerHTML = icons[name][value];
          }
        }
      });
    });
  }

  buildPickers(selects, icons) {
    this.pickers = selects.map((select) => {
      if (select.classList.contains('ql-align')) {
        if (select.querySelector('option') == null) {
          fillSelect(select, ALIGNS);
        }
        return new IconPicker(select, icons.align);
      } else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {
        let format = select.classList.contains('ql-background') ? 'background' : 'color';
        if (select.querySelector('option') == null) {
          fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000');
        }
        return new ColorPicker(select, icons[format]);
      } else {
        if (select.querySelector('option') == null) {
          if (select.classList.contains('ql-font')) {
            fillSelect(select, FONTS);
          } else if (select.classList.contains('ql-header')) {
            fillSelect(select, HEADERS);
          } else if (select.classList.contains('ql-size')) {
            fillSelect(select, SIZES);
          }
        }
        return new Picker(select);
      }
    });
    let update = () => {
      this.pickers.forEach(function(picker) {
        picker.update();
      });
    };
    this.quill.on(Emitter.events.EDITOR_CHANGE, update);
  }
}
BaseTheme.DEFAULTS = extend(true, {}, Theme.DEFAULTS, {
  modules: {
    toolbar: {
      handlers: {
        formula: function() {
          this.quill.theme.tooltip.edit('formula');
        },
        image: function() {
          let fileInput = this.container.querySelector('input.ql-image[type=file]');
          if (fileInput == null) {
            fileInput = document.createElement('input');
            fileInput.setAttribute('type', 'file');
            fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');
            fileInput.classList.add('ql-image');
            fileInput.addEventListener('change', () => {
              if (fileInput.files != null && fileInput.files[0] != null) {
                let reader = new FileReader();
                reader.onload = (e) => {
                  let range = this.quill.getSelection(true);
                  this.quill.updateContents(new Delta()
                    .retain(range.index)
                    .delete(range.length)
                    .insert({ image: e.target.result })
                  , Emitter.sources.USER);
                  fileInput.value = "";
                }
                reader.readAsDataURL(fileInput.files[0]);
              }
            });
            this.container.appendChild(fileInput);
          }
          fileInput.click();
        },
        video: function() {
          this.quill.theme.tooltip.edit('video');
        }
      }
    }
  }
});

class BaseTooltip extends Tooltip {
  constructor(quill, boundsContainer) {
    super(quill, boundsContainer);
    this.textbox = this.root.querySelector('input[type="text"]');
    this.listen();
  }

  listen() {
    this.textbox.addEventListener('keydown', (event) => {
      if (Keyboard.match(event, 'enter')) {
        this.save();
        event.preventDefault();
      } else if (Keyboard.match(event, 'escape')) {
        this.cancel();
        event.preventDefault();
      }
    });
  }

  cancel() {
    this.hide();
  }

  edit(mode = 'link', preview = null) {
    this.root.classList.remove('ql-hidden');
    this.root.classList.add('ql-editing');
    if (preview != null) {
      this.textbox.value = preview;
    } else if (mode !== this.root.getAttribute('data-mode')) {
      this.textbox.value = '';
    }
    this.position(this.quill.getBounds(this.quill.selection.savedRange));
    this.textbox.select();
    this.textbox.setAttribute('placeholder', this.textbox.getAttribute(`data-${mode}`) || '');
    this.root.setAttribute('data-mode', mode);
  }

  restoreFocus() {
    let scrollTop = this.quill.scrollingContainer.scrollTop;
    this.quill.focus();
    this.quill.scrollingContainer.scrollTop = scrollTop;
  }

  save() {
    let value = this.textbox.value;
    switch(this.root.getAttribute('data-mode')) {
      case 'link': {
        let scrollTop = this.quill.root.scrollTop;
        if (this.linkRange) {
          this.quill.formatText(this.linkRange, 'link', value, Emitter.sources.USER);
          delete this.linkRange;
        } else {
          this.restoreFocus();
          this.quill.format('link', value, Emitter.sources.USER);
        }
        this.quill.root.scrollTop = scrollTop;
        break;
      }
      case 'video': {
        value = extractVideoUrl(value);
      } // eslint-disable-next-line no-fallthrough
      case 'formula': {
        if (!value) break;
        let range = this.quill.getSelection(true);
        if (range != null) {
          let index = range.index + range.length;
          this.quill.insertEmbed(index, this.root.getAttribute('data-mode'), value, Emitter.sources.USER);
          if (this.root.getAttribute('data-mode') === 'formula') {
            this.quill.insertText(index + 1, ' ', Emitter.sources.USER);
          }
          this.quill.setSelection(index + 2, Emitter.sources.USER);
        }
        break;
      }
      default:
    }
    this.textbox.value = '';
    this.hide();
  }
}

function extractVideoUrl(url) {
  let match = url.match(/^(https?):\/\/(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/) ||
              url.match(/^(https?):\/\/(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/);
  if (match) {
    return match[1] + '://www.youtube.com/embed/' + match[2] + '?showinfo=0';
  }
  if (match = url.match(/^(https?):\/\/(?:www\.)?vimeo\.com\/(\d+)/)) {  // eslint-disable-line no-cond-assign
    return match[1] + '://player.vimeo.com/video/' + match[2] + '/';
  }
  return url;
}

function fillSelect(select, values, defaultValue = false) {
  values.forEach(function(value) {
    let option = document.createElement('option');
    if (value === defaultValue) {
      option.setAttribute('selected', 'selected');
    } else {
      option.setAttribute('value', value);
    }
    select.appendChild(option);
  });
}

export { BaseTooltip, BaseTheme as default };
<|endoftext|>