<commit_before>// LineManager.js

//---------------------------------------------------------------------------
// ★LineGraphクラス 主に線や色分けの情報を管理する
//---------------------------------------------------------------------------
pzpr.classmgr.makeCommon({
"LineGraph:GraphBase":{
	initialize : function(){
		if(this.moveline){ this.relation['cell.qnum'] = 'move';}
	},

	enabled : false,
	relation : {'border.line':'link'},

	pointgroup : 'cell',
	linkgroup  : 'border',

	isLineCross : false,	// 線が交差するパズル

	makeClist : false,		// 線が存在するclistを生成する
	moveline  : false,		// 丸数字などを動かすパズル

	coloring : true,

	//--------------------------------------------------------------------------------
	// linegraph.setComponentRefs()    objにcomponentの設定を行う (性能対策)
	// linegraph.isedgeexistsbylinkobj() linkobjにedgeが存在するか判定する
	//
	// linegraph.getObjNodeList()      objにあるnodeを取得する
	// linegraph.resetObjNodeList()    objからnodeをクリアする
	//--------------------------------------------------------------------------------
	setComponentRefs : function(obj, component){ obj.path = component;},
	isedgeexistsbylinkobj : function(linkobj){ return linkobj.path!==null;},

	getObjNodeList   : function(nodeobj){ return nodeobj.pathnodes;},
	resetObjNodeList : function(nodeobj){
		nodeobj.pathnodes = [];
		if(this.moveline){ this.resetExtraData(nodeobj);}
	},

	//--------------------------------------------------------------------------------
	// linegraph.isnodevalid()           そのセルにNodeが存在すべきかどうか返す
	// linegraph.isedgevalidbylinkobj()  そのborderにEdgeが存在すべきかどうか返す
	// linegraph.iscrossing()            そのセルで交差するかどうか返す
	//--------------------------------------------------------------------------------
	isnodevalid          : function(cell)  { return cell.lcnt>0 || (this.moveline && cell.isNum());},
	isedgevalidbylinkobj : function(border){ return border.isLine();},
	iscrossing           : function(cell)  { return this.isLineCross;},

	//---------------------------------------------------------------------------
	// linegraph.rebuild()  既存の情報からデータを再設定する
	// linegraph.rebuild2() 継承先に固有のデータを設定する
	//---------------------------------------------------------------------------
	rebuild : function(){
		if(this.board.borderAsLine){ this.pointgroup = 'cross';}
		pzpr.common.GraphBase.prototype.rebuild.call(this);
	},
	rebuild2 : function(){
		if(!!this.incdecLineCount){
			this.resetLineCount();
		}
		pzpr.common.GraphBase.prototype.rebuild2.call(this);
	},

	//---------------------------------------------------------------------------
	// linegraph.resetLineCount()  初期化時に、lcnt情報を初期化する
	// linegraph.incdecLineCount() 線が引かれたり消された時に、lcnt変数を生成し直す
	//---------------------------------------------------------------------------
	resetLineCount : function(){
		var cells = this.board[this.pointgroup], borders = this.board[this.linkgroup];
		this.ltotal=[cells.length];
		for(var c=0;c<cells.length;c++){
			cells[c].lcnt = 0;
		}
		for(var id=0;id<borders.length;id++){
			if(this.isedgevalidbylinkobj(borders[id])){
				this.incdecLineCount(borders[id], true);
			}
		}
	},
	incdecLineCount : function(border, isset){
		if(border.group!==this.linkgroup){ return;}
		for(var i=0;i<2;i++){
			var cell = border.sideobj[i];
			if(!cell.isnull){
				this.ltotal[cell.lcnt]--;
				if(isset){ cell.lcnt++;}else{ cell.lcnt--;}
				this.ltotal[cell.lcnt] = (this.ltotal[cell.lcnt] || 0) + 1;
			}
		}
	},

	//---------------------------------------------------------------------------
	// linegraph.setEdgeByLinkObj() 線が引かれたり消された時に、lcnt変数や線の情報を生成しなおす
	//---------------------------------------------------------------------------
	setEdgeByLinkObj : function(linkobj){
		var isset = this.isedgevalidbylinkobj(linkobj);
		if(isset===this.isedgeexistsbylinkobj(linkobj)){ return;}

		if(!!this.incdecLineCount){
			this.incdecLineCount(linkobj, isset);
		}

		if(isset){ this.addEdgeByLinkObj(linkobj);}
		else     { this.removeEdgeByLinkObj(linkobj);}
	},

	//---------------------------------------------------------------------------
	// graph.addEdgeByLinkObj()    指定されたオブジェクトの場所にEdgeを生成する
	// graph.removeEdgeByLinkObj() 指定されたオブジェクトの場所からEdgeを除去する
	//---------------------------------------------------------------------------
	addEdgeByLinkObj : function(linkobj){ // 線(など)を引いた時の処理
		var enattach = (this.modifyNodes.length===0);
		var sidenodeobj = this.getSideObjByLinkObj(linkobj);

		// 周囲のNodeをグラフに追加するかどうか確認する
		this.createNodeIfEmpty(sidenodeobj[0]);
		this.createNodeIfEmpty(sidenodeobj[1]);

		// linkするNodeを取得する
		var sidenodes = this.getSideNodesByLinkObj(linkobj);

		// 周囲のNodeとlink
		this.addEdge(sidenodes[0], sidenodes[1]);

		// 周囲のComponentにくっついただけの場合は情報を更新して終了
		if(this.rebuildmode || !enattach){ return;}
		var attachnodes = null, node1 = sidenodes[0], node2 = sidenodes[1];
		if     (node1.obj.lcnt===1 && node1.component===null && node2.component!==null){ attachnodes = [sidenodes[0], sidenodes[1]];}
		else if(node2.obj.lcnt===1 && node2.component===null && node1.component!==null){ attachnodes = [sidenodes[1], sidenodes[0]];}
		if(!!attachnodes){
			this.attachNode(attachnodes[0], attachnodes[1].component);
			this.modifyNodes = [];
		}
	},
	removeEdgeByLinkObj : function(linkobj){ // 線(など)を消した時の処理
		// unlinkするNodeを取得する
		var endetach = (this.modifyNodes.length===0);
		var sidenodes = this.getSideNodesByLinkObj(linkobj);

		// 周囲のNodeとunlink
		this.removeEdge(sidenodes[0], sidenodes[1]);

		// 周囲のNodeをグラフから取り除くかどうか確認する
		this.deleteNodeIfEmpty(sidenodes[0].obj);
		this.deleteNodeIfEmpty(sidenodes[1].obj);

		this.setComponentRefs(linkobj, null);

		// 周囲のComponent末端から切り離されただけの場合は情報を更新して終了
		if(!endetach){ return;}
		var detachnodes = null, node1 = sidenodes[0], node2 = sidenodes[1];
		var lcnt1 = node1.obj.lcnt, lcnt2 = node2.obj.lcnt;
		if     (lcnt1===0 && ((lcnt2===1 || (!this.isLineCross && lcnt2>1)) && node2.component!==null)){ detachnodes = [sidenodes[0], sidenodes[1]];}
		else if(lcnt2===0 && ((lcnt1===1 || (!this.isLineCross && lcnt1>1)) && node1.component!==null)){ detachnodes = [sidenodes[1], sidenodes[0]];}
		if(!!detachnodes){
			this.setComponentInfo(detachnodes[1].component);
			this.modifyNodes = [];
		}
	},

	//---------------------------------------------------------------------------
	// linegraph.setOtherInformation() 移動系パズルで数字などが入力された時に線の情報を生成しなおす
	//---------------------------------------------------------------------------
	modifyOtherInfo : function(cell, relation){
		var haspath = !!cell.path;
		if(haspath!==this.isnodevalid(cell)){
			if(haspath){ this.deleteNodeIfEmpty(cell);}
			else       { this.createNodeIfEmpty(cell);}
		}
		else{
			if(haspath){ this.setComponentInfo(cell.path);}
			else       { this.resetExtraData(cell);}
		}
	},

	//---------------------------------------------------------------------------
	// linegraph.createNodeIfEmpty()  指定されたオブジェクトの場所にNodeを生成する
	// linegraph.deleteNodeIfEmpty()  指定されたオブジェクトの場所からNodeを除去する
	//---------------------------------------------------------------------------
	createNodeIfEmpty : function(cell){
		var nodes = this.getObjNodeList(cell);

		// 周囲のNode生成が必要かもしれないのでチェック＆create
		if(nodes.length===0){
			this.createNode(cell);
		}
		// 交差あり盤面の処理
		else if(!nodes[1] && nodes[0].nodes.length===2 && this.iscrossing(cell)){
			// 2本->3本になる時はNodeを追加して分離します
			this.createNode(cell);

			// 上下/左右の線が1本ずつだった場合は左右の線をnodes[1]に付加し直します
			var nbnodes = nodes[0].nodes;
			var isvert = [cell.getvert(nbnodes[0].obj, 2), cell.getvert(nbnodes[1].obj, 2)];
			if(isvert[0]!==isvert[1]){
				var lrnode = nbnodes[!isvert[0]?0:1];
				this.removeEdge(nodes[0], lrnode);
				this.addEdge(nodes[1], lrnode);
			}
			// 両方左右線の場合はnodes[0], nodes[1]を交換してnodes[0]に0本、nodes[1]に2本付加する
			else if(!isvert[0] && !isvert[1]){
				nodes.push(nodes.shift());
			}
		}
	},
	deleteNodeIfEmpty : function(cell){
		var nodes = this.getObjNodeList(cell);

		// 周囲のNodeが消えるかもしれないのでチェック＆remove
		if(nodes.length===1 && nodes[0].nodes.length===0 && !this.isnodevalid(cell)){
			this.deleteNode(nodes[0]);
		}
		// 交差あり盤面の処理
		else if(!!nodes[1] && nodes[0].nodes.length+nodes[1].nodes.length===2 && this.iscrossing(cell)){
			// 3本->2本になってnodes[0], nodes[1]に1本ずつEdgeが存在する場合はnodes[0]に統合
			if(nodes[1].nodes.length===1){
				var lrnode = nodes[1].nodes[0];
				this.removeEdge(nodes[1], lrnode);
				this.addEdge(nodes[0], lrnode);
			}
			// 両方左右線の場合はnodes[0], nodes[1]を交換してnodes[0]に2本、nodes[1]に0本にする
			else if(nodes[1].nodes.length===2){
				nodes.push(nodes.shift());
			}

			// 不要になったNodeを削除
			this.deleteNode(nodes[1]);
		}
	},

	//--------------------------------------------------------------------------------
	// linegraph.resetExtraData() 指定されたオブジェクトの拡張データをリセットする
	// linegraph.setExtraData()   指定された領域の拡張データを設定する
	//--------------------------------------------------------------------------------
	resetExtraData : function(nodeobj){
		if(this.moveline){ nodeobj.base = (nodeobj.isNum() ? nodeobj : this.board.emptycell);}
	},
	setExtraData : function(component){
		if(this.coloring && !component.color){
			component.color = this.puzzle.painter.getNewLineColor();
		}

		var edgeobjs = component.getedgeobjs();
		for(var i=0;i<edgeobjs.length;i++){
			this.setComponentRefs(edgeobjs[i], component);
		}

		if(this.makeClist || this.moveline){
			component.clist = new this.klass.CellList(component.getnodeobjs());
			if(this.moveline){ this.setMovedBase(component);}
		}
	},

	//--------------------------------------------------------------------------------
	// linegraph.repaintNodes() ブロックを再描画する
	//--------------------------------------------------------------------------------
	repaintNodes : function(components){
		var blist_all = new this.klass.BorderList();
		for(var i=0;i<components.length;i++){
			blist_all.extend(components[i].getedgeobjs());
		}
		this.puzzle.painter.repaintLines(blist_all);
	},

	//--------------------------------------------------------------------------------
	// linemgr.initMovedBase()   指定されたセルの移動情報を初期化する
	// linemgr.setMovedBase()    指定された領域の移動情報を設定する
	//--------------------------------------------------------------------------------
	setMovedBase : function(component){
		var emptycell = this.board.emptycell;
		component.departure = component.destination = emptycell;
		component.movevalid = false;

		var clist = component.clist;
		if(clist.length<1){ return;}
		for(var i=0;i<clist.length;i++){
			var cell=clist[i];
			cell.base = (cell.isNum() ? cell : emptycell);
		}

		var before=null, after=null, point=0;
		if(clist.length===1){
			before = after = clist[0];
			point = 2;
		}
		else{
			for(var i=0;i<clist.length;i++){
				var cell=clist[i];
				if(cell.lcnt===1){
					point++;
					if(cell.isNum()){ before=cell;}else{ after=cell;}
				}
			}
		}
		if(before!==null && after!==null && point===2){
			before.base = emptycell;
			component.departure   = after.base = before;
			component.destination = after;
			component.movevalid = true;
		}
	}
}
});
<commit_msg>LineGraph: Fix wrong linegraph attach for line-crossing genres<commit_after>// LineManager.js

