<commit_before>var _ = require('underscore');

var exchanger = {
    mtgox: {
        currency: '$',
        site: 'https://mtgox.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=MtGoxTicker&suffix=0.8636577818542719'
    },
    bitstamp: {
        currency: '$',
        site: 'https://www.bitstamp.net/',
        url: 'https://www.bitstamp.net/api/ticker'
    },
    btce: {
        currency: '$',
        site: 'https://btc-e.com/',
        url: 'https://btc-e.com/api/2/btc_usd/ticker'
    },
    futures796: {
        currency: '$',
        site: 'http://bitcoinwisdom.com/markets/796/futures',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=796futuresTicker&suffix=0.38433733163401484'
    },
    coinbase: {
        currency: '$',
        site: 'https://coinbase.com/',
        url: 'http://coinbase.com/api/v1/prices/spot_rate'
    },
    btcchina: {
        currency: '¥',
        site: 'https://www.btcchina.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=btcchinaTicker&suffix=0.3849131213501096'
    },
    okcoin: {
        currency: '¥',
        site: 'https://www.okcoin.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=okcoinTicker&suffix=0.7636065774131566'
    },
    chbtc: {
        currency: '¥',
        site: 'https://www.chbtc.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=chbtcTicker&suffix=0.5108873315621167'
    },
    fxbtc: {
        currency: '¥',
        site: 'http://www.fxbtc.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=fxbtcTicker&suffix=0.19148686854168773'
    },
    btctrade: {
        currency: '¥',
        site: 'http://www.btctrade.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=btctradeTicker&suffix=0.1531917753163725'
    },
    btc100: {
        currency: '¥',
        site: 'https://btc100.org/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=btc100Ticker&suffix=0.16103304247371852'
    }
};

exports.list = function() {
    return _.map(exchanger, function(v,k){
        v.name = k;
        return v;
    });
}

exports.map = exchanger;
<commit_msg>let's use the https coinbase api url<commit_after>var _ = require('underscore');

var exchanger = {
    mtgox: {
        currency: '$',
        site: 'https://mtgox.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=MtGoxTicker&suffix=0.8636577818542719'
    },
    bitstamp: {
        currency: '$',
        site: 'https://www.bitstamp.net/',
        url: 'https://www.bitstamp.net/api/ticker'
    },
    btce: {
        currency: '$',
        site: 'https://btc-e.com/',
        url: 'https://btc-e.com/api/2/btc_usd/ticker'
    },
    futures796: {
        currency: '$',
        site: 'http://bitcoinwisdom.com/markets/796/futures',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=796futuresTicker&suffix=0.38433733163401484'
    },
    coinbase: {
        currency: '$',
        site: 'https://coinbase.com/',
        url: 'https://coinbase.com/api/v1/prices/spot_rate'
    },
    btcchina: {
        currency: '¥',
        site: 'https://www.btcchina.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=btcchinaTicker&suffix=0.3849131213501096'
    },
    okcoin: {
        currency: '¥',
        site: 'https://www.okcoin.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=okcoinTicker&suffix=0.7636065774131566'
    },
    chbtc: {
        currency: '¥',
        site: 'https://www.chbtc.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=chbtcTicker&suffix=0.5108873315621167'
    },
    fxbtc: {
        currency: '¥',
        site: 'http://www.fxbtc.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=fxbtcTicker&suffix=0.19148686854168773'
    },
    btctrade: {
        currency: '¥',
        site: 'http://www.btctrade.com/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=btctradeTicker&suffix=0.1531917753163725'
    },
    btc100: {
        currency: '¥',
        site: 'https://btc100.org/',
        url: 'http://www.btc123.com/e/interfaces/tickers.php?type=btc100Ticker&suffix=0.16103304247371852'
    }
};

exports.list = function() {
    return _.map(exchanger, function(v,k){
        v.name = k;
        return v;
    });
}

exports.map = exchanger;
<|endoftext|>


<commit_before>$(document).ready(() => {
    var webComponent = document.querySelector('openvidu-webcomponent');
    var form = document.getElementById('main');

    if(webComponent.getAttribute("openvidu-secret") != undefined && webComponent.getAttribute("openvidu-server-url") != undefined ){
        form.style.display = 'none';
        webComponent.style.display = 'block';
    }

    webComponent.addEventListener('joinSession', (event) => {});
    webComponent.addEventListener('leaveSession', (event) => {
        form.style.display = 'block';
        webComponent.style.display = 'none';
    });
    webComponent.addEventListener('error', (event) => {
        console.log('Error event', event.detail);
    });
});

function joinSession() {
    var sessionName = document.getElementById('sessionName').value;
    var user = document.getElementById('user').value;
    var form = document.getElementById('main');
    var webComponent = document.querySelector('openvidu-webcomponent');
    var tokens = [];

    form.style.display = 'none';
    webComponent.style.display = 'block';

    if(webComponent.getAttribute("openvidu-secret") != undefined && webComponent.getAttribute("openvidu-server-url") != undefined ){
        location.reload();
    }else {
        getToken(sessionName).then((token1) => {
            tokens.push(token1);
            getToken(sessionName).then((token2) => {
                tokens.push(token2);
                webComponent.sessionConfig = { sessionName, user, tokens };
            });
        });
    }
}

/**
 * --------------------------
 * SERVER-SIDE RESPONSIBILITY
 * --------------------------
 * These methods retrieve the mandatory user token from OpenVidu Server.
 * This behavior MUST BE IN YOUR SERVER-SIDE IN PRODUCTION (by using
 * the API REST, openvidu-java-client or openvidu-node-client):
 *   1) Initialize a session in OpenVidu Server	(POST /api/sessions)
 *   2) Generate a token in OpenVidu Server		(POST /api/tokens)
 *   3) Configure OpenVidu Web Component in your client side with the token
 */

var OPENVIDU_SERVER_URL = "https://" + location.hostname + ":4443";
var OPENVIDU_SERVER_SECRET = 'MY_SECRET';

function getToken(sessionName) {
    return createSession(sessionName).then((sessionId) => createToken(sessionId));
}

function createSession(sessionName) { // See https://openvidu.io/docs/reference-docs/REST-API/#post-apisessions
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: OPENVIDU_SERVER_URL + '/api/sessions',
            data: JSON.stringify({ customSessionId: sessionName }),
            headers: {
                Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET),
                'Content-Type': 'application/json',
            },
            success: (response) => resolve(response.id),
            error: (error) => {
                if (error.status === 409) {
                    resolve(sessionName);
                } else {
                    console.warn('No connection to OpenVidu Server. This may be a certificate error at ' + OPENVIDU_SERVER_URL);
                    if (
                        window.confirm(
                            'No connection to OpenVidu Server. This may be a certificate error at "' +
                                OPENVIDU_SERVER_URL +
                                '"\n\nClick OK to navigate and accept it. ' +
                                'If no certificate warning is shown, then check that your OpenVidu Server is up and running at "' +
                                OPENVIDU_SERVER_URL +
                                '"',
                        )
                    ) {
                        location.assign(OPENVIDU_SERVER_URL + '/accept-certificate');
                    }
                }
            },
        });
    });
}

function createToken(sessionId) {
    // See https://openvidu.io/docs/reference-docs/REST-API/#post-apitokens
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: OPENVIDU_SERVER_URL + '/api/tokens',
            data: JSON.stringify({ session: sessionId }),
            headers: {
                Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET),
                'Content-Type': 'application/json',
            },
            success: (response) => resolve(response.token),
            error: (error) => reject(error),
        });
    });
}
<commit_msg>openvidu-webcomponent: Updated to async await functions<commit_after>$(document).ready(() => {
    var webComponent = document.querySelector('openvidu-webcomponent');
    var form = document.getElementById('main');

    if(webComponent.getAttribute("openvidu-secret") != undefined && webComponent.getAttribute("openvidu-server-url") != undefined ){
        form.style.display = 'none';
        webComponent.style.display = 'block';
    }

    webComponent.addEventListener('joinSession', (event) => {});
    webComponent.addEventListener('leaveSession', (event) => {
        form.style.display = 'block';
        webComponent.style.display = 'none';
    });
    webComponent.addEventListener('error', (event) => {
        console.log('Error event', event.detail);
    });
});

async function joinSession() {
    var sessionName = document.getElementById('sessionName').value;
    var user = document.getElementById('user').value;
    var form = document.getElementById('main');
    var webComponent = document.querySelector('openvidu-webcomponent');
    var tokens = [];

    form.style.display = 'none';
    webComponent.style.display = 'block';

    if(webComponent.getAttribute("openvidu-secret") != undefined && webComponent.getAttribute("openvidu-server-url") != undefined ){
       location.reload();
    }else {
        var token1 = await getToken(sessionName)
        var token2 = await getToken(sessionName);
        tokens.push(token1, token2);
        webComponent.sessionConfig = { sessionName, user, tokens };
    }
}

/**
 * --------------------------
 * SERVER-SIDE RESPONSIBILITY
 * --------------------------
 * These methods retrieve the mandatory user token from OpenVidu Server.
 * This behavior MUST BE IN YOUR SERVER-SIDE IN PRODUCTION (by using
 * the API REST, openvidu-java-client or openvidu-node-client):
 *   1) Initialize a session in OpenVidu Server	(POST /api/sessions)
 *   2) Generate a token in OpenVidu Server		(POST /api/tokens)
 *   3) Configure OpenVidu Web Component in your client side with the token
 */

var OPENVIDU_SERVER_URL = "https://" + location.hostname + ":4443";
var OPENVIDU_SERVER_SECRET = 'MY_SECRET';

function getToken(sessionName) {
    return createSession(sessionName).then((sessionId) => createToken(sessionId));
}

function createSession(sessionName) { // See https://openvidu.io/docs/reference-docs/REST-API/#post-apisessions
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: OPENVIDU_SERVER_URL + '/api/sessions',
            data: JSON.stringify({ customSessionId: sessionName }),
            headers: {
                Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET),
                'Content-Type': 'application/json',
            },
            success: (response) => resolve(response.id),
            error: (error) => {
                if (error.status === 409) {
                    resolve(sessionName);
                } else {
                    console.warn('No connection to OpenVidu Server. This may be a certificate error at ' + OPENVIDU_SERVER_URL);
                    if (
                        window.confirm(
                            'No connection to OpenVidu Server. This may be a certificate error at "' +
                                OPENVIDU_SERVER_URL +
                                '"\n\nClick OK to navigate and accept it. ' +
                                'If no certificate warning is shown, then check that your OpenVidu Server is up and running at "' +
                                OPENVIDU_SERVER_URL +
                                '"',
                        )
                    ) {
                        location.assign(OPENVIDU_SERVER_URL + '/accept-certificate');
                    }
                }
            },
        });
    });
}

function createToken(sessionId) {
    // See https://openvidu.io/docs/reference-docs/REST-API/#post-apitokens
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: OPENVIDU_SERVER_URL + '/api/tokens',
            data: JSON.stringify({ session: sessionId }),
            headers: {
                Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET),
                'Content-Type': 'application/json',
            },
            success: (response) => resolve(response.token),
            error: (error) => reject(error),
        });
    });
}
<|endoftext|>


