<commit_before>var styles = {
        textSize: 5,
        corners: 5,
        leftOffset: 2,
        padding: 2,
        infoWidth : 30 },
    uoiTypes = {
        protein: "mt:prot",
        binding: "ct:bind"
    };

//This is syntactic sugar.
var setAttr = function(elem, x, y) {
        elem.setAttributeNS(null, x, y);
    },
    createElem = function(elemName) {
        return document.createElementNS("http://www.w3.org/2000/svg", elemName);
    }

var interaction;
document.addEventListener("DOMContentLoaded", function(event) {
    $.get({
        dataType: "json",
        url: "http://www.ebi.ac.uk/intact/complex-ws/export/EBI-10828997"
    }, function(data) {

        new MIModel(data).load().then(function(model) {

            function UnitOfInformation(info) {
                this.info = uoiTypes[info];
                this.node = createElem("g");
                setAttr(this.node, "transform", "translate(0,3)")
                var text = createElem("text")
                text.appendChild(document.createTextNode(this.info));
                var rect = createElem("rect");

                setAttr(rect, "width", styles.infoWidth);
                setAttr(rect, "height", styles.textSize+2);
                this.node.appendChild(rect);

                setAttr(text, "x", styles.leftOffset);
                setAttr(text, "y", styles.textSize);
                setAttr(text, "font-size", styles.textSize);
                this.node.appendChild(text);
            }
            UnitOfInformation.prototype.updateOutlines = function(parentBB) {
                var top = parentBB.height - styles.textSize - styles.padding;
                setAttr(this.node, "transform", "translate(0," + top + ")");
            }

            function StateVariable(info) {
                this.info = info;
                this.node = createElem("g");
                setAttr(this.node, "transform", "translate(0," + (-2 * (styles.textSize + styles.padding)) + ")")
                var text = createElem("text")
                text.appendChild(document.createTextNode(this.info));
                var rect = createElem("rect");

                setAttr(rect, "width", styles.infoWidth);
                setAttr(rect, "rx", 5);
                setAttr(rect, "ry", 5);
                setAttr(rect, "height", styles.textSize + 2);
                this.node.appendChild(rect);

                setAttr(text, "x", styles.leftOffset * 2);
                setAttr(text, "y", "5");
                setAttr(text, "font-size", styles.textSize);
                this.node.appendChild(text);
            }

            function BindingSite(model, count) {
                this.model = model;
                this.count = count + 1;
                this.node = createElem("g");

                var loc = -45;
                if (count > 0) {
                    loc = 45;
                }
                setAttr(this.node, "transform", "translate(" + loc + ",10)");
                var text = createElem("text")
                text.appendChild(document.createTextNode("binding region"));
                var rect = this.rect = createElem("rect");

                setAttr(rect, "width", 1.5*styles.infoWidth);
                setAttr(rect, "x", (styles.leftOffset * -3));
                setAttr(rect, "y", -2 * styles.textSize);
                setAttr(rect, "rx", 1);
                setAttr(rect, "ry", 1);
                setAttr(rect, "height", styles.textSize * 3);
                this.node.appendChild(rect);

                setAttr(text, "x", (styles.leftOffset * -2));
                setAttr(text, "y", "-1");
                setAttr(text, "font-size", styles.textSize);
                this.node.appendChild(text);
                this.node.appendChild(new UnitOfInformation("binding").node);
                this.node.appendChild(new StateVariable(model.get("pos")).node);
                return this;
            }

            BindingSite.prototype.updateOutlines = function() {
                bb = this.node.getBBox();
                setAttr(this.rect, "x", (bb.x - styles.padding));
                setAttr(this.rect, "width", (bb.width + (styles.padding * 2)));
                //height is pretty simple so we don't update it.
            }

            function Participant(model) {
                console.log("%cmodel", "color:firebrick;font-weight:bold;", model);
                this.init(model);
                return this;
            }

            var location = 0; // temp var until we get positioning properly

            Participant.prototype.setLocation = function(x,y) {
              setAttr(this.node, "transform", "translate(" + Math.round(x) + "," + Math.round(y) + ")");
              console.log("%cthis","color:darkseagreen;font-weight:bold;",this.node);
            }

            Participant.prototype.init = function(model) {
                this.model = model;
                this.node = createElem("g");
                this.interactor = this.model.get("interactor");
                this.initFeatures();
                this.initBindingSites();

                this.node.rect = createElem("rect");
                this.node.append(this.node.rect);

                setAttr(this.node.rect, "rx", 1);
                setAttr(this.node.rect, "ry", 1);

                var text = createElem("text");
                var label = document.createTextNode(this.interactor.get("label"));
                text.appendChild(label);
                setAttr(text, "font-size", styles.textSize);

                this.node.appendChild(text);

                this.node.uoi = new UnitOfInformation(this.interactor.get("type").name);
                this.node.appendChild(this.node.uoi.node);

                if (this.bindingSites) {
                    var parent = this;
                    parent.node.bindingSites = [];
                    this.bindingSites.map(function(site, i) {
                        var newSite = new BindingSite(site, i);
                        parent.node.bindingSites.push(newSite);
                        parent.node.appendChild(newSite.node);
                    });
                }
            }

            Participant.prototype.updateOutlines = function() {

                //update binding sites and uois
                this.node.bindingSites.map(function(site) {
                    site.updateOutlines();
                });

                var bb = this.node.getBBox();

                setAttr(this.node.rect, "x", (bb.x - styles.padding));
                setAttr(this.node.rect, "y", (bb.y - styles.padding));
                setAttr(this.node.rect, "width", (bb.width + (styles.padding * 2)));
                setAttr(this.node.rect, "height", (bb.height + (styles.padding * 2)));

                this.node.uoi.updateOutlines(bb);

            }

            Participant.prototype.initFeatures = function() {
                this.features = this.model.get("features");
                if (this.features.length > 0) {
                    this.features = this.features.models;
                } else {
                    this.features = false;
                }
                if (this.features) {
                  this.links = [];
                  var links = [];
                  this.features.map(function(feature) {
                    var id1 = parseInt(feature.get("linkedFeatures").models[0].get("id"),10);
                    var id2 = parseInt(feature.get("id"),10);
                    links = links.concat(feature.get("linkedFeatures").models);
                  });
                }
            }

            Participant.prototype.initBindingSites = function() {
                var binding = [];
                if (this.features) {
                    this.features.map(function(feature) {
                        binding = binding.concat(feature.get("sequenceData").models);
                    });
                    if (binding.length > 0) {
                        this.bindingSites = binding;
                    }
                }
            }

            var ComplexView = Backbone.View.extend({
                className: "sbgnContainer",
                interactors: model.attributes.interactors.models,
                participants: [],
                initialize: function() {
                    this.render();
                    this.listenTo(this.model, "change", this.render);
                },
                render: function() {
                    var parent = this;
                    var mynode = createElem("rect");
                    setAttr(mynode, "x", 10);
                    setAttr(mynode, "y", 10);
                    setAttr(mynode, "height", 300);
                    this.el.appendChild(mynode);

                    try {
                        //first, instantiate the nodes and print the text
                        model.get("interactions").at(0).get("participants").map(function(participant) {
                            var newParticipant = new Participant(participant);
                            parent.el.appendChild(newParticipant.node);
                            parent.participants.push(newParticipant);
                        });

                        //once we have the text, we can adjust outlines based on text width
                        parent.participants.map(function(participant) {
                            participant.updateOutlines();
                        });

                        var nodeLocations = [], links = [], nodeIndexLookup = {};
                        parent.participants.map(function(participant) {
                            var bb = participant.node.getBBox(),
                              id = participant.model.get("id");

                              //TODO THIS IS HARDWIRED. Needs to dynamically output all links.

                              //suggest keep a node index lookup table.
                            nodeLocations.push({width : bb.width,height : bb.height, participant : participant})
                            links.push({source: 0,target: 1});
                          });

                        //now we need to lay out the major boxes:
                        var newLayout = layout(nodeLocations, links);
                        newLayout.nodes().map(function(node) {
                          node.participant.setLocation(node.x,node.y);
                        });

                    } catch (e) {
                        console.log("%ce", "color:navajowhite;font-weight:bold;", e);
                    }
                    return this;
                }

            });

            function layout(nodeLocations,linkIds) {
              //drawn from https://github.com/tgdwyer/WebCola/blob/893f1ae744f35b83c59451836065ef0d1897a688/WebCola/test/apitests.ts#L77
                const nodeSize = 20,
                    threshold = 0.01;
                let layout = new cola.Layout()
                    .handleDisconnected(false) // handle disconnected repacks the components which would hide any drift
                    .linkDistance(1)
                    .avoidOverlaps(true) // force non-overlap
                    .links(linkIds)
                    .constraints([{
                        gap:10,
                        top:10,
                        bottom:50,
                        offsets: [{
                                node: 0,
                                offset: 0
                            },
                            {
                                node: 1,
                                offset: 0
                            },
                        ]
                    }]).nodes(nodeLocations)
                layout.start(); // first layout
                return layout;
            }

            var Title = Backbone.View.extend({
                initialize: function() {
                    this.render();
                    this.listenTo(this.model, "change", this.render);
                },
                render: function() {
                  var ids = model.get("interactions").models[0].get("identifiers"),
                  title;
                  ids.map(function(id){
                    //this might be a little fragile. Will they all have Intact IDs? haha.
                    if (id.db === "intact") {
                      title = id.id;
                    }
                  });
                    this.$el.html(title);
                }
              });

            new ComplexView({
                model: model,
                el: document.getElementById('mi-sbgn')
            });
            new Title({
                model:model,
                el: document.getElementById('complextitle')
            });
        });
    });
});
<commit_msg>remove more dead code<commit_after>var styles = {
        textSize: 5,
        corners: 5,
        leftOffset: 2,
        padding: 2,
        infoWidth : 30 },
    uoiTypes = {
        protein: "mt:prot",
        binding: "ct:bind"
    };

//This is syntactic sugar.
var setAttr = function(elem, x, y) {
        elem.setAttributeNS(null, x, y);
    },
    createElem = function(elemName) {
        return document.createElementNS("http://www.w3.org/2000/svg", elemName);
    }

