<commit_before>import MobileDetect from 'mobile-detect';
<commit_msg>Added class skeleton<commit_after>import MobileDetect from 'mobile-detect';

class Complaint {
    constructor(options) {
        this.keys = {};

        this.keys.app = !isValidKey(options.app) || options.app;
        this.keys.platform = !isValidKey(options.platform) || options.platform;
        this.keys.range = !isValidKey(options.range) || options.range;

        this.md = new MobileDetect(window.navigator.userAgent);
    }

    get isMobile() { // TODO: Consider converting into proxy
        let mobile = this.md.mobile();

        return mobile != null;
    }

    get app() { // TODO: Consider converting into proxy
        let mobile = this.md.mobile();

        return mobile || null;
    }

    get platform() { // TODO: Consider converting into proxy
        let mobile = this.md.mobile();

        return mobile || null;
    }

    get user() { // TODO: Consider converting into proxy
        let mobile = this.md.mobile();

        return mobile || null;
    }

    get device() { // TODO: Consider converting into proxy
        let mobile = this.md.mobile();

        return mobile || null;
    }

    get screen() { // TODO: Consider converting into proxy
        let mobile = this.md.mobile();

        return mobile || null;
    }

    get browser() { // TODO: Consider converting into proxy
        let mobile = this.md.mobile();

        return mobile || null;
    }

    set rating(value) { // TODO: Validate (is int?) / consider converting into proxy
        if (value >= -127 || value <= 127) this.rating = value;
    }

    set description(value) { // TODO: Validate / consider converting into proxy
        this.description = value;
    }

    set comment(value) { // TODO: Validate / consider converting into proxy
        this.comment = value;
    }

    set user(value) { // TODO: Validate / consider converting into proxy
        this.user = value;
    }

    build() {

    }

    send() {

    }

    isValidKey(key) { // TODO: Consider converting into proxy (for each key)
        if (key.length != 32) throw new Error({ name: 'RatingError', message: 'Invalid key' });

        return true;
    }
}<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View
} from 'react-native';

export default class LodashExample extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Hello, world :)
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  }
});

<commit_msg>example<commit_after>import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View
} from 'react-native';

import _ from 'lodash';

export default class LodashExample extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Your lucky number is { _.random(100) }!
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  }
});

<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';

export default class Checkbox extends Component {

  handleChange(e) {
    const { onChange, checked } = this.props;
    onChange(e.target.checked);
  }

  render() {
    const { text, checked } = this.props;
    return (

        {text}

          <input onChange={(e) => this.handleChange(e)}
            type="checkbox"
            defaultChecked={checked}
            ref="checkbox" />

    );
  }
}

Checkbox.propTypes = {
  text: PropTypes.string.isRequired,
  onChange: PropTypes.func.isRequired
};
<commit_msg>add checked prop to prototypes<commit_after>import React, { Component, PropTypes } from 'react';

export default class Checkbox extends Component {

  handleChange(e) {
    const { onChange, checked } = this.props;
    onChange(e.target.checked);
  }

  render() {
    const { text, checked } = this.props;
    return (

        {text}

          <input onChange={(e) => this.handleChange(e)}
            type="checkbox"
            defaultChecked={checked}
            ref="checkbox" />

    );
  }
}

Checkbox.propTypes = {
  text: PropTypes.string.isRequired,
  checked: PropTypes.bool.isRequired,
  onChange: PropTypes.func.isRequired
};
<|endoftext|>


<commit_before>const DataLoader = require('dataloader')
const { parseAsync } = require('json2csv')
const _ = require('lodash/fp')

const filters = require('../../filters')
const cashOutTx = require('../../../cash-out/cash-out-tx')
const cashInTx = require('../../../cash-in/cash-in-tx')
const transactions = require('../../services/transactions')
const anonymous = require('../../../constants').anonymousCustomer
const logDateFormat = require('../../../logs').logDateFormat

const transactionsLoader = new DataLoader(ids => transactions.getCustomerTransactionsBatch(ids))
const txLogFields = ['txClass', 'id', 'deviceId', 'toAddress', 'cryptoAtoms',
  'cryptoCode', 'fiat', 'fiatCode', 'fee', 'status', 'fiatProfit',
  'dispense', 'notified', 'redeem', 'phone', 'error',
  'created', 'confirmedAt', 'hdIndex', 'swept', 'timedout',
  'dispenseConfirmed', 'provisioned1', 'provisioned2',
  'denomination1', 'denomination2', 'errorCode', 'customerId',
  'txVersion', 'publishedAt', 'termsAccepted', 'layer2Address',
  'commissionPercentage', 'rawTickerPrice', 'receivedCryptoAtoms',
  'discount', 'txHash', 'customerPhone', 'customerIdCardDataNumber',
  'customerIdCardDataExpiration', 'customerIdCardData', 'customerName',
  'customerFrontCameraPath', 'customerIdCardPhotoPath', 'expired', 'machineName', 'walletScore']

const resolvers = {
  Customer: {
    transactions: parent => transactionsLoader.load(parent.id)
  },
  Transaction: {
    isAnonymous: parent => (parent.customerId === anonymous.uuid)
  },
  Query: {
    transactions: (...[, { from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers }]) =>
      transactions.batch(from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers),
    transactionsCsv: (...[, { from, until, limit, offset, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, timezone, excludeTestingCustomers, simplified }]) =>
      transactions.batch(from, until, limit, offset, null, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers, simplified)
        .then(data => parseAsync(logDateFormat(timezone, data, ['created', 'sendTime']), { fields: txLogFields })),
    transactionCsv: (...[, { id, txClass, timezone }]) =>
      transactions.getTx(id, txClass).then(data =>
        parseAsync(logDateFormat(timezone, [data], ['created', 'sendTime']))
      ),
    txAssociatedDataCsv: (...[, { id, txClass, timezone }]) =>
      transactions.getTxAssociatedData(id, txClass).then(data =>
        parseAsync(logDateFormat(timezone, data, ['created']))
      ),
    transactionFilters: () => filters.transaction()
  },
  Mutation: {
    cancelCashOutTransaction: (...[, { id }]) => cashOutTx.cancel(id),
    cancelCashInTransaction: (...[, { id }]) => cashInTx.cancel(id)
  }
}

module.exports = resolvers
<commit_msg>fix: disable cache<commit_after>const DataLoader = require('dataloader')
const { parseAsync } = require('json2csv')
const _ = require('lodash/fp')

const filters = require('../../filters')
const cashOutTx = require('../../../cash-out/cash-out-tx')
const cashInTx = require('../../../cash-in/cash-in-tx')
const transactions = require('../../services/transactions')
const anonymous = require('../../../constants').anonymousCustomer
const logDateFormat = require('../../../logs').logDateFormat

const transactionsLoader = new DataLoader(ids => transactions.getCustomerTransactionsBatch(ids), { cache: false })
const txLogFields = ['txClass', 'id', 'deviceId', 'toAddress', 'cryptoAtoms',
  'cryptoCode', 'fiat', 'fiatCode', 'fee', 'status', 'fiatProfit',
  'dispense', 'notified', 'redeem', 'phone', 'error',
  'created', 'confirmedAt', 'hdIndex', 'swept', 'timedout',
  'dispenseConfirmed', 'provisioned1', 'provisioned2',
  'denomination1', 'denomination2', 'errorCode', 'customerId',
  'txVersion', 'publishedAt', 'termsAccepted', 'layer2Address',
  'commissionPercentage', 'rawTickerPrice', 'receivedCryptoAtoms',
  'discount', 'txHash', 'customerPhone', 'customerIdCardDataNumber',
  'customerIdCardDataExpiration', 'customerIdCardData', 'customerName',
  'customerFrontCameraPath', 'customerIdCardPhotoPath', 'expired', 'machineName', 'walletScore']

const resolvers = {
  Customer: {
    transactions: parent => transactionsLoader.load(parent.id)
  },
  Transaction: {
    isAnonymous: parent => (parent.customerId === anonymous.uuid)
  },
  Query: {
    transactions: (...[, { from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers }]) =>
      transactions.batch(from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers),
    transactionsCsv: (...[, { from, until, limit, offset, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, timezone, excludeTestingCustomers, simplified }]) =>
      transactions.batch(from, until, limit, offset, null, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers, simplified)
        .then(data => parseAsync(logDateFormat(timezone, data, ['created', 'sendTime']), { fields: txLogFields })),
    transactionCsv: (...[, { id, txClass, timezone }]) =>
      transactions.getTx(id, txClass).then(data =>
        parseAsync(logDateFormat(timezone, [data], ['created', 'sendTime']))
      ),
    txAssociatedDataCsv: (...[, { id, txClass, timezone }]) =>
      transactions.getTxAssociatedData(id, txClass).then(data =>
        parseAsync(logDateFormat(timezone, data, ['created']))
      ),
    transactionFilters: () => filters.transaction()
  },
  Mutation: {
    cancelCashOutTransaction: (...[, { id }]) => cashOutTx.cancel(id),
    cancelCashInTransaction: (...[, { id }]) => cashInTx.cancel(id)
  }
}