//---------------------------------------------------------------------------
// ★LineGraphクラス 主に線や色分けの情報を管理する
//---------------------------------------------------------------------------
pzpr.classmgr.makeCommon({
"LineGraph:GraphBase":{
	initialize : function(){
		if(this.moveline){ this.relation['cell.qnum'] = 'move';}
	},

	enabled : false,
	relation : {'border.line':'link'},

	pointgroup : 'cell',
	linkgroup  : 'border',

	isLineCross : false,	// 線が交差するパズル

	makeClist : false,		// 線が存在するclistを生成する
	moveline  : false,		// 丸数字などを動かすパズル

	coloring : true,

	//--------------------------------------------------------------------------------
	// linegraph.setComponentRefs()    objにcomponentの設定を行う (性能対策)
	// linegraph.isedgeexistsbylinkobj() linkobjにedgeが存在するか判定する
	//
	// linegraph.getObjNodeList()      objにあるnodeを取得する
	// linegraph.resetObjNodeList()    objからnodeをクリアする
	//--------------------------------------------------------------------------------
	setComponentRefs : function(obj, component){ obj.path = component;},
	isedgeexistsbylinkobj : function(linkobj){ return linkobj.path!==null;},

	getObjNodeList   : function(nodeobj){ return nodeobj.pathnodes;},
	resetObjNodeList : function(nodeobj){
		nodeobj.pathnodes = [];
		if(this.moveline){ this.resetExtraData(nodeobj);}
	},

	//--------------------------------------------------------------------------------
	// linegraph.isnodevalid()           そのセルにNodeが存在すべきかどうか返す
	// linegraph.isedgevalidbylinkobj()  そのborderにEdgeが存在すべきかどうか返す
	// linegraph.iscrossing()            そのセルで交差するかどうか返す
	//--------------------------------------------------------------------------------
	isnodevalid          : function(cell)  { return cell.lcnt>0 || (this.moveline && cell.isNum());},
	isedgevalidbylinkobj : function(border){ return border.isLine();},
	iscrossing           : function(cell)  { return this.isLineCross;},

	//---------------------------------------------------------------------------
	// linegraph.rebuild()  既存の情報からデータを再設定する
	// linegraph.rebuild2() 継承先に固有のデータを設定する
	//---------------------------------------------------------------------------
	rebuild : function(){
		if(this.board.borderAsLine){ this.pointgroup = 'cross';}
		pzpr.common.GraphBase.prototype.rebuild.call(this);
	},
	rebuild2 : function(){
		if(!!this.incdecLineCount){
			this.resetLineCount();
		}
		pzpr.common.GraphBase.prototype.rebuild2.call(this);
	},

	//---------------------------------------------------------------------------
	// linegraph.resetLineCount()  初期化時に、lcnt情報を初期化する
	// linegraph.incdecLineCount() 線が引かれたり消された時に、lcnt変数を生成し直す
	//---------------------------------------------------------------------------
	resetLineCount : function(){
		var cells = this.board[this.pointgroup], borders = this.board[this.linkgroup];
		this.ltotal=[cells.length];
		for(var c=0;c<cells.length;c++){
			cells[c].lcnt = 0;
		}
		for(var id=0;id<borders.length;id++){
			if(this.isedgevalidbylinkobj(borders[id])){
				this.incdecLineCount(borders[id], true);
			}
		}
	},
	incdecLineCount : function(border, isset){
		if(border.group!==this.linkgroup){ return;}
		for(var i=0;i<2;i++){
			var cell = border.sideobj[i];
			if(!cell.isnull){
				this.ltotal[cell.lcnt]--;
				if(isset){ cell.lcnt++;}else{ cell.lcnt--;}
				this.ltotal[cell.lcnt] = (this.ltotal[cell.lcnt] || 0) + 1;
			}
		}
	},

	//---------------------------------------------------------------------------
	// linegraph.setEdgeByLinkObj() 線が引かれたり消された時に、lcnt変数や線の情報を生成しなおす
	//---------------------------------------------------------------------------
	setEdgeByLinkObj : function(linkobj){
		var isset = this.isedgevalidbylinkobj(linkobj);
		if(isset===this.isedgeexistsbylinkobj(linkobj)){ return;}

		if(!!this.incdecLineCount){
			this.incdecLineCount(linkobj, isset);
		}

		if(isset){ this.addEdgeByLinkObj(linkobj);}
		else     { this.removeEdgeByLinkObj(linkobj);}
	},

	//---------------------------------------------------------------------------
	// graph.addEdgeByLinkObj()    指定されたオブジェクトの場所にEdgeを生成する
	// graph.removeEdgeByLinkObj() 指定されたオブジェクトの場所からEdgeを除去する
	//---------------------------------------------------------------------------
	addEdgeByLinkObj : function(linkobj){ // 線(など)を引いた時の処理
		var enattach = (this.modifyNodes.length===0);
		var sidenodeobj = this.getSideObjByLinkObj(linkobj);

		// 周囲のNodeをグラフに追加するかどうか確認する
		this.createNodeIfEmpty(sidenodeobj[0]);
		this.createNodeIfEmpty(sidenodeobj[1]);

		// linkするNodeを取得する
		var sidenodes = this.getSideNodesByLinkObj(linkobj);

		// 周囲のNodeとlink
		this.addEdge(sidenodes[0], sidenodes[1]);

		// 周囲のComponentにくっついただけの場合は情報を更新して終了
		if(this.rebuildmode || !enattach){ return;}
		var attachnodes = null, node1 = sidenodes[0], node2 = sidenodes[1];
		var lcnt1 = node1.obj.lcnt, lcnt2 = node2.obj.lcnt;
		if     (lcnt1===1 && (lcnt2===2 || (!this.isLineCross && lcnt2>2)) && node1.component===null && node2.component!==null){ attachnodes = [sidenodes[0], sidenodes[1]];}
		else if(lcnt2===1 && (lcnt1===2 || (!this.isLineCross && lcnt1>2)) && node2.component===null && node1.component!==null){ attachnodes = [sidenodes[1], sidenodes[0]];}
		if(!!attachnodes){
			this.attachNode(attachnodes[0], attachnodes[1].component);
			this.modifyNodes = [];
		}
	},
	removeEdgeByLinkObj : function(linkobj){ // 線(など)を消した時の処理
		// unlinkするNodeを取得する
		var endetach = (this.modifyNodes.length===0);
		var sidenodes = this.getSideNodesByLinkObj(linkobj);

		// 周囲のNodeとunlink
		this.removeEdge(sidenodes[0], sidenodes[1]);

		// 周囲のNodeをグラフから取り除くかどうか確認する
		this.deleteNodeIfEmpty(sidenodes[0].obj);
		this.deleteNodeIfEmpty(sidenodes[1].obj);

		this.setComponentRefs(linkobj, null);

		// 周囲のComponent末端から切り離されただけの場合は情報を更新して終了
		if(!endetach){ return;}
		var detachnodes = null, node1 = sidenodes[0], node2 = sidenodes[1];
		var lcnt1 = node1.obj.lcnt, lcnt2 = node2.obj.lcnt;
		if     (lcnt1===0 && ((lcnt2===1 || (!this.isLineCross && lcnt2>1)) && node2.component!==null)){ detachnodes = [sidenodes[0], sidenodes[1]];}
		else if(lcnt2===0 && ((lcnt1===1 || (!this.isLineCross && lcnt1>1)) && node1.component!==null)){ detachnodes = [sidenodes[1], sidenodes[0]];}
		if(!!detachnodes){
			this.setComponentInfo(detachnodes[1].component);
			this.modifyNodes = [];
		}
	},

	//---------------------------------------------------------------------------
	// linegraph.setOtherInformation() 移動系パズルで数字などが入力された時に線の情報を生成しなおす
	//---------------------------------------------------------------------------
	modifyOtherInfo : function(cell, relation){
		var haspath = !!cell.path;
		if(haspath!==this.isnodevalid(cell)){
			if(haspath){ this.deleteNodeIfEmpty(cell);}
			else       { this.createNodeIfEmpty(cell);}
		}
		else{
			if(haspath){ this.setComponentInfo(cell.path);}
			else       { this.resetExtraData(cell);}
		}
	},

	//---------------------------------------------------------------------------
	// linegraph.createNodeIfEmpty()  指定されたオブジェクトの場所にNodeを生成する
	// linegraph.deleteNodeIfEmpty()  指定されたオブジェクトの場所からNodeを除去する
	//---------------------------------------------------------------------------
	createNodeIfEmpty : function(cell){
		var nodes = this.getObjNodeList(cell);

		// 周囲のNode生成が必要かもしれないのでチェック＆create
		if(nodes.length===0){
			this.createNode(cell);
		}
		// 交差あり盤面の処理
		else if(!nodes[1] && nodes[0].nodes.length===2 && this.iscrossing(cell)){
			// 2本->3本になる時はNodeを追加して分離します
			this.createNode(cell);

			// 上下/左右の線が1本ずつだった場合は左右の線をnodes[1]に付加し直します
			var nbnodes = nodes[0].nodes;
			var isvert = [cell.getvert(nbnodes[0].obj, 2), cell.getvert(nbnodes[1].obj, 2)];
			if(isvert[0]!==isvert[1]){
				var lrnode = nbnodes[!isvert[0]?0:1];
				this.removeEdge(nodes[0], lrnode);
				this.addEdge(nodes[1], lrnode);
			}
			// 両方左右線の場合はnodes[0], nodes[1]を交換してnodes[0]に0本、nodes[1]に2本付加する
			else if(!isvert[0] && !isvert[1]){
				nodes.push(nodes.shift());
			}
		}
	},
	deleteNodeIfEmpty : function(cell){
		var nodes = this.getObjNodeList(cell);

		// 周囲のNodeが消えるかもしれないのでチェック＆remove
		if(nodes.length===1 && nodes[0].nodes.length===0 && !this.isnodevalid(cell)){
			this.deleteNode(nodes[0]);
		}
		// 交差あり盤面の処理
		else if(!!nodes[1] && nodes[0].nodes.length+nodes[1].nodes.length===2 && this.iscrossing(cell)){
			// 3本->2本になってnodes[0], nodes[1]に1本ずつEdgeが存在する場合はnodes[0]に統合
			if(nodes[1].nodes.length===1){
				var lrnode = nodes[1].nodes[0];
				this.removeEdge(nodes[1], lrnode);
				this.addEdge(nodes[0], lrnode);
			}
			// 両方左右線の場合はnodes[0], nodes[1]を交換してnodes[0]に2本、nodes[1]に0本にする
			else if(nodes[1].nodes.length===2){
				nodes.push(nodes.shift());
			}

			// 不要になったNodeを削除
			this.deleteNode(nodes[1]);
		}
	},

	//--------------------------------------------------------------------------------
	// linegraph.resetExtraData() 指定されたオブジェクトの拡張データをリセットする
	// linegraph.setExtraData()   指定された領域の拡張データを設定する
	//--------------------------------------------------------------------------------
	resetExtraData : function(nodeobj){
		if(this.moveline){ nodeobj.base = (nodeobj.isNum() ? nodeobj : this.board.emptycell);}
	},
	setExtraData : function(component){
		if(this.coloring && !component.color){
			component.color = this.puzzle.painter.getNewLineColor();
		}

		var edgeobjs = component.getedgeobjs();
		for(var i=0;i<edgeobjs.length;i++){
			this.setComponentRefs(edgeobjs[i], component);
		}

		if(this.makeClist || this.moveline){
			component.clist = new this.klass.CellList(component.getnodeobjs());
			if(this.moveline){ this.setMovedBase(component);}
		}
	},

	//--------------------------------------------------------------------------------
	// linegraph.repaintNodes() ブロックを再描画する
	//--------------------------------------------------------------------------------
	repaintNodes : function(components){
		var blist_all = new this.klass.BorderList();
		for(var i=0;i<components.length;i++){
			blist_all.extend(components[i].getedgeobjs());
		}
		this.puzzle.painter.repaintLines(blist_all);
	},

	//--------------------------------------------------------------------------------
	// linemgr.initMovedBase()   指定されたセルの移動情報を初期化する
	// linemgr.setMovedBase()    指定された領域の移動情報を設定する
	//--------------------------------------------------------------------------------
	setMovedBase : function(component){
		var emptycell = this.board.emptycell;
		component.departure = component.destination = emptycell;
		component.movevalid = false;

		var clist = component.clist;
		if(clist.length<1){ return;}
		for(var i=0;i<clist.length;i++){
			var cell=clist[i];
			cell.base = (cell.isNum() ? cell : emptycell);
		}

		var before=null, after=null, point=0;
		if(clist.length===1){
			before = after = clist[0];
			point = 2;
		}
		else{
			for(var i=0;i<clist.length;i++){
				var cell=clist[i];
				if(cell.lcnt===1){
					point++;
					if(cell.isNum()){ before=cell;}else{ after=cell;}
				}
			}
		}
		if(before!==null && after!==null && point===2){
			before.base = emptycell;
			component.departure   = after.base = before;
			component.destination = after;
			component.movevalid = true;
		}
	}
}
});
<|endoftext|>