var interaction;
document.addEventListener("DOMContentLoaded", function(event) {
    $.get({
        dataType: "json",
        url: "http://www.ebi.ac.uk/intact/complex-ws/export/EBI-10828997"
    }, function(data) {

        new MIModel(data).load().then(function(model) {

            function UnitOfInformation(info) {
                this.info = uoiTypes[info];
                this.node = createElem("g");
                setAttr(this.node, "transform", "translate(0,3)")
                var text = createElem("text")
                text.appendChild(document.createTextNode(this.info));
                var rect = createElem("rect");

                setAttr(rect, "width", styles.infoWidth);
                setAttr(rect, "height", styles.textSize+2);
                this.node.appendChild(rect);

                setAttr(text, "x", styles.leftOffset);
                setAttr(text, "y", styles.textSize);
                setAttr(text, "font-size", styles.textSize);
                this.node.appendChild(text);
            }
            UnitOfInformation.prototype.updateOutlines = function(parentBB) {
                var top = parentBB.height - styles.textSize - styles.padding;
                setAttr(this.node, "transform", "translate(0," + top + ")");
            }

            function StateVariable(info) {
                this.info = info;
                this.node = createElem("g");
                setAttr(this.node, "transform", "translate(0," + (-2 * (styles.textSize + styles.padding)) + ")")
                var text = createElem("text")
                text.appendChild(document.createTextNode(this.info));
                var rect = createElem("rect");

                setAttr(rect, "width", styles.infoWidth);
                setAttr(rect, "rx", 5);
                setAttr(rect, "ry", 5);
                setAttr(rect, "height", styles.textSize + 2);
                this.node.appendChild(rect);

                setAttr(text, "x", styles.leftOffset * 2);
                setAttr(text, "y", "5");
                setAttr(text, "font-size", styles.textSize);
                this.node.appendChild(text);
            }

            function BindingSite(model, count) {
                this.model = model;
                this.count = count + 1;
                this.node = createElem("g");

                var loc = -45;
                if (count > 0) {
                    loc = 45;
                }
                setAttr(this.node, "transform", "translate(" + loc + ",10)");
                var text = createElem("text")
                text.appendChild(document.createTextNode("binding region"));
                var rect = this.rect = createElem("rect");

                setAttr(rect, "width", 1.5*styles.infoWidth);
                setAttr(rect, "x", (styles.leftOffset * -3));
                setAttr(rect, "y", -2 * styles.textSize);
                setAttr(rect, "rx", 1);
                setAttr(rect, "ry", 1);
                setAttr(rect, "height", styles.textSize * 3);
                this.node.appendChild(rect);

                setAttr(text, "x", (styles.leftOffset * -2));
                setAttr(text, "y", "-1");
                setAttr(text, "font-size", styles.textSize);
                this.node.appendChild(text);
                this.node.appendChild(new UnitOfInformation("binding").node);
                this.node.appendChild(new StateVariable(model.get("pos")).node);
                return this;
            }

            BindingSite.prototype.updateOutlines = function() {
                bb = this.node.getBBox();
                setAttr(this.rect, "x", (bb.x - styles.padding));
                setAttr(this.rect, "width", (bb.width + (styles.padding * 2)));
                //height is pretty simple so we don't update it.
            }

            function Participant(model) {
                console.log("%cmodel", "color:firebrick;font-weight:bold;", model);
                this.init(model);
                return this;
            }

            var location = 0; // temp var until we get positioning properly

            Participant.prototype.setLocation = function(x,y) {
              setAttr(this.node, "transform", "translate(" + Math.round(x) + "," + Math.round(y) + ")");
              console.log("%cthis","color:darkseagreen;font-weight:bold;",this.node);
            }

            Participant.prototype.init = function(model) {
                this.model = model;
                this.node = createElem("g");
                this.interactor = this.model.get("interactor");
                this.initFeatures();
                this.initBindingSites();

                this.node.rect = createElem("rect");
                this.node.append(this.node.rect);

                setAttr(this.node.rect, "rx", 1);
                setAttr(this.node.rect, "ry", 1);

                var text = createElem("text");
                var label = document.createTextNode(this.interactor.get("label"));
                text.appendChild(label);
                setAttr(text, "font-size", styles.textSize);

                this.node.appendChild(text);

                this.node.uoi = new UnitOfInformation(this.interactor.get("type").name);
                this.node.appendChild(this.node.uoi.node);

                if (this.bindingSites) {
                    var parent = this;
                    parent.node.bindingSites = [];
                    this.bindingSites.map(function(site, i) {
                        var newSite = new BindingSite(site, i);
                        parent.node.bindingSites.push(newSite);
                        parent.node.appendChild(newSite.node);
                    });
                }
            }

            Participant.prototype.updateOutlines = function() {

                //update binding sites and uois
                this.node.bindingSites.map(function(site) {
                    site.updateOutlines();
                });

                var bb = this.node.getBBox();

                setAttr(this.node.rect, "x", (bb.x - styles.padding));
                setAttr(this.node.rect, "y", (bb.y - styles.padding));
                setAttr(this.node.rect, "width", (bb.width + (styles.padding * 2)));
                setAttr(this.node.rect, "height", (bb.height + (styles.padding * 2)));

                this.node.uoi.updateOutlines(bb);

            }

            Participant.prototype.initFeatures = function() {
                this.features = this.model.get("features");
                if (this.features.length > 0) {
                    this.features = this.features.models;
                } else {
                    this.features = false;
                }
                if (this.features) {
                  this.links = [];
                  var links = [];
                  this.features.map(function(feature) {
                    var id1 = parseInt(feature.get("linkedFeatures").models[0].get("id"),10);
                    var id2 = parseInt(feature.get("id"),10);
                    links = links.concat(feature.get("linkedFeatures").models);
                  });
                }
            }

            Participant.prototype.initBindingSites = function() {
                var binding = [];
                if (this.features) {
                    this.features.map(function(feature) {
                        binding = binding.concat(feature.get("sequenceData").models);
                    });
                    if (binding.length > 0) {
                        this.bindingSites = binding;
                    }
                }
            }

            var ComplexView = Backbone.View.extend({
                className: "sbgnContainer",
                interactors: model.attributes.interactors.models,
                participants: [],
                initialize: function() {
                    this.render();
                    this.listenTo(this.model, "change", this.render);
                },
                render: function() {
                    var parent = this;
                    var mynode = createElem("rect");
                    setAttr(mynode, "x", 10);
                    setAttr(mynode, "y", 10);
                    setAttr(mynode, "height", 300);
                    this.el.appendChild(mynode);

                    try {
                        //first, instantiate the nodes and print the text
                        model.get("interactions").at(0).get("participants").map(function(participant) {
                            var newParticipant = new Participant(participant);
                            parent.el.appendChild(newParticipant.node);
                            parent.participants.push(newParticipant);
                        });

                        //once we have the text, we can adjust outlines based on text width
                        parent.participants.map(function(participant) {
                            participant.updateOutlines();
                        });

                        var nodeLocations = [], links = [], nodeIndexLookup = {};
                        parent.participants.map(function(participant) {
                            var bb = participant.node.getBBox(),
                              id = participant.model.get("id");

                              //TODO THIS IS HARDWIRED. Needs to dynamically output all links.

                              //suggest keep a node index lookup table.
                            nodeLocations.push({width : bb.width,height : bb.height, participant : participant})
                            links.push({source: 0,target: 1});
                          });

                        //now we need to lay out the major boxes:
                        var newLayout = layout(nodeLocations, links);
                        newLayout.nodes().map(function(node) {
                          node.participant.setLocation(node.x,node.y);
                        });

                    } catch (e) {
                        console.log("%ce", "color:navajowhite;font-weight:bold;", e);
                    }
                    return this;
                }

            });

            function layout(nodeLocations,linkIds) {
              //drawn from https://github.com/tgdwyer/WebCola/blob/893f1ae744f35b83c59451836065ef0d1897a688/WebCola/test/apitests.ts#L77
                let layout = new cola.Layout()
                    .handleDisconnected(false) // handle disconnected repacks the components which would hide any drift
                    .linkDistance(1)
                    .avoidOverlaps(true) // force non-overlap
                    .links(linkIds)
                    .constraints([{
                        gap:10,
                        top:10,
                        bottom:50,
                        offsets: [{
                                node: 0,
                                offset: 0
                            },
                            {
                                node: 1,
                                offset: 0
                            },
                        ]
                    }]).nodes(nodeLocations);
                layout.start(); // first layout
                return layout;
            }

            var Title = Backbone.View.extend({
                initialize: function() {
                    this.render();
                    this.listenTo(this.model, "change", this.render);
                },
                render: function() {
                  var ids = model.get("interactions").models[0].get("identifiers"),
                  title;
                  ids.map(function(id){
                    //this might be a little fragile. Will they all have Intact IDs? haha.
                    if (id.db === "intact") {
                      title = id.id;
                    }
                  });
                    this.$el.html(title);
                }
              });

            new ComplexView({
                model: model,
                el: document.getElementById('mi-sbgn')
            });
            new Title({
                model:model,
                el: document.getElementById('complextitle')
            });
        });
    });
});
<|endoftext|>