module.exports = resolvers
<|endoftext|>


<commit_before>// initalize pages server
require('./pages')
<commit_msg>moving core init into main servers index file<commit_after>// init core partials, routes, etc.
require('ses-core');

// initalize pages server
require('./pages');
<|endoftext|>


<commit_before>(function(window,document,undefined){
    function addEvent(elem,type,fn,useCapture){
        if (elem.addEventListener) {
            if(useCapture == undefined)
                useCapture = false;
            elem.addEventListener(type, fn, useCapture);//DOM2.0
            return;
        }else if(elem.attachEvent){
            elem.attachEvent('on'+type, fn);
            return;
        }else{
            elem['on'+type] = fn;
            return;
        }
    }

    function assert(fn){
        var div = document.createElement("div");
        try{
            return !!fn(div);
        }catch(e){
            return false;
        }finally{
            if(div.parentNode){
                div.parentNode.removeChild( div );
            }

            div = null;
        }
    }

    var Pizzle = (function(window){
        var Express = {};

        var Support = {};

        function getExpr(){
            var whitespace = "[\\x20\\t\\r\\n\\f]";//空白字符正则字符串
            var operators = "([*^$|!~]?=)";//可用的属性操作符
            var rcomma = "^" + whitespace + "*," + whitespace + "*";//并联选择器的正则
            var rcombinators = "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*";//关系选择器正则
            var characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+";//字符编码正则字符串
            var identifier = characterEncoding.replace( "w", "w#" );
            var attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
                "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]";//属性选择器正则
            var pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)";//伪类正则字符串
            var rtrim = "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$"; // 去掉两端空白和字符串中的反斜杠（如果连续两个去掉一个）
            //var rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/;//仅仅单个id或tag、class选择器正则（用来快速解析并获取元素）
            var rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
            var rnative = /^[^{]+\{\s*\[native code/;//原生函数正则
            var expando = "pizzle" + 1 * new Date();

            return {
                rcomma:new RegExp(rcomma),
                rcombinators:new RegExp(rcombinators),
                characterEncoding:characterEncoding,
                attributes:attributes,
                pseudos:pseudos,
                rtrim:new RegExp(rtrim,"g"),
                rquickExpr:new RegExp(rquickExpr),
                rnative:rnative,
                expando:expando
            };
        }

        function getMatchExpr(){
            var idExpr = "^#(" + Express.expr['characterEncoding'] + ")";
            var classExpr = "^\\.(" + Express.expr['characterEncoding'] + ")";
            var tagExpr =  "^(" + Express.expr['characterEncoding'].replace( "w", "w*" ) + ")";
            var attrExpr = "^" + Express.expr['attributes'];
            var pseudosExpr = "^" + Express.expr['pseudos'];
            var childEpr = "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + Express.expr['whitespace'] +
                "*(even|odd|(([+-]|)(\\d*)n|)" + Express.expr['whitespace'] + "*(?:([+-]|)" + Express.expr['whitespace'] +
                "*(\\d+)|))" + Express.expr['whitespace'] + "*\\)|)";

            return {
                "ID":new RegExp(idExpr),
                "CLASS":new RegExp(classExpr),
                "TAG":new RegExp(tagExpr),
                "ATTR":new RegExp(attrExpr),
                "PSEUDO":new RegExp(pseudosExpr),
                "CHILD":new RegExp(childEpr, "i" )
            };
        }

        function Pizzle(selector){
            if(!selector)
                return;

            return Pizzle.participle(selector);
        }

        Pizzle.init = function(){
            Express.expr = getExpr();
            Express.matchExpr = getMatchExpr();
            Support.isQSA = Express.expr['rnative'].test(document.querySelectorAll);
        };

        /**
         * participle selector
         * @param selector
         */
        Pizzle.participle = function(selector){
            if(typeof selector !== "string"){
                return [];
            }

            Pizzle.init();
            var match,matched,tokens = [],groups = [];

            //Handle HTML strings
            if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
                // Assume that strings that start and end with <> are HTML and skip the regex check
                var m = selector.substring(1,selector.length - 1);
                match = [ null, null,m, null ];
            } else {
                match = Express.expr.rquickExpr.exec(selector);
            }

            if(match){
                matched = match[1] || match[2] || match[3] ||'';
                var type = (matched == match[1]) ? "ID" : ((matched == match[2]) ? "TAG" : (matched == match[3]) ? "CLASS" : "");
                var sep = undefined,newMatched = undefined,oldMatched = match[0];
                if(oldMatched.charAt(0) === "#" || oldMatched.charAt(0) === "." || oldMatched.charAt(0) === ":"){
                    sep = oldMatched.charAt(0);
                    newMatched = oldMatched.substring(1,oldMatched.length);
                }

                tokens.push({
                    value: newMatched ? newMatched : matched,
                    type: type,
                    sep: sep ? sep : "",
                    matches: selector
                });

                groups.push(tokens);
                return groups;
            }else{
                return Pizzle.tokenize(selector);
            }
        };

        /**
         * participle many selectors
         * @param selector
         * @returns {Array}
         */
        Pizzle.tokenize = function(selector){
            var matched,match,tokens,type,str = selector,groups = [];
            while(str){
                //,
                if(!matched || (match = Express.expr.rcomma.exec(str))){
                    if(match){
                        str = str.slice(match[0].length) || str;
                    }
                    groups.push(tokens = []);
                }

                matched = false;
                //+-~
                if((match = Express.expr.rcombinators.exec(str))!=null){
                    matched = match.shift();
                    tokens.push({
                        value:matched,
                        type:match[0].replace(Express.expr.rtrim," ")
                    });

                    str = str.slice(matched.length);
                }

                for(type in Express.matchExpr){
                    if((match = Express.matchExpr[ type ].exec( str ))!=null){
                        matched = match.shift();
                        var sep = undefined,newMatched = undefined;
                        if(matched.charAt(0) === "#" || matched.charAt(0) === "." || matched.charAt(0) === ":"){
                            sep = matched.charAt(0);
                            newMatched = matched.substring(1,matched.length);
                        }
                        tokens.push({
                            value: newMatched ? newMatched : matched,
                            type: type,
                            sep: sep ? sep : "",
                            matches: match
                        });

                        str = str.slice(matched.length);
                    }
                }

                if ( !matched ) {
                    break;
                }
            }

            return groups;
        };

        Pizzle.error = function(message){
            throw new Error( "Syntax error, unrecognized expression: " + message );
        };

        Pizzle.isXml = function(elem){
            var documentElement = elem && (elem.ownerDocument || elem).documentElement;
            return documentElement ? documentElement.nodeName !== "HTML" : false;
        };

        return Pizzle;
    })(window);

    var Selector = (function(Pizzle){

        /*function ready(callback){
         addEvent(window,'load',function(){
         callback();
         });
         }*/
        var Normal = {

            isArray : function(obj){
                var length = "length" in obj && obj.length;

                if(typeof obj === "function"){
                    return false;
                }

                if(obj.nodeType === 1 && length)
                    return true;

                return typeof obj === "array" || length === 0 ||
                    typeof length === "number" && length > 0 && (length - 1) in obj;

            },

            isNative:function(fn){
                return Expr.rnative.test(fn + "");
            }

        };

        var Expr = {
            whitespace : "[\\x20\\t\\r\\n\\f]",//空白字符正则字符串
            rnative : /^[^{]+\{\s*\[native code/,//原生函数正则
        };

        var Support = {
            getElementsByClassName : Normal.isNative(document.getElementsByClassName)

        };

        var hooks = {
            val : function(){
                if(this.context.length == 0)
                    return "";
                /*if(!this.groups || this.groups.length == 0)
                    return "";
                var result = analysisGroup(true)(this.groups);

                if(result.length == 0 || !result[0][0].context){
                    return "";
                }*/

                var elem = this.context;
                var ret = elem.value;
                return ret ? ((typeof ret === "string") ? ret : "") : "";
            },

            each:function(obj,callback,args){
                var value,i = 0,length = obj.length;
                var isArray = Normal.isArray(obj);
                if(args){
                    if(isArray){
                        for(;i < length ; i++){
                            value = callback.apply(obj[i][0],args);
                            if(value === false){
                                break;
                            }
                        }
                    }else{
                        for(i in obj){
                            value = callback.apply(obj[i][0],args);
                            if(value === false){
                                break;
                            }
                        }
                    }
                }else{
                    if(isArray){
                        for(;i<length;i++){
                            value = callback.call(obj[i][0], i, obj[i][0].context);

                            if (value === false) {
                                break;
                            }
                        }
                    }else{
                        for(i in obj){
                            value = callback.call(obj[i][0], i, obj[i][0].context);

                            if (value === false) {
                                break;
                            }
                        }
                    }
                }

                return obj;
            }
        };

        function analysisGroup(parseOnly){
            return function(groups){
                var results = [];
                var i = 0,len = groups.length;
                for(;i<len;i++){
                    var obj = groups[i];
                    if(!obj || !obj[0])  break;

                    var _obj = obj[0];
                    var type = _obj.type;
                    var value = _obj.value;

                    if(!value)
                        continue;

                    var object = filter[type](value)() || [];

                    if(object.length == 0)
                        continue;

                    if(object.length > 1){
                        for(var j =0;j<object.length;j++){
                            results.push([object[j]]);
                        }
                    }else{
                        results.push(object);
                    }

                    if(parseOnly){
                        break;
                    }
                }

                return results;
            };
        }

        function Selector(selector){
            return (typeof selector === "function" ) ? Selector.ready(selector) : Selector.init.call(this,selector);
        }

        Selector.ready = function(callback){
            document.onreadystatechange = function(){
                if(document.readyState == "complete"){
                    callback();
                }
            }
        };

        Selector.init = function(selector){
            if(typeof selector === "string"){
                var groups = Pizzle(selector);
                this.groups = groups;
                this.context = [];

                if(!this.groups || this.groups.length == 0)
                    return this;

                var result = analysisGroup()(this.groups);

                if(result.length == 0){
                    return this;
                }

                this.context = result;
                console.log(this);
                return this;
            }else if(selector.context && selector.context.nodeType && selector.context.nodeType === 1){//elem
                var groups = [];
                groups.push([{
                    sep:selector.sep?selector.sep:"",
                    type:selector.type?selector.type:"",
                    value:selector.value?selector.value:""
                }]);
                this.groups = groups;
                this.context = selector.context;
                return this;
            }

        };

        Selector.prototype.val = hooks.val;
        Selector.prototype.each = function(callback,args){
            return hooks.each(this.context,callback,args);
        };

        var filter = {
            "ID":function(id){
                return function(elem){
                    elem = elem ? elem :((typeof document.getElementById(id) !== "undefined")?document.getElementById(id) : document.getAttributeNode(id));
                    var results = [];
                    if(!elem)
                        return results;

                    var isAttr = (elem.getAttribute("id") === id) ? true : false;
                    if(!isAttr){
                        return results;
                    }
                    results.push({
                        context:elem,
                        type:"ID",
                        value:id,
                        sep:"#"
                    });

                    return results;
                }
            },

            "CLASS":function(className){
                return function(){
                    if(Support.getElementsByClassName){
                        var contexts = document.getElementsByClassName(className);
                        var len = contexts.length,i = 0;
                        var results = [];
                        if(!contexts.length){
                            return results;
                        }

                        for(; i < len; i++){
                            results.push({
                                context:contexts[i]?contexts[i]:"",
                                type:"CLASS",
                                sep:".",
                                value:className
                            });
                        }

                        return results;
                    }else{
                        return [];
                    }
                }
            },

            "TAG":function(nodeNameSelector){
                var nodeName = nodeNameSelector.toLowerCase();
                return nodeNameSelector === "*" ? function(){return true;} :function(elem){
                    return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
                }
            },

            "ATTR":function(name, operator, check){

            },

            "CHILD":function(type, what, argument, first, last){

            }
        };

        return Selector;
    })(Pizzle);

    function getSelector(selector){
        return new Selector(selector);
    }

    window._ = window.$ = getSelector;
    window.Selector = getSelector;
})(window,document,undefined);

/**
 节点类型	                      描述	                                                   子节点
 1	Element	                      代表元素	                                                Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReference
 2	Attr	                      代表属性	                                                Text, EntityReference
 3	Text	                      代表元素或属性中的文本内容。	                                None
 4	CDATASection	              代表文档中的 CDATA 部分（不会由解析器解析的文本）。	            None
 5	EntityReference	              代表实体引用。	                                            Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
 6	Entity	                      代表实体。	                                                Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
 7	ProcessingInstruction	      代表处理指令。	                                            None
 8	Comment	                      代表注释。	                                                None
 9	Document	                  代表整个文档（DOM 树的根节点）。	                            Element, ProcessingInstruction, Comment, DocumentType
 10	DocumentType	              向为文档定义的实体提供接口	                                None
 11	DocumentFragment	          代表轻量级的 Document 对象，能够容纳文档的某个部分	            Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
 12	Notation	                  代表 DTD 中声明的符号。	                                    None;
 **/
<commit_msg>Update selector.js<commit_after>(function(window,document,undefined){
    function addEvent(elem,type,fn,useCapture){
        if (elem.addEventListener) {
            if(useCapture == undefined)
                useCapture = false;
            elem.addEventListener(type, fn, useCapture);//DOM2.0
            return;
        }else if(elem.attachEvent){
            elem.attachEvent('on'+type, fn);
            return;
        }else{
            elem['on'+type] = fn;
            return;
        }
    }

    function assert(fn){
        var div = document.createElement("div");
        try{
            return !!fn(div);
        }catch(e){
            return false;
        }finally{
            if(div.parentNode){
                div.parentNode.removeChild( div );
            }

            div = null;
        }
    }

    var Pizzle = (function(window){
        var Express = {};

        var Support = {};

        function getExpr(){
            var whitespace = "[\\x20\\t\\r\\n\\f]";//空白字符正则字符串
            var operators = "([*^$|!~]?=)";//可用的属性操作符
            var rcomma = "^" + whitespace + "*," + whitespace + "*";//并联选择器的正则
            var rcombinators = "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*";//关系选择器正则
            var characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+";//字符编码正则字符串
            var identifier = characterEncoding.replace( "w", "w#" );
            var attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
                "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]";//属性选择器正则
            var pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)";//伪类正则字符串
            var rtrim = "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$"; // 去掉两端空白和字符串中的反斜杠（如果连续两个去掉一个）
            //var rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/;//仅仅单个id或tag、class选择器正则（用来快速解析并获取元素）
            var rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
            var rnative = /^[^{]+\{\s*\[native code/;//原生函数正则
            var expando = "pizzle" + 1 * new Date();

            return {
                rcomma:new RegExp(rcomma),
                rcombinators:new RegExp(rcombinators),
                characterEncoding:characterEncoding,
                attributes:attributes,
                pseudos:pseudos,
                rtrim:new RegExp(rtrim,"g"),
                rquickExpr:new RegExp(rquickExpr),
                rnative:rnative,
                expando:expando
            };
        }

        function getMatchExpr(){
            var idExpr = "^#(" + Express.expr['characterEncoding'] + ")";
            var classExpr = "^\\.(" + Express.expr['characterEncoding'] + ")";
            var tagExpr =  "^(" + Express.expr['characterEncoding'].replace( "w", "w*" ) + ")";
            var attrExpr = "^" + Express.expr['attributes'];
            var pseudosExpr = "^" + Express.expr['pseudos'];
            var childEpr = "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + Express.expr['whitespace'] +
                "*(even|odd|(([+-]|)(\\d*)n|)" + Express.expr['whitespace'] + "*(?:([+-]|)" + Express.expr['whitespace'] +
                "*(\\d+)|))" + Express.expr['whitespace'] + "*\\)|)";

            return {
                "ID":new RegExp(idExpr),
                "CLASS":new RegExp(classExpr),
                "TAG":new RegExp(tagExpr),
                "ATTR":new RegExp(attrExpr),
                "PSEUDO":new RegExp(pseudosExpr),
                "CHILD":new RegExp(childEpr, "i" )
            };
        }

        function Pizzle(selector){
            if(!selector)
                return;

            return Pizzle.participle(selector);
        }

        Pizzle.init = function(){
            Express.expr = getExpr();
            Express.matchExpr = getMatchExpr();
            Support.isQSA = Express.expr['rnative'].test(document.querySelectorAll);
        };

        /**
         * participle selector
         * @param selector
         */
        Pizzle.participle = function(selector){
            if(typeof selector !== "string"){
                return [];
            }

            Pizzle.init();
            var match,matched,tokens = [],groups = [];

            //Handle HTML strings
            if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
                // Assume that strings that start and end with <> are HTML and skip the regex check
                var m = selector.substring(1,selector.length - 1);
                match = [ null, null,m, null ];
            } else {
                match = Express.expr.rquickExpr.exec(selector);
            }

            if(match){
                matched = match[1] || match[2] || match[3] ||'';
                var type = (matched == match[1]) ? "ID" : ((matched == match[2]) ? "TAG" : (matched == match[3]) ? "CLASS" : "");
                var sep = undefined,newMatched = undefined,oldMatched = match[0];
                if(oldMatched.charAt(0) === "#" || oldMatched.charAt(0) === "." || oldMatched.charAt(0) === ":"){
                    sep = oldMatched.charAt(0);
                    newMatched = oldMatched.substring(1,oldMatched.length);
                }

                tokens.push({
                    value: newMatched ? newMatched : matched,
                    type: type,
                    sep: sep ? sep : "",
                    matches: selector
                });

                groups.push(tokens);
                return groups;
            }else{
                return Pizzle.tokenize(selector);
            }
        };

        /**
         * participle many selectors
         * @param selector
         * @returns {Array}
         */
        Pizzle.tokenize = function(selector){
            var matched,match,tokens,type,str = selector,groups = [];
            while(str){
                //,
                if(!matched || (match = Express.expr.rcomma.exec(str))){
                    if(match){
                        str = str.slice(match[0].length) || str;
                    }
                    groups.push(tokens = []);
                }

                matched = false;
                //+-~
                if((match = Express.expr.rcombinators.exec(str))!=null){
                    matched = match.shift();
                    tokens.push({
                        value:matched,
                        type:match[0].replace(Express.expr.rtrim," ")
                    });

                    str = str.slice(matched.length);
                }

                for(type in Express.matchExpr){
                    if((match = Express.matchExpr[ type ].exec( str ))!=null){
                        matched = match.shift();
                        var sep = undefined,newMatched = undefined;
                        if(matched.charAt(0) === "#" || matched.charAt(0) === "." || matched.charAt(0) === ":"){
                            sep = matched.charAt(0);
                            newMatched = matched.substring(1,matched.length);
                        }
                        tokens.push({
                            value: newMatched ? newMatched : matched,
                            type: type,
                            sep: sep ? sep : "",
                            matches: match
                        });

                        str = str.slice(matched.length);
                    }
                }

                if ( !matched ) {
                    break;
                }
            }

            return groups;
        };

        Pizzle.error = function(message){
            throw new Error( "Syntax error, unrecognized expression: " + message );
        };

        Pizzle.isXml = function(elem){
            var documentElement = elem && (elem.ownerDocument || elem).documentElement;
            return documentElement ? documentElement.nodeName !== "HTML" : false;
        };

        return Pizzle;
    })(window);

    var Selector = (function(Pizzle){

        /*function ready(callback){
         addEvent(window,'load',function(){
         callback();
         });
         }*/
        var Normal = {

            isArray : function(obj){
                var length = "length" in obj && obj.length;

                if(typeof obj === "function"){
                    return false;
                }

                if(obj.nodeType === 1 && length)
                    return true;

                return typeof obj === "array" || length === 0 ||
                    typeof length === "number" && length > 0 && (length - 1) in obj;

            },

            isNative:function(fn){
                return Expr.rnative.test(fn + "");
            }

        };

        var Expr = {
            whitespace : "[\\x20\\t\\r\\n\\f]",//空白字符正则字符串
            rnative : /^[^{]+\{\s*\[native code/,//原生函数正则
        };

        var Support = {
            getElementsByClassName : Normal.isNative(document.getElementsByClassName)

        };

        var hooks = {
            val : function(){
                if(this.context.length == 0)
                    return "";
                /*if(!this.groups || this.groups.length == 0)
                    return "";
                var result = analysisGroup(true)(this.groups);

                if(result.length == 0 || !result[0][0].context){
                    return "";
                }*/

                var elem = this.context;
                var ret = elem.value;
                return ret ? ((typeof ret === "string") ? ret : "") : "";
            },

            each:function(obj,callback,args){
                var value,i = 0,length = obj.length;
                var isArray = Normal.isArray(obj);
                if(args){
                    if(isArray){
                        for(;i < length ; i++){
                            value = callback.apply(obj[i][0],args);
                            if(value === false){
                                break;
                            }
                        }
                    }else{
                        for(i in obj){
                            value = callback.apply(obj[i][0],args);
                            if(value === false){
                                break;
                            }
                        }
                    }
                }else{
                    if(isArray){
                        for(;i<length;i++){
                            value = callback.call(obj[i][0], i, obj[i][0].context);

                            if (value === false) {
                                break;
                            }
                        }
                    }else{
                        for(i in obj){
                            value = callback.call(obj[i][0], i, obj[i][0].context);

                            if (value === false) {
                                break;
                            }
                        }
                    }
                }

                return obj;
            }
        };

        function analysisGroup(parseOnly){
            return function(groups){
                var results = [];
                var i = 0,len = groups.length;
                for(;i<len;i++){
                    var obj = groups[i];
                    if(!obj || !obj[0])  break;

                    var _obj = obj[0];
                    var type = _obj.type;
                    var value = _obj.value;

                    if(!value)
                        continue;

                    var object = filter[type](value)() || [];

                    if(object.length == 0)
                        continue;

                    if(object.length > 1){
                        for(var j =0;j<object.length;j++){
                            results.push([object[j]]);
                        }
                    }else{
                        results.push(object);
                    }

                    if(parseOnly){
                        break;
                    }
                }

                return results;
            };
        }

        function Selector(selector){
            return (typeof selector === "function" ) ? Selector.ready(selector) : Selector.init.call(this,selector);
        }

        Selector.ready = function(callback){
            document.onreadystatechange = function(){
                if(document.readyState == "complete"){
                    callback();
                }
            }
        };

        Selector.init = function(selector){
            if(typeof selector === "string"){
                var groups = Pizzle(selector);
                this.groups = groups;
                this.context = [];

                if(!this.groups || this.groups.length == 0)
                    return this;

                var result = analysisGroup()(this.groups);

                if(result.length == 0){
                    return this;
                }

                this.context = result;
                console.log(this);
                return this;
            }else if(selector.context && selector.context.nodeType && selector.context.nodeType === 1){//elem
                var groups = [];
                groups.push([{
                    sep:selector.sep?selector.sep:"",
                    type:selector.type?selector.type:"",
                    value:selector.value?selector.value:""
                }]);
                this.groups = groups;
                this.context = selector.context;
                return this;
            }

        };

        Selector.prototype.val = hooks.val;
        Selector.prototype.each = function(callback,args){
            return hooks.each(this.context,callback,args);
        };

        var filter = {
            "ID":function(id){
                return function(elem){
                    elem = elem ? elem :((typeof document.getElementById(id) !== "undefined")?document.getElementById(id) : document.getAttributeNode(id));
                    var results = [];
                    if(!elem)
                        return results;

                    var isAttr = (elem.getAttribute("id") === id) ? true : false;
                    if(!isAttr){
                        return results;
                    }
                    results.push({
                        context:elem,
                        type:"ID",
                        value:id,
                        sep:"#"
                    });

                    return results;
                }
            },

            "CLASS":function(className){
                return function(){
                    if(Support.getElementsByClassName){
                        var contexts = document.getElementsByClassName(className);
                        var len = contexts.length,i = 0;
                        var results = [];
                        if(!contexts.length){
                            return results;
                        }

                        for(; i < len; i++){
                            results.push({
                                context:contexts[i]?contexts[i]:"",
                                type:"CLASS",
                                sep:".",
                                value:className
                            });
                        }

                        return results;
                    }else{
                        return [];
                    }
                }
            },

            "TAG":function(tagName){
                var elem,tmp = [],i = 0;
                var results = document.getElementsByTagName(tagName);
                if(tagName === "*"){
                    while ((elem = results[i++])){
                        if(elem.nodeType === 1){
                            tmp.push({
                                context:elem?elem:"",
                                type:"TAG",
                                sep:"",
                                value:tagName
                            });
                        }
                    }

                    return tmp;
                }

                if(results.length == 0){
                    return tmp;
                }

                for(;i<results.length;i++){
                    tmp.push({
                        context:results[i]?results[i]:"",
                        type:"TAG",
                        sep:"",
                        value:tagName
                    });
                }

                return tmp;
            },

            "ATTR":function(name, operator, check){

            },

            "CHILD":function(type, what, argument, first, last){

            }
        };

        return Selector;
    })(Pizzle);

    function getSelector(selector){
        return new Selector(selector);
    }

    window._ = window.$ = getSelector;
    window.Selector = getSelector;
})(window,document,undefined);

/**
 节点类型	                      描述	                                                   子节点
 1	Element	                      代表元素	                                                Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReference
 2	Attr	                      代表属性	                                                Text, EntityReference
 3	Text	                      代表元素或属性中的文本内容。	                                None
 4	CDATASection	              代表文档中的 CDATA 部分（不会由解析器解析的文本）。	            None
 5	EntityReference	              代表实体引用。	                                            Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
 6	Entity	                      代表实体。	                                                Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
 7	ProcessingInstruction	      代表处理指令。	                                            None
 8	Comment	                      代表注释。	                                                None
 9	Document	                  代表整个文档（DOM 树的根节点）。	                            Element, ProcessingInstruction, Comment, DocumentType
 10	DocumentType	              向为文档定义的实体提供接口	                                None
 11	DocumentFragment	          代表轻量级的 Document 对象，能够容纳文档的某个部分	            Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
 12	Notation	                  代表 DTD 中声明的符号。	                                    None;
 **/
<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const Bone = require('../lib/Bone');

const extensionManager = require('./ExtensionManager.js')

const Sensor = require('./Sensor.js').Sensor;

const serviceConfigKey = "bone:service:config";

const syncInterval = 1000 * 3600 * 4; // sync every 4 hours
const rclient = require('../util/redis_manager.js').getRedisClient()

const SysManager = require('../net2/SysManager.js');
const sysManager = new SysManager('info');

const License = require('../util/license');

const fConfig = require('../net2/config.js').getConfig();

const sem = require('../sensor/SensorEventManager.js').getInstance();

const execAsync = require('child-process-promise').exec

const CLOUD_URL_KEY = "sys:bone:url";
const FORCED_CLOUD_URL_KEY = "sys:bone:url:forced";

class BoneSensor extends Sensor {
  scheduledJob() {
    Bone.waitUntilCloudReady(() => {
      this.checkIn()
        .then(() => {})
        .catch((err) => {
          log.error("Failed to check in", err);
        })

    })
  }

  apiRun() {
    // register get/set handlers for fireapi
    extensionManager.onGet("cloudInstance", async (msg) => {
      return this.getCloudInstanceURL();
    })

    extensionManager.onGet("boneJWToken", async (msg) => {
      const jwt = await rclient.getAsync("sys:bone:jwt");
      return {jwt};
    })

    extensionManager.onSet("cloudInstance", async (msg, data) => {
      if(data.instance) {
        const url = `https://firewalla.encipher.io/bone/api/${data.instance}`;
        return this.setCloudInstanceURL(url);
      }
    })

    extensionManager.onCmd("recheckin", async (msg, data) => {

      return new Promise((resolve, reject) => {

        sem.once("CloudReCheckinComplete", async (event) => {
          const jwt = await rclient.getAsync("sys:bone:jwt");
          resolve({jwt});
        });

        sem.sendEventToFireMain({
          type: 'CloudReCheckin',
          message: "",
        });
      });
    });
  }

  async getCloudInstanceURL() {
    return rclient.getAsync(CLOUD_URL_KEY);
  }

  async getForcedCloudInstanceURL() {
    return rclient.getAsync(FORCED_CLOUD_URL_KEY);
  }

  async setCloudInstanceURL(url) {
    const curUrl = await this.getCloudInstanceURL();
    if(curUrl === url) {
      return;
    }

    log.info(`Applying new cloud url: ${url}`);

    await rclient.setAsync(CLOUD_URL_KEY, url);
    sem.emitEvent({
      type: 'CloudURLUpdate',
      toProcess: 'FireMain',
      message: "",
      url: url
    });
  }

  async setForcedCloudInstanceURL(url) {
    const curUrl = await this.getCloudInstanceURL();
    if(curUrl === url) {
      return;
    }

    log.info(`Applying new forced cloud url: ${url}`);

    await rclient.setAsync(FORCED_CLOUD_URL_KEY, url);

    return this.setCloudInstanceURL(url);
  }

  async applyNewCloudInstanceURL() {
    const curUrl = await this.getCloudInstanceURL();

    log.info(`Applying new cloud server url ${curUrl}`);

    return this.checkIn();
  }

  async checkIn() {
    const url = await this.getForcedCloudInstanceURL();

    if(url) {
      Bone.setEndpoint(url);
    }

    const license = License.getLicense();

    if(!license) {
      log.error("License file is required!");
      // return Promise.resolve();
    }

    let sysInfo = await sysManager.getSysInfoAsync();

    log.debug("Checking in Cloud...", sysInfo);

    // First checkin usually have no meaningful data ...
    //
    try {
      if (this.lastCheckedIn) {
        let HostManager = require("../net2/HostManager.js");
        let hostManager = new HostManager("cli", 'server', 'info');
        sysInfo.hostInfo = await hostManager.getCheckInAsync();
      }
    } catch (e) {
      log.error("BoneCheckIn Error fetching hostInfo",e);
    }

    const data = await Bone.checkinAsync(fConfig, license, sysInfo);

    this.lastCheckedIn = Date.now() / 1000;

    log.info("Cloud checked in successfully:")//, JSON.stringify(data));

    await rclient.setAsync("sys:bone:info",JSON.stringify(data));

    const existingDDNS = await rclient.hgetAsync("sys:network:info", "ddns");
    if (data.ddns) {
      sysManager.ddns = data.ddns;
      await rclient.hsetAsync(
        "sys:network:info",
        "ddns",
        JSON.stringify(data.ddns)); // use JSON.stringify for backward compatible
    }

    let existingPublicIP = await rclient.hgetAsync("sys:network:info", "publicIp");
    if(data.publicIp) {
      sysManager.publicIp = data.publicIp;
      await rclient.hsetAsync(
        "sys:network:info",
        "publicIp",
        JSON.stringify(data.publicIp)); // use JSON.stringify for backward compatible
    }

    // broadcast new change
    if(existingDDNS !== JSON.stringify(data.ddns) ||
    existingPublicIP !== JSON.stringify(data.publicIp)) {
      sem.emitEvent({
        type: 'DDNS:Updated',
        toProcess: 'FireApi',
        publicIp: data.publicIp,
        ddns: data.ddns,
        message: 'DDNS is updated'
      })
    }

    if (data && data.upgrade) {
      log.info("Bone:Upgrade", data.upgrade);
      if (data.upgrade.type == "soft") {
        log.info("Bone:Upgrade:Soft", data.upgrade);
        execAsync('sync & /home/pi/firewalla/scripts/fireupgrade.sh soft')
      } else if (data.upgrade.type == "hard") {
        log.info("Bone:Upgrade:Hard", data.upgrade);
        execAsync('sync & /home/pi/firewalla/scripts/fireupgrade.sh hard')
      }
    }

    if (data && data.frpToken) {
      await rclient.hsetAsync("sys:config", "frpToken", data.frpToken)
    }
  }

  run() {
    setInterval(() => {
      this.scheduledJob();
    }, syncInterval);

    sem.on("CloudURLUpdate", async () => {
      return this.applyNewCloudInstanceURL()
    })

    sem.on("PublicIP:Updated", () => {
      this.checkIn();
    });

    sem.on("CloudReCheckin", async () => {
      try {
        await this.checkIn();
      } catch(err) {
        log.error('Failed to re-checkin to cloud', err)
      }

      sem.sendEventToFireApi({
        type: 'CloudReCheckinComplete',
        message: ""
      });
    });
  }

  // make config redis-friendly..
  flattenConfig(config) {
    let sConfig = {};

    let keys = ["adblock.dns", "family.dns"];

    keys.filter((key) => config[key]).forEach((key) => {
      if (config[key].constructor.name === 'Object' ||
        config[key].constructor.name === 'Array') {
        sConfig[key] = JSON.stringify(config[key]);
      } else {
        sConfig[key] = config[key];
      }
    })

    return sConfig;
  }

  loadServiceConfig() {
    log.info("Loading service config from cloud...");
    Bone.getServiceConfig((err, config) => {

      if(config && config.constructor.name === 'Object') {
        rclient.hmsetAsync(serviceConfigKey, this.flattenConfig(config))
          .then(() => {
            log.info("Service config is updated");
          }).catch((err) => {
          log.error("Failed to store service config in redis:", err);
        })
      }
    })
  }
}

module.exports = BoneSensor;
<commit_msg>Add total number of created alarms/rules/exceptions in checkin data (#1793)<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const Bone = require('../lib/Bone');

const extensionManager = require('./ExtensionManager.js')

const Sensor = require('./Sensor.js').Sensor;

const serviceConfigKey = "bone:service:config";

const syncInterval = 1000 * 3600 * 4; // sync every 4 hours
const rclient = require('../util/redis_manager.js').getRedisClient()

const SysManager = require('../net2/SysManager.js');
const sysManager = new SysManager('info');

const License = require('../util/license');

const fConfig = require('../net2/config.js').getConfig();

const sem = require('../sensor/SensorEventManager.js').getInstance();

const execAsync = require('child-process-promise').exec

const CLOUD_URL_KEY = "sys:bone:url";
const FORCED_CLOUD_URL_KEY = "sys:bone:url:forced";

class BoneSensor extends Sensor {
  scheduledJob() {
    Bone.waitUntilCloudReady(() => {
      this.checkIn()
        .then(() => { })
        .catch((err) => {
          log.error("Failed to check in", err);
        })

    })
  }

  apiRun() {
    // register get/set handlers for fireapi
    extensionManager.onGet("cloudInstance", async (msg) => {
      return this.getCloudInstanceURL();
    })

    extensionManager.onGet("boneJWToken", async (msg) => {
      const jwt = await rclient.getAsync("sys:bone:jwt");
      return { jwt };
    })

    extensionManager.onSet("cloudInstance", async (msg, data) => {
      if (data.instance) {
        const url = `https://firewalla.encipher.io/bone/api/${data.instance}`;
        return this.setCloudInstanceURL(url);
      }
    })

    extensionManager.onCmd("recheckin", async (msg, data) => {

      return new Promise((resolve, reject) => {

        sem.once("CloudReCheckinComplete", async (event) => {
          const jwt = await rclient.getAsync("sys:bone:jwt");
          resolve({ jwt });
        });

        sem.sendEventToFireMain({
          type: 'CloudReCheckin',
          message: "",
        });
      });
    });
  }

  async getCloudInstanceURL() {
    return rclient.getAsync(CLOUD_URL_KEY);
  }

  async getForcedCloudInstanceURL() {
    return rclient.getAsync(FORCED_CLOUD_URL_KEY);
  }

  async setCloudInstanceURL(url) {
    const curUrl = await this.getCloudInstanceURL();
    if (curUrl === url) {
      return;
    }

    log.info(`Applying new cloud url: ${url}`);

    await rclient.setAsync(CLOUD_URL_KEY, url);
    sem.emitEvent({
      type: 'CloudURLUpdate',
      toProcess: 'FireMain',
      message: "",
      url: url
    });
  }

  async setForcedCloudInstanceURL(url) {
    const curUrl = await this.getCloudInstanceURL();
    if (curUrl === url) {
      return;
    }

    log.info(`Applying new forced cloud url: ${url}`);

    await rclient.setAsync(FORCED_CLOUD_URL_KEY, url);

    return this.setCloudInstanceURL(url);
  }

  async applyNewCloudInstanceURL() {
    const curUrl = await this.getCloudInstanceURL();

    log.info(`Applying new cloud server url ${curUrl}`);

    return this.checkIn();
  }
  async countTotal() {
    return {
      totalAlarms: await rclient.getAsync("alarm:id"),
      totalRules: await rclient.getAsync("policy:id"),
      totalExceptions: await rclient.getAsync("exception:id")
    }
  }

  async checkIn() {
    const url = await this.getForcedCloudInstanceURL();

    if (url) {
      Bone.setEndpoint(url);
    }

    const license = License.getLicense();

    if (!license) {
      log.error("License file is required!");
      // return Promise.resolve();
    }

    let sysInfo = await sysManager.getSysInfoAsync();
    Object.assign(sysInfo, await this.countTotal());

    log.debug("Checking in Cloud...", sysInfo);

    // First checkin usually have no meaningful data ...
    //
    try {
      if (this.lastCheckedIn) {
        let HostManager = require("../net2/HostManager.js");
        let hostManager = new HostManager("cli", 'server', 'info');
        sysInfo.hostInfo = await hostManager.getCheckInAsync();
      }
    } catch (e) {
      log.error("BoneCheckIn Error fetching hostInfo", e);
    }

    const data = await Bone.checkinAsync(fConfig, license, sysInfo);

    this.lastCheckedIn = Date.now() / 1000;

    log.info("Cloud checked in successfully:")//, JSON.stringify(data));

    await rclient.setAsync("sys:bone:info", JSON.stringify(data));

    const existingDDNS = await rclient.hgetAsync("sys:network:info", "ddns");
    if (data.ddns) {
      sysManager.ddns = data.ddns;
      await rclient.hsetAsync(
        "sys:network:info",
        "ddns",
        JSON.stringify(data.ddns)); // use JSON.stringify for backward compatible
    }

    let existingPublicIP = await rclient.hgetAsync("sys:network:info", "publicIp");
    if (data.publicIp) {
      sysManager.publicIp = data.publicIp;
      await rclient.hsetAsync(
        "sys:network:info",
        "publicIp",
        JSON.stringify(data.publicIp)); // use JSON.stringify for backward compatible
    }

    // broadcast new change
    if (existingDDNS !== JSON.stringify(data.ddns) ||
      existingPublicIP !== JSON.stringify(data.publicIp)) {
      sem.emitEvent({
        type: 'DDNS:Updated',
        toProcess: 'FireApi',
        publicIp: data.publicIp,
        ddns: data.ddns,
        message: 'DDNS is updated'
      })
    }

    if (data && data.upgrade) {
      log.info("Bone:Upgrade", data.upgrade);
      if (data.upgrade.type == "soft") {
        log.info("Bone:Upgrade:Soft", data.upgrade);
        execAsync('sync & /home/pi/firewalla/scripts/fireupgrade.sh soft')
      } else if (data.upgrade.type == "hard") {
        log.info("Bone:Upgrade:Hard", data.upgrade);
        execAsync('sync & /home/pi/firewalla/scripts/fireupgrade.sh hard')
      }
    }

    if (data && data.frpToken) {
      await rclient.hsetAsync("sys:config", "frpToken", data.frpToken)
    }
  }

  run() {
    setInterval(() => {
      this.scheduledJob();
    }, syncInterval);

    sem.on("CloudURLUpdate", async () => {
      return this.applyNewCloudInstanceURL()
    })

    sem.on("PublicIP:Updated", () => {
      this.checkIn();
    });

    sem.on("CloudReCheckin", async () => {
      try {
        await this.checkIn();
      } catch (err) {
        log.error('Failed to re-checkin to cloud', err)
      }

      sem.sendEventToFireApi({
        type: 'CloudReCheckinComplete',
        message: ""
      });
    });
  }

  // make config redis-friendly..
  flattenConfig(config) {
    let sConfig = {};

    let keys = ["adblock.dns", "family.dns"];

    keys.filter((key) => config[key]).forEach((key) => {
      if (config[key].constructor.name === 'Object' ||
        config[key].constructor.name === 'Array') {
        sConfig[key] = JSON.stringify(config[key]);
      } else {
        sConfig[key] = config[key];
      }
    })

    return sConfig;
  }

  loadServiceConfig() {
    log.info("Loading service config from cloud...");
    Bone.getServiceConfig((err, config) => {

      if (config && config.constructor.name === 'Object') {
        rclient.hmsetAsync(serviceConfigKey, this.flattenConfig(config))
          .then(() => {
            log.info("Service config is updated");
          }).catch((err) => {
            log.error("Failed to store service config in redis:", err);
          })
      }
    })
  }
}

module.exports = BoneSensor;
<|endoftext|>


<commit_before>import React, { Component } from 'react';

import { View } from 'react-native';
import { Spinner, Text } from 'native-base';

import { colors } from '../settings';
import type { Poster } from '../types';
import styles from './styles';

type Props = {};

class Fetching extends Component {

  props: Props;

  constructor(props: Props) {
    super(props);

    this.state = {
      hasFetchedData: false,
    };
  };

  render() {
    return (
      <View style={styles.Fetching}>
        <Text style={styles.FetchingMessage}>
          Collecting poster from Pauling database…
        </Text>
        <Spinner color={colors.textAltPrimaryColor} />
      </View>
    );
  }
}

export default Fetching;
<commit_msg>Make Fetching a functional component<commit_after>import React from 'react';

import { View } from 'react-native';
import { Spinner, Text } from 'native-base';

import { colors } from '../settings';
import styles from './styles';

const Fetching = () => (
  <View style={styles.Fetching}>
    <Text style={styles.FetchingMessage}>
      Collecting poster from Pauling database…
    </Text>
    <Spinner color={colors.textAltPrimaryColor} />
  </View>
);

export default Fetching;
<|endoftext|>


<commit_before>( function ( mw, $ ) {
	/**
	 * Notification group item widget for echo popup.
	 *
	 * @class
	 * @extends mw.echo.ui.NotificationItemWidget
	 * @mixins OO.ui.mixin.PendingElement
	 * @mixins OO.ui.mixin.GroupElement
	 * @mixins OO.ui.mixin.GroupWidget
	 *
	 * @constructor
	 * @param {mw.echo.dm.NotificationGroupItem} model Item model
	 * @param {Object} [config] Configuration object
	 */
	mw.echo.ui.NotificationGroupItemWidget = function MwEchoUiNotificationGroupItemWidget( model, config ) {
		config = config || {};

		// Parent constructor
		mw.echo.ui.NotificationGroupItemWidget.parent.call( this, model, config );

		// Mixin constructor
		OO.ui.mixin.GroupWidget.call( this, config );
		OO.ui.mixin.PendingElement.call( this, config );

		this.setPendingElement( this.$group );
		this.$group
			.addClass( 'mw-echo-ui-notificationGroupItemWidget-group' )
			// We have to manually set the display to 'none' here because
			// otherwise the 'slideUp' and 'slideDown' jQuery effects don't
			// work
			.css( 'display', 'none' );

		this.showTitles = false;
		this.expanded = false;

		// Add "expand" button
		this.toggleExpandButton = new OO.ui.ButtonOptionWidget( {
			icon: 'expand',
			framed: false,
			classes: [ 'mw-echo-ui-notificationItemWidget-content-actions-button' ]
		} );
		this.updateExpandButton();
		this.actionsButtonSelectWidget.addItems( [ this.toggleExpandButton ] );

		// Events
		this.toggleExpandButton.connect( this, { click: 'onToggleExpandButtonClick' } );

		this.model.connect( this, {
			add: 'onModelAddGroup',
			remove: 'onModelRemoveGroup',
			clear: 'onModelClearGroups'
		} );

		// TODO: Handle cases where the group became empty or a case where the group only has 1 item left.
		// Note: Right now this code works primarily for cross-wiki notifications. These act differently
		// than local bundles. Cross-wiki notifications, when they "lose" their items for being read, they
		// vanish from the list. Unlike them, the plan for local bundles is that read sub-items go outside
		// the bundle and become their own items in the general notificationsWidget, and when the local bundle
		// has 1 notification left, the group will actually transform into that last notification item.
		// We don't listen to the empty event right now, because the entire item is deleted in cross-wiki
		// notifications. When we work on local bundles, we will have to add that event listener per item.

		// Initialization
		this.populateFromModel();
		this.toggleExpand( false );
		this.$element
			.addClass( 'mw-echo-ui-notificationGroupItemWidget' )
			.append(
				$( '<div>' )
					.addClass( 'mw-echo-ui-notificationGroupItemWidget-separator' ),
				this.$group
			);
	};

	/* Initialization */

	OO.inheritClass( mw.echo.ui.NotificationGroupItemWidget, mw.echo.ui.NotificationItemWidget );
	OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.PendingElement );
	// Need to mixin base class as well
	OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.GroupElement );
	OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.GroupWidget );

	/* Methods */

	/**
	 * Respond to model clearing the group
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.onModelClearGroups = function () {
		this.clearItems();
		this.handleEmptyGroups();
	};

	/**
	 * Respond to model adding group
	 *
	 * @param {mw.echo.dm.NotificationsModel} notificationsModel Added group
	 * @param {number} index Position the group was added
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.onModelAddGroup = function ( notificationsModel, index ) {
		this.addItems(
			[
				new mw.echo.ui.BundledNotificationGroupWidget( notificationsModel, {
					showTitle: this.showTitles,
					$overlay: this.$overlay
				} )
			],
			index
		);

		this.checkShowTitles();
	};

	/**
	 * Respond to toggle expand button click
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.onToggleExpandButtonClick = function () {
		var widget = this;

		this.toggleExpand( !this.expanded );
		this.updateExpandButton();

		if ( this.expanded ) {
			// Expand
			this.pushPending();

			// Query all sources
			this.model.fetchAllNotificationsInGroups()
				.then( function ( /* Groups */ ) {
					widget.popPending();
				} );
		}
	};

	/**
	 * Update the expand button label
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.updateExpandButton = function () {
		var type = this.model.getType();
		if ( $.isArray( type ) ) {
			type = 'all';
		}
		this.toggleExpandButton.setLabel(
			this.expanded ?
				mw.msg( 'notification-link-text-collapse-all' ) :
				// Messages that appear here are:
				// notification-link-text-expand-alert-count
				// notification-link-text-expand-message-count
				mw.msg( 'notification-link-text-expand-' + this.model.getType() + '-count', this.model.getCount() )
		);
		this.toggleExpandButton.setIcon(
			this.expanded ?
				'collapse' :
				'expand'
		);
	};

	/**
	 * Populate the items from the model
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.populateFromModel = function () {
		var i,
			items = this.model.getItems(),
			widgets = [];

		for ( i = 0; i < items.length; i++ ) {
			widgets.push(
				new mw.echo.ui.BundledNotificationGroupWidget( items[ i ], {
					showTitle: this.showTitles,
					$overlay: this.$overlay
				} )
			);
		}

		this.addItems( widgets );
		this.checkShowTitles();
	};

	/**
	 * Respond to model removing a group
	 *
	 * @param {mw.echo.dm.NotificationsModel} notificationsModel Removed group
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.onModelRemoveGroup = function ( notificationsModel ) {
		var widget = this.getItemFromData( notificationsModel.getId() );

		if ( widget ) {
			this.removeItems( [ widget ] );
		}
	};

	/**
	 * Toggle the expand/collapsed state of this group widget
	 *
	 * @param {boolean} show Show the widget expanded
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.toggleExpand = function ( show ) {
		this.expanded = show !== undefined ? !!show : !this.expanded;

		if ( show ) {
			this.$group.slideDown();
		} else {
			this.$group.slideUp();
		}
	};

	/**
	 * Check whether the titles should be shown and toggle them in the items.
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.checkShowTitles = function () {
		var i,
			items = this.getItems(),
			numItems = items.length,
			showTitles = numItems > 1;

		if ( this.showTitles !== showTitles ) {
			this.showTitles = showTitles;
			for ( i = 0; i < numItems; i++ ) {
				items[ i ].toggleTitle( showTitles );
			}
		}
	};

} )( mediaWiki, jQuery );
<commit_msg>Expand group item on click<commit_after>( function ( mw, $ ) {
	/**
	 * Notification group item widget for echo popup.
	 *
	 * @class
	 * @extends mw.echo.ui.NotificationItemWidget
	 * @mixins OO.ui.mixin.PendingElement
	 * @mixins OO.ui.mixin.GroupElement
	 * @mixins OO.ui.mixin.GroupWidget
	 *
	 * @constructor
	 * @param {mw.echo.dm.NotificationGroupItem} model Item model
	 * @param {Object} [config] Configuration object
	 */
	mw.echo.ui.NotificationGroupItemWidget = function MwEchoUiNotificationGroupItemWidget( model, config ) {
		config = config || {};

		// Parent constructor
		mw.echo.ui.NotificationGroupItemWidget.parent.call( this, model, config );

		// Mixin constructor
		OO.ui.mixin.GroupWidget.call( this, config );
		OO.ui.mixin.PendingElement.call( this, config );

		this.setPendingElement( this.$group );
		this.$group
			.addClass( 'mw-echo-ui-notificationGroupItemWidget-group' )
			// We have to manually set the display to 'none' here because
			// otherwise the 'slideUp' and 'slideDown' jQuery effects don't
			// work
			.css( 'display', 'none' );

		this.showTitles = false;
		this.expanded = false;

		// Add "expand" button
		this.toggleExpandButton = new OO.ui.ButtonOptionWidget( {
			icon: 'expand',
			framed: false,
			classes: [ 'mw-echo-ui-notificationItemWidget-content-actions-button' ]
		} );
		this.updateExpandButton();
		this.actionsButtonSelectWidget.addItems( [ this.toggleExpandButton ] );

		// Events
		this.toggleExpandButton.connect( this, { click: 'expand' } );
		this.$element.on( 'click', this.expand.bind( this ) );
		this.model.connect( this, {
			add: 'onModelAddGroup',
			remove: 'onModelRemoveGroup',
			clear: 'onModelClearGroups'
		} );

		// TODO: Handle cases where the group became empty or a case where the group only has 1 item left.
		// Note: Right now this code works primarily for cross-wiki notifications. These act differently
		// than local bundles. Cross-wiki notifications, when they "lose" their items for being read, they
		// vanish from the list. Unlike them, the plan for local bundles is that read sub-items go outside
		// the bundle and become their own items in the general notificationsWidget, and when the local bundle
		// has 1 notification left, the group will actually transform into that last notification item.
		// We don't listen to the empty event right now, because the entire item is deleted in cross-wiki
		// notifications. When we work on local bundles, we will have to add that event listener per item.

		// Initialization
		this.populateFromModel();
		this.toggleExpanded( false );
		this.$element
			.addClass( 'mw-echo-ui-notificationGroupItemWidget' )
			.append(
				$( '<div>' )
					.addClass( 'mw-echo-ui-notificationGroupItemWidget-separator' ),
				this.$group
			);
	};

	/* Initialization */

	OO.inheritClass( mw.echo.ui.NotificationGroupItemWidget, mw.echo.ui.NotificationItemWidget );
	OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.PendingElement );
	// Need to mixin base class as well
	OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.GroupElement );
	OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.GroupWidget );

	/* Methods */

	/**
	 * Respond to model clearing the group
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.onModelClearGroups = function () {
		this.clearItems();
		this.handleEmptyGroups();
	};

	/**
	 * Respond to model adding group
	 *
	 * @param {mw.echo.dm.NotificationsModel} notificationsModel Added group
	 * @param {number} index Position the group was added
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.onModelAddGroup = function ( notificationsModel, index ) {
		this.addItems(
			[
				new mw.echo.ui.BundledNotificationGroupWidget( notificationsModel, {
					showTitle: this.showTitles,
					$overlay: this.$overlay
				} )
			],
			index
		);

		this.checkShowTitles();
	};

	/**
	 * Expand the notification group
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.expand = function () {
		var widget = this;

		this.toggleExpanded( !this.expanded );
		this.updateExpandButton();

		if ( this.expanded ) {
			// Expand
			this.pushPending();

			// Query all sources
			this.model.fetchAllNotificationsInGroups()
				.then( function ( /* Groups */ ) {
					widget.popPending();
				} );
		}
	};

	/**
	 * Update the expand button label
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.updateExpandButton = function () {
		var type = this.model.getType();
		if ( $.isArray( type ) ) {
			type = 'all';
		}
		this.toggleExpandButton.setLabel(
			this.expanded ?
				mw.msg( 'notification-link-text-collapse-all' ) :
				// Messages that appear here are:
				// notification-link-text-expand-alert-count
				// notification-link-text-expand-message-count
				mw.msg( 'notification-link-text-expand-' + this.model.getType() + '-count', this.model.getCount() )
		);
		this.toggleExpandButton.setIcon(
			this.expanded ?
				'collapse' :
				'expand'
		);
	};

	/**
	 * Populate the items from the model
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.populateFromModel = function () {
		var i,
			items = this.model.getItems(),
			widgets = [];

		for ( i = 0; i < items.length; i++ ) {
			widgets.push(
				new mw.echo.ui.BundledNotificationGroupWidget( items[ i ], {
					showTitle: this.showTitles,
					$overlay: this.$overlay
				} )
			);
		}

		this.addItems( widgets );
		this.checkShowTitles();
	};

	/**
	 * Respond to model removing a group
	 *
	 * @param {mw.echo.dm.NotificationsModel} notificationsModel Removed group
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.onModelRemoveGroup = function ( notificationsModel ) {
		var widget = this.getItemFromData( notificationsModel.getId() );

		if ( widget ) {
			this.removeItems( [ widget ] );
		}
	};

	/**
	 * Toggle the expand/collapsed state of this group widget
	 *
	 * @param {boolean} show Show the widget expanded
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.toggleExpanded = function ( show ) {
		this.expanded = show !== undefined ? !!show : !this.expanded;

		if ( show ) {
			this.$group.slideDown();
		} else {
			this.$group.slideUp();
		}
	};

	/**
	 * Check whether the titles should be shown and toggle them in the items.
	 */
	mw.echo.ui.NotificationGroupItemWidget.prototype.checkShowTitles = function () {
		var i,
			items = this.getItems(),
			numItems = items.length,
			showTitles = numItems > 1;

		if ( this.showTitles !== showTitles ) {
			this.showTitles = showTitles;
			for ( i = 0; i < numItems; i++ ) {
				items[ i ].toggleTitle( showTitles );
			}
		}
	};

} )( mediaWiki, jQuery );
<|endoftext|>