<commit_before>module.exports = function(grunt)
{

  grunt.registerTask('build', ['jshint', 'karma:dist', 'browserify', 'uglify']);
}
<commit_msg>change build task to dist<commit_after>module.exports = function(grunt)
{

  grunt.registerTask('dist', ['jshint', 'karma:dist', 'browserify', 'uglify']);
}
<|endoftext|>


<commit_before>"use strict";

class gsuiPianoroll extends gsuiBlocksManager {
	constructor() {
		const root = gsuiPianoroll.template.cloneNode( true );

		super( root );
		this.uiBlc.row = ( el, rowIncr ) => {
			this.uiBlc.key( el, this.data[ el.dataset.id ].key - rowIncr );
		};
		this.uiBlc.key = ( el, midi ) => {
			const row = this._getRowByMidi( midi );

			el.dataset.key = gsuiPianoroll.noteNames.en[ row.dataset.key ];
			row.firstChild.append( el );
		};

		this.uiKeys = new gsuiKeys();
		this.data = this._proxyCreate();
		this._ = Object.seal( {
			idMax: 1,
			rowsByMidi: {},
			currKeyDuration: 1,
		} );
		this.__sideContent.append( this.uiKeys.rootElement );
	}

	empty() {
		Object.keys( this.data ).forEach( k => delete this.data[ k ] );
	}
	resized() {
		this.__resized();
		this.__gridPanelResized();
	}
	attached() {
		this.__attached();
		this.scrollToMiddle();
	}
	octaves( from, nb ) {
		const _ = this._,
			rows = this.uiKeys.rootElement.getElementsByClassName( "gsui-row" );

		this.empty();
		Object.keys( _.rowsByMidi ).forEach( k => delete _.rowsByMidi[ k ] );
		this.uiKeys.octaves( from, nb );
		Object.values( rows ).forEach( el => {
			el.onmousedown = this._rowMousedown.bind( this, +el.dataset.midi );
			el.firstChild.style.fontSize = this.__pxPerBeat + "px";
			_.rowsByMidi[ el.dataset.midi ] = el;
		} );
		Element.prototype.prepend.apply( this.__rowsContainer, rows );
	}

	// Blocks manager callback
	// ........................................................................
	blcsManagerCallback( status, blcsMap, valA, valB ) {
		const obj = {},
			data = this.data;

		switch ( status ) {
			case "selecting":
				blcsMap.forEach( ( _, id ) => {
					const d = data[ id ],
						selected = !d.selected;

					obj[ id ] = { selected };
					d.selected = selected;
				} );
				break;
			case "moving":
				valA = Math.abs( valA ) > .000001 ? valA : 0;
				blcsMap.forEach( ( _, id ) => {
					const d = data[ id ],
						o = {};

					obj[ id ] = o;
					if ( valA ) {
						o.when =
						d.when += valA;
					}
					if ( valB ) {
						o.key =
						d.key -= valB;
					}
				} );
				break;
			case "cropping-b":
				blcsMap.forEach( ( _, id ) => {
					const d = data[ id ],
						duration = d.duration + valA;

					obj[ id ] = { duration };
					d.duration = duration;
				} );
				break;
			case "deleting":
				blcsMap.forEach( ( _, id ) => {
					obj[ id ] = null;
					delete data[ id ];
				} );
				this._unselectKeys( obj );
				break;
		}
		this.onchange( obj );
	}

	// Private small getters
	// ........................................................................
	_getData() { return this.data; }
	_getRowByMidi( midi ) { return this._.rowsByMidi[ midi ]; }

	// Mouse and keyboard events
	// ........................................................................
	_onkeydown( e ) { this.__keydown( e ); }
	_mousemove( e ) { this.__mousemove( e ); }
	_mouseup( e ) { this.__mouseup( e ); }
	_blcMousedown( id, e ) {
		e.stopPropagation();
		this.__mousedown( e );
	}
	_rowMousedown( key, e ) {
		this.__mousedown( e );
		if ( e.button === 0 && !e.shiftKey ) {
			const id = this._.idMax + 1,
				keyObj = {
					key,
					when: this.__getWhenByPageX( e.pageX ),
					duration: this._.currKeyDuration,
					selected: false
				};

			this.data[ id ] = keyObj;
			this.onchange( this._unselectKeys( { [ id ]: keyObj } ) );
		}
	}

	// Key's functions
	// ........................................................................
	_deleteKey( id ) {
		this.__blcs.get( id ).remove();
		this.__blcs.delete( id );
		this.__blcsSelected.delete( id );
	}
	_setKey( id, obj ) {
		const blc = document.createElement( "div" ),
			crop = document.createElement( "div" );

		blc.dataset.id = id;
		blc.className = "gsui-block";
		blc.onmousedown = this._blcMousedown.bind( this, id );
		obj.selected
			? this.__blcsSelected.set( id, blc )
			: this.__blcsSelected.delete( id );
		crop.className = "gsui-block-crop gsui-block-cropB";
		blc.append( crop );
		this.__blcs.set( id, blc );
		this.uiBlc.key( blc, obj.key );
		this.uiBlc.when( blc, obj.when );
		this.uiBlc.duration( blc, obj.duration );
		this.uiBlc.selected( blc, obj.selected );
	}
	_setKeyProp( id, prop, val ) {
		const uiFn = this.uiBlc[ prop ];

		if ( uiFn ) {
			const blc = this.__blcs.get( id );

			uiFn( blc, val );
			if ( prop === "duration" ) {
				this._.currKeyDuration = val;
			} else if ( prop === "selected" ) {
				val
					? this.__blcsSelected.set( id, blc )
					: this.__blcsSelected.delete( id );
			}
		}
	}
	_unselectKeys( obj ) {
		this.__blcsSelected.forEach( ( blc, id ) => {
			if ( !( id in obj ) ) {
				this.data[ id ].selected = false;
				obj[ id ] = { selected: false };
			}
		} );
		return obj;
	}

	// Data proxy
	// ........................................................................
	_proxyCreate() {
		return new Proxy( {}, {
			set: this._proxySetKey.bind( this ),
			deleteProperty: this._proxyDeleteKey.bind( this )
		} );
	}
	_proxyDeleteKey( tar, id ) {
		if ( id in tar ) {
			this._deleteKey( id );
			delete tar[ id ];
		} else {
			console.warn( `gsuiPianoroll: proxy useless deletion of [${ id }]` );
		}
		return true;
	}
	_proxySetKey( tar, id, obj ) {
		if ( id in tar || !obj ) {
			this._proxyDeleteKey( tar, id );
		}
		if ( obj ) {
			const prox = new Proxy( Object.seal( Object.assign( {
					key: 60,
					when: 0,
					duration: 1,
					selected: false
				}, obj ) ), {
					set: this._proxySetKeyProp.bind( this, id )
				} );

			tar[ id ] = prox;
			this._.idMax = Math.max( this._.idMax, id );
			this._setKey( id, prox );
		}
		return true;
	}
	_proxySetKeyProp( id, tar, prop, val ) {
		if ( prop === "offset" ) {
			console.warn( `gsuiPianoroll: proxy set useless 'offset' to key` );
		} else {
			tar[ prop ] = val;
			this._setKeyProp( id, prop, val );
		}
		return true;
	}
}

gsuiPianoroll.noteNames = {
	en: [ "c",  "c#",  "d",  "d#",  "e",  "f",  "f#",  "g",   "g#",   "a",  "a#",  "b" ],
	fr: [ "do", "do#", "ré", "ré#", "mi", "fa", "fa#", "sol", "sol#", "la", "la#", "si" ]
};

gsuiPianoroll.template = document.querySelector( "#gsuiPianoroll-template" );
gsuiPianoroll.template.remove();
gsuiPianoroll.template.removeAttribute( "id" );
<commit_msg>gsuiPianoroll: simplify code<commit_after>"use strict";

class gsuiPianoroll extends gsuiBlocksManager {
	constructor() {
		const root = gsuiPianoroll.template.cloneNode( true );

		super( root );
		this.uiBlc.row = ( el, rowIncr ) => {
			this.uiBlc.key( el, this.data[ el.dataset.id ].key - rowIncr );
		};
		this.uiBlc.key = ( el, midi ) => {
			const row = this._getRowByMidi( midi );

			el.dataset.key = gsuiPianoroll.noteNames.en[ row.dataset.key ];
			row.firstChild.append( el );
		};

		this.data = this._proxyCreate();
		this.uiKeys = new gsuiKeys();
		this._idMax = 1;
		this._rowsByMidi = {};
		this._currKeyDuration = 1;
		this.__sideContent.append( this.uiKeys.rootElement );
	}

	empty() {
		Object.keys( this.data ).forEach( k => delete this.data[ k ] );
	}
	resized() {
		this.__resized();
		this.__gridPanelResized();
	}
	attached() {
		this.__attached();
		this.scrollToMiddle();
	}
	octaves( from, nb ) {
		const rows = this.uiKeys.rootElement.getElementsByClassName( "gsui-row" );

		this.empty();
		Object.keys( this._rowsByMidi ).forEach( k => delete this._rowsByMidi[ k ] );
		this.uiKeys.octaves( from, nb );
		Object.values( rows ).forEach( el => {
			el.onmousedown = this._rowMousedown.bind( this, +el.dataset.midi );
			el.firstChild.style.fontSize = this.__pxPerBeat + "px";
			this._rowsByMidi[ el.dataset.midi ] = el;
		} );
		Element.prototype.prepend.apply( this.__rowsContainer, rows );
	}

	// Blocks manager callback
	// ........................................................................
	blcsManagerCallback( status, blcsMap, valA, valB ) {
		const obj = {},
			data = this.data;

		switch ( status ) {
			case "selecting":
				blcsMap.forEach( ( _, id ) => {
					const d = data[ id ],
						selected = !d.selected;

					obj[ id ] = { selected };
					d.selected = selected;
				} );
				break;
			case "moving":
				valA = Math.abs( valA ) > .000001 ? valA : 0;
				blcsMap.forEach( ( _, id ) => {
					const d = data[ id ],
						o = {};

					obj[ id ] = o;
					if ( valA ) {
						o.when =
						d.when += valA;
					}
					if ( valB ) {
						o.key =
						d.key -= valB;
					}
				} );
				break;
			case "cropping-b":
				blcsMap.forEach( ( _, id ) => {
					const d = data[ id ],
						duration = d.duration + valA;

					obj[ id ] = { duration };
					d.duration = duration;
				} );
				break;
			case "deleting":
				blcsMap.forEach( ( _, id ) => {
					obj[ id ] = null;
					delete data[ id ];
				} );
				this._unselectKeys( obj );
				break;
		}
		this.onchange( obj );
	}