<commit_before>var AudioRecorder = new Class({
	Implements:Events,
	initialize:function(input, cfg){
		var me=this;
		me._initialize(input,cfg);

	},
	_initialize:function(input, cfg){
		var me=this;

		me.config = Object.append({

		},cfg);

		var bufferLen = me.config.bufferLen || 4096;
		var inputChannels = me.config.numInputChannels || 2;
		var outputChannels = me.config.numInputChannels || 2;

		var context = input.context;
		var script=null;
		if(!context.createScriptProcessor){
			script = context.createJavaScriptNode(bufferLen, inputChannels, outputChannels);
		}else{
			script = context.createScriptProcessor(bufferLen, inputChannels, outputChannels);
		}

		me.recLength = 0,
		me.recBuffersL = [],
		me.recBuffersR = [],
		me.sampleRate=context.sampleRate;

		me.recording = false,
		me.loopback=false;
		me.loopbackGain=1.0;

		script.onaudioprocess = function(e){
			if (!me.recording) return;

			var data=[
				         new Float32Array(e.inputBuffer.getChannelData(0)), //put channel data in new array or it will be a reference to the input buffers.
				         new Float32Array(e.inputBuffer.getChannelData(1))
				      ];

			me._store(data);

		};

		var l=context.createBiquadFilter();
		l.type = BiquadFilterNode.LOWPASS;
		l.frequency.value = 3400;

		var h=context.createBiquadFilter();
		h.type = BiquadFilterNode.HIGHPASS;
		h.frequency.value = 300;

		input.connect(l);
		l.connect(h);
		h.connect(script);
		//node.connect(context.destination); //this does not get data through to destination
		var g=context.createGain(); //firefox and chrome
		g.gain.value=me.loopback?me.loopbackGain:0.0;

		me.loopbackGainNode=g; //node to control volume to speaker if looback.
		me.node=h; //node to give to caller if they want to process.

		h.connect(g);
		script.connect(context.destination);

		g.connect(context.destination);

		window.AudioNodes=[input, l, h, script, g, context.destination];

		me.fireEvent('onCreatedRecorder');
		//source.connect(context.destination);   // if the script node is not connected to an output the "onaudioprocess" event is not triggered in chrome.
	},

	record:function(){
		var me=this;
		me._clear();
		me.recording = true;
		me.fireEvent('onRecordStart');
	},

	stop:function(){
		var me=this;
		me.recording = false;
		me.fireEvent('onRecordStop');
	},
	clear:function(){
		var me=this;
		me._clear();
		me.fireEvent('onRecordClear');
	},

	enableLoopback:function(){
		var me=this;
		me.loopback=true;
		me.loopbackGainNode.gain.value=me.loopbackGain;
	},
	disableLoopback:function(){
		var me=this;
		me.loopback=false;
		me.loopbackGainNode.gain.value=0.0;
	},
	setLoopbackGain:function(value){
		var me=this;
		me.loopbackGain=Math.max(0.0, Math.min(1.0,value));
		if(me.loopback){
			me.loopbackGainNode.gain.value=me.loopbackGain;
		}
	},
	getAudioNode:function(){
		var me=this;
		return me.node;
	},
	getAudioStream:function(){
		var me=this;
		var output=me.node.context.createMediaStreamDestination();
		me.node.connect(output);
		return output.stream;
	},

	_store:function(data){
		var me=this;
		me.recBuffersL.push(data[0]); //left
		me.recBuffersR.push(data[1]); //right
		me.recLength += data[0].length;
	},

	getAudioBlob:function(options){
		var me=this;
		var bufferL = me._mergeBuffers(me.recBuffersL, me.recLength);
		var bufferR = me._mergeBuffers(me.recBuffersR, me.recLength);

		config=Object.append({type: 'audio/wav'}, options);

		var dataview = me._encodeWAV(bufferL, bufferR, config);
		var audioBlob = new Blob([dataview],{type: config.type});

		return audioBlob;
	},

	getMonoAudioBlob:function(options){
		var me=this;
		var bufferL = me._mergeBuffers(me.recBuffersL, me.recLength);

		config=Object.append({type: 'audio/wav', mono:true}, options);

		var dataview = me._encodeWAV(bufferL, null, config);
		var audioBlob = new Blob([dataview], { type: config.type });

		return audioBlob;
	},

	_clear:function(){
		var me=this;
		me.recLength = 0;
		me.recBuffersL = [];
		me.recBuffersR = [];
	},

	_mergeBuffers:function(recBuffers, recLength){
		var result = new Float32Array(recLength);
		var offset = 0;
		for (var i = 0; i < recBuffers.length; i++){
			result.set(recBuffers[i], offset);
			offset += recBuffers[i].length;
		}
		return result;
	},

	_interleave:function(inputL, inputR){
		var length = inputL.length + inputR.length;
		var result = new Float32Array(length);

		var index = 0;
		var inputIndex = 0;

		while (index < length){
			result[index++] = inputL[inputIndex];
			result[index++] = inputR[inputIndex];
			inputIndex++;
		}
		return result;
	},

	/**
	 * this does not affect the internal sample buffers. it takes (interleaved) samples array and reduces
	 * it to match the new lower sample rate. by averaging consecutive samples within calculated (rounded) step offsets.
	 *
	 * there seems to be an issue when resampling mono data
	 *
	 * @param samples
	 * @param newSampleRate
	 * @returns Float32Array
	 */
	_resampleBuffer:function(samples, newSampleRate) {
		var me=this;
		var oldSampleRate=me.sampleRate;

	    if (newSampleRate == oldSampleRate) {
	        return samples;
	    }
	    if (newSampleRate > oldSampleRate) {
	        throw "sample rate must be lower than original sample rate";
	    }
	    var sampleRateRatio = oldSampleRate / newSampleRate;
	    var newLength = Math.round(samples.length / sampleRateRatio);
	    var result = new Float32Array(newLength);
	    var offsetResult = 0;
	    var offsetBuffer = 0;
	    while (offsetResult < result.length) {
	        var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
	        var accum = 0, count = 0;
	        for (var i = offsetBuffer; i < nextOffsetBuffer && i < samples.length; i++) {
	            accum += samples[i];
	            count++;
	        }
	        result[offsetResult] = accum / count;
	        offsetResult++;
	        offsetBuffer = nextOffsetBuffer;
	    }
	    return result;
	},

	_encodeWAV:function(left, right, options){
		var me=this;

		var config=Object.append({
			mono:false,
			sampleRate:me.sampleRate
		},options);

		var samples=left;
		if(config.mono){
			if(!samples){
				samples=right;
			}

		}else{
			samples = me._interleave(left, right);
		}

		if(config.sampleRate<me.sampleRate){

			samples=me._resampleBuffer(samples, config.sampleRate);

		}else{
			config.sampleRate=me.sampleRate; //can't upsample
		}

		var buffer = new ArrayBuffer(44 + samples.length * 2);
		var view = new DataView(buffer);

		var _text=function(data, start, string){
			for(var i=0;i<string.length;i++){
				data.setUint8(start+i, string.charCodeAt(i));
			}
		};

		/* RIFF identifier */
		_text(view, 0, 'RIFF');
		/* file length */
		view.setUint32(4, 32 + samples.length * 2, true);
		/* RIFF type */
		_text(view, 8, 'WAVE');
		/* format chunk identifier */
		_text(view, 12, 'fmt ');
		/* format chunk length */
		view.setUint32(16, 16, true);
		/* sample format (raw) */
		view.setUint16(20, 1, true);
		/* channel count */
		view.setUint16(22, config.mono?1:2, true);
		/* sample rate */
		view.setUint32(24, config.sampleRate, true);
		/* byte rate (sample rate * block align) */
		view.setUint32(28, config.sampleRate * 4, true);
		/* block align (channel count * bytes per sample) */
		view.setUint16(32, 4, true);
		/* bits per sample */
		view.setUint16(34, 16, true);
		/* data chunk identifier */
		_text(view, 36, 'data');
		/* data chunk length */
		view.setUint32(40, samples.length * 2, true);

		var offset=44;
		for (var i = 0; i < samples.length; i++, offset+=2){
			var s = Math.max(-1, Math.min(1, samples[i]));
			view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
		}

		return view;
	}

});