<commit_before>/*
	The int binding will accept any int, all other chars are removed.
	use binding postivie: true if you require only posies.
	type = 'text' is required. HTML5 numbers does not play nice.
	https://github.com/circuitcreature/Knockout-Bindings.
*/
;(function(){
	ko.bindingHandlers['int'] = {
		init: function(element,valueAccessor, allBindings){
			var all = allBindings();
			if(all.hasOwnProperty('value') || all.hasOwnProperty('float')){
				throw new Error('The int binding cannot be used with value or float bindings.')
			}
			var val = valueAccessor(),
				positive = false;
			if(all.hasOwnProperty('positive') && allBindings().positive){
				positive = true;
			}
			var update_binding = function(val){
				var obs = valueAccessor(),
					elm_val = ko.selectExtensions.readValue(element);
				obs(elm_val);
			};
			var update = function(){
				var nv = ko.utils.unwrapObservable( val() ),
					elm_v = ko.selectExtensions.readValue(element);
				if(nv){
					if(positive){
						nv = (nv += '').replace( /[^0-9]/g ,'');
					}else{
						nv = (nv += '').replace( /[^0-9-]/g ,'');
						if(nv.charAt(0) == '-'){
							nv = '-' + nv;
						}
					}
				}
				if(nv !== elm_v){
					ko.selectExtensions.writeValue(element, nv)
				}
			};
			ko.computed(update, null, {disposeWhenNodeIsRemoved: element});
			element.addEventListener('change',function(){
				update_binding();
			});
			if(allBindings().hasOwnProperty('valueUpdate')){
				element.addEventListener(allBindings()['valueUpdate'],function(){
					update_binding();
				});
			}
		}
	};
})();
<commit_msg>fixes to int binding, added object<commit_after>/*
	The int binding will accept any int, all other chars are removed.
	use an object to require postivie: int: {value: (), positive: true}.
	input type = 'text' is required. HTML5 numbers does not play nice.
	https://github.com/circuitcreature/Knockout-Bindings.
*/
;(function(){
	ko.bindingHandlers['int'] = {
		init: function(element,valueAccessor, allBindings){
			var all = allBindings();
			if(all.hasOwnProperty('value') || all.hasOwnProperty('float')){
				throw new Error('The int binding cannot be used with value or float bindings.')
			}
			var val = valueAccessor(),
				positive = false;
			if(({}).toString.call(val) == '[object Object]'){
				if(val.hasOwnProperty('value')){
					val = val.value;
				}else{
					throw new Error('The int binding is in object form and requires a "value" property.')
				}
				if(val.hasOwnProperty('positive') && val.positive){
					positive = true;
				}
			}
			var update_binding = function(){
				var obs = val,
					elm_val = ko.selectExtensions.readValue(element);
				obs(elm_val);
			};
			var update = function(){
				var nv = ko.utils.unwrapObservable( val() );
				if(!nv){ return; }
				var elm_v = ko.selectExtensions.readValue(element);
				if(positive){
					nv = (nv += '').replace( /[^0-9]/g ,'');
				}else{
					nv = (nv += '').replace( /(?!^-)[^0-9]/g ,'');
				}
				if(nv !== elm_v){
					ko.selectExtensions.writeValue(element, nv)
				}
			};
			ko.computed(update, null, {disposeWhenNodeIsRemoved: element});
			element.addEventListener('change',function(){
				update_binding();
			});
			if(allBindings().hasOwnProperty('valueUpdate')){
				element.addEventListener(allBindings()['valueUpdate'],function(){
					update_binding();
				});
			}
		}
	};
})();
<|endoftext|>


<commit_before>/*
Copyright 2014 Ian Gulliver

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var cameraGrid = {};

/**
 * @callback getUrl
 * @param {String} sourceUrl Base URL of the camera
 * @param {number} width Valid resolution width in pixels
 * @param {number} height Valid resolution height in pixels
 */

/**
 * @constructor
 * @export
 * @param {Node} container DOM container object to hold UI
 * @param {String[]} sourceUrls Array of Axis camera URLs
 * @param {number[][]=} resolutions Array of [width,height] resolution tuples
 * @param {getUrl=} getUrl Callback to generate URL for a given camera
 */
cameraGrid.CameraGrid = function(container, sourceUrls, resolutions, getUrl) {
  this.container_ = container;
  this.sourceUrls_ = sourceUrls;
  this.resolutions_ = resolutions || this.defaultResolutions_;
  this.getUrl_ = getUrl || this.defaultGetUrl_;

  this.tileScaleWidth_ = this.resolutions_[0][0];
  this.tileScaleHeight_ = this.resolutions_[0][1];

  this.gridWidthCells_ = 0;
  this.gridHeightCells_ = 0;

  this.imgWidthPx_ = 0;
  this.imgHeightPx_ = 0;
  this.constraint_ = null;

  this.containerImgWidthPx_ = 0;
  this.ctonainerImgHeightPx_ = 0;
  this.containerConstraint_ = null;

  this.selected_ = null;
  this.scanning_ = false;

  this.buildCells_();
  this.buildStylesheet_();

  this.container_.tabIndex = 0;
  this.container_.focus();
  this.container_.addEventListener('keypress', this.onKeyPress_.bind(this), false);
  this.container_.addEventListener('keydown', this.onKeyDown_.bind(this), false);

  window.addEventListener('resize', this.rebuildIfNeeded_.bind(this), false);
  this.rebuildIfNeeded_();

  window.setInterval(this.onScanTimer_.bind(this), 3000);
};

// Resolution list must be sorted ascending.
// All resolutions must be the same aspect ratio.
cameraGrid.CameraGrid.prototype.defaultResolutions_ = [
  [ 160, 120 ],
  [ 240, 180 ],
  [ 320, 240 ],
  [ 480, 360 ],
  [ 640, 480 ],
  [ 800, 600 ],
  [ 1024, 768 ],
  [ 1280, 960 ],
];

cameraGrid.CameraGrid.prototype.defaultGetUrl_ = function(sourceUrl, width, height) {
  return sourceUrl + 'mjpg/video.mjpg?resolution=' + width + 'x' + height;
};

cameraGrid.CameraGrid.prototype.disableScanning_ = function() {
  if (this.scanning_) {
    this.scanning_ = false;
    // Images might all be higher res than needed, so we refresh.
    this.buildImages_();
  }
};

cameraGrid.CameraGrid.prototype.setSelectedNoScan_ = function(index) {
  this.setSelected_(index);
  this.disableScanning_();
};

cameraGrid.CameraGrid.prototype.setSelected_ = function(index) {
  var old_index = null;

  if (this.selected_ == index) {
    this.removeCSSClass_(this.cells_[this.selected_], 'cameraGridFullScreen');
    old_index = this.selected_;
    this.selected_ = null;
  } else {
    if (this.selected_ != null) {
      this.removeCSSClass_(this.cells_[this.selected_], 'cameraGridFullScreen');
      old_index = this.selected_;
    }
    this.addCSSClass_(this.cells_[index], 'cameraGridFullScreen');
    this.selected_ = index;
  }

  if (this.containerImgWidthPx_ != this.imgWidthPx_ ||
      this.containerImgHeightPx_ != this.imgHeightPx_) {
    // Image stream should change when toggling full screen.
    if (old_index != null) {
      this.buildImage_(old_index);
    }
    if (this.selected_ != null) {
      this.buildImage_(this.selected_);
    }
  }
};

cameraGrid.CameraGrid.prototype.buildCells_ = function() {
  this.cells_ = [];
  for (var i = 0; i < this.sourceUrls_.length; i++) {
    var cell = document.createElement('cameraGridCell');
    this.cells_.push(cell);
  }
};

cameraGrid.CameraGrid.prototype.addCSSClass_ = function(node, className) {
  var classes = node.className.split(' ').filter(function(className) { return className; });
  if (classes.indexOf(className) != -1) {
    // Already has class.
    return;
  }
  classes.push(className);
  node.className = classes.join(' ');
}

cameraGrid.CameraGrid.prototype.removeCSSClass_ = function(node, className) {
  var classes = node.className.split(' ').filter(function(className) { return className; });
  var i = classes.indexOf(className);
  if (i == -1) {
    // Already doesn't have class.
    return;
  }
  delete classes[i];
  node.className = classes.join(' ');
}

cameraGrid.CameraGrid.prototype.buildStylesheet_ = function() {
  var style = document.createElement('style');
  document.head.appendChild(style);

  style.sheet.insertRule('cameraGridRow {}', 0);
  this.rowHeightRule_ = style.sheet.cssRules[0];
  style.sheet.insertRule('cameraGridCell {}', 0);
  this.cellWidthRule_ = style.sheet.cssRules[0];
  style.sheet.insertRule('cameraGridImgContainer img {}', 0);
  this.imageScaleRule_ = style.sheet.cssRules[0];
  style.sheet.insertRule('cameraGridCell.cameraGridFullScreen cameraGridImgContainer img {}', 0);
  this.containerImageScaleRule_ = style.sheet.cssRules[0];

  style.sheet.insertRule('cameraGridRow { display: block; width: 100% }', 0);
  style.sheet.insertRule('cameraGridCell { display: inline-block; height: 100%; position: relative }', 0);
  style.sheet.insertRule('cameraGridImgContainer { position: absolute; top: 0; left: 0; bottom: 0; right: 0; text-align: center }', 0);
  style.sheet.insertRule('cameraGridImgContainer img { max-height: 100%; max-width: 100% }', 0);
  style.sheet.insertRule('.cameraGridContainer { font-size: 0; text-align: center; -webkit-user-select: none; -moz-user-select: none; }', 0);
  style.sheet.insertRule('cameraGridCell.cameraGridFullScreen { position: static }', 0);
  style.sheet.insertRule('cameraGridCell.cameraGridFullScreen cameraGridImgContainer { z-index: 1 }', 0);

  this.addCSSClass_(this.container_, 'cameraGridContainer');
};

cameraGrid.CameraGrid.prototype.calculateGrid_ = function() {
  var containerWidth = this.container_.offsetWidth;
  var containerHeight = this.container_.offsetHeight;
  var numTiles = this.sourceUrls_.length;

  var scaleFactor = ((containerHeight / this.tileScaleHeight_)
                     / (containerWidth / this.tileScaleWidth_));

  var gridHeight = Math.sqrt(scaleFactor * numTiles);
  var gridWidth = Math.sqrt(numTiles / scaleFactor);

  var gridOptions = [
    [ Math.ceil(gridWidth), Math.floor(gridHeight) ],
    [ Math.floor(gridWidth), Math.ceil(gridHeight) ],
    [ Math.ceil(gridWidth), Math.ceil(gridHeight) ],
  ];

  // Check all possible options.
  // We are optimizing for several dimensions (decreasing priority):
  // 1) Be able to fit all the tiles.
  // 2) Maximum scale for an image in each cell.
  // 3) Minimize number of cells.
  var minCells = Number.MAX_VALUE;
  var maxScale = 0.0;
  var chosenHeight, chosenWidth, chosenConstraint;
  for (var i = 0; i < gridOptions.length; i++) {
    var gridOption = gridOptions[i];
    var numCells = gridOption[0] * gridOption[1];
    if (numCells < numTiles) {
      // Can't fit all the tiles in (we've rounded down too far).
      continue;
    }
    var widthScale = (containerWidth / gridOption[0]) / this.tileScaleWidth_;
    var heightScale = (containerHeight / gridOption[1]) / this.tileScaleHeight_;
    var scale, constraint;
    if (widthScale < heightScale) {
      scale = widthScale;
      constraint = 'width';
    } else {
      scale = heightScale;
      constraint = 'height';
    }
    if (scale < maxScale) {
      // This would make cells smaller than another viable solution.
      continue;
    }
    if (scale == maxScale && numCells > minCells) {
      // Same cell size as another viable solution, but ours has more cells.
      continue;
    }
    chosenWidth = gridOption[0];
    chosenHeight = gridOption[1];
    chosenConstraint = constraint;
    minCells = numCells;
    maxScale = scale;
  }

  return {
    gridWidthCells: chosenWidth,
    gridHeightCells: chosenHeight,
    constraint: chosenConstraint,
    containerConstraint: scaleFactor > 1 ? 'width' : 'height',
    cellWidthPx: this.tileScaleWidth_ * maxScale,
    cellHeightPx: this.tileScaleHeight_ * maxScale,
  };
};

cameraGrid.CameraGrid.prototype.findMinimumResolution_ = function(tileWidth, tileHeight) {
  for (var i = 0; i < this.resolutions_.length; i++) {
    var resolution = this.resolutions_[i];
    if (i + 1 < this.resolutions_.length &&
        (resolution[0] < tileWidth && resolution[1] < tileHeight)) {
      continue;
    }
    return {
      imgWidthPx: resolution[0],
      imgHeightPx: resolution[1],
    };
  }
};

cameraGrid.CameraGrid.prototype.deletePreviousSiblings_ = function(element) {
  while (element.previousSibling) {
    element.parentNode.removeChild(element.previousSibling);
  }
};

cameraGrid.CameraGrid.prototype.buildImage_ = function(index) {
  var sourceUrl = this.sourceUrls_[index];
  var imgUrl = (
      (this.scanning_ || index == this.selected_) ?
      this.getUrl_(sourceUrl, this.containerImgWidthPx_, this.containerImgHeightPx_) :
      this.getUrl_(sourceUrl, this.imgWidthPx_, this.imgHeightPx_));
  var cell = this.cells_[index];

  // cell > imgContainer(s) > img
  // Last imgContainer will eventually win.
  if (cell.lastChild && cell.lastChild.firstChild.src == imgUrl) {
    // We'd be re-adding the same image; skip.
    return;
  }

  var img = document.createElement('img');
  img.src = imgUrl;
  var imgContainer = document.createElement('cameraGridImgContainer');
  imgContainer.addEventListener('click', this.setSelected_.bind(this, index), false);
  img.addEventListener('load', this.deletePreviousSiblings_.bind(this, imgContainer), false);
  imgContainer.appendChild(img);
  cell.appendChild(imgContainer);
};

cameraGrid.CameraGrid.prototype.buildImages_ = function() {
  for (var i = 0; i < this.sourceUrls_.length; i++) {
    this.buildImage_(i);
  }
};