<commit_before>var Octoliner = function(opts) {
  opts = opts || {};
  if (opts.i2c === undefined) {
    I2C1.setup({
      sda: SDA,
      scl: SCL,
      bitrate: 100000
    });
    opts.i2c = I2C1;
  }
  this.expander = require('@amperka/gpio-expander').connect(opts);
  this.expander.pwmFreq(60000);
  this._sensePin = 0;
  this._ledBrightnessPin = 9;
  this.expander.analogWrite(this._ledBrightnessPin, 1);
  this._sensorPinMap = [4, 5, 6, 8, 7, 3, 2, 1];
  this._lineState = [];
  this.setSensitivity(0.91);
  this.mapPatternToLine = this.defaultMapPatternToLine;
  this.mapAnalogToPattern = this.defaultMapAnalogToPattern;
};

Octoliner.prototype.setSensitivity = function(sense) {
  this.expander.analogWrite(this._sensePin, sense);
};

// deprecated
Octoliner.prototype.setBrightness = function(brightness) { };

Octoliner.prototype.analogRead = function(sensor) {
  return this.expander.analogRead(this._sensorPinMap[sensor & 0x07]);
};

Octoliner.prototype.analogReadAll = function(analogArray) {
  for(var i = 0; i < 8; i++) {
    analogArray[i] = this.analogRead(_sensorPinMap[i]);
  }
}