MicrophoneRecorder=new Class({
	Extends:AudioRecorder,
	initialize:function(options){
		var me=this;
		me.config={};

		navigator.getUserMedia  = navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;
		window.AudioContext = window.AudioContext||window.webkitAudioContext;

		var context = new AudioContext();
		me.fireEvent('onMicrophoneRequestStart');
		navigator.getUserMedia({audio: true}, function(stream){

			var microphone = context.createMediaStreamSource(stream);
			me.fireEvent('onMicrophoneRequestAccepted');
			me._initialize(microphone, options);
			//window.audiorecorder=recorder;
		}, function(e){
			me.fireEvent('onMicrophoneRequestFailed');
			JSConsoleError(['AudioContext Error', e]);

		});

	}

});<commit_msg>mono wave header fixes<commit_after>var AudioRecorder = new Class({
	Implements:Events,
	initialize:function(input, cfg){
		var me=this;
		me._initialize(input,cfg);

	},
	_initialize:function(input, cfg){
		var me=this;

		me.config = Object.append({

		},cfg);

		var bufferLen = me.config.bufferLen || 4096;
		var inputChannels = me.config.numInputChannels || 2;
		var outputChannels = me.config.numInputChannels || 2;

		var context = input.context;
		var script=null;
		if(!context.createScriptProcessor){
			script = context.createJavaScriptNode(bufferLen, inputChannels, outputChannels);
		}else{
			script = context.createScriptProcessor(bufferLen, inputChannels, outputChannels);
		}

		me.recLength = 0,
		me.recBuffersL = [],
		me.recBuffersR = [],
		me.sampleRate=context.sampleRate;

		me.recording = false,
		me.loopback=false;
		me.loopbackGain=1.0;

		script.onaudioprocess = function(e){
			if (!me.recording) return;

			var data=[
				         new Float32Array(e.inputBuffer.getChannelData(0)), //put channel data in new array or it will be a reference to the input buffers.
				         new Float32Array(e.inputBuffer.getChannelData(1))
				      ];

			me._store(data);

		};

		var l=context.createBiquadFilter();
		l.type = BiquadFilterNode.LOWPASS;
		l.frequency.value = 3400;

		var h=context.createBiquadFilter();
		h.type = BiquadFilterNode.HIGHPASS;
		h.frequency.value = 300;

		input.connect(l);
		l.connect(h);
		h.connect(script);
		//node.connect(context.destination); //this does not get data through to destination
		var g=context.createGain(); //firefox and chrome
		g.gain.value=me.loopback?me.loopbackGain:0.0;

		me.loopbackGainNode=g; //node to control volume to speaker if looback.
		me.node=h; //node to give to caller if they want to process.

		h.connect(g);
		script.connect(context.destination);

		g.connect(context.destination);

		window.AudioNodes=[input, l, h, script, g, context.destination];

		me.fireEvent('onCreatedRecorder');
		//source.connect(context.destination);   // if the script node is not connected to an output the "onaudioprocess" event is not triggered in chrome.
	},

	record:function(){
		var me=this;
		me._clear();
		me.recording = true;
		me.fireEvent('onRecordStart');
	},

	stop:function(){
		var me=this;
		me.recording = false;
		me.fireEvent('onRecordStop');
	},
	clear:function(){
		var me=this;
		me._clear();
		me.fireEvent('onRecordClear');
	},

	enableLoopback:function(){
		var me=this;
		me.loopback=true;
		me.loopbackGainNode.gain.value=me.loopbackGain;
	},
	disableLoopback:function(){
		var me=this;
		me.loopback=false;
		me.loopbackGainNode.gain.value=0.0;
	},
	setLoopbackGain:function(value){
		var me=this;
		me.loopbackGain=Math.max(0.0, Math.min(1.0,value));
		if(me.loopback){
			me.loopbackGainNode.gain.value=me.loopbackGain;
		}
	},
	getAudioNode:function(){
		var me=this;
		return me.node;
	},
	getAudioStream:function(){
		var me=this;
		var output=me.node.context.createMediaStreamDestination();
		me.node.connect(output);
		return output.stream;
	},

	_store:function(data){
		var me=this;
		me.recBuffersL.push(data[0]); //left
		me.recBuffersR.push(data[1]); //right
		me.recLength += data[0].length;
	},

	getAudioBlob:function(options){
		var me=this;
		var bufferL = me._mergeBuffers(me.recBuffersL, me.recLength);
		var bufferR = me._mergeBuffers(me.recBuffersR, me.recLength);

		config=Object.append({type: 'audio/wav'}, options);

		var dataview = me._encodeWAV(bufferL, bufferR, config);
		var audioBlob = new Blob([dataview],{type: config.type});

		return audioBlob;
	},

	getMonoAudioBlob:function(options){
		var me=this;
		var bufferL = me._mergeBuffers(me.recBuffersL, me.recLength);

		config=Object.append({type: 'audio/wav', mono:true}, options);

		var dataview = me._encodeWAV(bufferL, null, config);
		var audioBlob = new Blob([dataview], { type: config.type });

		return audioBlob;
	},

	_clear:function(){
		var me=this;
		me.recLength = 0;
		me.recBuffersL = [];
		me.recBuffersR = [];
	},

	_mergeBuffers:function(recBuffers, recLength){
		var result = new Float32Array(recLength);
		var offset = 0;
		for (var i = 0; i < recBuffers.length; i++){
			result.set(recBuffers[i], offset);
			offset += recBuffers[i].length;
		}
		return result;
	},

	_interleave:function(inputL, inputR){
		var length = inputL.length + inputR.length;
		var result = new Float32Array(length);

		var index = 0;
		var inputIndex = 0;

		while (index < length){
			result[index++] = inputL[inputIndex];
			result[index++] = inputR[inputIndex];
			inputIndex++;
		}
		return result;
	},

	/**
	 * this does not affect the internal sample buffers. it takes (interleaved) samples array and reduces
	 * it to match the new lower sample rate. by averaging consecutive samples within calculated (rounded) step offsets.
	 *
	 * there seems to be an issue when resampling mono data
	 *
	 * @param samples
	 * @param newSampleRate
	 * @returns Float32Array
	 */
	_resampleBuffer:function(samples, newSampleRate) {
		var me=this;
		var oldSampleRate=me.sampleRate;

	    if (newSampleRate == oldSampleRate) {
	        return samples;
	    }
	    if (newSampleRate > oldSampleRate) {
	        throw "sample rate must be lower than original sample rate";
	    }
	    var sampleRateRatio = oldSampleRate / newSampleRate;
	    var newLength = Math.round(samples.length / sampleRateRatio);
	    var result = new Float32Array(newLength);
	    var offsetResult = 0;
	    var offsetBuffer = 0;
	    while (offsetResult < result.length) {
	        var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
	        var accum = 0, count = 0;
	        for (var i = offsetBuffer; i < nextOffsetBuffer && i < samples.length; i++) {
	            accum += samples[i];
	            count++;
	        }
	        result[offsetResult] = accum / count;
	        offsetResult++;
	        offsetBuffer = nextOffsetBuffer;
	    }
	    return result;
	},

	_encodeWAV:function(left, right, options){
		var me=this;

		var config=Object.append({
			mono:false,
			sampleRate:me.sampleRate
		},options);

		var samples=left;
		if(config.mono){
			if(!samples){
				samples=right;
			}

		}else{
			samples = me._interleave(left, right);
		}

		if(config.sampleRate<me.sampleRate){

			samples=me._resampleBuffer(samples, config.sampleRate);

		}else{
			config.sampleRate=me.sampleRate; //can't upsample
		}

		var ch=config.mono?2:1;
		var bitps=16;
		var byteps=(bitps/8)*ch;

		var buffer = new ArrayBuffer(44 + samples.length * ch);
		var view = new DataView(buffer);

		var _text=function(data, start, string){
			for(var i=0;i<string.length;i++){
				data.setUint8(start+i, string.charCodeAt(i));
			}
		};

		/* RIFF identifier */
		_text(view, 0, 'RIFF');
		/* file length */
		view.setUint32(4, 32 + samples.length * ch, true);
		/* RIFF type */
		_text(view, 8, 'WAVE');
		/* format chunk identifier */
		_text(view, 12, 'fmt ');
		/* format chunk length */
		view.setUint32(16, 16, true);
		/* sample format (raw) */
		view.setUint16(20, 1, true);
		/* channel count */
		view.setUint16(22, config.mono?1:2, true);
		/* sample rate */
		view.setUint32(24, config.sampleRate, true);
		/* byte rate (sample rate * block align) */
		view.setUint32(28, config.sampleRate * byteps, true);
		/* block align (channel count * bytes per sample) */
		view.setUint16(32, byteps, true);
		/* bits per sample */
		view.setUint16(34, 16, true);
		/* data chunk identifier */
		_text(view, 36, 'data');
		/* data chunk length */
		view.setUint32(40, samples.length * ch, true);

		var offset=44;
		for (var i = 0; i < samples.length; i++, offset+=2){
			var s = Math.max(-1, Math.min(1, samples[i]));
			view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
		}

		return view;
	}

});

MicrophoneRecorder=new Class({
	Extends:AudioRecorder,
	initialize:function(options){
		var me=this;
		me.config={};

		navigator.getUserMedia  = navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;
		window.AudioContext = window.AudioContext||window.webkitAudioContext;

		var context = new AudioContext();
		me.fireEvent('onMicrophoneRequestStart');
		navigator.getUserMedia({audio: true}, function(stream){

			var microphone = context.createMediaStreamSource(stream);
			me.fireEvent('onMicrophoneRequestAccepted');
			me._initialize(microphone, options);
			//window.audiorecorder=recorder;
		}, function(e){
			me.fireEvent('onMicrophoneRequestFailed');
			JSConsoleError(['AudioContext Error', e]);

		});

	}

});<|endoftext|>


<commit_before>// options
const storageGet = () => new Promise((resolve) => chrome.storage.local.get(resolve));
let options = {
    'player': 'html5', // original swf iframe bilih5 html5 html5hd html5ld
    'rel_search': 'with', // off without with
    'trackingBlock': true,
    'scrollToPlayer': true,
    'api': 'offical',
    'opacity': 1,
    'prop': false,
    'scale': 1,
    'volume': 0.5,
    'replace': true,
};
const refreshOptions = () => storageGet().then((obj) => Object.assign(options, obj)).then(() => chrome.storage.local.set(options));
refreshOptions();

// static data
const trackingUrls = [
    '*://data.bilibilijj.com/free/*.txt*',
    '*://tajs.qq.com/stats*',
    '*://*.secureweb24.net/*',
    '*://*.cnzz.com/stat.php*',
    '*://*.hdslb.com/images/base/loading.gif',
    '*://*.hdslb.com/message/img/infocenterIcon-*.png',
    '*://*.hdslb.com/images/v3images/img_loading.png',
    '*://*.hdslb.com/images/v2images/topicons.png',
    '*://api.bilibili.com/crossdomain.xml*',
    '*://*.hdslb.com/crossdomain.xml*',
    '*://interface.bilibili.com/serverdate.js*',
    '*://*.hdslb.com/images/base/app-box.png',
    '*://api.bilibili.com/x/elec/show*',
    '*://static.hdslb.com/images/elecrank-empty.png',
    '*://live-feed.bilibili.com/ajax/feed/count*',
    '*://comment.bilibili.com/playtag,*',
    '*://interface.bilibili.com/msg.xml*',
    '*://data.bilibili.com/crossdomain.xml*',
    '*://*.bilibili.com/index/index-icon.json*',
    '*://*.bilibili.com/widget/getSearchDefaultWords*',
    '*://*.bilibili.com/v/web/*',
    '*://*.hdslb.com/images/v2images/footpic.png',
    '*://*.hdslb.com/ad-images/wx.jpg',
    '*://*.hdslb.com/player/images/player_error.png',
    '*://data.bilibili.com/a/access.js*',
    '*://api.bilibili.com/plaza/banner*',
    '*://*.hdslb.com/vip/dist/js/vipPlugin.js*',
    '*://*.hdslb.com/ad-images/wx.gif*',
    '*://*.hdslb.com/bfs/static/anime/js/bangumi.play.js*',
    '*://*.hdslb.com/bfs/static/anime/js/sponsor.js*',
    '*://data.bilibili.com/a/bangumi.js*',
    '*://*.up.lxdns.com/report.php*',
    '*://*.hdslb.com/js/video.min.js',
    '*://*.hdslb.com/js/bfd.js',
    '*://*.bilibili.com/rec.js',
    '*://*.hdslb.com/js/video.min.js*',
    '*://*.hdslb.com/js/bfd.js*',
    '*://data.bilibili.com/rec.js*',
    '*://data.bilibili.com/v/flashplay/h5_player_op*',
];
const videoPlaybackHosts = ['*://*.hdslb.com/*', '*://*.acgvideo.com/*', '*://*/*.acgvideo.com/*', '*://*.biliplus.com/BPplayurl.php*'];

// message listener
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    switch (request.command) {
    case 'injectCSS':
        chrome.tabs.insertCSS(sender.tab.id, {file: 'bilibili-helper.css'});
        chrome.tabs.insertCSS(sender.tab.id, {file: 'colpick.css'});
        chrome.tabs.insertCSS(sender.tab.id, {file: 'ABPlayer.css'}, sendResponse);
        return true;
    case 'refreshOptions':
        refreshOptions();
        return true;
    case 'requestForDownload':
        chrome.downloads.download({
            saveAs: true,
            url: request.url,
            filename: 'Bilibili/' + request.filename,
            conflictAction: 'prompt',
        });
        return true;
    default:
        sendResponse({
            result: 'unknown',
        });
        return false;
    }
});

// webRequest listener
chrome.webRequest.onBeforeRequest.addListener(function(details) {
    chrome.tabs.sendMessage(details.tabId, {
        command: 'error',
    });
}, {
    urls: ['http://comment.bilibili.com/1272.xml'],
});