cameraGrid.CameraGrid.prototype.buildGrid_ = function() {
  this.container_.innerHTML = '';

  this.rowHeightRule_.style.height = 100 / this.gridHeightCells_ + '%';
  this.cellWidthRule_.style.width = 100 / this.gridWidthCells_ + '%';

  var i = 0;
  for (var y = 0; y < this.gridHeightCells_; y++) {
    var row = document.createElement('cameraGridRow');
    for (var x = 0; x < this.gridWidthCells_; x++) {
      if (i < this.cells_.length) {
        var cell = this.cells_[i];
        row.appendChild(cell);
        i++;
      }
    }
    this.container_.appendChild(row);
  }
};

cameraGrid.CameraGrid.prototype.setUpscaleRule_ = function(constraint, rule) {
  if (constraint == 'height') {
    rule.style.minWidth = 0;
    rule.style.minHeight = '100%';
  } else {
    rule.style.minWidth = '100%';
    rule.style.minHeight = 0;
  }
};

cameraGrid.CameraGrid.prototype.rebuildIfNeeded_ = function() {
  var grid = this.calculateGrid_();
  var resolution = this.findMinimumResolution_(grid.cellWidthPx, grid.cellHeightPx);
  var containerResolution = this.findMinimumResolution_(this.container_.offsetWidth, this.container_.offsetHeight);

  if (grid.constraint != this.constraint_) {
    this.constraint_ = grid.constraint;
    this.setUpscaleRule_(this.constraint_, this.imageScaleRule_);
  }

  if (grid.containerConstraint != this.containerConstraint_) {
    this.containerConstraint_ = grid.containerConstraint;
    this.setUpscaleRule_(this.containerConstraint_, this.containerImageScaleRule_);
  }

  if (resolution.imgWidthPx != this.imgWidthPx_ ||
      resolution.imgHeightPx != this.imgHeightPx_) {
    // Need to recache images.
    this.imgWidthPx_ = resolution.imgWidthPx;
    this.imgHeightPx_ = resolution.imgHeightPx;
    this.buildImages_();
  }

  if (containerResolution.imgWidthPx != this.containerImgWidthPx_ ||
      containerResolution.imgHeightPx != this.containerImgHeightPx_) {
    this.containerImgWidthPx_ = containerResolution.imgWidthPx;
    this.containerImgHeightPx_ = containerResolution.imgHeightPx;
    if (this.selected_ != null) {
      this.buildImage_(this.selected_);
    }
  }

  if (grid.gridWidthCells != this.gridWidthCells_ ||
      grid.gridHeightCells != this.gridHeightCells_) {
    this.gridWidthCells_ = grid.gridWidthCells;
    this.gridHeightCells_ = grid.gridHeightCells;
    this.buildGrid_();
  }
};

cameraGrid.CameraGrid.prototype.onKeyPress_ = function(e) {
  var character = String.fromCharCode(e.charCode);
  switch (character) {
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case '0':
      var index = e.charCode - '1'.charCodeAt(0);
      if (index == -1) {
        index = 10;
      }
      if (index < this.cells_.length) {
        this.setSelectedNoScan_(index);
      }
      break;
    case 's':
      if (this.scanning_ && this.selected_ != null) {
        // Toggle off
        this.setSelectedNoScan_(this.selected_);
        return;
      }
      this.scanning_ = true;
      if (this.selected_ == null) {
        this.setSelected_(0);
      }
      break;
    case ' ':
      this.scanning_ = !this.scanning_;
      break;
  }
};

cameraGrid.CameraGrid.prototype.scanLeft_ = function() {
  this.setSelected_(this.selected_ > 0 ? this.selected_ - 1 : this.cells_.length - 1);
};

cameraGrid.CameraGrid.prototype.scanRight_ = function() {
  this.setSelected_((this.selected_ + 1) % this.cells_.length);
};

cameraGrid.CameraGrid.prototype.onKeyDown_ = function(e) {
  switch (e.keyCode) {
    case 27: // Esc
      if (this.selected_ != null) {
        // Toggle selected feed off.
        this.setSelectedNoScan_(this.selected_);
      }
      break;
    case 37: // Left arrow
      if (this.selected_ != null) {
        this.scanLeft_();
        this.disableScanning_();
      }
      break;
    case 39: // Right arrow
      if (this.selected_ != null) {
        this.scanRight_();
        this.disableScanning_();
      }
      break;
  }
};

cameraGrid.CameraGrid.prototype.onScanTimer_ = function() {
  if (!this.scanning_ || this.selected_ == null) {
    return;
  }
  this.scanRight_();
};
<commit_msg>Fix jsdoc for closure syntax<commit_after>/*
Copyright 2014 Ian Gulliver

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var cameraGrid = {};

/**
 * @name getUrl
 * @function
 * @param {String} sourceUrl Base URL of the camera
 * @param {number} width Valid resolution width in pixels
 * @param {number} height Valid resolution height in pixels
 * @return String
 */

/**
 * @constructor
 * @export
 * @param {Node} container DOM container object to hold UI
 * @param {Array.<String>} sourceUrls Array of Axis camera URLs
 * @param {Array.<Array.<number>>=} resolutions Array of [width,height] resolution tuples
 * @param {getUrl=} getUrl Callback to generate URL for a given camera
 */
cameraGrid.CameraGrid = function(container, sourceUrls, resolutions, getUrl) {
  this.container_ = container;
  this.sourceUrls_ = sourceUrls;
  this.resolutions_ = resolutions || this.defaultResolutions_;
  this.getUrl_ = getUrl || this.defaultGetUrl_;

  this.tileScaleWidth_ = this.resolutions_[0][0];
  this.tileScaleHeight_ = this.resolutions_[0][1];

  this.gridWidthCells_ = 0;
  this.gridHeightCells_ = 0;

  this.imgWidthPx_ = 0;
  this.imgHeightPx_ = 0;
  this.constraint_ = null;

  this.containerImgWidthPx_ = 0;
  this.ctonainerImgHeightPx_ = 0;
  this.containerConstraint_ = null;

  this.selected_ = null;
  this.scanning_ = false;

  this.buildCells_();
  this.buildStylesheet_();

  this.container_.tabIndex = 0;
  this.container_.focus();
  this.container_.addEventListener('keypress', this.onKeyPress_.bind(this), false);
  this.container_.addEventListener('keydown', this.onKeyDown_.bind(this), false);

  window.addEventListener('resize', this.rebuildIfNeeded_.bind(this), false);
  this.rebuildIfNeeded_();

  window.setInterval(this.onScanTimer_.bind(this), 3000);
};

// Resolution list must be sorted ascending.
// All resolutions must be the same aspect ratio.
cameraGrid.CameraGrid.prototype.defaultResolutions_ = [
  [ 160, 120 ],
  [ 240, 180 ],
  [ 320, 240 ],
  [ 480, 360 ],
  [ 640, 480 ],
  [ 800, 600 ],
  [ 1024, 768 ],
  [ 1280, 960 ],
];

cameraGrid.CameraGrid.prototype.defaultGetUrl_ = function(sourceUrl, width, height) {
  return sourceUrl + 'mjpg/video.mjpg?resolution=' + width + 'x' + height;
};

cameraGrid.CameraGrid.prototype.disableScanning_ = function() {
  if (this.scanning_) {
    this.scanning_ = false;
    // Images might all be higher res than needed, so we refresh.
    this.buildImages_();
  }
};

cameraGrid.CameraGrid.prototype.setSelectedNoScan_ = function(index) {
  this.setSelected_(index);
  this.disableScanning_();
};

cameraGrid.CameraGrid.prototype.setSelected_ = function(index) {
  var old_index = null;

  if (this.selected_ == index) {
    this.removeCSSClass_(this.cells_[this.selected_], 'cameraGridFullScreen');
    old_index = this.selected_;
    this.selected_ = null;
  } else {
    if (this.selected_ != null) {
      this.removeCSSClass_(this.cells_[this.selected_], 'cameraGridFullScreen');
      old_index = this.selected_;
    }
    this.addCSSClass_(this.cells_[index], 'cameraGridFullScreen');
    this.selected_ = index;
  }

  if (this.containerImgWidthPx_ != this.imgWidthPx_ ||
      this.containerImgHeightPx_ != this.imgHeightPx_) {
    // Image stream should change when toggling full screen.
    if (old_index != null) {
      this.buildImage_(old_index);
    }
    if (this.selected_ != null) {
      this.buildImage_(this.selected_);
    }
  }
};

cameraGrid.CameraGrid.prototype.buildCells_ = function() {
  this.cells_ = [];
  for (var i = 0; i < this.sourceUrls_.length; i++) {
    var cell = document.createElement('cameraGridCell');
    this.cells_.push(cell);
  }
};

cameraGrid.CameraGrid.prototype.addCSSClass_ = function(node, className) {
  var classes = node.className.split(' ').filter(function(className) { return className; });
  if (classes.indexOf(className) != -1) {
    // Already has class.
    return;
  }
  classes.push(className);
  node.className = classes.join(' ');
}

cameraGrid.CameraGrid.prototype.removeCSSClass_ = function(node, className) {
  var classes = node.className.split(' ').filter(function(className) { return className; });
  var i = classes.indexOf(className);
  if (i == -1) {
    // Already doesn't have class.
    return;
  }
  delete classes[i];
  node.className = classes.join(' ');
}

cameraGrid.CameraGrid.prototype.buildStylesheet_ = function() {
  var style = document.createElement('style');
  document.head.appendChild(style);

  style.sheet.insertRule('cameraGridRow {}', 0);
  this.rowHeightRule_ = style.sheet.cssRules[0];
  style.sheet.insertRule('cameraGridCell {}', 0);
  this.cellWidthRule_ = style.sheet.cssRules[0];
  style.sheet.insertRule('cameraGridImgContainer img {}', 0);
  this.imageScaleRule_ = style.sheet.cssRules[0];
  style.sheet.insertRule('cameraGridCell.cameraGridFullScreen cameraGridImgContainer img {}', 0);
  this.containerImageScaleRule_ = style.sheet.cssRules[0];

  style.sheet.insertRule('cameraGridRow { display: block; width: 100% }', 0);
  style.sheet.insertRule('cameraGridCell { display: inline-block; height: 100%; position: relative }', 0);
  style.sheet.insertRule('cameraGridImgContainer { position: absolute; top: 0; left: 0; bottom: 0; right: 0; text-align: center }', 0);
  style.sheet.insertRule('cameraGridImgContainer img { max-height: 100%; max-width: 100% }', 0);
  style.sheet.insertRule('.cameraGridContainer { font-size: 0; text-align: center; -webkit-user-select: none; -moz-user-select: none; }', 0);
  style.sheet.insertRule('cameraGridCell.cameraGridFullScreen { position: static }', 0);
  style.sheet.insertRule('cameraGridCell.cameraGridFullScreen cameraGridImgContainer { z-index: 1 }', 0);

  this.addCSSClass_(this.container_, 'cameraGridContainer');
};

cameraGrid.CameraGrid.prototype.calculateGrid_ = function() {
  var containerWidth = this.container_.offsetWidth;
  var containerHeight = this.container_.offsetHeight;
  var numTiles = this.sourceUrls_.length;

  var scaleFactor = ((containerHeight / this.tileScaleHeight_)
                     / (containerWidth / this.tileScaleWidth_));

  var gridHeight = Math.sqrt(scaleFactor * numTiles);
  var gridWidth = Math.sqrt(numTiles / scaleFactor);

  var gridOptions = [
    [ Math.ceil(gridWidth), Math.floor(gridHeight) ],
    [ Math.floor(gridWidth), Math.ceil(gridHeight) ],
    [ Math.ceil(gridWidth), Math.ceil(gridHeight) ],
  ];

  // Check all possible options.
  // We are optimizing for several dimensions (decreasing priority):
  // 1) Be able to fit all the tiles.
  // 2) Maximum scale for an image in each cell.
  // 3) Minimize number of cells.
  var minCells = Number.MAX_VALUE;
  var maxScale = 0.0;
  var chosenHeight, chosenWidth, chosenConstraint;
  for (var i = 0; i < gridOptions.length; i++) {
    var gridOption = gridOptions[i];
    var numCells = gridOption[0] * gridOption[1];
    if (numCells < numTiles) {
      // Can't fit all the tiles in (we've rounded down too far).
      continue;
    }
    var widthScale = (containerWidth / gridOption[0]) / this.tileScaleWidth_;
    var heightScale = (containerHeight / gridOption[1]) / this.tileScaleHeight_;
    var scale, constraint;
    if (widthScale < heightScale) {
      scale = widthScale;
      constraint = 'width';
    } else {
      scale = heightScale;
      constraint = 'height';
    }
    if (scale < maxScale) {
      // This would make cells smaller than another viable solution.
      continue;
    }
    if (scale == maxScale && numCells > minCells) {
      // Same cell size as another viable solution, but ours has more cells.
      continue;
    }
    chosenWidth = gridOption[0];
    chosenHeight = gridOption[1];
    chosenConstraint = constraint;
    minCells = numCells;
    maxScale = scale;
  }

  return {
    gridWidthCells: chosenWidth,
    gridHeightCells: chosenHeight,
    constraint: chosenConstraint,
    containerConstraint: scaleFactor > 1 ? 'width' : 'height',
    cellWidthPx: this.tileScaleWidth_ * maxScale,
    cellHeightPx: this.tileScaleHeight_ * maxScale,
  };
};