	// Private small getters
	// ........................................................................
	_getData() { return this.data; }
	_getRowByMidi( midi ) { return this._rowsByMidi[ midi ]; }

	// Mouse and keyboard events
	// ........................................................................
	_onkeydown( e ) { this.__keydown( e ); }
	_mousemove( e ) { this.__mousemove( e ); }
	_mouseup( e ) { this.__mouseup( e ); }
	_blcMousedown( id, e ) {
		e.stopPropagation();
		this.__mousedown( e );
	}
	_rowMousedown( key, e ) {
		this.__mousedown( e );
		if ( e.button === 0 && !e.shiftKey ) {
			const id = this._idMax + 1,
				keyObj = {
					key,
					when: this.__getWhenByPageX( e.pageX ),
					duration: this._currKeyDuration,
					selected: false
				};

			this.data[ id ] = keyObj;
			this.onchange( this._unselectKeys( { [ id ]: keyObj } ) );
		}
	}

	// Key's functions
	// ........................................................................
	_deleteKey( id ) {
		this.__blcs.get( id ).remove();
		this.__blcs.delete( id );
		this.__blcsSelected.delete( id );
	}
	_setKey( id, obj ) {
		const blc = document.createElement( "div" ),
			crop = document.createElement( "div" );

		blc.dataset.id = id;
		blc.className = "gsui-block";
		blc.onmousedown = this._blcMousedown.bind( this, id );
		obj.selected
			? this.__blcsSelected.set( id, blc )
			: this.__blcsSelected.delete( id );
		crop.className = "gsui-block-crop gsui-block-cropB";
		blc.append( crop );
		this.__blcs.set( id, blc );
		this.uiBlc.key( blc, obj.key );
		this.uiBlc.when( blc, obj.when );
		this.uiBlc.duration( blc, obj.duration );
		this.uiBlc.selected( blc, obj.selected );
	}
	_setKeyProp( id, prop, val ) {
		const uiFn = this.uiBlc[ prop ];

		if ( uiFn ) {
			const blc = this.__blcs.get( id );

			uiFn( blc, val );
			if ( prop === "duration" ) {
				this._currKeyDuration = val;
			} else if ( prop === "selected" ) {
				val
					? this.__blcsSelected.set( id, blc )
					: this.__blcsSelected.delete( id );
			}
		}
	}
	_unselectKeys( obj ) {
		this.__blcsSelected.forEach( ( blc, id ) => {
			if ( !( id in obj ) ) {
				this.data[ id ].selected = false;
				obj[ id ] = { selected: false };
			}
		} );
		return obj;
	}

	// Data proxy
	// ........................................................................
	_proxyCreate() {
		return new Proxy( {}, {
			set: this._proxySetKey.bind( this ),
			deleteProperty: this._proxyDeleteKey.bind( this )
		} );
	}
	_proxyDeleteKey( tar, id ) {
		if ( id in tar ) {
			this._deleteKey( id );
			delete tar[ id ];
		} else {
			console.warn( `gsuiPianoroll: proxy useless deletion of [${ id }]` );
		}
		return true;
	}
	_proxySetKey( tar, id, obj ) {
		if ( id in tar || !obj ) {
			this._proxyDeleteKey( tar, id );
			if ( obj ) {
				console.warn( `gsuiPianoroll: reassignation of [${ id }]` );
			}
		}
		if ( obj ) {
			const prox = new Proxy( Object.seal( Object.assign( {
					key: 60,
					when: 0,
					duration: 1,
					selected: false
				}, obj ) ), {
					set: this._proxySetKeyProp.bind( this, id )
				} );

			tar[ id ] = prox;
			this._idMax = Math.max( this._idMax, id );
			this._setKey( id, prox );
		}
		return true;
	}
	_proxySetKeyProp( id, tar, prop, val ) {
		if ( prop === "offset" ) {
			console.warn( `gsuiPianoroll: proxy set useless 'offset' to key` );
		} else {
			tar[ prop ] = val;
			this._setKeyProp( id, prop, val );
		}
		return true;
	}
}

gsuiPianoroll.noteNames = {
	en: [ "c",  "c#",  "d",  "d#",  "e",  "f",  "f#",  "g",   "g#",   "a",  "a#",  "b" ],
	fr: [ "do", "do#", "ré", "ré#", "mi", "fa", "fa#", "sol", "sol#", "la", "la#", "si" ]
};

gsuiPianoroll.template = document.querySelector( "#gsuiPianoroll-template" );
gsuiPianoroll.template.remove();
gsuiPianoroll.template.removeAttribute( "id" );
<|endoftext|>


<commit_before>/*
 * grunt-cache-breaker
 * https://github.com/shakyShane/grunt-cache-breaker
 *
 * Copyright (c) 2013 Shane Osbourne
 * Licensed under the MIT license.
 */

'use strict';

/**

Usage :

  cachebreaker : {
    css : {
      asset_url : '/asset/url.min.js',
      files : {
        src : ['path/1.php', 'path/1.html']
      }
    },
  }
  cachebreaker : {
    css : {
      asset_url : '/asset/url.min.js',
      files : {
        src : 'path/to/single/file.html'
      }
    },
  }
  cachebreaker : {
    css : {
      asset_url : '/asset/url.min.js',
      files : {
        src : 'path/to/many/files/*.html'
      }
    },
  }
  cachebreaker : {
    css : {
      asset_url : '/asset/url.min.js',
      file : 'path/to/single/file.html'
    },
  }

 */

/**
 * Strip unwanted path info from the start of a string
 * @param {String} filename
 * @param {String} prefix
 * @returns {String}
 */
var removePrefixes = function( filename, prefix ) {
  return filename.replace( prefix, "" );
};

/**
 * Append a timestamp to a string
 * @param {String} filename
 * @returns {String}
 */
var makeNewUrl = function( filename ) {
  return (filename + '?rel=' + new Date().getTime());
};

/**
 * Make a regular expression that matched a string up until the closing ["]
 * @param {String} string
 * @returns {RegExp}
 */
var makeTagRegex = function( string ) {
  return new RegExp( string + '(.+)?(?=")' );
};

var msgs = {
  success : function(path){
    return 'Asset URL in "' + path + '" has been updated to break the cache.';
  },
  errors : {
    string       : function(path) {
      return 'Found the file: "' + path + '", but I couldn\'t find the Asset Url to replace in it. Please check your config.' ;
    },
    file_write   : 'Could not write the file!',
    file_find    : 'Could not find the source file for this task! ( Tip : the path to this file should be RELATIVE to Grunt.js )',
    config : function(prop) {
     return  'Sorry, your config is invalid. Please ensure you provide the `'+prop+'` property';
    },
    generic : 'Sorry, there was an unknown problem. Check all your config properties.'
  }
};

module.exports = function(grunt) {

  /**
   * Console.log an error - nicer than exceptions in command line
   * @param {string} msg
   * @returns {*}
   */
  var e = function( msg ) {
    return grunt.log.error( 'Error : '.red + msg.yellow );
  };

  /**
   * Console.log an error
   * @param { string } msg
   * @returns {*}
   */
  var s = function( msg ) {
    return grunt.log.success( 'Success : '.green + msg.cyan );
  };

  /**
   * @param {string} src
   * @param {string} dest
   * @param {object} options
   * @returns {*}
   */
  var processFile = function( src, dest, options ) {

    if( grunt.file.exists( src ) ) {
      return ( breakCache( options, { src : src, dest : dest } ) );
    } else {
      return e( msgs.errors.file_find );
    }

  };

  /**
   * @param {object} options
   * @param {grunt.file} f
   * @returns {string}
   */
  var breakCache = function( options, f ) {

    var data = grunt.file.read( f.src );
    var cleanUrl   = removePrefixes( options.asset_url, options.remove ),
          regex    = makeTagRegex( cleanUrl ),
          url      = makeNewUrl( cleanUrl ),
          match    = data.match( regex ),
          newData;

      if ( match !== null ) {
        newData  = data.replace( regex, url );
        if ( grunt.file.write( f.dest, newData ) ) {
          return s( msgs.success( f.src ) );
        } else {
          return e( msgs.errors.file_write );
        }
      } else {
        return e( msgs.errors.string( f.src ) );
      }

  };

  grunt.registerMultiTask('cachebreaker', 'Add a timestamp to a url string', function() {

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
        remove : 'app' // default
    });

    if( !this.data.asset_url ) {
      return e( msgs.errors.config('asset_url') );
    }

    if( !this.data.file && this.files.length < 1) {
      return e( msgs.errors.config('file or files') );
    }

    // Make asset_url always availble to options.
    options.asset_url = this.data.asset_url;

    // An array passed to [file]
    if ( Array.isArray(this.data.file) ) {
      return this.data.file.forEach( function( path ) {
        return processFile( path, path, options );
      });
    }

    // A String passed to [file]
    if ( typeof this.data.file === 'string' ) {
      return processFile( this.data.file, this.data.file, options);
    }

    // Files key used
    if (this.files) {
      return this.files.forEach(function(f) {
        if (f.src instanceof Array) {
          if (f.dest && f.dest === "src") {
            return f.src.forEach(function (fv) {
              return processFile(fv, fv, options);
            });
          } else {
            if (!f.src[0]) {
              return e( msgs.errors.file_find );
            }
            return processFile( f.src[0], f.dest,  options );
          }
        }
      });
    }

    // Error if this point is reached.
    return e( msgs.errors.generic );

  });
};

module.exports.removePrefixes  = removePrefixes;
module.exports.makeNewUrl      = makeNewUrl;
module.exports.makeTagRegex    = makeTagRegex;<commit_msg>Ensure good error message is output if the file cannot be found<commit_after>/*
 * grunt-cache-breaker
 * https://github.com/shakyShane/grunt-cache-breaker
 *
 * Copyright (c) 2013 Shane Osbourne
 * Licensed under the MIT license.
 */

'use strict';

/**

Usage :

  cachebreaker : {
    css : {
      asset_url : '/asset/url.min.js',
      files : {
        src : ['path/1.php', 'path/1.html']
      }
    },
  }
  cachebreaker : {
    css : {
      asset_url : '/asset/url.min.js',
      files : {
        src : 'path/to/single/file.html'
      }
    },
  }
  cachebreaker : {
    css : {
      asset_url : '/asset/url.min.js',
      files : {
        src : 'path/to/many/files/*.html'
      }
    },
  }
  cachebreaker : {
    css : {
      asset_url : '/asset/url.min.js',
      file : 'path/to/single/file.html'
    },
  }

 */

/**
 * Strip unwanted path info from the start of a string
 * @param {String} filename
 * @param {String} prefix
 * @returns {String}
 */
var removePrefixes = function( filename, prefix ) {
  return filename.replace( prefix, "" );
};

/**
 * Append a timestamp to a string
 * @param {String} filename
 * @returns {String}
 */
var makeNewUrl = function( filename ) {
  return (filename + '?rel=' + new Date().getTime());
};

/**
 * Make a regular expression that matched a string up until the closing ["]
 * @param {String} string
 * @returns {RegExp}
 */
var makeTagRegex = function( string ) {
  return new RegExp( string + '(.+)?(?=")' );
};

var msgs = {
  success : function(path){
    return 'Asset URL in "' + path + '" has been updated to break the cache.';
  },
  errors : {
    string       : function(path) {
      return 'Found the file: "' + path + '", but I couldn\'t find the Asset Url to replace in it. Please check your config.' ;
    },
    file_write   : 'Could not write the file!',
    file_find    : 'Could not find the source file for this task! ( Tip : the path to this file should be RELATIVE to Grunt.js )',
    config : function(prop) {
     return  'Sorry, your config is invalid. Please ensure you provide the `'+prop+'` property';
    },
    generic : 'Sorry, there was an unknown problem. Check all your config properties.'
  }
};