chrome.webRequest.onBeforeRequest.addListener(function() {
    if (options.trackingBlock) return {
        cancel: true,
    };
}, {
    urls: trackingUrls,
}, ['blocking']);

function receivedHeaderModifier(details) {
    let hasCORS = false;
    details.responseHeaders.forEach(function(v) {
        if (v.name.toLowerCase() === 'access-control-allow-origin') {
            hasCORS = true;
            v.value = '*';
        }
    });
    if (!hasCORS) {
        details.responseHeaders.push({
            name: 'Access-Control-Allow-Origin',
            value: '*',
        });
    }
    return {
        responseHeaders: details.responseHeaders,
    };
}

chrome.webRequest.onHeadersReceived.addListener(receivedHeaderModifier, {
    urls: videoPlaybackHosts,
}, ['responseHeaders', 'blocking']);

chrome.webRequest.onHeadersReceived.addListener(function(details) {
    let headers = details.responseHeaders;
    if (details.statusLine.indexOf('HTTP/1.1 302') === 0 && options.replace) {
        for (let i = 0; i < headers.length; i++) {
            if (headers[i].name.toLowerCase() === 'location') {
                headers.splice(i, 1, {
                    name: 'Set-Cookie',
                    value: 'redirectUrl=' + encodeURIComponent(headers[i].value),
                });
            }
        }
    }
    return {
        responseHeaders: headers,
    };
}, {
    urls: ['http://www.bilibili.com/video/av*', 'http://bangumi.bilibili.com/anime/v/*'],
}, ['responseHeaders', 'blocking']);

// video api replacing
// https://gist.github.com/myfreeer/d805427aba19f5b34480edafeb94e20d
class UrlParams {
    constructor(str) {
        if (str) this.parse(str);
    }
    toString(filterArray) {
        filterArray = filterArray && filterArray.map ? filterArray.filter((e) => this.hasOwnProperty(e)) : Object.keys(this);
        return filterArray.map((e) => this[e] === false || this[e] === 0 || this[e] ? e + '=' + encodeURIComponent(this[e]) : e).join('&');
    }
    parse(str) {
        if (!str) return false;
        if (str.charAt(0) === '?') str = str.substr(1);
        str.split('&').map((e) => e.split('=')).map((e) => this[e[0]] = e[1] && decodeURIComponent(e[1]));
        return this;
    }
}
chrome.webRequest.onBeforeRequest.addListener(function(details) {
    if (options.api.match('http')) {
        const search = (new URL(details.url)).search;
        let urlParams = new UrlParams(search);
        if (details.url.match('bangumi')) urlParams.cid += '|bangumi';
        return {
            redirectUrl: `${options.api}/BPplayurl.php?${urlParams.toString(['cid', 'otype', 'type', 'quality'])}`,
        };
    } else return {};
}, {
    urls: ['*://bangumi.bilibili.com/player/web_api/playurl*', '*://interface.bilibili.com/playurl*'],
}, ['blocking']);
<commit_msg>better cors bypassing<commit_after>// options
const storageGet = () => new Promise((resolve) => chrome.storage.local.get(resolve));
let options = {
    'player': 'html5', // original swf iframe bilih5 html5 html5hd html5ld
    'rel_search': 'with', // off without with
    'trackingBlock': true,
    'scrollToPlayer': true,
    'api': 'offical',
    'opacity': 1,
    'prop': false,
    'scale': 1,
    'volume': 0.5,
    'replace': true,
};
const refreshOptions = () => storageGet().then((obj) => Object.assign(options, obj)).then(() => chrome.storage.local.set(options));
refreshOptions();

// static data
const trackingUrls = [
    '*://data.bilibilijj.com/free/*.txt*',
    '*://tajs.qq.com/stats*',
    '*://*.secureweb24.net/*',
    '*://*.cnzz.com/stat.php*',
    '*://*.hdslb.com/images/base/loading.gif',
    '*://*.hdslb.com/message/img/infocenterIcon-*.png',
    '*://*.hdslb.com/images/v3images/img_loading.png',
    '*://*.hdslb.com/images/v2images/topicons.png',
    '*://api.bilibili.com/crossdomain.xml*',
    '*://*.hdslb.com/crossdomain.xml*',
    '*://interface.bilibili.com/serverdate.js*',
    '*://*.hdslb.com/images/base/app-box.png',
    '*://api.bilibili.com/x/elec/show*',
    '*://static.hdslb.com/images/elecrank-empty.png',
    '*://live-feed.bilibili.com/ajax/feed/count*',
    '*://comment.bilibili.com/playtag,*',
    '*://interface.bilibili.com/msg.xml*',
    '*://data.bilibili.com/crossdomain.xml*',
    '*://*.bilibili.com/index/index-icon.json*',
    '*://*.bilibili.com/widget/getSearchDefaultWords*',
    '*://*.bilibili.com/v/web/*',
    '*://*.hdslb.com/images/v2images/footpic.png',
    '*://*.hdslb.com/ad-images/wx.jpg',
    '*://*.hdslb.com/player/images/player_error.png',
    '*://data.bilibili.com/a/access.js*',
    '*://api.bilibili.com/plaza/banner*',
    '*://*.hdslb.com/vip/dist/js/vipPlugin.js*',
    '*://*.hdslb.com/ad-images/wx.gif*',
    '*://*.hdslb.com/bfs/static/anime/js/bangumi.play.js*',
    '*://*.hdslb.com/bfs/static/anime/js/sponsor.js*',
    '*://data.bilibili.com/a/bangumi.js*',
    '*://*.up.lxdns.com/report.php*',
    '*://*.hdslb.com/js/video.min.js',
    '*://*.hdslb.com/js/bfd.js',
    '*://*.bilibili.com/rec.js',
    '*://*.hdslb.com/js/video.min.js*',
    '*://*.hdslb.com/js/bfd.js*',
    '*://data.bilibili.com/rec.js*',
    '*://data.bilibili.com/v/flashplay/h5_player_op*',
];
const videoPlaybackHosts = ['*://*.hdslb.com/*', '*://api.bilibili.com/*', '*://interface.bilibili.com/*', '*://*.acgvideo.com/*', '*://*/*.acgvideo.com/*', '*://*.biliplus.com/BPplayurl.php*'];

// message listener
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    switch (request.command) {
    case 'injectCSS':
        chrome.tabs.insertCSS(sender.tab.id, {file: 'bilibili-helper.css'});
        chrome.tabs.insertCSS(sender.tab.id, {file: 'colpick.css'});
        chrome.tabs.insertCSS(sender.tab.id, {file: 'ABPlayer.css'}, sendResponse);
        return true;
    case 'refreshOptions':
        refreshOptions();
        return true;
    case 'requestForDownload':
        chrome.downloads.download({
            saveAs: true,
            url: request.url,
            filename: 'Bilibili/' + request.filename,
            conflictAction: 'prompt',
        });
        return true;
    default:
        sendResponse({
            result: 'unknown',
        });
        return false;
    }
});
// tabs listener
let tabs = [];
chrome.tabs.query({}, (info) => (tabs = info));
chrome.tabs.onUpdated.addListener(() => chrome.tabs.query({}, (info) => (tabs = info)));

// webRequest listener
chrome.webRequest.onBeforeRequest.addListener(function(details) {
    chrome.tabs.sendMessage(details.tabId, {
        command: 'error',
    });
}, {
    urls: ['http://comment.bilibili.com/1272.xml'],
});

chrome.webRequest.onBeforeRequest.addListener(function() {
    if (options.trackingBlock) return {
        cancel: true,
    };
}, {
    urls: trackingUrls,
}, ['blocking']);

function receivedHeaderModifier(details) {
    let hasCORS, hasCredentials;
    let cors = '*';
    for (let i of tabs) if (i.id === details.tabId) cors = (new URL(i.url)).protocol + '//' + (new URL(i.url)).hostname;
    details.responseHeaders.forEach(function(v) {
        if (v.name.toLowerCase() === 'access-control-allow-origin') {
            hasCORS = true;
            v.value = cors;
        } else if (v.name.toLowerCase() === 'access-control-allow-credentials') {
            v.value = 'true';
            hasCredentials = true;
        }
    });
    if (!hasCORS) {
        details.responseHeaders.push({
            name: 'Access-Control-Allow-Origin',
            value: cors,
        });
    }
    if (!hasCredentials) details.responseHeaders.push({
        name: 'Access-Control-Allow-Credentials',
        value: 'true',
    });
    return {
        responseHeaders: details.responseHeaders,
    };
}

chrome.webRequest.onHeadersReceived.addListener(receivedHeaderModifier, {
    urls: videoPlaybackHosts,
}, ['responseHeaders', 'blocking']);

chrome.webRequest.onHeadersReceived.addListener(function(details) {
    let headers = details.responseHeaders;
    if (details.statusLine.indexOf('HTTP/1.1 302') === 0 && options.replace) {
        for (let i = 0; i < headers.length; i++) {
            if (headers[i].name.toLowerCase() === 'location') {
                headers.splice(i, 1, {
                    name: 'Set-Cookie',
                    value: 'redirectUrl=' + encodeURIComponent(headers[i].value),
                });
            }
        }
    }
    return {
        responseHeaders: headers,
    };
}, {
    urls: ['http://www.bilibili.com/video/av*', 'http://bangumi.bilibili.com/anime/v/*'],
}, ['responseHeaders', 'blocking']);

// video api replacing
// https://gist.github.com/myfreeer/d805427aba19f5b34480edafeb94e20d
class UrlParams {
    constructor(str) {
        if (str) this.parse(str);
    }
    toString(filterArray) {
        filterArray = filterArray && filterArray.map ? filterArray.filter((e) => this.hasOwnProperty(e)) : Object.keys(this);
        return filterArray.map((e) => this[e] === false || this[e] === 0 || this[e] ? e + '=' + encodeURIComponent(this[e]) : e).join('&');
    }
    parse(str) {
        if (!str) return false;
        if (str.charAt(0) === '?') str = str.substr(1);
        str.split('&').map((e) => e.split('=')).map((e) => this[e[0]] = e[1] && decodeURIComponent(e[1]));
        return this;
    }
}
chrome.webRequest.onBeforeRequest.addListener(function(details) {
    if (options.api.match('http')) {
        const search = (new URL(details.url)).search;
        let urlParams = new UrlParams(search);
        if (details.url.match('bangumi')) urlParams.cid += '|bangumi';
        return {
            redirectUrl: `${options.api}/BPplayurl.php?${urlParams.toString(['cid', 'otype', 'type', 'quality'])}`,
        };
    } else return {};
}, {
    urls: ['*://bangumi.bilibili.com/player/web_api/playurl*', '*://interface.bilibili.com/playurl*'],
}, ['blocking']);
<|endoftext|>