Octoliner.prototype.digitalRead = function(sensor) {
  sensor &= 0x07;
  return this.expander.digitalRead(this._sensorPinMap[sensor]);
};

Octoliner.prototype.changeAddr = function(nAddr) {
  this.expander.changeAddr(nAddr);
};

Octoliner.prototype.saveAddr = function() {
  this.expander.saveAddr();
};

Octoliner.prototype.getBinaryLine = function(treshold) {
  treshold = treshold || 0.5;
  var result = 0;
  for (var i = 0; i < 8; ++i) {
    var value = this.analogRead(i);
    if (treshold < value) result |= 1 << i;
  }
  return result;
};

Octoliner.prototype.mapLine = function(Line) {
  var sum = 0;
  var avg = 0;
  var weight = [4, 3, 2, 1, -1, -2, -3, -4];
  var i;
  if (Array.isArray(Line)) {
    for (i = 0; i < 8; i++) {
      sum += Line[i];
      avg += Line[i] * weight[i];
    }
    if (sum != 0) {
      return avg / sum / 4.0;
    }
    return 0;
  } else {
    this._lineState = [];
    for (i = 0; i < 8; i++) {
      var mask = 1 << i;
      if (Line & mask) {
        this._lineState[i] = 1;
      } else {
        this._lineState[i] = 0;
      }
    }
    return this.mapLine(this._lineState);
  }
};