module.exports = function(grunt) {

  /**
   * Console.log an error - nicer than exceptions in command line
   * @param {string} msg
   * @returns {*}
   */
  var e = function( msg ) {
    return grunt.log.error( 'Error : '.red + msg.yellow );
  };

  /**
   * Console.log an error
   * @param { string } msg
   * @returns {*}
   */
  var s = function( msg ) {
    return grunt.log.success( 'Success : '.green + msg.cyan );
  };

  /**
   * @param {string} src
   * @param {string} dest
   * @param {object} options
   * @returns {*}
   */
  var processFile = function( src, dest, options ) {

    if( grunt.file.exists( src ) ) {
      return ( breakCache( options, { src : src, dest : dest } ) );
    } else {
      return e( msgs.errors.file_find );
    }

  };

  /**
   * @param {object} options
   * @param {grunt.file} f
   * @returns {string}
   */
  var breakCache = function( options, f ) {

    var data = grunt.file.read( f.src );
    var cleanUrl   = removePrefixes( options.asset_url, options.remove ),
          regex    = makeTagRegex( cleanUrl ),
          url      = makeNewUrl( cleanUrl ),
          match    = data.match( regex ),
          newData;

      if ( match !== null ) {
        newData  = data.replace( regex, url );
        if ( grunt.file.write( f.dest, newData ) ) {
          return s( msgs.success( f.src ) );
        } else {
          return e( msgs.errors.file_write );
        }
      } else {
        return e( msgs.errors.string( f.src ) );
      }

  };

  grunt.registerMultiTask('cachebreaker', 'Add a timestamp to a url string', function() {

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
        remove : 'app' // default
    });

    if( !this.data.asset_url ) {
      return e( msgs.errors.config('asset_url') );
    }

    if( !this.data.file && this.files.length < 1) {
      return e( msgs.errors.config('file or files') );
    }

    // Make asset_url always availble to options.
    options.asset_url = this.data.asset_url;

    // An array passed to [file]
    if ( Array.isArray(this.data.file) ) {
      return this.data.file.forEach( function( path ) {
        return processFile( path, path, options );
      });
    }

    // A String passed to [file]
    if ( typeof this.data.file === 'string' ) {
      return processFile( this.data.file, this.data.file, options);
    }

    // Files key used
    if (this.files) {
      return this.files.forEach(function(f) {
        if (f.src instanceof Array && f.src.length) {
          if (f.dest && f.dest === "src") {
            return f.src.forEach(function (fv) {
              return processFile(fv, fv, options);
            });
          } else {
            if (!f.src[0]) {
              return e( msgs.errors.file_find );
            }
            return processFile( f.src[0], f.dest,  options );
          }
        } else {
          return e( msgs.errors.file_find );
        }
      });
    }

    // Error if this point is reached.
    return e( msgs.errors.generic );

  });
};

module.exports.removePrefixes  = removePrefixes;
module.exports.makeNewUrl      = makeNewUrl;
module.exports.makeTagRegex    = makeTagRegex;<|endoftext|>


<commit_before>const commonUrl = {
  donateLink: 'https://secure.lglforms.com/form_engine/s/BRtP7QUKyHOyEYsZROsRew',
  leadershipCircleLink: 'https://secure.lglforms.com/form_engine/s/L428AQ2rrsFJQyy5Fbglvg'
};

export default commonUrl;
<commit_msg>Remove unnecessary link variable<commit_after>const commonUrl = {
  donateLink: 'https://secure.lglforms.com/form_engine/s/BRtP7QUKyHOyEYsZROsRew',
};

export default commonUrl;
<|endoftext|>


<commit_before>var assert = require('assert'),
    vows = require('vows'),
    ansiparse = require('../');

require('colors');

var dataSets = {
  simple: {
    input: 'hello world'.green,
    output: [{ text: 'hello world', foreground: 'green' }]
  },
  many: {
    input: 'hello '.green + 'world'.red,
    output: [{ text: 'hello ', foreground: 'green' }, { text: 'world', foreground: 'red' }]
  },
  'many same colors': {
    input: 'hello '.green + 'world'.green,
    output: [{ text: 'hello ', foreground: 'green' }, { text: 'world', foreground: 'green' }]
  },
  bold: {
    input: 'hello world'.bold,
    output: [{ text: 'hello world', bold: true }]
  },
  italics: {
    input: 'hello world'.italic,
    output: [{ text: 'hello world', italic: true }]
  },
  background: {
    input: '\033\[42mhello world\033\[49m',
    output: [{ text: 'hello world', background: 'green' }]
  },
  boldAndColor: {
    input: 'hello world'.red.bold,
    output: [{ text: 'hello world', bold: true, foreground: 'red' }]
  },
  noColor: {
    input: 'hello world',
    output: [{ text: 'hello world' }]
  },
  'unfinished color': {
    input: '\033\[32mhello world',
    output: [{ text: 'hello world', foreground: 'green' }]
  },
  nested: {
    input: ('hello ' + 'world'.red).green,
    output: [
      { text: 'hello ', foreground: 'green' },
      { text: 'world', foreground: 'red' }
    ]
  }
};

function getTopics() {
  var topics = {};
  Object.keys(dataSets).forEach(function (set) {
    topics['when using ' + set + ' data set'] = {
      topic: ansiparse(dataSets[set].input),
      'should return correct output': function (result) {
        assert.isArray(result);
        assert.deepEqual(result, dataSets[set].output);
      }
    };
  });
  return topics;
}

vows.describe('ansiparse').addBatch({
  'When using ansiparse': getTopics()
}).export(module);

<commit_msg>[test] Add `partly bolded` test<commit_after>var assert = require('assert'),
    vows = require('vows'),
    ansiparse = require('../');

require('colors');

var dataSets = {
  simple: {
    input: 'hello world'.green,
    output: [{ text: 'hello world', foreground: 'green' }]
  },
  many: {
    input: 'hello '.green + 'world'.red,
    output: [{ text: 'hello ', foreground: 'green' }, { text: 'world', foreground: 'red' }]
  },
  'many same colors': {
    input: 'hello '.green + 'world'.green,
    output: [{ text: 'hello ', foreground: 'green' }, { text: 'world', foreground: 'green' }]
  },
  bold: {
    input: 'hello world'.bold,
    output: [{ text: 'hello world', bold: true }]
  },
  italics: {
    input: 'hello world'.italic,
    output: [{ text: 'hello world', italic: true }]
  },
  background: {
    input: '\033\[42mhello world\033\[49m',
    output: [{ text: 'hello world', background: 'green' }]
  },
  boldAndColor: {
    input: 'hello world'.red.bold,
    output: [{ text: 'hello world', bold: true, foreground: 'red' }]
  },
  'partly bolded': {
    input: 'hello '.bold + 'world'.red,
    output: [
      { text: 'hello ', bold: true },
      { text: 'world', foreground: 'red', bold: false }
    ]
  },
  noColor: {
    input: 'hello world',
    output: [{ text: 'hello world' }]
  },
  'unfinished color': {
    input: '\033\[32mhello world',
    output: [{ text: 'hello world', foreground: 'green' }]
  },
  nested: {
    input: ('hello ' + 'world'.red).green,
    output: [
      { text: 'hello ', foreground: 'green' },
      { text: 'world', foreground: 'red' }
    ]
  }
};

function getTopics() {
  var topics = {};
  Object.keys(dataSets).forEach(function (set) {
    topics['when using ' + set + ' data set'] = {
      topic: ansiparse(dataSets[set].input),
      'should return correct output': function (result) {
        assert.isArray(result);
        assert.deepEqual(result, dataSets[set].output);
      }
    };
  });
  return topics;
}

vows.describe('ansiparse').addBatch({
  'When using ansiparse': getTopics()
}).export(module);

<|endoftext|>


<commit_before>var inherits = require('util').inherits,
    DuplexStream = require('stream').Duplex
                   || require('readable-stream').Duplex,
    ReadableStream = require('stream').Readable
                     || require('readable-stream').Readable,
    WritableStream = require('stream').Writable
                     || require('readable-stream').Writable;

var STDERR = require('ssh2-streams').constants.CHANNEL_EXTENDED_DATATYPE.STDERR;

var PACKET_SIZE = 32 * 1024,
    MAX_WINDOW = 1 * 1024 * 1024,
    CUSTOM_EVENTS = [
      'CHANNEL_EOF',
      'CHANNEL_CLOSE',
      'CHANNEL_DATA',
      'CHANNEL_EXTENDED_DATA',
      'CHANNEL_WINDOW_ADJUST',
      'CHANNEL_SUCCESS',
      'CHANNEL_FAILURE',
      'CHANNEL_REQUEST'
    ],
    CUSTOM_EVENTS_LEN = CUSTOM_EVENTS.length;

function Channel(info, client, opts) {
  var streamOpts = {
    highWaterMark: MAX_WINDOW,
    allowHalfOpen: (!opts || (opts && opts.allowHalfOpen))
  };

  this.allowHalfOpen = streamOpts.allowHalfOpen;

  DuplexStream.call(this, streamOpts);

  var self = this,
      server = opts && opts.server;

  this.type = info.type;
  this.subtype = undefined;
  /*
    incoming and outgoing contain these properties:
    {
      id: undefined,
      window: undefined,
      packetSize: undefined,
      state: 'closed'
    }
  */
  this.incoming = info.incoming;
  this.outgoing = info.outgoing;

  this._client = client;
  this._callbacks = [];
  this._hasX11 = false;

  function ondrain() {
    if (self._waitClientDrain) {
      self._waitClientDrain = false;
      if (!self._waitWindow) {
        if (self._chunk)
          self._write(self._chunk, null, self._chunkcb);
        else if (self._chunkcb)
          self._chunkcb();
        else if (self._chunkErr)
          self.stderr._write(self._chunkErr, null, self._chunkcbErr);
        else if (self._chunkcbErr)
          self._chunkcbErr();
      }
    }
  }
  client._sock.on('drain', ondrain);

  client._sshstream.once('CHANNEL_EOF:' + this.incoming.id, function() {
    if (self.incoming.state === 'closed')
      return;
    self.incoming.state = 'eof';

    if (self.readable)
      self.push(null);
    if (server && self.stderr.writable)
      self.stderr.end();
    else if (!server && self.stderr.readable)
      self.stderr.push(null);
  }).once('CHANNEL_CLOSE:' + this.incoming.id, function() {
    self.incoming.state = 'closed';

    if (self.readable)
      self.push(null);
    if (server && self.stderr.writable)
      self.stderr.end();
    else if (!server && self.stderr.readable)
      self.stderr.push(null);

    if (self.outgoing.state === 'open' || self.outgoing.state === 'eof')
      self.close();
    if (self.outgoing.state === 'closing')
      self.outgoing.state = 'closed';

    client._channels.splice(client._channels.indexOf(self.incoming.id), 1);

    var state = self._writableState;
    client._sock.removeListener('drain', ondrain);
    if (!state.ending && !state.finished)
      self.end();

    self.emit('close');
    if (!server)
      self.stderr.emit('close');

    for (var i = 0; i < CUSTOM_EVENTS_LEN; ++i) {
      // Since EventEmitters do not actually *delete* event names in the
      // emitter's event array, we must do this manually so as not to leak
      // our custom, channel-specific event names.
      delete client._sshstream._events[CUSTOM_EVENTS[i] + ':' + self.incoming.id];
    }

    // no need to listen for unexpected channel end anymore ...
    self._client.removeListener('close', onClientClose);
  }).on('CHANNEL_DATA:' + this.incoming.id, function(data) {
    self.incoming.window -= data.length;

    if (!self.push(data)) {
      self._waitChanDrain = true;
      return;
    }

    if (self.incoming.window === 0)
      windowAdjust(self);
  }).on('CHANNEL_WINDOW_ADJUST:' + this.incoming.id, function(amt) {
    // the server is allowing us to send `amt` more bytes of data
    self.outgoing.window += amt;

    if (self._waitWindow) {
      self._waitWindow = false;
      if (!self._waitClientDrain) {
        if (self._chunk)
          self._write(self._chunk, null, self._chunkcb);
        else if (self._chunkcb)
          self._chunkcb();
        else if (self._chunkErr)
          self.stderr._write(self._chunkErr, null, self._chunkcbErr);
        else if (self._chunkcbErr)
          self._chunkcbErr();
      }
    }
  }).on('CHANNEL_SUCCESS:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(false);
  }).on('CHANNEL_FAILURE:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(true);
  }).on('CHANNEL_REQUEST:' + this.incoming.id, function(info) {
    if (info.request === 'exit-status')
      self.emit('exit', info.code);
    else if (info.request === 'exit-signal') {
      self.emit('exit',
                null,
                'SIG' + info.signal,
                info.coredump,
                info.description,
                info.lang);
    } else
      return;
    self.close();
  });

  this.stdin = this.stdout = this;

  if (server)
    this.stderr = new ServerStderr(this);
  else {
    this.stderr = new ReadableStream(streamOpts);
    this.stderr._read = function(n) {
      if (self._waitChanDrain) {
        self._waitChanDrain = false;
        if (self.incoming.window === 0)
          windowAdjust(self);
      }
    };

    client._sshstream.on('CHANNEL_EXTENDED_DATA:' + this.incoming.id,
      function(type, data) {
        self.incoming.window -= data.length;

        if (!self.stderr.push(data)) {
          self._waitChanDrain = true;
          return;
        }

        if (self.incoming.window === 0)
          windowAdjust(self);
      }
    );
  }

  // outgoing data
  this._waitClientDrain = false; // Client stream-level backpressure
  this._waitWindow = false; // SSH-level backpressure

  // incoming data
  this._waitChanDrain = false; // Channel Readable side backpressure

  this._chunk = undefined;
  this._chunkcb = undefined;
  this._chunkErr = undefined;
  this._chunkcbErr = undefined;

  this.on('finish', function() {
    self.eof();
    if (server || (!server && !self.allowHalfOpen))
      self.close();
  });

  function onClientClose() {
    if (self.readable) {
      self.push(null);
      self.once('end', function() {
        process.nextTick(function() {
          self.emit('close');
        });
      });
    }
    if (!server && self.stderr.readable) {
      self.stderr.push(null);
      self.stderr.once('end', function() {
        process.nextTick(function() {
          self.stderr.emit('close');
        });
      });
    }
    if (self.writable)
      self.end();
    if (server && self.stderr.writable)
      self.stderr.end();
  }
  this._client.once('close', onClientClose);
}
inherits(Channel, DuplexStream);