cameraGrid.CameraGrid.prototype.findMinimumResolution_ = function(tileWidth, tileHeight) {
  for (var i = 0; i < this.resolutions_.length; i++) {
    var resolution = this.resolutions_[i];
    if (i + 1 < this.resolutions_.length &&
        (resolution[0] < tileWidth && resolution[1] < tileHeight)) {
      continue;
    }
    return {
      imgWidthPx: resolution[0],
      imgHeightPx: resolution[1],
    };
  }
};

cameraGrid.CameraGrid.prototype.deletePreviousSiblings_ = function(element) {
  while (element.previousSibling) {
    element.parentNode.removeChild(element.previousSibling);
  }
};

cameraGrid.CameraGrid.prototype.buildImage_ = function(index) {
  var sourceUrl = this.sourceUrls_[index];
  var imgUrl = (
      (this.scanning_ || index == this.selected_) ?
      this.getUrl_(sourceUrl, this.containerImgWidthPx_, this.containerImgHeightPx_) :
      this.getUrl_(sourceUrl, this.imgWidthPx_, this.imgHeightPx_));
  var cell = this.cells_[index];

  // cell > imgContainer(s) > img
  // Last imgContainer will eventually win.
  if (cell.lastChild && cell.lastChild.firstChild.src == imgUrl) {
    // We'd be re-adding the same image; skip.
    return;
  }

  var img = document.createElement('img');
  img.src = imgUrl;
  var imgContainer = document.createElement('cameraGridImgContainer');
  imgContainer.addEventListener('click', this.setSelected_.bind(this, index), false);
  img.addEventListener('load', this.deletePreviousSiblings_.bind(this, imgContainer), false);
  imgContainer.appendChild(img);
  cell.appendChild(imgContainer);
};

cameraGrid.CameraGrid.prototype.buildImages_ = function() {
  for (var i = 0; i < this.sourceUrls_.length; i++) {
    this.buildImage_(i);
  }
};

cameraGrid.CameraGrid.prototype.buildGrid_ = function() {
  this.container_.innerHTML = '';

  this.rowHeightRule_.style.height = 100 / this.gridHeightCells_ + '%';
  this.cellWidthRule_.style.width = 100 / this.gridWidthCells_ + '%';

  var i = 0;
  for (var y = 0; y < this.gridHeightCells_; y++) {
    var row = document.createElement('cameraGridRow');
    for (var x = 0; x < this.gridWidthCells_; x++) {
      if (i < this.cells_.length) {
        var cell = this.cells_[i];
        row.appendChild(cell);
        i++;
      }
    }
    this.container_.appendChild(row);
  }
};

cameraGrid.CameraGrid.prototype.setUpscaleRule_ = function(constraint, rule) {
  if (constraint == 'height') {
    rule.style.minWidth = 0;
    rule.style.minHeight = '100%';
  } else {
    rule.style.minWidth = '100%';
    rule.style.minHeight = 0;
  }
};

cameraGrid.CameraGrid.prototype.rebuildIfNeeded_ = function() {
  var grid = this.calculateGrid_();
  var resolution = this.findMinimumResolution_(grid.cellWidthPx, grid.cellHeightPx);
  var containerResolution = this.findMinimumResolution_(this.container_.offsetWidth, this.container_.offsetHeight);

  if (grid.constraint != this.constraint_) {
    this.constraint_ = grid.constraint;
    this.setUpscaleRule_(this.constraint_, this.imageScaleRule_);
  }

  if (grid.containerConstraint != this.containerConstraint_) {
    this.containerConstraint_ = grid.containerConstraint;
    this.setUpscaleRule_(this.containerConstraint_, this.containerImageScaleRule_);
  }

  if (resolution.imgWidthPx != this.imgWidthPx_ ||
      resolution.imgHeightPx != this.imgHeightPx_) {
    // Need to recache images.
    this.imgWidthPx_ = resolution.imgWidthPx;
    this.imgHeightPx_ = resolution.imgHeightPx;
    this.buildImages_();
  }

  if (containerResolution.imgWidthPx != this.containerImgWidthPx_ ||
      containerResolution.imgHeightPx != this.containerImgHeightPx_) {
    this.containerImgWidthPx_ = containerResolution.imgWidthPx;
    this.containerImgHeightPx_ = containerResolution.imgHeightPx;
    if (this.selected_ != null) {
      this.buildImage_(this.selected_);
    }
  }

  if (grid.gridWidthCells != this.gridWidthCells_ ||
      grid.gridHeightCells != this.gridHeightCells_) {
    this.gridWidthCells_ = grid.gridWidthCells;
    this.gridHeightCells_ = grid.gridHeightCells;
    this.buildGrid_();
  }
};

cameraGrid.CameraGrid.prototype.onKeyPress_ = function(e) {
  var character = String.fromCharCode(e.charCode);
  switch (character) {
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case '0':
      var index = e.charCode - '1'.charCodeAt(0);
      if (index == -1) {
        index = 10;
      }
      if (index < this.cells_.length) {
        this.setSelectedNoScan_(index);
      }
      break;
    case 's':
      if (this.scanning_ && this.selected_ != null) {
        // Toggle off
        this.setSelectedNoScan_(this.selected_);
        return;
      }
      this.scanning_ = true;
      if (this.selected_ == null) {
        this.setSelected_(0);
      }
      break;
    case ' ':
      this.scanning_ = !this.scanning_;
      break;
  }
};

cameraGrid.CameraGrid.prototype.scanLeft_ = function() {
  this.setSelected_(this.selected_ > 0 ? this.selected_ - 1 : this.cells_.length - 1);
};

cameraGrid.CameraGrid.prototype.scanRight_ = function() {
  this.setSelected_((this.selected_ + 1) % this.cells_.length);
};

cameraGrid.CameraGrid.prototype.onKeyDown_ = function(e) {
  switch (e.keyCode) {
    case 27: // Esc
      if (this.selected_ != null) {
        // Toggle selected feed off.
        this.setSelectedNoScan_(this.selected_);
      }
      break;
    case 37: // Left arrow
      if (this.selected_ != null) {
        this.scanLeft_();
        this.disableScanning_();
      }
      break;
    case 39: // Right arrow
      if (this.selected_ != null) {
        this.scanRight_();
        this.disableScanning_();
      }
      break;
  }
};

cameraGrid.CameraGrid.prototype.onScanTimer_ = function() {
  if (!this.scanning_ || this.selected_ == null) {
    return;
  }
  this.scanRight_();
};
<|endoftext|>


<commit_before>import fs from 'fs'
import parse from 'csv-parse'
import * as calculate from '../utils/calculate'
import * as getFromCSV from './scriptUtils/getFromCSV'
import R from 'ramda'

const readCSV = (filename, callback) => {
    const parser = parse({delimiter: ',', columns: true, relax: true, auto_parse: true}, (
        err, data) => {
            if (err) throw err
            callback(data)
    })
    fs.createReadStream(__dirname + '/source/' + filename).pipe(parser)
}

const createCourseObj = (csv) => {
    return csv.reduce((acc, ev) => {
        const year = getFromCSV.getYear(ev)
        const term = getFromCSV.getTerm(ev)
        const course = getFromCSV.getCourse(ev)
        const section = getFromCSV.getSection(ev)
        const courseName = getFromCSV.getCourseName(ev)
        const coureseLevel = getFromCSV.getCourseLevel(ev)
        const dept = getFromCSV.getDept(ev)
        const instructorName = getFromCSV.getInstructorName(ev)
        const PUID = getFromCSV.getPUID(ev)
        const gender = getFromCSV.getGender(ev)

        const uniqSectionInTerm = (x) => (x.year === year
            && x.course === course
            && x.term === term
            && x.section === section
            && x.instructorName === instructorName)

        if (acc.some(x => uniqSectionInTerm(x))) {
            const index = acc.findIndex(x => uniqSectionInTerm(x))
            // umi count
            if (typeof (acc[index].UMI1.count[getFromCSV.getUMI1(ev)]) == 'undefined') {
                acc[index].UMI1.count = { ...acc[index].UMI1.count, [getFromCSV.getUMI1(ev)]: 1 }
            } else acc[index].UMI1.count[getFromCSV.getUMI1(ev)] = acc[index].UMI1.count[getFromCSV.getUMI1(ev)] + 1

            if (typeof (acc[index].UMI2.count[getFromCSV.getUMI2(ev)]) == 'undefined') {
                acc[index].UMI2.count = { ...acc[index].UMI2.count, [getFromCSV.getUMI2(ev)]: 1 }
            } else acc[index].UMI2.count[getFromCSV.getUMI2(ev)] = acc[index].UMI2.count[getFromCSV.getUMI2(ev)] + 1

            if (typeof (acc[index].UMI3.count[getFromCSV.getUMI3(ev)]) == 'undefined') {
                acc[index].UMI3.count = { ...acc[index].UMI3.count, [getFromCSV.getUMI3(ev)]: 1 }
            } else acc[index].UMI3.count[getFromCSV.getUMI3(ev)] = acc[index].UMI3.count[getFromCSV.getUMI3(ev)] + 1

            if (typeof (acc[index].UMI4.count[getFromCSV.getUMI4(ev)]) == 'undefined') {
                acc[index].UMI4.count = { ...acc[index].UMI4.count, [getFromCSV.getUMI4(ev)]: 1 }
            } else acc[index].UMI4.count[getFromCSV.getUMI4(ev)] = acc[index].UMI4.count[getFromCSV.getUMI4(ev)] + 1

            if (typeof (acc[index].UMI5.count[getFromCSV.getUMI5(ev)]) == 'undefined') {
                acc[index].UMI5.count = { ...acc[index].UMI5.count, [getFromCSV.getUMI5(ev)]: 1 }
            } else acc[index].UMI5.count[getFromCSV.getUMI5(ev)] = acc[index].UMI5.count[getFromCSV.getUMI5(ev)] + 1

            if (typeof (acc[index].UMI6.count[getFromCSV.getUMI6(ev)]) == 'undefined') {
                acc[index].UMI6.count = { ...acc[index].UMI6.count, [getFromCSV.getUMI6(ev)]: 1}
            } else acc[index].UMI6.count[getFromCSV.getUMI6(ev)] = acc[index].UMI6.count[getFromCSV.getUMI6(ev)] + 1

            acc[index].gender[gender] = acc[index].gender[gender] + 1

            return acc

        } else {
            acc.push({
                year,
                term,
                course,
                section,
                courseName,
                coureseLevel,
                dept,
                instructorName,
                PUID,
                gender: {
                    Female: (gender==='Female') ?  1 : 0,
                    Male: (gender==='Male') ? 1 : 0
                },
                UMI1: {
                    count: {
                        [String(getFromCSV.getUMI1(ev))]: 1
                    }
                },
                UMI2: {
                    count: {
                        [String(getFromCSV.getUMI2(ev))]: 1
                    }
                },
                UMI3: {
                    count: {
                        [String(getFromCSV.getUMI3(ev))]: 1
                    }
                },
                UMI4: {
                    count: {
                        [String(getFromCSV.getUMI4(ev))]: 1
                    }
                },
                UMI5: {
                    count: {
                        [String(getFromCSV.getUMI5(ev))]: 1
                    }
                },
                UMI6: {
                    count: {
                        [String(getFromCSV.getUMI6(ev))]: 1
                    }
                }
            })
            return acc
        }
    }, [])
}

const insertDispersionIndex = (courseObj) => {
    courseObj.UMI1.dispersionIndex = calculate.dispersionIndex(courseObj.UMI1.count)
    courseObj.UMI2.dispersionIndex = calculate.dispersionIndex(courseObj.UMI2.count)
    courseObj.UMI3.dispersionIndex = calculate.dispersionIndex(courseObj.UMI3.count)
    courseObj.UMI4.dispersionIndex = calculate.dispersionIndex(courseObj.UMI4.count)
    courseObj.UMI5.dispersionIndex = calculate.dispersionIndex(courseObj.UMI5.count)
    courseObj.UMI6.dispersionIndex = calculate.dispersionIndex(courseObj.UMI6.count)
    return courseObj
}