Octoliner.prototype.reset = function() {
  this.expander.reset();
};

Octoliner.prototype.defaultMapAnalogToPattern = function(analogArray) {

}

Octoliner.prototype.defaultMapPatternToLine = function(pattern) {

}

Octoliner.prototype.trackLine = function(argument) {
  if(!argument) { // no argument

  } else if(typeof(argument) === 'array') { // analog array

  } else if(typeof(argument) === 'number') { // pattern

  } else { return NaN; }
}

exports.connect = function(opts) {
  return new Octoliner(opts);
};
<commit_msg>trackLine done<commit_after>var Octoliner = function(opts) {
  opts = opts || {};
  if (opts.i2c === undefined) {
    I2C1.setup({
      sda: SDA,
      scl: SCL,
      bitrate: 100000
    });
    opts.i2c = I2C1;
  }
  this.expander = require('@amperka/gpio-expander').connect(opts);
  this.expander.pwmFreq(60000);
  this._sensePin = 0;
  this._ledBrightnessPin = 9;
  this.expander.analogWrite(this._ledBrightnessPin, 1);
  this._sensorPinMap = [4, 5, 6, 8, 7, 3, 2, 1];
  this._lineState = [];
  this.setSensitivity(0.91);
  this.mapPatternToLine = this.defaultMapPatternToLine;
  this.mapAnalogToPattern = this.defaultMapAnalogToPattern;
};