Channel.prototype.eof = function() {
  var ret = true;

  if (this.outgoing.state === 'open') {
    this.outgoing.state = 'eof';
    ret = this._client._sshstream.channelEOF(this.outgoing.id);
  }

  return ret;
};

Channel.prototype.close = function() {
  var ret = true;

  if (this.outgoing.state === 'open' || this.outgoing.state === 'eof') {
    this.outgoing.state = 'closing';
    ret = this._client._sshstream.channelClose(this.outgoing.id);
  }

  return ret;
};

Channel.prototype._read = function(n) {
  if (this._waitChanDrain) {
    this._waitChanDrain = false;
    if (this.incoming.window === 0)
      windowAdjust(this);
  }
};

Channel.prototype._write = function(data, encoding, cb) {
  var sshstream = this._client._sshstream,
      len = data.length,
      p = 0,
      ret,
      buf,
      sliceLen;

  while (len - p > 0 && this.outgoing.window > 0) {
    sliceLen = len - p;
    if (sliceLen > this.outgoing.window)
      sliceLen = this.outgoing.window;
    if (sliceLen > this.outgoing.packetSize)
      sliceLen = this.outgoing.packetSize;

    ret = sshstream.channelData(this.outgoing.id, data.slice(p, p + sliceLen));

    p += sliceLen;
    this.outgoing.window -= sliceLen;

    if (!ret) {
      this._waitClientDrain = true;
      this._chunk = undefined;
      this._chunkcb = cb;
      break;
    }
  }

  if (len - p > 0) {
    if (this.outgoing.window === 0)
      this._waitWindow = true;
    if (p > 0) {
      // partial
      buf = new Buffer(len - p);
      data.copy(buf, 0, p);
      this._chunk = buf;
    } else
      this._chunk = data;
    this._chunkcb = cb;
    return;
  }

  if (!this._waitClientDrain)
    cb();
};

Channel.prototype.destroy = function() {
  this.end();
};

// session type-specific methods
Channel.prototype.setWindow = function(rows, cols, height, width) {
  if (this.type === 'session' && this.subtype === 'shell')
    return this._client._sshstream.windowChange(this.outgoing.id,
                                                rows,
                                                cols,
                                                height,
                                                width);
};
Channel.prototype.signal = function(signalName) {
  if (this.type === 'session'
      && (this.subtype === 'shell'
          || this.subtype === 'exec'))
    return this._client._sshstream.signal(this.outgoing.id, signalName);

  return true;
};
Channel.prototype.exit = function(name, coreDumped, msg) {
  if (!this.server)
    throw new Error('Server-only method called in client mode');

  if (this.type === 'session' && this.subtype === 'exec' && this.writable) {
    if (typeof name === 'number')
      return this._client._sshstream.exitStatus(this.outgoing.id, name);
    else {
      return this._client._sshstream.exitSignal(this.outgoing.id,
                                                name,
                                                coreDumped,
                                                msg);
    }
  }

  return true;
};

Channel.MAX_WINDOW = MAX_WINDOW;
Channel.PACKET_SIZE = PACKET_SIZE;

function windowAdjust(self, amt) {
  amt = amt || MAX_WINDOW;
  return self._client._sshstream.channelWindowAdjust(self.outgoing.id, amt);
}

function ServerStderr(channel) {
  WritableStream.call(this, { highWaterMark: MAX_WINDOW });
  this._channel = channel;
}
inherits(ServerStderr, WritableStream);

ServerStderr.prototype._write = function(data, encoding, cb) {
  var channel = this._channel,
      sshstream = channel._client._sshstream,
      len = data.length,
      p = 0,
      ret,
      buf,
      sliceLen;

  while (len - p > 0 && channel.outgoing.window > 0) {
    sliceLen = len - p;
    if (sliceLen > channel.outgoing.window)
      sliceLen = channel.outgoing.window;
    if (sliceLen > channel.outgoing.packetSize)
      sliceLen = channel.outgoing.packetSize;

    ret = sshstream.channelExtData(channel.outgoing.id,
                                   data.slice(p, p + sliceLen),
                                   STDERR);

    p += sliceLen;
    channel.outgoing.window -= sliceLen;

    if (!ret) {
      channel._waitClientDrain = true;
      channel._chunkErr = undefined;
      channel._chunkcbErr = cb;
      break;
    }
  }

  if (len - p > 0) {
    if (channel.outgoing.window === 0)
      channel._waitWindow = true;
    if (p > 0) {
      // partial
      buf = new Buffer(len - p);
      data.copy(buf, 0, p);
      channel._chunkErr = buf;
    } else
      channel._chunkErr = data;
    channel._chunkcbErr = cb;
    return;
  }

  if (!channel._waitClientDrain)
    cb();
};

module.exports = Channel;
<commit_msg>Channel: stricter checks<commit_after>var inherits = require('util').inherits,
    DuplexStream = require('stream').Duplex
                   || require('readable-stream').Duplex,
    ReadableStream = require('stream').Readable
                     || require('readable-stream').Readable,
    WritableStream = require('stream').Writable
                     || require('readable-stream').Writable;

var STDERR = require('ssh2-streams').constants.CHANNEL_EXTENDED_DATATYPE.STDERR;

var PACKET_SIZE = 32 * 1024,
    MAX_WINDOW = 1 * 1024 * 1024,
    CUSTOM_EVENTS = [
      'CHANNEL_EOF',
      'CHANNEL_CLOSE',
      'CHANNEL_DATA',
      'CHANNEL_EXTENDED_DATA',
      'CHANNEL_WINDOW_ADJUST',
      'CHANNEL_SUCCESS',
      'CHANNEL_FAILURE',
      'CHANNEL_REQUEST'
    ],
    CUSTOM_EVENTS_LEN = CUSTOM_EVENTS.length;

function Channel(info, client, opts) {
  var streamOpts = {
    highWaterMark: MAX_WINDOW,
    allowHalfOpen: (!opts || (opts && opts.allowHalfOpen))
  };

  this.allowHalfOpen = streamOpts.allowHalfOpen;

  DuplexStream.call(this, streamOpts);

  var self = this,
      server = opts && opts.server;

  this.type = info.type;
  this.subtype = undefined;
  /*
    incoming and outgoing contain these properties:
    {
      id: undefined,
      window: undefined,
      packetSize: undefined,
      state: 'closed'
    }
  */
  this.incoming = info.incoming;
  this.outgoing = info.outgoing;

  this._client = client;
  this._callbacks = [];
  this._hasX11 = false;

  function ondrain() {
    if (self._waitClientDrain) {
      self._waitClientDrain = false;
      if (!self._waitWindow) {
        if (self._chunk)
          self._write(self._chunk, null, self._chunkcb);
        else if (self._chunkcb)
          self._chunkcb();
        else if (self._chunkErr)
          self.stderr._write(self._chunkErr, null, self._chunkcbErr);
        else if (self._chunkcbErr)
          self._chunkcbErr();
      }
    }
  }
  client._sock.on('drain', ondrain);

  client._sshstream.once('CHANNEL_EOF:' + this.incoming.id, function() {
    if (self.incoming.state === 'closed')
      return;
    self.incoming.state = 'eof';

    if (self.readable)
      self.push(null);
    if (server && self.stderr.writable)
      self.stderr.end();
    else if (!server && self.stderr.readable)
      self.stderr.push(null);
  }).once('CHANNEL_CLOSE:' + this.incoming.id, function() {
    self.incoming.state = 'closed';

    if (self.readable)
      self.push(null);
    if (server && self.stderr.writable)
      self.stderr.end();
    else if (!server && self.stderr.readable)
      self.stderr.push(null);

    if (self.outgoing.state === 'open' || self.outgoing.state === 'eof')
      self.close();
    if (self.outgoing.state === 'closing')
      self.outgoing.state = 'closed';

    client._channels.splice(client._channels.indexOf(self.incoming.id), 1);

    var state = self._writableState;
    client._sock.removeListener('drain', ondrain);
    if (!state.ending && !state.finished)
      self.end();

    self.emit('close');
    if (!server)
      self.stderr.emit('close');

    for (var i = 0; i < CUSTOM_EVENTS_LEN; ++i) {
      // Since EventEmitters do not actually *delete* event names in the
      // emitter's event array, we must do this manually so as not to leak
      // our custom, channel-specific event names.
      delete client._sshstream._events[CUSTOM_EVENTS[i] + ':' + self.incoming.id];
    }

    // no need to listen for unexpected channel end anymore ...
    self._client.removeListener('close', onClientClose);
  }).on('CHANNEL_DATA:' + this.incoming.id, function(data) {
    self.incoming.window -= data.length;

    if (!self.push(data)) {
      self._waitChanDrain = true;
      return;
    }

    if (self.incoming.window === 0)
      windowAdjust(self);
  }).on('CHANNEL_WINDOW_ADJUST:' + this.incoming.id, function(amt) {
    // the server is allowing us to send `amt` more bytes of data
    self.outgoing.window += amt;

    if (self._waitWindow) {
      self._waitWindow = false;
      if (!self._waitClientDrain) {
        if (self._chunk)
          self._write(self._chunk, null, self._chunkcb);
        else if (self._chunkcb)
          self._chunkcb();
        else if (self._chunkErr)
          self.stderr._write(self._chunkErr, null, self._chunkcbErr);
        else if (self._chunkcbErr)
          self._chunkcbErr();
      }
    }
  }).on('CHANNEL_SUCCESS:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(false);
  }).on('CHANNEL_FAILURE:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(true);
  }).on('CHANNEL_REQUEST:' + this.incoming.id, function(info) {
    if (info.request === 'exit-status')
      self.emit('exit', info.code);
    else if (info.request === 'exit-signal') {
      self.emit('exit',
                null,
                'SIG' + info.signal,
                info.coredump,
                info.description,
                info.lang);
    } else
      return;
    self.close();
  });

  this.stdin = this.stdout = this;

  if (server)
    this.stderr = new ServerStderr(this);
  else {
    this.stderr = new ReadableStream(streamOpts);
    this.stderr._read = function(n) {
      if (self._waitChanDrain) {
        self._waitChanDrain = false;
        if (self.incoming.window === 0)
          windowAdjust(self);
      }
    };

    client._sshstream.on('CHANNEL_EXTENDED_DATA:' + this.incoming.id,
      function(type, data) {
        self.incoming.window -= data.length;

        if (!self.stderr.push(data)) {
          self._waitChanDrain = true;
          return;
        }

        if (self.incoming.window === 0)
          windowAdjust(self);
      }
    );
  }

  // outgoing data
  this._waitClientDrain = false; // Client stream-level backpressure
  this._waitWindow = false; // SSH-level backpressure

  // incoming data
  this._waitChanDrain = false; // Channel Readable side backpressure

  this._chunk = undefined;
  this._chunkcb = undefined;
  this._chunkErr = undefined;
  this._chunkcbErr = undefined;

  this.on('finish', function() {
    self.eof();
    if (server || (!server && !self.allowHalfOpen))
      self.close();
  });

  function onClientClose() {
    if (self.readable) {
      self.push(null);
      self.once('end', function() {
        process.nextTick(function() {
          self.emit('close');
        });
      });
    }
    if (!server && self.stderr.readable) {
      self.stderr.push(null);
      self.stderr.once('end', function() {
        process.nextTick(function() {
          self.stderr.emit('close');
        });
      });
    }
    if (self.writable)
      self.end();
    if (server && self.stderr.writable)
      self.stderr.end();
  }
  this._client.once('close', onClientClose);
}
inherits(Channel, DuplexStream);