<commit_before>/* globals Testem */

export { default as QUnitAdapter, nonTestDoneCallback } from './adapter';
export { loadTests } from './test-loader';

import './qunit-configuration';

if (typeof Testem !== 'undefined') {
  Testem.hookIntoTestFramework();
}

import { run } from '@ember/runloop';
import { assign } from '@ember/polyfills';
import { resetOnerror, getTestMetadata } from '@ember/test-helpers';
import { loadTests } from './test-loader';
import Ember from 'ember';
import * as QUnit from 'qunit';
import QUnitAdapter from './adapter';
import {
  setupContext,
  teardownContext,
  setupRenderingContext,
  setupApplicationContext,
  validateErrorHandler,
} from '@ember/test-helpers';
import { installTestNotIsolatedHook } from './test-isolation-validation';

let waitForSettled = true;

export function setupTest(hooks, _options) {
  let options =
    _options === undefined
      ? { waitForSettled }
      : assign({ waitForSettled }, _options);

  hooks.beforeEach(function (assert) {
    let testMetadata = getTestMetadata(this);
    testMetadata.framework = 'qunit';

    return setupContext(this, options).then(() => {
      let originalPauseTest = this.pauseTest;
      this.pauseTest = function QUnit_pauseTest() {
        assert.timeout(-1); // prevent the test from timing out

        // This is a temporary work around for
        // https://github.com/emberjs/ember-qunit/issues/496 this clears the
        // timeout that would fail the test when it hits the global testTimeout
        // value.
        clearTimeout(QUnit.config.timeout);
        return originalPauseTest.call(this);
      };
    });
  });

  hooks.afterEach(function () {
    return teardownContext(this, options);
  });
}

export function setupRenderingTest(hooks, _options) {
  let options =
    _options === undefined
      ? { waitForSettled }
      : assign({ waitForSettled }, _options);

  setupTest(hooks, options);

  hooks.beforeEach(function () {
    return setupRenderingContext(this);
  });
}

export function setupApplicationTest(hooks, _options) {
  let options =
    _options === undefined
      ? { waitForSettled }
      : assign({ waitForSettled }, _options);

  setupTest(hooks, options);

  hooks.beforeEach(function () {
    return setupApplicationContext(this);
  });
}

/**
   Uses current URL configuration to setup the test container.

   * If `?nocontainer` is set, the test container will be hidden.
   * If `?devmode` or `?fullscreencontainer` is set, the test container will be
     made full screen.

   @method setupTestContainer
 */
export function setupTestContainer() {
  let testContainer = document.getElementById('ember-testing-container');
  if (!testContainer) {
    return;
  }

  let params = QUnit.urlParams;
  let containerVisibility = params.nocontainer ? 'hidden' : 'visible';

  if (params.devmode || params.fullscreencontainer) {
    testContainer.className = ' full-screen';
  }

  testContainer.style.visibility = containerVisibility;
}

/**
   Instruct QUnit to start the tests.
   @method startTests
 */
export function startTests() {
  QUnit.start();
}

/**
   Sets up the `Ember.Test` adapter for usage with QUnit 2.x.

   @method setupTestAdapter
 */
export function setupTestAdapter() {
  Ember.Test.adapter = QUnitAdapter.create();
}

/**
  Ensures that `Ember.testing` is set to `true` before each test begins
  (including `before` / `beforeEach`), and reset to `false` after each test is
  completed. This is done via `QUnit.testStart` and `QUnit.testDone`.

 */
export function setupEmberTesting() {
  QUnit.testStart(() => {
    Ember.testing = true;
  });

  QUnit.testDone(() => {
    Ember.testing = false;
  });
}

/**
  Ensures that `Ember.onerror` (if present) is properly configured to re-throw
  errors that occur while `Ember.testing` is `true`.
*/
export function setupEmberOnerrorValidation() {
  QUnit.module('ember-qunit: Ember.onerror validation', function () {
    QUnit.test('Ember.onerror is functioning properly', function (assert) {
      assert.expect(1);
      let result = validateErrorHandler();
      assert.ok(
        result.isValid,
        `Ember.onerror handler with invalid testing behavior detected. An Ember.onerror handler _must_ rethrow exceptions when \`Ember.testing\` is \`true\` or the test suite is unreliable. See https://git.io/vbine for more details.`
      );
    });
  });
}

export function setupResetOnerror() {
  QUnit.testDone(resetOnerror);
}

export function setupTestIsolationValidation(delay) {
  waitForSettled = false;
  run.backburner.DEBUG = true;
  QUnit.on('testStart', () => installTestNotIsolatedHook(delay));
}

/**
   @method start
   @param {Object} [options] Options to be used for enabling/disabling behaviors
   @param {Boolean} [options.loadTests] If `false` tests will not be loaded automatically.
   @param {Boolean} [options.setupTestContainer] If `false` the test container will not
   be setup based on `devmode`, `dockcontainer`, or `nocontainer` URL params.
   @param {Boolean} [options.startTests] If `false` tests will not be automatically started
   (you must run `QUnit.start()` to kick them off).
   @param {Boolean} [options.setupTestAdapter] If `false` the default Ember.Test adapter will
   not be updated.
   @param {Boolean} [options.setupEmberTesting] `false` opts out of the
   default behavior of setting `Ember.testing` to `true` before all tests and
   back to `false` after each test will.
   @param {Boolean} [options.setupEmberOnerrorValidation] If `false` validation
   of `Ember.onerror` will be disabled.
   @param {Boolean} [options.setupTestIsolationValidation] If `false` test isolation validation
   will be disabled.
   @param {Number} [options.testIsolationValidationDelay] When using
   setupTestIsolationValidation this number represents the maximum amount of
   time in milliseconds that is allowed _after_ the test is completed for all
   async to have been completed. The default value is 50.
 */
export function start(options = {}) {
  if (options.loadTests !== false) {
    loadTests();
  }

  if (options.setupTestContainer !== false) {
    setupTestContainer();
  }

  if (options.setupTestAdapter !== false) {
    setupTestAdapter();
  }

  if (options.setupEmberTesting !== false) {
    setupEmberTesting();
  }

  if (options.setupEmberOnerrorValidation !== false) {
    setupEmberOnerrorValidation();
  }

  if (
    typeof options.setupTestIsolationValidation !== 'undefined' &&
    options.setupTestIsolationValidation !== false
  ) {
    setupTestIsolationValidation(options.testIsolationValidationDelay);
  }

  if (options.startTests !== false) {
    startTests();
  }

  setupResetOnerror();
}
<commit_msg>Stop using deprecated Ember.assign<commit_after>/* globals Testem */

export { default as QUnitAdapter, nonTestDoneCallback } from './adapter';
export { loadTests } from './test-loader';

import './qunit-configuration';

if (typeof Testem !== 'undefined') {
  Testem.hookIntoTestFramework();
}

import { run } from '@ember/runloop';
import { resetOnerror, getTestMetadata } from '@ember/test-helpers';
import { loadTests } from './test-loader';
import Ember from 'ember';
import * as QUnit from 'qunit';
import QUnitAdapter from './adapter';
import {
  setupContext,
  teardownContext,
  setupRenderingContext,
  setupApplicationContext,
  validateErrorHandler,
} from '@ember/test-helpers';
import { installTestNotIsolatedHook } from './test-isolation-validation';

let waitForSettled = true;

export function setupTest(hooks, _options) {
  let options = { waitForSettled, ..._options };

  hooks.beforeEach(function (assert) {
    let testMetadata = getTestMetadata(this);
    testMetadata.framework = 'qunit';

    return setupContext(this, options).then(() => {
      let originalPauseTest = this.pauseTest;
      this.pauseTest = function QUnit_pauseTest() {
        assert.timeout(-1); // prevent the test from timing out

        // This is a temporary work around for
        // https://github.com/emberjs/ember-qunit/issues/496 this clears the
        // timeout that would fail the test when it hits the global testTimeout
        // value.
        clearTimeout(QUnit.config.timeout);
        return originalPauseTest.call(this);
      };
    });
  });

  hooks.afterEach(function () {
    return teardownContext(this, options);
  });
}

export function setupRenderingTest(hooks, _options) {
  let options =
    _options === undefined
      ? { waitForSettled }
      : assign({ waitForSettled }, _options);

  setupTest(hooks, options);

  hooks.beforeEach(function () {
    return setupRenderingContext(this);
  });
}

export function setupApplicationTest(hooks, _options) {
  let options =
    _options === undefined
      ? { waitForSettled }
      : assign({ waitForSettled }, _options);

  setupTest(hooks, options);

  hooks.beforeEach(function () {
    return setupApplicationContext(this);
  });
}

/**
   Uses current URL configuration to setup the test container.

   * If `?nocontainer` is set, the test container will be hidden.
   * If `?devmode` or `?fullscreencontainer` is set, the test container will be
     made full screen.

   @method setupTestContainer
 */
export function setupTestContainer() {
  let testContainer = document.getElementById('ember-testing-container');
  if (!testContainer) {
    return;
  }

  let params = QUnit.urlParams;
  let containerVisibility = params.nocontainer ? 'hidden' : 'visible';

  if (params.devmode || params.fullscreencontainer) {
    testContainer.className = ' full-screen';
  }

  testContainer.style.visibility = containerVisibility;
}

/**
   Instruct QUnit to start the tests.
   @method startTests
 */
export function startTests() {
  QUnit.start();
}

/**
   Sets up the `Ember.Test` adapter for usage with QUnit 2.x.

   @method setupTestAdapter
 */
export function setupTestAdapter() {
  Ember.Test.adapter = QUnitAdapter.create();
}

/**
  Ensures that `Ember.testing` is set to `true` before each test begins
  (including `before` / `beforeEach`), and reset to `false` after each test is
  completed. This is done via `QUnit.testStart` and `QUnit.testDone`.

 */