const insertAvg = (courseObj) => {
    courseObj.UMI1.average = calculate.umiAvg(courseObj.UMI1.count)
    courseObj.UMI2.average = calculate.umiAvg(courseObj.UMI2.count)
    courseObj.UMI3.average = calculate.umiAvg(courseObj.UMI3.count)
    courseObj.UMI4.average = calculate.umiAvg(courseObj.UMI4.count)
    courseObj.UMI5.average = calculate.umiAvg(courseObj.UMI5.count)
    courseObj.UMI6.average = calculate.umiAvg(courseObj.UMI6.count)
    return courseObj
}

const insertPercentFav = (courseObj) => {
    courseObj.UMI1.percentFavourable = calculate.percentFavourable(courseObj.UMI1.count)
    courseObj.UMI2.percentFavourable = calculate.percentFavourable(courseObj.UMI2.count)
    courseObj.UMI3.percentFavourable = calculate.percentFavourable(courseObj.UMI3.count)
    courseObj.UMI4.percentFavourable = calculate.percentFavourable(courseObj.UMI4.count)
    courseObj.UMI5.percentFavourable = calculate.percentFavourable(courseObj.UMI5.count)
    courseObj.UMI6.percentFavourable = calculate.percentFavourable(courseObj.UMI6.count)
    return courseObj
}

const insertPercentileRanking = (courseObj) => {
    const sortedByUMI1Avg = R.compose(
        R.sort((a, b) => a.UMI1.average - b.UMI1.average)
    )(courseObj)
    sortedByUMI1Avg.map((course) => {
        const filteredByTerm = sortedByUMI1Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI1.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI1', filteredByTerm)
    })

    const sortedByUMI2Avg = R.compose(
        R.sort((a, b) => a.UMI2.average - b.UMI2.average)
    )(sortedByUMI1Avg)
    sortedByUMI2Avg.map((course) => {
        const filteredByTerm = sortedByUMI2Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI2.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI2', filteredByTerm)
    })

    const sortedByUMI3Avg = R.compose(
        R.sort((a, b) => a.UMI3.average - b.UMI3.average)
    )(sortedByUMI2Avg)
    sortedByUMI3Avg.map((course) => {
        const filteredByTerm = sortedByUMI2Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI3.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI3', filteredByTerm)
    })

    const sortedByUMI4Avg = R.compose(
        R.sort((a, b) => a.UMI4.average - b.UMI4.average)
    )(sortedByUMI3Avg)
    sortedByUMI4Avg.map((course) => {
        const filteredByTerm = sortedByUMI4Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI4.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI4', filteredByTerm)
    })

    const sortedByUMI5Avg = R.compose(
        R.sort((a, b) => a.UMI5.average - b.UMI5.average)
    )(sortedByUMI4Avg)
    sortedByUMI5Avg.map((course) => {
        const filteredByTerm = sortedByUMI5Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI5.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI5', filteredByTerm)
    })

    const sortedByUMI6Avg = R.compose(
        R.sort((a, b) => a.UMI6.average - b.UMI6.average)
    )(sortedByUMI5Avg)
    sortedByUMI6Avg.map((course) => {
        const filteredByTerm = sortedByUMI6Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI6.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI6', filteredByTerm)
    })

    return sortedByUMI6Avg
}
// crsnum is the unique identifier for a given year.
readCSV('mockRawData.csv', (csv) => {
    //console.log(csv)
    const courseObjs = createCourseObj(csv)

    courseObjs.map(courseObj => {
        return R.pipe(
            x => insertDispersionIndex(x),
            x => insertAvg(x),
            x => insertPercentFav(x)
        )(courseObj)
    })

    const courseObjWithPercentileRanking = insertPercentileRanking(courseObjs)

    //console.log(JSON.stringify(courseObjWithPercentileRanking, null, 2))

    //console.log(calculate.percentileRankingOfCourse(sortedByUMI1Avg[sortedByUMI1Avg.length-1], 'UMI1', sortedByUMI1Avg))

})

export {
    createCourseObj,
    insertDispersionIndex,
    insertAvg,
    insertPercentFav,
    insertPercentileRanking
}<commit_msg>Implemented percentileRankingByDept as well. Still need to test<commit_after>import fs from 'fs'
import parse from 'csv-parse'
import * as calculate from '../utils/calculate'
import * as getFromCSV from './scriptUtils/getFromCSV'
import R from 'ramda'

const readCSV = (filename, callback) => {
    const parser = parse({delimiter: ',', columns: true, relax: true, auto_parse: true}, (
        err, data) => {
            if (err) throw err
            callback(data)
    })
    fs.createReadStream(__dirname + '/source/' + filename).pipe(parser)
}

const createCourseObj = (csv) => {
    return csv.reduce((acc, ev) => {
        const year = getFromCSV.getYear(ev)
        const term = getFromCSV.getTerm(ev)
        const course = getFromCSV.getCourse(ev)
        const section = getFromCSV.getSection(ev)
        const courseName = getFromCSV.getCourseName(ev)
        const coureseLevel = getFromCSV.getCourseLevel(ev)
        const dept = getFromCSV.getDept(ev)
        const instructorName = getFromCSV.getInstructorName(ev)
        const PUID = getFromCSV.getPUID(ev)
        const gender = getFromCSV.getGender(ev)

        const uniqSectionInTerm = (x) => (x.year === year
            && x.course === course
            && x.term === term
            && x.section === section
            && x.instructorName === instructorName)

        if (acc.some(x => uniqSectionInTerm(x))) {
            const index = acc.findIndex(x => uniqSectionInTerm(x))
            // umi count
            if (typeof (acc[index].UMI1.count[getFromCSV.getUMI1(ev)]) == 'undefined') {
                acc[index].UMI1.count = { ...acc[index].UMI1.count, [getFromCSV.getUMI1(ev)]: 1 }
            } else acc[index].UMI1.count[getFromCSV.getUMI1(ev)] = acc[index].UMI1.count[getFromCSV.getUMI1(ev)] + 1

            if (typeof (acc[index].UMI2.count[getFromCSV.getUMI2(ev)]) == 'undefined') {
                acc[index].UMI2.count = { ...acc[index].UMI2.count, [getFromCSV.getUMI2(ev)]: 1 }
            } else acc[index].UMI2.count[getFromCSV.getUMI2(ev)] = acc[index].UMI2.count[getFromCSV.getUMI2(ev)] + 1

            if (typeof (acc[index].UMI3.count[getFromCSV.getUMI3(ev)]) == 'undefined') {
                acc[index].UMI3.count = { ...acc[index].UMI3.count, [getFromCSV.getUMI3(ev)]: 1 }
            } else acc[index].UMI3.count[getFromCSV.getUMI3(ev)] = acc[index].UMI3.count[getFromCSV.getUMI3(ev)] + 1

            if (typeof (acc[index].UMI4.count[getFromCSV.getUMI4(ev)]) == 'undefined') {
                acc[index].UMI4.count = { ...acc[index].UMI4.count, [getFromCSV.getUMI4(ev)]: 1 }
            } else acc[index].UMI4.count[getFromCSV.getUMI4(ev)] = acc[index].UMI4.count[getFromCSV.getUMI4(ev)] + 1

            if (typeof (acc[index].UMI5.count[getFromCSV.getUMI5(ev)]) == 'undefined') {
                acc[index].UMI5.count = { ...acc[index].UMI5.count, [getFromCSV.getUMI5(ev)]: 1 }
            } else acc[index].UMI5.count[getFromCSV.getUMI5(ev)] = acc[index].UMI5.count[getFromCSV.getUMI5(ev)] + 1

            if (typeof (acc[index].UMI6.count[getFromCSV.getUMI6(ev)]) == 'undefined') {
                acc[index].UMI6.count = { ...acc[index].UMI6.count, [getFromCSV.getUMI6(ev)]: 1}
            } else acc[index].UMI6.count[getFromCSV.getUMI6(ev)] = acc[index].UMI6.count[getFromCSV.getUMI6(ev)] + 1

            acc[index].gender[gender] = acc[index].gender[gender] + 1

            return acc

        } else {
            acc.push({
                year,
                term,
                course,
                section,
                courseName,
                coureseLevel,
                dept,
                instructorName,
                PUID,
                gender: {
                    Female: (gender==='Female') ?  1 : 0,
                    Male: (gender==='Male') ? 1 : 0
                },
                UMI1: {
                    count: {
                        [String(getFromCSV.getUMI1(ev))]: 1
                    }
                },
                UMI2: {
                    count: {
                        [String(getFromCSV.getUMI2(ev))]: 1
                    }
                },
                UMI3: {
                    count: {
                        [String(getFromCSV.getUMI3(ev))]: 1
                    }
                },
                UMI4: {
                    count: {
                        [String(getFromCSV.getUMI4(ev))]: 1
                    }
                },
                UMI5: {
                    count: {
                        [String(getFromCSV.getUMI5(ev))]: 1
                    }
                },
                UMI6: {
                    count: {
                        [String(getFromCSV.getUMI6(ev))]: 1
                    }
                }
            })
            return acc
        }
    }, [])
}

const insertDispersionIndex = (courseObj) => {
    courseObj.UMI1.dispersionIndex = calculate.dispersionIndex(courseObj.UMI1.count)
    courseObj.UMI2.dispersionIndex = calculate.dispersionIndex(courseObj.UMI2.count)
    courseObj.UMI3.dispersionIndex = calculate.dispersionIndex(courseObj.UMI3.count)
    courseObj.UMI4.dispersionIndex = calculate.dispersionIndex(courseObj.UMI4.count)
    courseObj.UMI5.dispersionIndex = calculate.dispersionIndex(courseObj.UMI5.count)
    courseObj.UMI6.dispersionIndex = calculate.dispersionIndex(courseObj.UMI6.count)
    return courseObj
}

const insertAvg = (courseObj) => {
    courseObj.UMI1.average = calculate.umiAvg(courseObj.UMI1.count)
    courseObj.UMI2.average = calculate.umiAvg(courseObj.UMI2.count)
    courseObj.UMI3.average = calculate.umiAvg(courseObj.UMI3.count)
    courseObj.UMI4.average = calculate.umiAvg(courseObj.UMI4.count)
    courseObj.UMI5.average = calculate.umiAvg(courseObj.UMI5.count)
    courseObj.UMI6.average = calculate.umiAvg(courseObj.UMI6.count)
    return courseObj
}

const insertPercentFav = (courseObj) => {
    courseObj.UMI1.percentFavourable = calculate.percentFavourable(courseObj.UMI1.count)
    courseObj.UMI2.percentFavourable = calculate.percentFavourable(courseObj.UMI2.count)
    courseObj.UMI3.percentFavourable = calculate.percentFavourable(courseObj.UMI3.count)
    courseObj.UMI4.percentFavourable = calculate.percentFavourable(courseObj.UMI4.count)
    courseObj.UMI5.percentFavourable = calculate.percentFavourable(courseObj.UMI5.count)
    courseObj.UMI6.percentFavourable = calculate.percentFavourable(courseObj.UMI6.count)
    return courseObj
}