Channel.prototype.eof = function() {
  var ret = true;

  if (this.outgoing.state === 'open') {
    this.outgoing.state = 'eof';
    ret = this._client._sshstream.channelEOF(this.outgoing.id);
  }

  return ret;
};

Channel.prototype.close = function() {
  var ret = true;

  if (this.outgoing.state === 'open' || this.outgoing.state === 'eof') {
    this.outgoing.state = 'closing';
    ret = this._client._sshstream.channelClose(this.outgoing.id);
  }

  return ret;
};

Channel.prototype._read = function(n) {
  if (this._waitChanDrain) {
    this._waitChanDrain = false;
    if (this.incoming.window === 0)
      windowAdjust(this);
  }
};

Channel.prototype._write = function(data, encoding, cb) {
  var sshstream = this._client._sshstream,
      len = data.length,
      p = 0,
      ret,
      buf,
      sliceLen;

  while (len - p > 0 && this.outgoing.window > 0) {
    sliceLen = len - p;
    if (sliceLen > this.outgoing.window)
      sliceLen = this.outgoing.window;
    if (sliceLen > this.outgoing.packetSize)
      sliceLen = this.outgoing.packetSize;

    ret = sshstream.channelData(this.outgoing.id, data.slice(p, p + sliceLen));

    p += sliceLen;
    this.outgoing.window -= sliceLen;

    if (!ret) {
      this._waitClientDrain = true;
      this._chunk = undefined;
      this._chunkcb = cb;
      break;
    }
  }

  if (len - p > 0) {
    if (this.outgoing.window === 0)
      this._waitWindow = true;
    if (p > 0) {
      // partial
      buf = new Buffer(len - p);
      data.copy(buf, 0, p);
      this._chunk = buf;
    } else
      this._chunk = data;
    this._chunkcb = cb;
    return;
  }

  if (!this._waitClientDrain)
    cb();
};

Channel.prototype.destroy = function() {
  this.end();
};

// session type-specific methods
Channel.prototype.setWindow = function(rows, cols, height, width) {
  if (this.server)
    throw new Error('Client-only method called in server mode');

  if (this.type === 'session' && this.subtype === 'shell' && this.writable) {
    return this._client._sshstream.windowChange(this.outgoing.id,
                                                rows,
                                                cols,
                                                height,
                                                width);
  }

  return true;
};
Channel.prototype.signal = function(signalName) {
  if (this.server)
    throw new Error('Client-only method called in server mode');

  if (this.type === 'session'
      && (this.subtype === 'shell' || this.subtype === 'exec')
      && this.writable)
    return this._client._sshstream.signal(this.outgoing.id, signalName);

  return true;
};
Channel.prototype.exit = function(name, coreDumped, msg) {
  if (!this.server)
    throw new Error('Server-only method called in client mode');

  if (this.type === 'session' && this.subtype === 'exec' && this.writable) {
    if (typeof name === 'number')
      return this._client._sshstream.exitStatus(this.outgoing.id, name);
    else {
      return this._client._sshstream.exitSignal(this.outgoing.id,
                                                name,
                                                coreDumped,
                                                msg);
    }
  }

  return true;
};

Channel.MAX_WINDOW = MAX_WINDOW;
Channel.PACKET_SIZE = PACKET_SIZE;

function windowAdjust(self, amt) {
  amt = amt || MAX_WINDOW;
  return self._client._sshstream.channelWindowAdjust(self.outgoing.id, amt);
}

function ServerStderr(channel) {
  WritableStream.call(this, { highWaterMark: MAX_WINDOW });
  this._channel = channel;
}
inherits(ServerStderr, WritableStream);

ServerStderr.prototype._write = function(data, encoding, cb) {
  var channel = this._channel,
      sshstream = channel._client._sshstream,
      len = data.length,
      p = 0,
      ret,
      buf,
      sliceLen;

  while (len - p > 0 && channel.outgoing.window > 0) {
    sliceLen = len - p;
    if (sliceLen > channel.outgoing.window)
      sliceLen = channel.outgoing.window;
    if (sliceLen > channel.outgoing.packetSize)
      sliceLen = channel.outgoing.packetSize;

    ret = sshstream.channelExtData(channel.outgoing.id,
                                   data.slice(p, p + sliceLen),
                                   STDERR);

    p += sliceLen;
    channel.outgoing.window -= sliceLen;

    if (!ret) {
      channel._waitClientDrain = true;
      channel._chunkErr = undefined;
      channel._chunkcbErr = cb;
      break;
    }
  }

  if (len - p > 0) {
    if (channel.outgoing.window === 0)
      channel._waitWindow = true;
    if (p > 0) {
      // partial
      buf = new Buffer(len - p);
      data.copy(buf, 0, p);
      channel._chunkErr = buf;
    } else
      channel._chunkErr = data;
    channel._chunkcbErr = cb;
    return;
  }

  if (!channel._waitClientDrain)
    cb();
};

module.exports = Channel;
<|endoftext|>


<commit_before>#! /usr/bin/env node

var execSync = require('child_process').execSync;
var path = require('path');
var fs = require('fs');
var mkdirp = require('mkdirp');
var program = require('./../src/customcommander');
var jsonfile = require('jsonfile');
var helper = require('./../src/clihelper');

var registryPath = path.join(__dirname, '..', 'registry.json');
var widgetName, widgetPath, registry;

program
    .alias('appsngen widget create')
    .usage('<name> [path]')
    .arguments('<name> [path]')
    .action(function (name, path) {
        widgetName = name;
        widgetPath = path;
    })
    .parse(process.argv);

if (typeof widgetName === 'undefined') {
    console.error('No widget name provided');
    process.exit(1);
}
if (typeof widgetPath !== 'undefined') {
    if (path.isAbsolute(widgetPath)) {
        widgetPath = path.join(widgetPath, widgetName);
    } else {
        widgetPath = path.join(process.cwd(), widgetPath, widgetName);
    }
} else {
    widgetPath = path.join(process.cwd(), widgetName);
}
helper.validateWidgetName(widgetName)
    .then(function () {
        try {
            if (fs.readdirSync(widgetPath).length !== 0) {
                throw 'Path already exists and is not empty: ' + path.resolve(widgetPath);
            }
        } catch (e) {
            if (e.code !== 'ENOENT') {
                throw e;
            }
        }
        try {
            registry = jsonfile.readFileSync(registryPath);
        } catch (e) {
            if (e.code !== 'ENOENT') {
                throw e;
            }
        }
        registry = registry || {};
        if (registry[widgetName]) {
            throw 'Widget with this name already exist.';
        }
        mkdirp.sync(widgetPath);
        execSync('npm run yo appsngen-web-widget "' + path.resolve(widgetPath) + '"', {
            cwd: path.join(__dirname, '..'),
            stdio: 'inherit'
        });
        registry[widgetName] = {
            path: path.resolve(widgetPath)
        };
        jsonfile.writeFileSync(registryPath, registry, {
            spaces: 4
        });
    })
    .catch(function (error) {
        console.error(error.toString());
        process.exit(1);
    });
<commit_msg>Update appsngen-cli-widget-create.js<commit_after>#! /usr/bin/env node

var execSync = require('child_process').execSync;
var path = require('path');
var fs = require('fs');
var mkdirp = require('mkdirp');
var program = require('./../src/customcommander');
var jsonfile = require('jsonfile');
var helper = require('./../src/clihelper');

var registryPath = path.join(__dirname, '..', 'registry.json');
var widgetName, widgetPath, registry;

program
    .alias('appsngen widget create')
    .usage('<name> [path]')
    .arguments('<name> [path]')
    .action(function (name, path) {
        widgetName = name;
        widgetPath = path;
    })
    .parse(process.argv);

if (typeof widgetName === 'undefined') {
    console.error('No widget name provided');
    process.exit(1);
}
if (typeof widgetPath !== 'undefined') {
    widgetPath = path.resolve(widgetPath);
} else {
    widgetPath = path.join(process.cwd(), widgetName);
}
helper.validateWidgetName(widgetName)
    .then(function () {
        try {
            if (fs.readdirSync(widgetPath).length !== 0) {
                throw 'Path already exists and is not empty: ' + path.resolve(widgetPath);
            }
        } catch (e) {
            if (e.code !== 'ENOENT') {
                throw e;
            }
        }
        try {
            registry = jsonfile.readFileSync(registryPath);
        } catch (e) {
            if (e.code !== 'ENOENT') {
                throw e;
            }
        }
        registry = registry || {};
        if (registry[widgetName]) {
            throw 'Widget with this name already exist.';
        }
        mkdirp.sync(widgetPath);
        execSync('npm run yo appsngen-web-widget "' + path.resolve(widgetPath) + '"', {
            cwd: path.join(__dirname, '..'),
            stdio: 'inherit'
        });
        registry[widgetName] = {
            path: path.resolve(widgetPath)
        };
        jsonfile.writeFileSync(registryPath, registry, {
            spaces: 4
        });
    })
    .catch(function (error) {
        console.error(error.toString());
        process.exit(1);
    });
<|endoftext|>