export function setupEmberTesting() {
  QUnit.testStart(() => {
    Ember.testing = true;
  });

  QUnit.testDone(() => {
    Ember.testing = false;
  });
}

/**
  Ensures that `Ember.onerror` (if present) is properly configured to re-throw
  errors that occur while `Ember.testing` is `true`.
*/
export function setupEmberOnerrorValidation() {
  QUnit.module('ember-qunit: Ember.onerror validation', function () {
    QUnit.test('Ember.onerror is functioning properly', function (assert) {
      assert.expect(1);
      let result = validateErrorHandler();
      assert.ok(
        result.isValid,
        `Ember.onerror handler with invalid testing behavior detected. An Ember.onerror handler _must_ rethrow exceptions when \`Ember.testing\` is \`true\` or the test suite is unreliable. See https://git.io/vbine for more details.`
      );
    });
  });
}

export function setupResetOnerror() {
  QUnit.testDone(resetOnerror);
}

export function setupTestIsolationValidation(delay) {
  waitForSettled = false;
  run.backburner.DEBUG = true;
  QUnit.on('testStart', () => installTestNotIsolatedHook(delay));
}

/**
   @method start
   @param {Object} [options] Options to be used for enabling/disabling behaviors
   @param {Boolean} [options.loadTests] If `false` tests will not be loaded automatically.
   @param {Boolean} [options.setupTestContainer] If `false` the test container will not
   be setup based on `devmode`, `dockcontainer`, or `nocontainer` URL params.
   @param {Boolean} [options.startTests] If `false` tests will not be automatically started
   (you must run `QUnit.start()` to kick them off).
   @param {Boolean} [options.setupTestAdapter] If `false` the default Ember.Test adapter will
   not be updated.
   @param {Boolean} [options.setupEmberTesting] `false` opts out of the
   default behavior of setting `Ember.testing` to `true` before all tests and
   back to `false` after each test will.
   @param {Boolean} [options.setupEmberOnerrorValidation] If `false` validation
   of `Ember.onerror` will be disabled.
   @param {Boolean} [options.setupTestIsolationValidation] If `false` test isolation validation
   will be disabled.
   @param {Number} [options.testIsolationValidationDelay] When using
   setupTestIsolationValidation this number represents the maximum amount of
   time in milliseconds that is allowed _after_ the test is completed for all
   async to have been completed. The default value is 50.
 */
export function start(options = {}) {
  if (options.loadTests !== false) {
    loadTests();
  }

  if (options.setupTestContainer !== false) {
    setupTestContainer();
  }

  if (options.setupTestAdapter !== false) {
    setupTestAdapter();
  }

  if (options.setupEmberTesting !== false) {
    setupEmberTesting();
  }

  if (options.setupEmberOnerrorValidation !== false) {
    setupEmberOnerrorValidation();
  }

  if (
    typeof options.setupTestIsolationValidation !== 'undefined' &&
    options.setupTestIsolationValidation !== false
  ) {
    setupTestIsolationValidation(options.testIsolationValidationDelay);
  }

  if (options.startTests !== false) {
    startTests();
  }

  setupResetOnerror();
}
<|endoftext|>


<commit_before>import { get, getWithDefault, set } from '@ember/object';
import { isEmpty, isPresent } from '@ember/utils';
import { getOwner } from '@ember/application';

import override from "./-override";

const bearerErrorCodes = [
  'invalid_token',
  'unknown_token',
  'token_disabled',
  'unknown_client',
  'client_disabled',
  'unknown_account',
  'account_disabled'
];

function applyDecorator (target, name, descriptor, options = {}) {
  const {
    scope,
    redirectParamName = 'redirect',
    accessTokenParamName = 'access_token'
  } = options;

  /**
   * Check that the current user is authenticated.
   *
   * @param transition
   * @private
   */
  target.prototype._checkSignedIn = function (transition) {
    let { to: { queryParams = {}}} = transition;
    const accessToken = queryParams[accessTokenParamName];

    if (isPresent (accessToken)) {
      // There is an access token in the query parameters. This takes precedence over the
      // status of the session.
      return this.session.openFrom (accessToken).catch (() => false);
    }
    else if (this.session.isSignedIn) {
      return Promise.resolve (true);
    }
    else {
      return Promise.resolve (false);
    }
  }

  // Make sure there is an actions object.
  target.prototype.actions = target.prototype.actions || {};

  // Define the error handling routine/action.
  target.prototype.actions.error = function (reason) {
    let errors = get (reason, 'errors');

    if (isEmpty (errors)) {
      return true;
    }

    for (let i = 0, len = errors.length; i < len; ++ i) {
      let error = errors[i];

      if (error.status === '403' && bearerErrorCodes.indexOf (error.code) !== -1) {
        // Redirect to sign in page, allowing the user to redirect back to the
        // original page. But, do not support the back button.
        let ENV = getOwner (this).resolveRegistration ('config:environment');
        let signInRoute = getWithDefault (ENV, 'gatekeeper.signInRoute', 'sign-in');

        // Display the error message.
        this.snackbar.show ({message: error.detail});

        // Force the user to sign out.
        this.session.signOut (true).then (() => {
          this.replaceWith (signInRoute);
        });

        return;
      }
    }

    return true;
  }

  override (target.prototype, 'beforeModel', function (transition) {
    return Promise.resolve (this._super.call (this, ...arguments))
      .then (() => this._checkSignedIn (transition))
      .then (authenticated => {
        if (authenticated) {
          let authorized = isEmpty (scope) || this.session.accessToken.supports (scope);
          let controller = this.controllerFor (this.routeName, true);

          if (isPresent (controller)) {
            set (controller, 'isAuthorized', authorized);
          }

          if (!authorized && isPresent (target.prototype.actions.unauthorized)) {
            transition.trigger ('unauthorized', transition);
          }
        }
        else {
          // The user is not signed into the application. Let's route the user to the
          // sign-in route for the application.

          let ENV = getOwner (this).resolveRegistration ('config:environment');
          let signInRoute = get (ENV, 'gatekeeper.signInRoute');

          if (isPresent (signInRoute)) {
            // Set the redirect to route so we can come back to this route when the
            // user has signed in.
            let options = { queryParams: { [redirectParamName]: transition.targetName } };
            this.replaceWith (signInRoute, options);
          }
        }
      });
  });
}

export default function authenticated (target, name, descriptor) {
  if (descriptor) {
    return applyDecorator (target, name, descriptor);
  }
  else {
    let options = target;

    return function (target, name, descriptor) {
      return applyDecorator (target, name, descriptor, options);
    }
  }
}
<commit_msg>fix: checking sign in from access token did not return true<commit_after>import { get, getWithDefault, set } from '@ember/object';
import { isEmpty, isPresent } from '@ember/utils';
import { getOwner } from '@ember/application';

import override from "./-override";

const bearerErrorCodes = [
  'invalid_token',
  'unknown_token',
  'token_disabled',
  'unknown_client',
  'client_disabled',
  'unknown_account',
  'account_disabled'
];

function applyDecorator (target, name, descriptor, options = {}) {
  const {
    scope,
    redirectParamName = 'redirect',
    accessTokenParamName = 'access_token'
  } = options;

  /**
   * Check that the current user is authenticated.
   *
   * @param transition
   * @private
   */
  target.prototype._checkSignedIn = function (transition) {
    let { to: { queryParams = {}}} = transition;
    const accessToken = queryParams[accessTokenParamName];

    if (isPresent (accessToken)) {
      // There is an access token in the query parameters. This takes precedence over the
      // status of the session.
      return this.session.openFrom (accessToken).then (() => true).catch (() => false);
    }
    else if (this.session.isSignedIn) {
      return Promise.resolve (true);
    }
    else {
      return Promise.resolve (false);
    }
  }

  // Make sure there is an actions object.
  target.prototype.actions = target.prototype.actions || {};

  // Define the error handling routine/action.
  target.prototype.actions.error = function (reason) {
    let errors = get (reason, 'errors');

    if (isEmpty (errors)) {
      return true;
    }

    for (let i = 0, len = errors.length; i < len; ++ i) {
      let error = errors[i];

      if (error.status === '403' && bearerErrorCodes.indexOf (error.code) !== -1) {
        // Redirect to sign in page, allowing the user to redirect back to the
        // original page. But, do not support the back button.
        let ENV = getOwner (this).resolveRegistration ('config:environment');
        let signInRoute = getWithDefault (ENV, 'gatekeeper.signInRoute', 'sign-in');

        // Display the error message.
        this.snackbar.show ({message: error.detail});

        // Force the user to sign out.
        this.session.signOut (true).then (() => {
          this.replaceWith (signInRoute);
        });

        return;
      }
    }

    return true;
  }

  override (target.prototype, 'beforeModel', function (transition) {
    return Promise.resolve (this._super.call (this, ...arguments))
      .then (() => this._checkSignedIn (transition))
      .then (authenticated => {
        if (authenticated) {
          let authorized = isEmpty (scope) || this.session.accessToken.supports (scope);
          let controller = this.controllerFor (this.routeName, true);

          if (isPresent (controller)) {
            set (controller, 'isAuthorized', authorized);
          }

          if (!authorized && isPresent (target.prototype.actions.unauthorized)) {
            transition.trigger ('unauthorized', transition);
          }
        }
        else {
          // The user is not signed into the application. Let's route the user to the
          // sign-in route for the application.

          let ENV = getOwner (this).resolveRegistration ('config:environment');
          let signInRoute = get (ENV, 'gatekeeper.signInRoute');

          if (isPresent (signInRoute)) {
            // Set the redirect to route so we can come back to this route when the
            // user has signed in.
            let options = { queryParams: { [redirectParamName]: transition.targetName } };
            this.replaceWith (signInRoute, options);
          }
        }
      });
  });
}

export default function authenticated (target, name, descriptor) {
  if (descriptor) {
    return applyDecorator (target, name, descriptor);
  }
  else {
    let options = target;

    return function (target, name, descriptor) {
      return applyDecorator (target, name, descriptor, options);
    }
  }
}
<|endoftext|>


<commit_before>var promise = require("deferred");
var Etcd = require("node-etcd");
var logger = require("winston");
var path = require("path");

function VccStore () {
	this.connected = false;
}

VccStore.prototype.connect = function (host, port) {
	var conn = "http://"+host+":"+port;
	logger.debug("kvstore is connecting to", conn);
	this.etcd = new Etcd(conn, {timeout: 5000});
	this.connected = true;
}