const insertPercentileRanking = (courseObj) => {
    const sortedByUMI1Avg = R.compose(
        R.sort((a, b) => a.UMI1.average - b.UMI1.average)
    )(courseObj)
    sortedByUMI1Avg.map((course) => {
        const filteredByTerm = sortedByUMI1Avg.filter(x => x.year === course.year && x.term === course.term)
        const filteredByDept = filteredByTerm.filter(x => x.dept === course.dept)
        course.UMI1.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI1', filteredByTerm)
        course.UMI1.percentileRankingByDept = calculate.percentileRankingOfCourse(course, 'UMI1', filteredByDept)
    })

    const sortedByUMI2Avg = R.compose(
        R.sort((a, b) => a.UMI2.average - b.UMI2.average)
    )(sortedByUMI1Avg)
    sortedByUMI2Avg.map((course) => {
        const filteredByTerm = sortedByUMI2Avg.filter(x => x.year === course.year && x.term === course.term)
        const filteredByDept = filteredByTerm.filter(x => x.dept === course.dept)
        course.UMI2.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI2', filteredByTerm)
        course.UMI2.percentileRankingByDept = calculate.percentileRankingOfCourse(course, 'UMI2', filteredByDept)
    })

    const sortedByUMI3Avg = R.compose(
        R.sort((a, b) => a.UMI3.average - b.UMI3.average)
    )(sortedByUMI2Avg)
    sortedByUMI3Avg.map((course) => {
        const filteredByTerm = sortedByUMI2Avg.filter(x => x.year === course.year && x.term === course.term)
        const filteredByDept = filteredByTerm.filter(x => x.dept === course.dept)
        course.UMI3.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI3', filteredByTerm)
        course.UMI3.percentileRankingByDept = calculate.percentileRankingOfCourse(course, 'UMI3', filteredByDept)
    })

    const sortedByUMI4Avg = R.compose(
        R.sort((a, b) => a.UMI4.average - b.UMI4.average)
    )(sortedByUMI3Avg)
    sortedByUMI4Avg.map((course) => {
        const filteredByTerm = sortedByUMI4Avg.filter(x => x.year === course.year && x.term === course.term)
        const filteredByDept = filteredByTerm.filter(x => x.dept === course.dept)
        course.UMI4.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI4', filteredByTerm)
        course.UMI4.percentileRankingByDept = calculate.percentileRankingOfCourse(course, 'UMI4', filteredByDept)
    })

    const sortedByUMI5Avg = R.compose(
        R.sort((a, b) => a.UMI5.average - b.UMI5.average)
    )(sortedByUMI4Avg)
    sortedByUMI5Avg.map((course) => {
        const filteredByTerm = sortedByUMI5Avg.filter(x => x.year === course.year && x.term === course.term)
        const filteredByDept = filteredByTerm.filter(x => x.dept === course.dept)
        course.UMI5.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI5', filteredByTerm)
        course.UMI5.percentileRankingByDept = calculate.percentileRankingOfCourse(course, 'UMI5', filteredByDept)
    })

    const sortedByUMI6Avg = R.compose(
        R.sort((a, b) => a.UMI6.average - b.UMI6.average)
    )(sortedByUMI5Avg)
    sortedByUMI6Avg.map((course) => {
        const filteredByTerm = sortedByUMI6Avg.filter(x => x.year === course.year && x.term === course.term)
        const filteredByDept = filteredByTerm.filter(x => x.dept === course.dept)
        course.UMI6.percentileRankingByFaculty = calculate.percentileRankingOfCourse(course, 'UMI6', filteredByTerm)
        course.UMI6.percentileRankingByDept = calculate.percentileRankingOfCourse(course, 'UMI6', filteredByDept)
    })

    return sortedByUMI6Avg
}
// crsnum is the unique identifier for a given year.
readCSV('mockRawData.csv', (csv) => {
    //console.log(csv)
    const courseObjs = createCourseObj(csv)

    courseObjs.map(courseObj => {
        return R.pipe(
            x => insertDispersionIndex(x),
            x => insertAvg(x),
            x => insertPercentFav(x)
        )(courseObj)
    })

    const courseObjWithPercentileRanking = insertPercentileRanking(courseObjs)

    //console.log(JSON.stringify(courseObjWithPercentileRanking, null, 2))

    //console.log(calculate.percentileRankingOfCourse(sortedByUMI1Avg[sortedByUMI1Avg.length-1], 'UMI1', sortedByUMI1Avg))

})

export {
    createCourseObj,
    insertDispersionIndex,
    insertAvg,
    insertPercentFav,
    insertPercentileRanking
}<|endoftext|>


<commit_before>'use strict';

/*
 * This is a wrapper around what is currently called
 * 'ruleQuestion'.
 */

module.exports =
angular.module('quill-grammar.services.question', [
  'underscore',
])
/*@ngInject*/
.factory('Question', function (_) {
  function Question(data) {
    if (data) {
      _.extend(this, data);
    }
    return this;
  }

  Question.MAX_NUM_ATTEMPTS = 2;
  Question.MAX_FIX_ATTEMPTS = 2;

  Question.ResponseStatus = {
    DEFAULT: 0,
    TYPING_ERROR_NON_STRICT: 2,
    TYPING_ERROR_NON_STRICT_UNFIXED: 3,
    INCORRECT: 4,
    CORRECT: 5,
    NO_ANSWER: 6,
    NOT_LONG_ENOUGH: 7,
    TOO_MANY_ATTEMPTS: 8,
  };

  var delim = {
    open: '{',
    close: '}'
  };

  var delimRegEx = {
    open: new RegExp(delim.open, 'g'),
    close: new RegExp(delim.close, 'g')
  };

  var matchAllDelimsStr = delim.open + '([^' + delim.open + '^' + delim.close + '.]*)' + delim.close;

  function removeDelimeters(b) {
    if (typeof (b) !== 'string') {
      throw new Error('Input must be type string removeDelimeters');
    }
    return b.replace(delimRegEx.open, '').replace(delimRegEx.close, '');
  }

  function getCorrectString(b) {
    var answers = _.chain(b)
      .map(removeDelimeters)
      .value();
    return '

' + answers.join('

') + '
';
  }

  Question.ResponseMessages = {};
  Question.ResponseMessages[Question.ResponseStatus.DEFAULT] = 'Check Work';
  Question.ResponseMessages[Question.ResponseStatus.NOT_LONG_ENOUGH] = 'Try again!Your answer is not long enough.';
  Question.ResponseMessages[Question.ResponseStatus.INCORRECT] = 'Try Again! Unfortunately, that answer is incorrect.';
  Question.ResponseMessages[Question.ResponseStatus.TYPING_ERROR_NON_STRICT] = 'You are correct, but you have some typing errors. Please correct them to continue.';
  Question.ResponseMessages[Question.ResponseStatus.TYPING_ERROR_NON_STRICT_UNFIXED] = 'You are correct, but you have some typing errors. You may correct them or continue.';
  Question.ResponseMessages[Question.ResponseStatus.TOO_MANY_ATTEMPTS] = function (question) {
    return 'Incorrect. Correct Answer: ' + getCorrectString(_.pluck(question.answers, 'text'));
  };
  Question.ResponseMessages[Question.ResponseStatus.CORRECT] = 'Well done! That\'s the correct answer.';
  Question.ResponseMessages[Question.ResponseStatus.NO_ANSWER] = 'You must enter a sentence for us to check.';

  function compareEntireAnswerToAnswers(answer) {
    return function (b) {
      var cleaned = removeDelimeters(b);
      return answer === cleaned;
    };
  }

  Question.prototype.ensureLengthIsProper = function (answer) {
    var threshold = 0.8;
    return function (possibleAnswer) {
      var b = removeDelimeters(possibleAnswer);
      return (answer.length / b.length) >= threshold;
    };
  };

  Question.prototype.compareGrammarElementToAnswers = function (answer) {
    var b = this.getPossibleAnswers();
    if (!answer) {
      return false;
    }
    return _.any(b, function (possibleAnswer) {
      var reg = new RegExp(matchAllDelimsStr, 'g');
      var grammarElements = [];
      var tmpArray;
      while ((tmpArray = reg.exec(possibleAnswer)) !== null) {
        grammarElements.push(tmpArray[1]);
      }
      return _.every(grammarElements, function (element) {
        var r = new RegExp('(^|\\W{1,1})' + element + '(\\W{1,1}|$)', 'g');
        return answer.search(r) !== -1;
      });
    });
  };

  Question.prototype.answerIsCorrect = function () {
    return this.status === Question.ResponseStatus.CORRECT ||
      this.status === Question.ResponseStatus.TYPING_ERROR_NON_STRICT;
  };

  Question.prototype.checkAnswer = function () {
    var answer = this.response;
    if (!answer) {
      this.status = Question.ResponseStatus.NO_ANSWER;
      return;
    }
    var possibleAnswers = this.getPossibleAnswers();
    var exactMatch = _.any(possibleAnswers, compareEntireAnswerToAnswers(answer));
    if (exactMatch) {
      this.status = Question.ResponseStatus.CORRECT;
    } else if (!_.every(possibleAnswers, this.ensureLengthIsProper(answer))) {
      this.status = Question.ResponseStatus.NOT_LONG_ENOUGH;
    } else if (this.compareGrammarElementToAnswers(answer)) {
      if (this.fixAttempts) {
        this.fixAttempts++;
      } else {
        this.fixAttempts = 1;
      }
      if (this.fixAttempts >= Question.MAX_FIX_ATTEMPTS) {
        this.status = Question.ResponseStatus.TYPING_ERROR_NON_STRICT_UNFIXED;
      } else {
        this.status = Question.ResponseStatus.TYPING_ERROR_NON_STRICT;
      }
    } else {
      this.status = Question.ResponseStatus.INCORRECT;
      if (this.attempts) {
        this.attempts++;
      } else {
        this.attempts = 1;
      }
    }

    if (this.attempts >= Question.MAX_NUM_ATTEMPTS) {
      if (!this.answerIsCorrect()) {
        this.status = Question.ResponseStatus.TOO_MANY_ATTEMPTS;
      }
    }
    return;
  };

  Question.prototype.getPossibleAnswers = function () {
    return _.pluck(this.answers, 'text');
  };

  Question.prototype.getResponseMessage = function () {
    var msg = Question.ResponseMessages[this.status];
    if (_.isFunction(msg)) {
      return msg(this);
    } else {
      return msg;
    }
  };

  return Question;
});
<commit_msg>Normalise strings for internationalisation.<commit_after>'use strict';

/*
 * This is a wrapper around what is currently called
 * 'ruleQuestion'.
 */

module.exports =
angular.module('quill-grammar.services.question', [
  'underscore',
])
/*@ngInject*/
.factory('Question', function (_) {
  function Question(data) {
    if (data) {
      _.extend(this, data);
    }
    return this;
  }

  Question.MAX_NUM_ATTEMPTS = 2;
  Question.MAX_FIX_ATTEMPTS = 2;

  Question.ResponseStatus = {
    DEFAULT: 0,
    TYPING_ERROR_NON_STRICT: 2,
    TYPING_ERROR_NON_STRICT_UNFIXED: 3,
    INCORRECT: 4,
    CORRECT: 5,
    NO_ANSWER: 6,
    NOT_LONG_ENOUGH: 7,
    TOO_MANY_ATTEMPTS: 8,
  };

  var delim = {
    open: '{',
    close: '}'
  };

  var delimRegEx = {
    open: new RegExp(delim.open, 'g'),
    close: new RegExp(delim.close, 'g')
  };

  var matchAllDelimsStr = delim.open + '([^' + delim.open + '^' + delim.close + '.]*)' + delim.close;

  function removeDelimeters(b) {
    if (typeof (b) !== 'string') {
      throw new Error('Input must be type string removeDelimeters');
    }
    return b.replace(delimRegEx.open, '').replace(delimRegEx.close, '');
  }

  function getCorrectString(b) {
    var answers = _.chain(b)
      .map(removeDelimeters)
      .value();
    return '

' + answers.join('

') + '
';
  }

  Question.ResponseMessages = {};
  Question.ResponseMessages[Question.ResponseStatus.DEFAULT] = 'Check Work';
  Question.ResponseMessages[Question.ResponseStatus.NOT_LONG_ENOUGH] = 'Try again!Your answer is not long enough.';
  Question.ResponseMessages[Question.ResponseStatus.INCORRECT] = 'Try Again! Unfortunately, that answer is incorrect.';
  Question.ResponseMessages[Question.ResponseStatus.TYPING_ERROR_NON_STRICT] = 'You are correct, but you have some typing errors. Please correct them to continue.';
  Question.ResponseMessages[Question.ResponseStatus.TYPING_ERROR_NON_STRICT_UNFIXED] = 'You are correct, but you have some typing errors. You may correct them or continue.';
  Question.ResponseMessages[Question.ResponseStatus.TOO_MANY_ATTEMPTS] = function (question) {
    return 'Incorrect. Correct Answer: ' + getCorrectString(_.pluck(question.answers, 'text'));
  };
  Question.ResponseMessages[Question.ResponseStatus.CORRECT] = 'Well done! That\'s the correct answer.';
  Question.ResponseMessages[Question.ResponseStatus.NO_ANSWER] = 'You must enter a sentence for us to check.';

  function normalize(text) {
    return text.replace(/[\u2018\u2019]/g, '\u0027').replace(/[\u201C\u201D]/g, '\u0022').replace('‚', ',');
  }

  function compareEntireAnswerToAnswers(answer) {
    return function (b) {
      var cleaned = removeDelimeters(b);
      return normalize(answer) === normalize(cleaned);
    };
  }

  Question.prototype.ensureLengthIsProper = function (answer) {
    var threshold = 0.8;
    return function (possibleAnswer) {
      var b = removeDelimeters(possibleAnswer);
      return (answer.length / b.length) >= threshold;
    };
  };

  Question.prototype.compareGrammarElementToAnswers = function (answer) {
    var b = this.getPossibleAnswers();
    if (!answer) {
      return false;
    }
    return _.any(b, function (possibleAnswer) {
      var reg = new RegExp(matchAllDelimsStr, 'g');
      var grammarElements = [];
      var tmpArray;
      while ((tmpArray = reg.exec(possibleAnswer)) !== null) {
        grammarElements.push(tmpArray[1]);
      }
      return _.every(grammarElements, function (element) {
        var r = new RegExp('(^|\\W{1,1})' + element + '(\\W{1,1}|$)', 'g');
        return answer.search(r) !== -1;
      });
    });
  };

  Question.prototype.answerIsCorrect = function () {
    return this.status === Question.ResponseStatus.CORRECT ||
      this.status === Question.ResponseStatus.TYPING_ERROR_NON_STRICT;
  };

  Question.prototype.checkAnswer = function () {
    var answer = this.response;
    if (!answer) {
      this.status = Question.ResponseStatus.NO_ANSWER;
      return;
    }
    var possibleAnswers = this.getPossibleAnswers();
    var exactMatch = _.any(possibleAnswers, compareEntireAnswerToAnswers(answer));
    if (exactMatch) {
      this.status = Question.ResponseStatus.CORRECT;
    } else if (!_.every(possibleAnswers, this.ensureLengthIsProper(answer))) {
      this.status = Question.ResponseStatus.NOT_LONG_ENOUGH;
    } else if (this.compareGrammarElementToAnswers(answer)) {
      if (this.fixAttempts) {
        this.fixAttempts++;
      } else {
        this.fixAttempts = 1;
      }
      if (this.fixAttempts >= Question.MAX_FIX_ATTEMPTS) {
        this.status = Question.ResponseStatus.TYPING_ERROR_NON_STRICT_UNFIXED;
      } else {
        this.status = Question.ResponseStatus.TYPING_ERROR_NON_STRICT;
      }
    } else {
      this.status = Question.ResponseStatus.INCORRECT;
      if (this.attempts) {
        this.attempts++;
      } else {
        this.attempts = 1;
      }
    }

    if (this.attempts >= Question.MAX_NUM_ATTEMPTS) {
      if (!this.answerIsCorrect()) {
        this.status = Question.ResponseStatus.TOO_MANY_ATTEMPTS;
      }
    }
    return;
  };

  Question.prototype.getPossibleAnswers = function () {
    return _.pluck(this.answers, 'text');
  };

  Question.prototype.getResponseMessage = function () {
    var msg = Question.ResponseMessages[this.status];
    if (_.isFunction(msg)) {
      return msg(this);
    } else {
      return msg;
    }
  };

  return Question;
});
<|endoftext|>