<commit_before>(function(d3) {

    d3.ui = d3.ui || {};

    d3.ui.FpsMeter = function Histogram(on, style, config) {
    var hist = d3.select(on).append("div")
            .style({
                "background-color": 'rgba(207,203,196,0.1)',
                display: "inline-block",
                margin: "0 0 0 6px"
            })
            .attr("id", "histogram")
            .append("svg")
            .style(merge({display: "inline-block"}, style))
            .attr(config),
        plot = hist.append("g")
            .attr(transplot(config.height))
            .attr({"fill": "none", stroke: 'rgba(207,203,196,0.3)'});

    function update(data) {
        if (!data || !data.length) return;
        var x = d3.scale.linear()
                .domain(config.domain || [0, d3.max(data, config.values)])
                .range([0, config.width]),
            h = makeHist(data, config.values, x),
            xAxis = d3.svg.axis()
                .scale(x)
                .orient("top")
                .tickFormat("")
                .ticks(3)
                .tickSize(config.tickSize || 3),
            y = d3.scale.linear()
                .domain([0,d3.max(h, function(d){return d.y})])
                .range([0,config.height]),
            bars = plot.selectAll(".bar").data(h);

        bars.enter().append("rect")
            .attr("class", "bar")
            .style({fill: "orange", stroke: "none"});

        bars.exit().remove();

        bars.attr("height", function(d) {
            return y(d.y)
        })
            .attr("width", function(d) {
                return x(d.dx)
            })
            .attr("x", function(d){
                return x(d.x)
            });
        plot.call(xAxis);
    }

    return update;

    function makeHist(data, values, x) {
        var bins = d3.scale.ordinal()
            .domain(d3.range(100))
            .rangeBands(x.domain())
            .range();
        return d3.layout.histogram()
            .bins(bins)
            .value(values)(data)
    }

    function merge(source, target) {
        for(var p in source) if(target && !target.hasOwnProperty(p)) target[p] = source[p];
        return target;
    }
}

})(d3)
<commit_msg>plot - added fps-histogram<commit_after>(function(d3) {

    d3.ui = d3.ui || {};

    d3.ui.FpsMeter = function Histogram(on, style, config) {
    var hist = d3.select(on).append("div")
            .style({
                "background-color": 'rgba(207,203,196,0.1)',
                display: "inline-block",
                margin: "0 0 0 6px"
            })
            .attr("id", "histogram")
            .append("svg")
            .style(merge({display: "inline-block", overflow: "visible"}, style))
            .attr(config),
        plot = hist.append("g")
            .attr(transplot(config.height))
            .attr({"fill": "none", stroke: 'rgba(207,203,196,0.3)'});

    function update(data) {
        if (!data || !data.length) return;
        var x = d3.scale.linear()
                .domain(config.domain || [0, d3.max(data, config.values)])
                .range([0, config.width]),
            h = makeHist(data, config.values, x),
            xAxis = d3.svg.axis()
                .scale(x)
                .orient("top")
                .tickFormat("")
                .ticks(3)
                .tickSize(config.tickSize || 3),
            y = d3.scale.linear()
                .domain([0,d3.max(h, function(d){return d.y})])
                .range([0,config.height]),
            bars = plot.selectAll(".bar").data(h);

        bars.enter().append("rect")
            .attr("class", "bar")
            .style({fill: "orange", stroke: "none"});

        bars.exit().remove();

        bars.attr("height", function(d) {
            return y(d.y)
        })
            .attr("width", function(d) {
                return x(d.dx)
            })
            .attr("x", function(d){
                return x(d.x)
            });
        plot.call(xAxis);
    }

    return update;

    function makeHist(data, values, x) {
        var bins = d3.scale.ordinal()
            .domain(d3.range(100))
            .rangeBands(x.domain())
            .range();
        return d3.layout.histogram()
            .bins(bins)
            .value(values)(data)
    }

    function merge(source, target) {
        for(var p in source) if(target && !target.hasOwnProperty(p)) target[p] = source[p];
        return target;
    }
}

})(d3)
<|endoftext|>


<commit_before>(function () {
    "use strict";

    var appView = Windows.UI.ViewManagement.ApplicationView;
    var displayProps = Windows.Graphics.Display.DisplayProperties;
    var nav = WinJS.Navigation;
    var ui = WinJS.UI;
    var utils = WinJS.Utilities;

    WinJS.Namespace.define("Application", {
        PageControlNavigator: WinJS.Class.define(
            // Define the constructor function for the PageControlNavigator.
            function PageControlNavigator(element, options) {
                this.element = element || document.createElement("div");
                this.element.appendChild(this._createPageElement());

                this.home = options.home;
                this.lastViewstate = appView.value;

                nav.onnavigated = this._navigated.bind(this);
                window.onresize = this._resized.bind(this);

                document.body.onkeyup = this._keyupHandler.bind(this);
                document.body.onkeypress = this._keypressHandler.bind(this);
                document.body.onmspointerup = this._mspointerupHandler.bind(this);

                Application.navigator = this;
            }, {
                /// <field domElement="true" />
                element: null,
                home: "",
                lastViewstate: 0,

                // This function creates a new container for each page.
                _createPageElement: function () {
                    var element = document.createElement("div");
                    element.style.width = "100%";
                    element.style.height = "100%";
                    return element;
                },

                // This function responds to keypresses to only navigate when
                // the backspace key is not used elsewhere.
                _keypressHandler: function (args) {
                    if (args.key === "Backspace") {
                        nav.back();
                    }
                },

                // This function responds to keyup to enable keyboard navigation.
                _keyupHandler: function (args) {
                    if ((args.key === "Left" && args.altKey) || (args.key === "BrowserBack")) {
                        nav.back();
                    } else if ((args.key === "Right" && args.altKey) || (args.key === "BrowserForward")) {
                        nav.forward();
                    }
                },

                _mspointerupHandler: function (args) {
                    if (args.button === 3) {
                        nav.back();
                    } else if (args.button === 4) {
                        nav.forward();
                    }
                },

                // This function responds to navigation by adding new pages to the DOM.
                _navigated: function (args) {
                    var that = this;
                    var oldElement = that.pageElement;
                    var newElement = that._createPageElement();
                    var parentedComplete;
                    var parented = new WinJS.Promise(function (c) { parentedComplete = c; });

                    args.detail.setPromise(
                        WinJS.Promise.timeout().then(function () {
                            if (oldElement.winControl && oldElement.winControl.unload) {
                                oldElement.winControl.unload();
                            }
                            return WinJS.UI.Pages.render(args.detail.location, newElement, args.detail.state, parented);
                        }).then(function parentElement(control) {
                            that.element.appendChild(newElement);
                            that.element.removeChild(oldElement);
                            oldElement.innerText = "";
                            that.navigated();
                            parentedComplete();

                            //add the "see the code" link next to the page title
                            if (args.detail.location.startsWith("/demos")) {
                                var seeCodeSpan = document.createElement("span");
                                seeCodeSpan.className = "seecode win-type-x-large win-type-interactive";
                                seeCodeSpan.textContent = "see the code";
                                q(".pagetitle").parentElement.appendChild(seeCodeSpan);

                                q(".seecode", null, { forceArray: true }).forEach(function(elem) {
                                    elem.onclick = function () {
                                        WinJS.Navigation.navigate("/pages/demoCode/demoCode.html", args.detail.state);
                                    };
                                });
                            }

                        })
                    );
                },

                _resized: function (args) {
                    if (this.pageControl && this.pageControl.updateLayout) {
                        this.pageControl.updateLayout.call(this.pageControl, this.pageElement, appView.value, this.lastViewstate);
                    }
                    this.lastViewstate = appView.value;
                },

                // This function updates application controls once a navigation has completed.
                navigated: function () {
                    // Do application specific on-navigated work here
                    var backButton = this.pageElement.querySelector("header[role=banner] .win-backbutton");
                    if (backButton) {
                        backButton.onclick = function () { nav.back(); };

                        if (nav.canGoBack) {
                            backButton.removeAttribute("disabled");
                        } else {
                            backButton.setAttribute("disabled", "disabled");
                        }
                    }
                },

                // This is the PageControlNavigator object.
                pageControl: {
                    get: function () { return this.pageElement && this.pageElement.winControl; }
                },

                // This is the root element of the current page.
                pageElement: {
                    get: function () { return this.element.firstElementChild; }
                }
            }
        )
    });
})();
<commit_msg>Add "see the demo" when the user is looking at the code<commit_after>(function () {
    "use strict";

    var appView = Windows.UI.ViewManagement.ApplicationView;
    var displayProps = Windows.Graphics.Display.DisplayProperties;
    var nav = WinJS.Navigation;
    var ui = WinJS.UI;
    var utils = WinJS.Utilities;

    WinJS.Namespace.define("Application", {
        PageControlNavigator: WinJS.Class.define(
            // Define the constructor function for the PageControlNavigator.
            function PageControlNavigator(element, options) {
                this.element = element || document.createElement("div");
                this.element.appendChild(this._createPageElement());

                this.home = options.home;
                this.lastViewstate = appView.value;

                nav.onnavigated = this._navigated.bind(this);
                window.onresize = this._resized.bind(this);

                document.body.onkeyup = this._keyupHandler.bind(this);
                document.body.onkeypress = this._keypressHandler.bind(this);
                document.body.onmspointerup = this._mspointerupHandler.bind(this);

                Application.navigator = this;
            }, {
                /// <field domElement="true" />
                element: null,
                home: "",
                lastViewstate: 0,

                // This function creates a new container for each page.
                _createPageElement: function () {
                    var element = document.createElement("div");
                    element.style.width = "100%";
                    element.style.height = "100%";
                    return element;
                },

                // This function responds to keypresses to only navigate when
                // the backspace key is not used elsewhere.
                _keypressHandler: function (args) {
                    if (args.key === "Backspace") {
                        nav.back();
                    }
                },

                // This function responds to keyup to enable keyboard navigation.
                _keyupHandler: function (args) {
                    if ((args.key === "Left" && args.altKey) || (args.key === "BrowserBack")) {
                        nav.back();
                    } else if ((args.key === "Right" && args.altKey) || (args.key === "BrowserForward")) {
                        nav.forward();
                    }
                },

                _mspointerupHandler: function (args) {
                    if (args.button === 3) {
                        nav.back();
                    } else if (args.button === 4) {
                        nav.forward();
                    }
                },

                // This function responds to navigation by adding new pages to the DOM.
                _navigated: function (args) {
                    var that = this;
                    var oldElement = that.pageElement;
                    var newElement = that._createPageElement();
                    var parentedComplete;
                    var parented = new WinJS.Promise(function (c) { parentedComplete = c; });

                    args.detail.setPromise(
                        WinJS.Promise.timeout().then(function () {
                            if (oldElement.winControl && oldElement.winControl.unload) {
                                oldElement.winControl.unload();
                            }
                            return WinJS.UI.Pages.render(args.detail.location, newElement, args.detail.state, parented);
                        }).then(function parentElement(control) {
                            that.element.appendChild(newElement);
                            that.element.removeChild(oldElement);
                            oldElement.innerText = "";
                            that.navigated();
                            parentedComplete();

                            //add the "see the code" link next to the page title
                            if (args.detail.location.startsWith("/demos")) {
                                var span = document.createElement("span");
                                span.className = "seecode win-type-x-large win-type-interactive";
                                span.textContent = "see the code";
                                q(".pagetitle").parentElement.appendChild(span);

                                span.onclick = function () {
                                    WinJS.Navigation.navigate("/pages/demoCode/demoCode.html", args.detail.state);
                                };
                            }
                            else if (args.detail.location = "/pages/demoCode/demoCode.html") {
                                var span = document.createElement("span");
                                span.className = "seedemo win-type-x-large win-type-interactive";
                                span.textContent = "see the demo";
                                q(".pagetitle").parentElement.appendChild(span);

                                span.onclick = function () {
                                    var location = format("/demos/{0}/{0}.html", args.detail.state.key);
                                    WinJS.Navigation.navigate(location, args.detail.state);
                                };
                            }

                        })
                    );
                },

                _resized: function (args) {
                    if (this.pageControl && this.pageControl.updateLayout) {
                        this.pageControl.updateLayout.call(this.pageControl, this.pageElement, appView.value, this.lastViewstate);
                    }
                    this.lastViewstate = appView.value;
                },

                // This function updates application controls once a navigation has completed.
                navigated: function () {
                    // Do application specific on-navigated work here
                    var backButton = this.pageElement.querySelector("header[role=banner] .win-backbutton");
                    if (backButton) {
                        backButton.onclick = function () { nav.back(); };

                        if (nav.canGoBack) {
                            backButton.removeAttribute("disabled");
                        } else {
                            backButton.setAttribute("disabled", "disabled");
                        }
                    }
                },

                // This is the PageControlNavigator object.
                pageControl: {
                    get: function () { return this.pageElement && this.pageElement.winControl; }
                },

                // This is the root element of the current page.
                pageElement: {
                    get: function () { return this.element.firstElementChild; }
                }
            }
        )
    });
})();
<|endoftext|>


<commit_before>Function.prototype.bind = Function.prototype.bind || function (target) {
  var self = this;
  return function (args) {
    if (!(args instanceof Array)) {
      args = [args];
    }
    self.apply(target, args);
  };
};
<commit_msg>Delete bind_polyfill.js<commit_after><|endoftext|>