Octoliner.prototype.setSensitivity = function(sense) {
  this.expander.analogWrite(this._sensePin, sense);
};

// deprecated
Octoliner.prototype.setBrightness = function(brightness) { };

Octoliner.prototype.analogRead = function(sensor) {
  return this.expander.analogRead(this._sensorPinMap[sensor & 0x07]);
};

Octoliner.prototype.analogReadAll = function(analogArray) {
  for(var i = 0; i < 8; i++) {
    analogArray[i] = this.analogRead(_sensorPinMap[i]);
  }
}

Octoliner.prototype.digitalRead = function(sensor) {
  sensor &= 0x07;
  return this.expander.digitalRead(this._sensorPinMap[sensor]);
};

Octoliner.prototype.changeAddr = function(nAddr) {
  this.expander.changeAddr(nAddr);
};

Octoliner.prototype.saveAddr = function() {
  this.expander.saveAddr();
};

Octoliner.prototype.getBinaryLine = function(treshold) {
  treshold = treshold || 0.5;
  var result = 0;
  for (var i = 0; i < 8; ++i) {
    var value = this.analogRead(i);
    if (treshold < value) result |= 1 << i;
  }
  return result;
};

Octoliner.prototype.mapLine = function(Line) {
  var sum = 0;
  var avg = 0;
  var weight = [4, 3, 2, 1, -1, -2, -3, -4];
  var i;
  if (Array.isArray(Line)) {
    for (i = 0; i < 8; i++) {
      sum += Line[i];
      avg += Line[i] * weight[i];
    }
    if (sum != 0) {
      return avg / sum / 4.0;
    }
    return 0;
  } else {
    this._lineState = [];
    for (i = 0; i < 8; i++) {
      var mask = 1 << i;
      if (Line & mask) {
        this._lineState[i] = 1;
      } else {
        this._lineState[i] = 0;
      }
    }
    return this.mapLine(this._lineState);
  }
};