<commit_before>/**
 * a library for the Ping.fm API
 * @class SpazPingFM
 *
 */
function SpazPingFM() {};

<commit_msg>- added a bunch of stubs to the ping.fm library<commit_after>/**
 * A library to interact with Ping.FM
 */

/**
 * events raised here
 */
if (!sc.events) { sc.events = {} };
sc.events.pingfmGetUserKeySuccess		= 'pingfmGetUserKeySuccess';
sc.events.pingfmGetUserKeyFailure		= 'pingfmGetUserKeyFailure';
sc.events.pingfmValidateUserKeySuccess	= 'pingfmValidateUserKeySuccess';
sc.events.pingfmValidateUserKeyFailure	= 'pingfmValidateUserKeyFailure';
sc.events.pingfmGetServicesSuccess		= 'pingfmGetServicesSuccess';
sc.events.pingfmGetServicesFailure		= 'pingfmGetServicesFailure';
sc.events.pingfmGetTriggersSuccess		= 'pingfmGetTriggersSuccess';
sc.events.pingfmGetTriggersFailure		= 'pingfmGetTriggersFailure';
sc.events.pingfmGetLatestSuccess		= 'pingfmGetLatestSuccess';
sc.events.pingfmGetLatestFailure		= 'pingfmGetLatestFailure';
sc.events.pingfmPostSuccess				= 'pingfmPostSuccess';
sc.events.pingfmPostFailure				= 'pingfmPostFailure';
sc.events.pingfmTriggerPostSuccess		= 'pingfmTriggerPostSuccess';
sc.events.pingfmTriggerPostFailure		= 'pingfmTriggerPostFailure';

/**
 * a library for the Ping.fm API
 * @class SpazPingFM
 * @param {object} opts
 */
function SpazPingFM(opts) {

	this.apikey  = opts.apikey  || null;
	this.devkey  = opts.devkey  || null;
	this.userkey = opts.userkey || null;

};

SpazPingFM.prototype.getAPIKey = function() {
	return this.apikey;
};
SpazPingFM.prototype.setAPIKey = function(apikey) {
	this.apikey = apikey;
};

SpazPingFM.prototype.getUserKey = function() {
	return this.userkey;
};
SpazPingFM.prototype.setUserKey = function(userkey) {
	this.userkey = userkey;
};

/**
 * Given a mobile key, request a user key via Ajax. Raise event on success or failure
 * Users can get their key from http://ping.fm/key/
 *
 * All ping.fm responses are in XML. This sucks. Oh well.
 *
 * @param {string} mobilekey
 * @return void
 */
SpazPingFM.prototype.getUserKeyWithMobileKey = function(mobilekey) {

};

SpazPingFM.prototype.validateUserKey = function(userkey) {

};

/**
 * get the user's set-up services
 */
SpazPingFM.prototype.getServices = function() {

};

SpazPingFM.prototype.getTriggers = function() {};

SpazPingFM.prototype.getLatest = function(limit, order) {};

SpazPingFM.prototype.post = function(msg, opts) {};

SpazPingFM.prototype.triggerPost = function(msg, trigger, opts) {};<|endoftext|>


<commit_before>export default {
    chan: {
        headers: {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, sdch",
            "Accept-Language": "en-US,en;q=0.8",
            "Host": "a.4cdn.org",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36",
            "Origin": "a.4cdn.org",
            "Referer": "http://boards.4chan.org/",
            "If-Modified-Since": "0",
            "DNT": 1,
            "Upgrade-Insecure-Requests": 1
        }
    }
}

<commit_msg>Add headers for Reddit requests<commit_after>export default {
    chan: {
        headers: {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, sdch",
            "Accept-Language": "en-US,en;q=0.8",
            "Host": "a.4cdn.org",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36",
            "Origin": "a.4cdn.org",
            "Referer": "http://boards.4chan.org/",
            "If-Modified-Since": "0",
            "DNT": 1,
            "Upgrade-Insecure-Requests": 1
        }
    },
    reddit: {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, sdch, br",
        "Accept-Language": "en-US,en;q=0.8",
        "Cache-Control": "max-age=0",
        "Connection": "keep-alive",
        "Host": "www.reddit.com",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36",
        "Upgrade-Insecure-Requests": 1,
        "DNT": 1
    }
}

<|endoftext|>


<commit_before>goog.provide('ngeo.DecorateInteraction');

goog.require('goog.asserts');
goog.require('ngeo');

/**
 * @typedef {function(ol.interaction.Interaction)}
 */
ngeo.DecorateInteraction;

/**
 * This service provides a function that adds an "active" property (using
 * `Object.defineProperty`) to the interaction, making it possible to
 * use ngModel to activate/deactivate interactions.
 *
 * Example:
 * <input type="checkbox" ngModel="interaction.active" />
 *
 * @param {ol.interaction.Interaction} interaction Interaction to decorate.
 */
ngeo.decorateInteraction = function(interaction) {
  goog.asserts.assertInstanceof(interaction, ol.interaction.Interaction);

  Object.defineProperty(interaction, 'active', {
    get: function() {
      return interaction.getActive();
    },
    set: function(val) {
      interaction.setActive(val);
    }
  });
};

ngeoModule.value('ngeoDecorateInteraction', ngeo.decorateInteraction);
<commit_msg>Add @fileoverview to decorateinteraction.js<commit_after>/**
 * @fileoverview Provides a function that adds an "active" property (using
 * `Object.defineProperty`) to an interaction, making it possible to use ngModel
 * to activate/deactivate interactions.
 *
 * Example:
 * <input type="checkbox" ngModel="interaction.active" />
 */

goog.provide('ngeo.DecorateInteraction');

goog.require('goog.asserts');
goog.require('ngeo');

/**
 * @typedef {function(ol.interaction.Interaction)}
 */
ngeo.DecorateInteraction;

/**
 * @param {ol.interaction.Interaction} interaction Interaction to decorate.
 */
ngeo.decorateInteraction = function(interaction) {
  goog.asserts.assertInstanceof(interaction, ol.interaction.Interaction);

  Object.defineProperty(interaction, 'active', {
    get: function() {
      return interaction.getActive();
    },
    set: function(val) {
      interaction.setActive(val);
    }
  });
};

ngeoModule.value('ngeoDecorateInteraction', ngeo.decorateInteraction);
<|endoftext|>


<commit_before>'use strict';

const globalHooks = require('../../../hooks');
const hooks = require('feathers-hooks');

exports.before = {
  all: [],
  find: [],
  get: [],
  create: [],
  update: [],
  patch: [],
  remove: []
};

exports.after = {
  all: [],
  find: [],
  get: [],
  create: [],
  update: [],
  patch: [],
  remove: []
};
<commit_msg>Specify "Requires authentication" for a service<commit_after>'use strict';

const globalHooks = require('../../../hooks');
const hooks = require('feathers-hooks');
const auth = require('feathers-authentication').hooks;

exports.before = {
  all: [
    auth.verifyToken(),
    auth.populateUser(),
    auth.restrictToAuthenticated()
  ],
  find: [],
  get: [],
  create: [],
  update: [],
  patch: [],
  remove: []
};

exports.after = {
  all: [],
  find: [],
  get: [],
  create: [],
  update: [],
  patch: [],
  remove: []
};
<|endoftext|>


<commit_before>/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactErrorUtils
 */

'use strict';

var SyntheticDragEvent = require('SyntheticDragEvent');

var caughtError = null;

/**
 * Call a function while guarding against errors that happens within it.
 *
 * @param {?String} name of the guard to use for logging or debugging
 * @param {Function} func The function to invoke
 * @param {*} a First argument
 * @param {*} b Second argument
 */
function invokeGuardedCallback(name, func, a, b) {
  try {
    return func(a, b);
  } catch (x) {
    if (caughtError === null) {
      caughtError = x;
    }
    return undefined;
  }
}

var ReactErrorUtils = {
  invokeGuardedCallback: invokeGuardedCallback,

  /**
   * Invoked by ReactTestUtils.Simulate so that any errors thrown by the event
   * handler are sure to be rethrown by rethrowCaughtError.
   */
  invokeGuardedCallbackWithCatch: invokeGuardedCallback,

  /**
   * During execution of guarded functions we will capture the first error which
   * we will rethrow to be handled by the top level error handler.
   */
  rethrowCaughtError: function() {
    if (caughtError) {
      var error = caughtError;
      caughtError = null;
      throw error;
    }
  },
};

if (__DEV__) {
  /**
   * To help development we can get better devtools integration by simulating a
   * real browser event.
   */
  if (typeof window !== 'undefined' &&
      typeof window.dispatchEvent === 'function' &&
      typeof document !== 'undefined' &&
      typeof document.createEvent === 'function') {
    var fakeNode = document.createElement('react');
    ReactErrorUtils.invokeGuardedCallback = function(name, func, a, b) {
      if (!canWrapEvent(a)) {
        invokeGuardedCallback(name, func, a, b);
        return;
      }

      var boundFunc = func.bind(null, a, b);
      var evtType = `react-${name}`;
      fakeNode.addEventListener(evtType, boundFunc, false);
      var evt = document.createEvent('Event');
      evt.initEvent(evtType, false, false);
      fakeNode.dispatchEvent(evt);
      fakeNode.removeEventListener(evtType, boundFunc, false);
    };
  }

  var cacheCanWrapEvent = null;

  /**
   * IE and Edge don't allow access to the DataTransfer.dropEffect property when
   * it's wrapped in another event. This function detects whether we're in an
   * environment that behaves this way.
   *
   * @param {*} ev Event that is being tested
   */
  function canWrapEvent(ev) {
    if (!(ev instanceof SyntheticDragEvent)) {
      return true;
    } else if (cacheCanWrapEvent !== null) {
      return cacheCanWrapEvent;
    }

    var canAccessDropEffect = false;
    function handleWrappedEvent() {
      try {
        ev.dataTransfer.dropEffect; // eslint-disable-line no-unused-expressions
        canAccessDropEffect = true;
      } catch (e) {}
    }

    var wrappedEventName = 'react-wrappeddragevent';
    var wrappedEvent = document.createEvent('Event');
    wrappedEvent.initEvent(wrappedEventName, false, false);
    fakeNode.addEventListener(wrappedEventName, handleWrappedEvent, false);
    fakeNode.dispatchEvent(wrappedEvent);
    fakeNode.removeEventListener(wrappedEventName, handleWrappedEvent, false);

    cacheCanWrapEvent = canAccessDropEffect;
    return canAccessDropEffect;
  }
}