VccStore.prototype.set = function (key, value, ttl) {
	var deferred = promise();
	if (!this.connected) {
		deferred.reject("not connected");
	}
	// set the key optionally with ttl
	logger.debug("key set:", key, value, ttl);
	var options = {};
	if (ttl) {
		options.ttl = ttl;
	}
	// call the etcd function
	this.etcd.set(key, value, options, function (err, res) {
		if (err) {
			deferred.reject(err);
		} else {
			deferred.resolve();
		}
	});
	// return promise
	return deferred.promise();
}

VccStore.prototype.get = function (key, recursive) {
	var deferred = promise();
	var me = this;
	if (!this.connected) {
		deferred.reject("not connected");
	}
	logger.debug("key get:", key, recursive);
	var options = {};
	if (recursive) {
		options.recursive = true;
	}
	// call the etcd function
	this.etcd.get(key, function (err, res) {
		if (err) {
			deferred.reject(err);
		} else {
			// check the response
			if (res.node) {
				if (recursive) {
					if (res.node.nodes) {
						// convert a list of objects into a list
						// only show the base name of the full path
						var listresult = res.node.nodes.reduce(function (r, i) {
							r.push(path.basename(i.key));
							return r;
						}, []);
						deferred.resolve(listresult);
					} else {
						deferred.reject("recursive, expecting nodes but didn't get any");
					}
				} else {
					if (res.node.dir) {
						deferred.reject(res.node.key + " is a directory");
					} else if (res.node.value) {
						deferred.resolve(res.node.value);
					} else {
						deferred.reject("no value in response");
					}
				}
			} else {
				deferred.reject("no node in response");
			}
		}
	});
	// return promise
	return deferred.promise();
}

VccStore.prototype.watch = function (key) {
	var deferred = promise();
	if (!this.connected) {
		deferred.reject("not connected");
	}
	// returns an event emitter on change
	logger.debug("key watch:", key);
	deferred.resolve(this.etcd.watcher(key));
	// return promise
	return deferred.promise();
}

VccStore.prototype.list = function (key) {
	// call the get function with recursive option
	return this.get(key, true);
}

VccStore.prototype.register = function (key, value, ttl) {
	// this is the only function that does not return a promise
	// because there is no obvious resolution
	if (!this.connected) {
		throw "kvstore is not connected";
	}
	logger.debug("key register:", key, value, ttl, "refresh in", (ttl*1000)-10000, "ms");
	var me = this;
	this.set(key, value, ttl);
	setTimeout(function() {
		me.register(key, value, ttl);
	}, (ttl*1000)-10000);
}

module.exports = VccStore;<commit_msg>allow list to return values in one call<commit_after>var promise = require("deferred");
var Etcd = require("node-etcd");
var logger = require("winston");
var path = require("path");

function VccStore () {
	this.connected = false;
}

VccStore.prototype.connect = function (host, port) {
	var conn = "http://"+host+":"+port;
	logger.debug("kvstore is connecting to", conn);
	this.etcd = new Etcd(conn, {timeout: 5000});
	this.connected = true;
}

VccStore.prototype.set = function (key, value, ttl) {
	var deferred = promise();
	if (!this.connected) {
		deferred.reject("not connected");
	}
	// set the key optionally with ttl
	logger.debug("key set:", key, value, ttl);
	var options = {};
	if (ttl) {
		options.ttl = ttl;
	}
	// call the etcd function
	this.etcd.set(key, value, options, function (err, res) {
		if (err) {
			deferred.reject(err);
		} else {
			deferred.resolve();
		}
	});
	// return promise
	return deferred.promise();
}

VccStore.prototype.get = function (key, recursive, recursive_with_vals) {
	var deferred = promise();
	var me = this;
	if (!this.connected) {
		deferred.reject("not connected");
	}
	logger.debug("key get:", key, recursive);
	var options = {};
	if (recursive) {
		options.recursive = true;
	}
	// call the etcd function
	this.etcd.get(key, function (err, res) {
		if (err) {
			deferred.reject(err);
		} else {
			// check the response
			if (res.node) {
				if (recursive) {
					if (res.node.nodes) {
						// convert a list of objects into the required format
						// only show the base name of the full path
						if (recursive_with_vals) {
							// we need an object with {key: value}
							var listresult = res.node.nodes.reduce(function (r, i) {
								if (!i.dir) {
									r[path.basename(i.key)] = i.value;
								}
								return r;
							}, {});
						} else {
							// we just need list of keys
							var listresult = res.node.nodes.reduce(function (r, i) {
								r.push(path.basename(i.key));
								return r;
							}, []);
						}
						deferred.resolve(listresult);
					} else {
						deferred.reject("recursive, expecting nodes but didn't get any");
					}
				} else {
					if (res.node.dir) {
						deferred.reject(res.node.key + " is a directory");
					} else if (res.node.value) {
						deferred.resolve(res.node.value);
					} else {
						deferred.reject("no value in response");
					}
				}
			} else {
				deferred.reject("no node in response");
			}
		}
	});
	// return promise
	return deferred.promise();
}

VccStore.prototype.watch = function (key) {
	var deferred = promise();
	if (!this.connected) {
		deferred.reject("not connected");
	}
	// returns an event emitter on change
	logger.debug("key watch:", key);
	deferred.resolve(this.etcd.watcher(key));
	// return promise
	return deferred.promise();
}

VccStore.prototype.list = function (key, with_vals) {
	// call the get function with recursive option
	// with_vals allows us to get the values when listing in the same call
	return this.get(key, true, with_vals);
}

VccStore.prototype.register = function (key, value, ttl) {
	// this is the only function that does not return a promise
	// because there is no obvious resolution
	if (!this.connected) {
		throw "kvstore is not connected";
	}
	logger.debug("key register:", key, value, ttl, "refresh in", (ttl*1000)-10000, "ms");
	var me = this;
	this.set(key, value, ttl);
	setTimeout(function() {
		me.register(key, value, ttl);
	}, (ttl*1000)-10000);
}

module.exports = VccStore;<|endoftext|>


<commit_before>/**
 * @class L.Draw.Rectangle
 * @aka Draw.Rectangle
 * @inherits L.Draw.SimpleShape
 */
L.Draw.Rectangle = L.Draw.SimpleShape.extend({
	statics: {
		TYPE: 'rectangle'
	},

	options: {
		shapeOptions: {
			stroke: true,
			color: '#3388ff',
			weight: 4,
			opacity: 0.5,
			fill: true,
			fillColor: null, //same as color by default
			fillOpacity: 0.2,
			showArea: true,
			clickable: true
		},
		metric: true // Whether to use the metric measurement system or imperial
	},

	// @method initialize(): void
	initialize: function (map, options) {
		// Save the type so super can fire, need to do this as cannot do this.TYPE :(
		this.type = L.Draw.Rectangle.TYPE;

		this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;

		L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
	},

	// @method disable(): void
	disable: function () {
		if (!this._enabled) {
			return;
		}

		this._twoClicksToggle = false;
		L.Draw.SimpleShape.prototype.disable.call(this);
	},

	_onMouseUp: function (e) {
		if (!this._shape && !this._twoClicksToggle) {
			this._twoClicksToggle = true;
			return;
		}

		// Make sure closing click is on map
		if (this._twoClicksToggle && !_hasAncestor(e.target, 'leaflet-pane')) {
			return;
		}

		L.Draw.SimpleShape.prototype._onMouseUp.call(this);
	},

	_drawShape: function (latlng) {
		if (!this._shape) {
			this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
			this._map.addLayer(this._shape);
		} else {
			this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
		}
	},

	_fireCreatedEvent: function () {
		var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);
		L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);
	},

	_getTooltipText: function () {
		var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this),
			shape = this._shape,
			showArea = this.options.showArea,
			latLngs, area, subtext;

		if (shape) {
			latLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs();
			area = L.GeometryUtil.geodesicArea(latLngs);
			subtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : ''
		}

		return {
			text: tooltipText.text,
			subtext: subtext
		};
	}
});

function _hasAncestor (el, cls) {
	while ((el = el.parentElement) && !el.classList.contains(cls));
	return el;
}
<commit_msg>changed _twoClicksToggle to a more descriptive name<commit_after>/**
 * @class L.Draw.Rectangle
 * @aka Draw.Rectangle
 * @inherits L.Draw.SimpleShape
 */
L.Draw.Rectangle = L.Draw.SimpleShape.extend({
	statics: {
		TYPE: 'rectangle'
	},

	options: {
		shapeOptions: {
			stroke: true,
			color: '#3388ff',
			weight: 4,
			opacity: 0.5,
			fill: true,
			fillColor: null, //same as color by default
			fillOpacity: 0.2,
			showArea: true,
			clickable: true
		},
		metric: true // Whether to use the metric measurement system or imperial
	},

	// @method initialize(): void
	initialize: function (map, options) {
		// Save the type so super can fire, need to do this as cannot do this.TYPE :(
		this.type = L.Draw.Rectangle.TYPE;

		this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;

		L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
	},

	// @method disable(): void
	disable: function () {
		if (!this._enabled) {
			return;
		}

		this._isCurrentlyTwoClickDrawing = false;
		L.Draw.SimpleShape.prototype.disable.call(this);
	},

	_onMouseUp: function (e) {
		if (!this._shape && !this._isCurrentlyTwoClickDrawing) {
			this._isCurrentlyTwoClickDrawing = true;
			return;
		}

		// Make sure closing click is on map
		if (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) {
			return;
		}

		L.Draw.SimpleShape.prototype._onMouseUp.call(this);
	},

	_drawShape: function (latlng) {
		if (!this._shape) {
			this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
			this._map.addLayer(this._shape);
		} else {
			this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
		}
	},

	_fireCreatedEvent: function () {
		var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);
		L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);
	},

	_getTooltipText: function () {
		var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this),
			shape = this._shape,
			showArea = this.options.showArea,
			latLngs, area, subtext;

		if (shape) {
			latLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs();
			area = L.GeometryUtil.geodesicArea(latLngs);
			subtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : ''
		}

		return {
			text: tooltipText.text,
			subtext: subtext
		};
	}
});

function _hasAncestor (el, cls) {
	while ((el = el.parentElement) && !el.classList.contains(cls));
	return el;
}
<|endoftext|>