Octoliner.prototype.reset = function() {
  this.expander.reset();
};

Octoliner.prototype.defaultMapAnalogToPattern = function(analogArray) {

}

Octoliner.prototype.defaultMapPatternToLine = function(pattern) {

}

Octoliner.prototype.trackLine = function(argument) {
  if(!argument) { // no argument
    var pattern = [0, 0, 0, 0, 0, 0, 0, 0];
    this.analogReadAll(pattern);
    return this.mapPatternToLine(this.mapAnalogToPattern(pattern));
  } else if(Array.isArray(argument)) { // argument is analog array
    return this.mapPatternToLine(this.mapAnalogToPattern(argument));
  } else if(typeof(argument) === 'number') { // argument is pattern
    return this.mapPatternToLine(argument);
  } else { return NaN; }
}

exports.connect = function(opts) {
  return new Octoliner(opts);
};
<|endoftext|>


<commit_before>const express = require('express');
const passport = require('passport');

const auth = express.Router();

auth.get('/signin', passport.authenticate('signIn', { scope: ['user-read-email', 'user-read-private', 'playlist-modify-public', 'playlist-modify-private', 'user-top-read', 'playlist-read-private'], showDialog: true }));

auth.get('/callback', passport.authenticate('signIn', { failureRedirect: '/', successRedirect: '/api/home' }));

auth.get('/logout', (req, res) => {
  console.log('REQ USER', req.user);
  req.logOut();
  console.log('USER AFTER LOGOUT', req.user);
  req.session.destroy(() => {
    console.log('SESSSION?', req.session);
    res.send('logout');
  });
});

module.exports = auth;
<commit_msg>Changes<commit_after>const express = require('express');
const passport = require('passport');

const auth = express.Router();

auth.get('/signin', passport.authenticate('signIn', { scope: ['user-read-email', 'user-read-private', 'playlist-modify-public', 'playlist-modify-private', 'user-top-read', 'playlist-read-private'], showDialog: true }));

auth.get('/callback', passport.authenticate('signIn', { failureRedirect: '/', successRedirect: '/api/home' }));

auth.get('/logout', (req, res) => {
  req.logOut();
  req.session.destroy(() => {
    res.send('logout');
  });
});

module.exports = auth;
<|endoftext|>