module.exports = ReactErrorUtils;
<commit_msg>Revert "Don't wrap drag events in IE/Edge in dev builds" (#6741)<commit_after>/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactErrorUtils
 */

'use strict';

var caughtError = null;

/**
 * Call a function while guarding against errors that happens within it.
 *
 * @param {?String} name of the guard to use for logging or debugging
 * @param {Function} func The function to invoke
 * @param {*} a First argument
 * @param {*} b Second argument
 */
function invokeGuardedCallback(name, func, a, b) {
  try {
    return func(a, b);
  } catch (x) {
    if (caughtError === null) {
      caughtError = x;
    }
    return undefined;
  }
}

var ReactErrorUtils = {
  invokeGuardedCallback: invokeGuardedCallback,

  /**
   * Invoked by ReactTestUtils.Simulate so that any errors thrown by the event
   * handler are sure to be rethrown by rethrowCaughtError.
   */
  invokeGuardedCallbackWithCatch: invokeGuardedCallback,

  /**
   * During execution of guarded functions we will capture the first error which
   * we will rethrow to be handled by the top level error handler.
   */
  rethrowCaughtError: function() {
    if (caughtError) {
      var error = caughtError;
      caughtError = null;
      throw error;
    }
  },
};

if (__DEV__) {
  /**
   * To help development we can get better devtools integration by simulating a
   * real browser event.
   */
  if (typeof window !== 'undefined' &&
      typeof window.dispatchEvent === 'function' &&
      typeof document !== 'undefined' &&
      typeof document.createEvent === 'function') {
    var fakeNode = document.createElement('react');
    ReactErrorUtils.invokeGuardedCallback = function(name, func, a, b) {
      var boundFunc = func.bind(null, a, b);
      var evtType = `react-${name}`;
      fakeNode.addEventListener(evtType, boundFunc, false);
      var evt = document.createEvent('Event');
      evt.initEvent(evtType, false, false);
      fakeNode.dispatchEvent(evt);
      fakeNode.removeEventListener(evtType, boundFunc, false);
    };
  }
}

module.exports = ReactErrorUtils;
<|endoftext|>


<commit_before>/*global require, global*/

var test = require('tape');
var fs = require('fs');
var path = require('path');

var Screen = require('base-widget/spec/util').screenFactory;
var Editor = require('../.');

test("Editor", function (t) {
  var screen = new Screen();
  screen.key('C-q', function () { process.exit(); });
  var editor = new Editor({parent: screen});

  t.test(".open", function (st) {
    st.test("should throw EACCES for a file with perms 000", function (sst) {
      sst.plan(1);

      var perms000File = path.resolve(__dirname, 'fixtures/perms-000');

      // can't be checked in with 000 perms
      var originalPerms = (fs.statSync(perms000File).mode.toString(8).match(/[0-7]{3}$/) || [])[0] || '644';
      fs.chmodSync(perms000File, '000');

      editor.open(perms000File)
        .then(function () { sst.ok(false); })
        .catch(function (err) { sst.equal(err.code, 'EACCES'); })
        .finally(function () { fs.chmodSync(perms000File, originalPerms); })
        .done();
    });
  });

  t.on('end', function () {
    Editor.highlightClient
      .then(function (client) { client.dontRespawn = true; })
      .then(function () { editor.detach(); })
      .done();
  });
});
<commit_msg>style(tests): a bit cleaner<commit_after>/*global require, global*/

var test = require('tape');
var fs = require('fs');
var path = require('path');

var Screen = require('base-widget/spec/util').screenFactory;
var Editor = require('../.');

test("Editor", function (t) {
  var screen = new Screen();
  screen.key('C-q', function () { process.exit(); });
  var editor = new Editor({parent: screen});

  t.test(".open", function (st) {
    st.test("should throw EACCES for a file with perms 000", function (sst) {
      sst.plan(1);

      var perms000File = path.resolve(__dirname, 'fixtures/perms-000');

      // can't be checked in with 000 perms
      var originalPerms = (fs.statSync(perms000File).mode.toString(8).match(/[0-7]{3}$/) || [])[0] || '644';
      fs.chmodSync(perms000File, '000');

      editor.open(perms000File)
        .then(function () { sst.ok(false); })
        .catch(function (err) { sst.equal(err.code, 'EACCES'); })
        .finally(function () { fs.chmodSync(perms000File, originalPerms); })
        .done();
    });
  });

  t.on('end', function () {
    Editor.highlightClient.done(function (client) {
      client.dontRespawn = true;
      editor.detach();
    });
  });
});
<|endoftext|>


<commit_before>import {
  TITLEBAR_HEIGHT
} from '../../../constants'

import {
  backbuttonListener
} from '../../backbutton'

import {
  isTabBarPage
} from '../../../bridge'

import tabBar from '../../tab-bar'

function initPopupSubNVue (subNVueWebview, style, maskWebview) {
  if (!maskWebview.popupSubNVueWebviews) {
    maskWebview.popupSubNVueWebviews = {}
  }

  maskWebview.popupSubNVueWebviews[subNVueWebview.id] = subNVueWebview

  if (process.env.NODE_ENV !== 'production') {
    console.log(
      `UNIAPP[webview][${maskWebview.id}]:add.popupSubNVueWebview[${subNVueWebview.id}]`
    )
  }

  const hideSubNVue = function () {
    maskWebview.setStyle({
      mask: 'none'
    })
    subNVueWebview.hide('auto')
  }
  maskWebview.addEventListener('maskClick', hideSubNVue)
  let isRemoved = false // 增加个 remove 标记，防止出错
  subNVueWebview.addEventListener('show', () => {
    if (!isRemoved) {
      plus.key.removeEventListener('backbutton', backbuttonListener)
      plus.key.addEventListener('backbutton', hideSubNVue)
      isRemoved = true
    }
  })
  subNVueWebview.addEventListener('hide', () => {
    if (isRemoved) {
      plus.key.removeEventListener('backbutton', hideSubNVue)
      plus.key.addEventListener('backbutton', backbuttonListener)
      isRemoved = false
    }
  })
  subNVueWebview.addEventListener('close', () => {
    delete maskWebview.popupSubNVueWebviews[subNVueWebview.id]
    if (isRemoved) {
      plus.key.removeEventListener('backbutton', hideSubNVue)
      plus.key.addEventListener('backbutton', backbuttonListener)
      isRemoved = false
    }
  })
}

function initNormalSubNVue (subNVueWebview, style, webview) {
  webview.append(subNVueWebview)
}

function initSubNVue (subNVue, routeOptions, webview) {
  if (!subNVue.path) {
    return
  }
  const style = subNVue.style || {}
  const isNavigationBar = subNVue.type === 'navigationBar'
  const isPopup = subNVue.type === 'popup'

  delete style.type

  if (isPopup && !subNVue.id) {
    console.warn('subNVue[' + subNVue.path + '] 尚未配置 id')
  }
  // TODO lazyload

  style.uniNView = {
    path: subNVue.path.replace('.nvue', '.js'),
    defaultFontSize: __uniConfig.defaultFontSize,
    viewport: __uniConfig.viewport
  }

  const extras = {
    __uniapp_host: routeOptions.path,
    __uniapp_origin: style.uniNView.path.split('?')[0].replace('.js', ''),
    __uniapp_origin_id: webview.id,
    __uniapp_origin_type: webview.__uniapp_type
  }

  let maskWebview

  if (isNavigationBar) {
    style.position = 'dock'
    style.dock = 'top'
    style.top = 0
    style.width = '100%'
    style.height = TITLEBAR_HEIGHT + plus.navigator.getStatusbarHeight()
    delete style.left
    delete style.right
    delete style.bottom
    delete style.margin
  } else if (isPopup) {
    style.position = 'absolute'
    console.log(isTabBarPage(routeOptions.path))
    if (isTabBarPage(routeOptions.path)) {
      maskWebview = tabBar
    } else {
      maskWebview = webview
    }
    extras.__uniapp_mask = style.mask || 'rgba(0,0,0,0.5)'
    extras.__uniapp_mask_id = maskWebview.id
  }

  if (process.env.NODE_ENV !== 'production') {
    console.log(
      `UNIAPP[webview][${webview.id}]:create[${subNVue.id}]:${JSON.stringify(style)}`
    )
  }
  const subNVueWebview = plus.webview.create('', subNVue.id, style, extras)

  if (isPopup) {
    initPopupSubNVue(subNVueWebview, style, maskWebview)
  } else {
    initNormalSubNVue(subNVueWebview, style, webview)
  }
}

export function initSubNVues (routeOptions, webview) {
  const subNVues = routeOptions.window.subNVues
  if (!subNVues || !subNVues.length) {
    return
  }
  subNVues.forEach(subNVue => {
    initSubNVue(subNVue, routeOptions, webview)
  })
}
<commit_msg>update: 去除无用输出<commit_after>import {
  TITLEBAR_HEIGHT
} from '../../../constants'

import {
  backbuttonListener
} from '../../backbutton'

import {
  isTabBarPage
} from '../../../bridge'

import tabBar from '../../tab-bar'

function initPopupSubNVue (subNVueWebview, style, maskWebview) {
  if (!maskWebview.popupSubNVueWebviews) {
    maskWebview.popupSubNVueWebviews = {}
  }

  maskWebview.popupSubNVueWebviews[subNVueWebview.id] = subNVueWebview

  if (process.env.NODE_ENV !== 'production') {
    console.log(
      `UNIAPP[webview][${maskWebview.id}]:add.popupSubNVueWebview[${subNVueWebview.id}]`
    )
  }

  const hideSubNVue = function () {
    maskWebview.setStyle({
      mask: 'none'
    })
    subNVueWebview.hide('auto')
  }
  maskWebview.addEventListener('maskClick', hideSubNVue)
  let isRemoved = false // 增加个 remove 标记，防止出错
  subNVueWebview.addEventListener('show', () => {
    if (!isRemoved) {
      plus.key.removeEventListener('backbutton', backbuttonListener)
      plus.key.addEventListener('backbutton', hideSubNVue)
      isRemoved = true
    }
  })
  subNVueWebview.addEventListener('hide', () => {
    if (isRemoved) {
      plus.key.removeEventListener('backbutton', hideSubNVue)
      plus.key.addEventListener('backbutton', backbuttonListener)
      isRemoved = false
    }
  })
  subNVueWebview.addEventListener('close', () => {
    delete maskWebview.popupSubNVueWebviews[subNVueWebview.id]
    if (isRemoved) {
      plus.key.removeEventListener('backbutton', hideSubNVue)
      plus.key.addEventListener('backbutton', backbuttonListener)
      isRemoved = false
    }
  })
}

function initNormalSubNVue (subNVueWebview, style, webview) {
  webview.append(subNVueWebview)
}

function initSubNVue (subNVue, routeOptions, webview) {
  if (!subNVue.path) {
    return
  }
  const style = subNVue.style || {}
  const isNavigationBar = subNVue.type === 'navigationBar'
  const isPopup = subNVue.type === 'popup'

  delete style.type

  if (isPopup && !subNVue.id) {
    console.warn('subNVue[' + subNVue.path + '] 尚未配置 id')
  }
  // TODO lazyload

  style.uniNView = {
    path: subNVue.path.replace('.nvue', '.js'),
    defaultFontSize: __uniConfig.defaultFontSize,
    viewport: __uniConfig.viewport
  }

  const extras = {
    __uniapp_host: routeOptions.path,
    __uniapp_origin: style.uniNView.path.split('?')[0].replace('.js', ''),
    __uniapp_origin_id: webview.id,
    __uniapp_origin_type: webview.__uniapp_type
  }

  let maskWebview

  if (isNavigationBar) {
    style.position = 'dock'
    style.dock = 'top'
    style.top = 0
    style.width = '100%'
    style.height = TITLEBAR_HEIGHT + plus.navigator.getStatusbarHeight()
    delete style.left
    delete style.right
    delete style.bottom
    delete style.margin
  } else if (isPopup) {
    style.position = 'absolute'
    if (isTabBarPage(routeOptions.path)) {
      maskWebview = tabBar
    } else {
      maskWebview = webview
    }
    extras.__uniapp_mask = style.mask || 'rgba(0,0,0,0.5)'
    extras.__uniapp_mask_id = maskWebview.id
  }

  if (process.env.NODE_ENV !== 'production') {
    console.log(
      `UNIAPP[webview][${webview.id}]:create[${subNVue.id}]:${JSON.stringify(style)}`
    )
  }
  const subNVueWebview = plus.webview.create('', subNVue.id, style, extras)

  if (isPopup) {
    initPopupSubNVue(subNVueWebview, style, maskWebview)
  } else {
    initNormalSubNVue(subNVueWebview, style, webview)
  }
}

export function initSubNVues (routeOptions, webview) {
  const subNVues = routeOptions.window.subNVues
  if (!subNVues || !subNVues.length) {
    return
  }
  subNVues.forEach(subNVue => {
    initSubNVue(subNVue, routeOptions, webview)
  })
}
<|endoftext|>