<commit_before>var addic7edApi = require('addic7ed-api');
var fs = require('fs');
var Common = require('../common.js');
var Mongoose = require('mongoose');
var Mongo = require('../db/mongo.js');
var ScheduleManager = require('../schedule/scheduleManager.js');
var TvMaze = require('../libs/tvMaze.js');
var Logger = require('../log/logger.js');

exports.addic7edGetSubtitle = function (session, languages, bot, chat, sessionsList) {
    if(session.choosenEpisode.indexOf('-')===-1){
        getSingleEpisodeSubs(session, languages, bot, chat, sessionsList, session.choosenEpisode, true)
    }else{
        var start = +session.choosenEpisode.substr(0, session.choosenEpisode.indexOf('-'));
        var end = +session.choosenEpisode.substr(session.choosenEpisode.indexOf('-') + 1, session.choosenEpisode.length);
        while (start <= end){
            getSingleEpisodeSubs(session, languages, bot, chat, sessionsList, start, start == end),
            start++;
        }
    }
}

function getSingleEpisodeSubs (session, languages = [], bot, chat, sessionsList, episode, sendAmbiguousMessage) {
    Logger.logEvent("get", languages, session);
    addic7edApi.search(session.choosenSeries.show.name, session.choosenSeason,
        episode, languages).then(function (subtitlesList) {
            var subInfo = subtitlesList[0];
            if (subInfo != undefined) {
                var filename = session.choosenSeries.show.name + '_S' + session.choosenSeason + '_E' + episode + '.srt';
                addic7edApi.download(subInfo, filename).then(function () {
                    fs.access(filename, function (err) {
                        if (!err) {
                            //console.log('Subtitles file saved.');
                            Common.removeSession(sessionsList, session);
                            bot.sendMessage(chat, Common.buildLinkMessage(subInfo.link));
                            bot.sendDocument(chat, filename).then(function () {
                                fs.unlinkSync(filename, function(err){
                                    if(err) console.log("error, file not deleted");
                                });
                            });
                            if (sendAmbiguousMessage && session.choosenSeries.show.name.indexOf("(") > -1 && session.choosenSeries.show.name.indexOf(")") > -1) {
                                bot.sendMessage(chat, Common.ambigousSubtitleMessage);
                            }
                        }
                    });
                 }).catch(function (err) {
                    console.log("error downloading subs", err);
                });
            }
            else
                bot.sendMessage(chat, Common.subtitleNotFoundInAddic7edMessage);
         }).catch(function (err) {
            console.log("error searching subs - ", err);
        });
}

exports.addic7edGetSubtitleAlert = function (alert, job, bot, doneJobInterval) {
    addic7edApi.search(alert.show_name, alert.nextepisode_season, alert.nextepisode_episode, alert.language).then(function (subtitlesList) {
        var subInfo = subtitlesList[0];
        if (subInfo != undefined) {
            var filename = alert.show_name + '_S' + alert.nextepisode_season + '_E' + alert.nextepisode_episode + "_" + alert.language + '.srt';
            addic7edApi.download(subInfo, filename).then(function () {
                fs.access(filename, function (err) {
                    if (!err) {
                        //console.log('Subtitles file saved.');
                        Mongo.User.find({ alerts: alert._doc._id.toString() }, function (err, users) {
                            users.forEach(function (user) {
                                var userDoc = user._doc;
                                bot.sendMessage(userDoc.chatId, Common.newEpisodeAlertMessage(userDoc.first_name, alert._doc.show_name));
                                bot.sendMessage(userDoc.chatId, Common.buildLinkMessage(subInfo.link));
                                bot.sendDocument(userDoc.chatId, filename).then(function () {
                                    console.log("File sent to user " + userDoc.first_name);
                                });
                                if (Common.isAmbiguousTitle(alert._doc.show_name)) {
                                    bot.sendMessage(userDoc.chatId, Common.ambigousSubtitleMessage);
                                }
                            });
                            job.attrs.data.hasToBeRemoved = true;
                            fs.unlinkSync(filename, function(err){
                                if(err) console.log("error, file not deleted");
                            });
                            doneJobInterval();
                        });
                    }
                });
             }).catch(function (err) {
                console.log("error download subs - ", err);
            });
        } else {
            job.attrs.data.hasToBeRemoved = false;
            doneJobInterval();
        }
     }).catch(function (err) {
        console.log("error searching subs - ", err);
    });
}<commit_msg>fixed bug of “‘0’ of undefined”<commit_after>var addic7edApi = require('addic7ed-api');
var fs = require('fs');
var Common = require('../common.js');
var Mongoose = require('mongoose');
var Mongo = require('../db/mongo.js');
var ScheduleManager = require('../schedule/scheduleManager.js');
var TvMaze = require('../libs/tvMaze.js');
var Logger = require('../log/logger.js');

exports.addic7edGetSubtitle = function (session, languages, bot, chat, sessionsList) {
    if(session.choosenEpisode.indexOf('-')===-1){
        getSingleEpisodeSubs(session, languages, bot, chat, sessionsList, session.choosenEpisode, true)
    }else{
        var start = +session.choosenEpisode.substr(0, session.choosenEpisode.indexOf('-'));
        var end = +session.choosenEpisode.substr(session.choosenEpisode.indexOf('-') + 1, session.choosenEpisode.length);
        while (start <= end){
            getSingleEpisodeSubs(session, languages, bot, chat, sessionsList, start, start == end),
            start++;
        }
    }
}

function getSingleEpisodeSubs (session, languages = [], bot, chat, sessionsList, episode, sendAmbiguousMessage) {
    Logger.logEvent("get", languages, session);
    addic7edApi.search(session.choosenSeries.show.name, session.choosenSeason,
        episode, languages).then(function (subtitlesList) {
            var subInfo = subtitlesList[0];
            if (subInfo != undefined) {
                var filename = session.choosenSeries.show.name + '_S' + session.choosenSeason + '_E' + episode + '.srt';
                addic7edApi.download(subInfo, filename).then(function () {
                    fs.access(filename, function (err) {
                        if (!err) {
                            //console.log('Subtitles file saved.');
                            Common.removeSession(sessionsList, session);
                            bot.sendMessage(chat, Common.buildLinkMessage(subInfo.link));
                            bot.sendDocument(chat, filename).then(function () {
                                fs.unlinkSync(filename, function(err){
                                    if(err) console.log("error, file not deleted");
                                });
                            });
                            if (sendAmbiguousMessage && session.choosenSeries.show.name.indexOf("(") > -1 && session.choosenSeries.show.name.indexOf(")") > -1) {
                                bot.sendMessage(chat, Common.ambigousSubtitleMessage);
                            }
                        }
                    });
                 }).catch(function (err) {
                    console.log("error downloading subs", err);
                });
            }
            else
                bot.sendMessage(chat, Common.subtitleNotFoundInAddic7edMessage);
         }).catch(function (err) {
            console.log("error searching subs - ", err);
        });
}

exports.addic7edGetSubtitleAlert = function (alert, job, bot, doneJobInterval) {
    addic7edApi.search(alert.show_name, alert.nextepisode_season, alert.nextepisode_episode, alert.language).then(function (subtitlesList) {
        var subInfo = subtitlesList != undefined && subtitlesList.length > 0 ? subtitlesList[0] : undefined;
        if (subInfo != undefined) {
            var filename = alert.show_name + '_S' + alert.nextepisode_season + '_E' + alert.nextepisode_episode + "_" + alert.language + '.srt';
            addic7edApi.download(subInfo, filename).then(function () {
                fs.access(filename, function (err) {
                    if (!err) {
                        //console.log('Subtitles file saved.');
                        Mongo.User.find({ alerts: alert._doc._id.toString() }, function (err, users) {
                            users.forEach(function (user) {
                                var userDoc = user._doc;
                                bot.sendMessage(userDoc.chatId, Common.newEpisodeAlertMessage(userDoc.first_name, alert._doc.show_name));
                                bot.sendMessage(userDoc.chatId, Common.buildLinkMessage(subInfo.link));
                                bot.sendDocument(userDoc.chatId, filename).then(function () {
                                    console.log("File sent to user " + userDoc.first_name);
                                });
                                if (Common.isAmbiguousTitle(alert._doc.show_name)) {
                                    bot.sendMessage(userDoc.chatId, Common.ambigousSubtitleMessage);
                                }
                            });
                            job.attrs.data.hasToBeRemoved = true;
                            fs.unlinkSync(filename, function(err){
                                if(err) console.log("error, file not deleted");
                            });
                            doneJobInterval();
                        });
                    }
                });
             }).catch(function (err) {
                console.log("error download subs - ", err);
            });
        } else {
            job.attrs.data.hasToBeRemoved = false;
            doneJobInterval();
        }
     }).catch(function (err) {
        console.log("error searching subs - ", err);
    });
}<|endoftext|>


<commit_before>(function(w,u){w.MarioGolf=function(c){var t=[],S=SyntaxError,m={c:c,R:'',i:'',s:[],r:{R:0,I:0,P:0,L:0,Z:0},v:{},o:''},R=function(B){};if(!c||!c.match(/(?:^|[\r\n])([MLPBTYK]+)\|(?:(.*)\|)?([OQ|\-1RNM])(?:$|[\r\n])/))throw new S('Expecting identifier, got '+(c?'invalid program':'0-length code')+'.','code',0);else if((t[0]=c[1].matches(/\{(?=([^"]*"[^"]*")*[^"]*$)/g))!=(t[1]=c[1].matches(/\}(?=([^"]*"[^"]*")*[^"]*$)/g)))throw new S( 'Missing "' + ( tmp[0] < tmp[1] ? '{' : '}' ) + '" before the last identifier.', 'code', 0 );return {run:function(i){m={c:m.c,R:'',i:i,s:[],r:{R:0,I:0,P:0,L:0,Z:0},v:{},o:''};return R(m.c);},getStack:function(){return m.s;},getReturn:function(){return m.R;},getVars:function(){return m.v;},getOutput:function(){return m.o;}}};})([]['slice']['constructor']('return this;')());
<commit_msg>Update mariogolf.min.js<commit_after>(function(w,u){w.MarioGolf=function(c){var t=[],S=SyntaxError,m={c:c,R:'',i:'',s:[],r:{R:0,I:0,P:0,L:0,Z:0},v:{},o:''},R=function(B){};if(!c||!c.match(/(?:^|[\r\n])([MLPBTYK]+)\|(?:(.*)\|)?([OQ|\-1RNM])(?:$|[\r\n])/))throw new S('Expecting identifier, got '+(c?'invalid program':'0-length code')+'.','code',0);else if((t[0]=c[1].matches(/\{(?=([^"]*"[^"]*")*[^"]*$)/g))!=(t[1]=c[1].matches(/\}(?=([^"]*"[^"]*")*[^"]*$)/g)))throw new S( 'Missing "' + ( tmp[0] < tmp[1] ? '{' : '}' ) + '" before the last identifier.', 'code', 0 );return {run:function(i){m={c:m.c,R:'',i:i,s:[],r:{R:0,I:0,P:0,L:0,Z:0},v:{},o:''};return m.R=R(m.c)},getStack:function(){return m.s},getReturn:function(){return m.R},getVars:function(){return m.v},getOutput:function(){return m.o}}};})([]['slice']['constructor']('return this;')())
<|endoftext|>


<commit_before>'use strict';

define(['bitcoinjs-lib', 'util/btc'], function(Bitcoin, BtcUtils) {

/**
 * User oriented history view.
 * @param {Object} store Store for the object.
 * @param {Object} identity Parent identity for the object
 * @constructor
 */
function History(store, identity) {
    this.history = [];
    this.identity = identity;
}

/**
 * Add a row into the history.
 * Will make sure it's inserted sorted by its height.
 * @param {Object} newRow row coming from history.buildHistoryRow
 * Will return -1 if row is confirmed, -2 if still not confirmed, -3 if already was confirmed
 * @return {Number} 0 if inserted, 1 confirmed, 2 still not confirmed, 3 was already confirmed
 * @private
 */
History.prototype.addHistoryRow = function(newRow) {
    // Look for repeated element
    for(var idx=this.history.length-1; idx>=0; idx--) {
        var row = this.history[idx];
        if (row.hash == newRow.hash) {
            // replace by new row
            this.history[idx] = newRow;
            // set return code depending on status
            if (!row.height && newRow.height) {
                return 1; // confirmed
            } else if (!row.height && !newRow.height) {
                return 2; // still not confirmed
            } else {
                // keep initial height
                this.history[idx].height = row.height;
                return 3; // already confirmed
            }
        }
    }
    // Not found so insert
    this.history.push(newRow);
    return 0;
};

/**
 * Build a history row from a transaction.
 * Will find out which inputs and outputs correspond to the identity wallet and calculate impact.
 * @param {String} transaction Transaction in serialized form
 * @param {Number} height Height for the transaction
 * @return {Object} The new row
 */
History.prototype.buildHistoryRow = function(transaction, height) {
    var identity = this.identity,
        btcWallet = identity.wallet.wallet,
        inMine = 0,
        outAddresses = [],
        myInValue = 0,
        myOutValue = 0,
        txAddr = "",
        txObj = Bitcoin.Transaction.fromHex(transaction);
    var isStealth = false;
    var txHash = txObj.getId();

    var pocketImpact = {};
    var addPocketImpact = function(pocketId, outPocketType, amount) {
        if (!pocketImpact.hasOwnProperty(pocketId)) {
            pocketImpact[pocketId] = {ins: 0, outs: 0, total: 0, type: outPocketType};
        }
        if (amount > 0) {
            pocketImpact[pocketId].outs += amount;
        } else {
            pocketImpact[pocketId].ins -= amount;
        }
        pocketImpact[pocketId].total += amount;
    };

    var inAddress, inPocket, outPocket, internal;

    // Check inputs
    for(var idx=0; idx<txObj.ins.length; idx++) {
        var anIn = txObj.ins[idx];
        var outIdx = Bitcoin.bufferutils.reverse(anIn.hash).toString('hex')+":"+anIn.index;
        if (btcWallet.outputs[outIdx]) {
            var output = btcWallet.outputs[outIdx];
            // save in pocket
            var inWalletAddress = identity.wallet.getWalletAddress(output.address);
            inPocket = identity.wallet.pockets.getAddressPocketId(inWalletAddress);
            var inPocketType = identity.wallet.pockets.getPocketType(inWalletAddress.type);
            // counters
            inMine += 1;
            myInValue += output.value;
            addPocketImpact(inPocket, inPocketType, -output.value);
        } else {
            try {
                var address = BtcUtils.getInputAddress(anIn, this.identity.wallet.versions);
                inAddress = address || inAddress;
            } catch (e) {
                console.log("error decoding input", anIn);
            }
        }
    }
    if (!inMine) {
        txAddr = inAddress || 'unknown';
    }
    // Check outputs
    for(var idx=0; idx<txObj.outs.length; idx++) {
        var outAddress;
        var anOut = txObj.outs[idx];
        try {
            outAddress = Bitcoin.Address.fromOutputScript(anOut.script, Bitcoin.networks[this.identity.wallet.network]);
        } catch(e) {
            outAddress = "";
        }
        var outWalletAddress = identity.wallet.getWalletAddress(outAddress.toString());
        if (outWalletAddress) {
            var output = btcWallet.outputs[txHash+":"+idx];
            // TODO: mark also when input is mine and output not
            if (output && output.stealth) {
                isStealth = true;
            }
            if (outAddresses.indexOf(outWalletAddress) == -1) {
                outAddresses.push(outWalletAddress);
            }
            myOutValue += anOut.value;
            var _outPocket = identity.wallet.pockets.getAddressPocketId(outWalletAddress);
            var outPocketType = identity.wallet.pockets.getPocketType(outWalletAddress.type);
            // check if this is change, seq[0] for oldstealth and old hd, seq[1] for new hd
            var isChange = ['oldstealth', undefined].indexOf(outWalletAddress.type) ? (outWalletAddress.index[0]%2) : false;
            isChange = isChange || ((outWalletAddress.type == 'hd') ? outWalletAddress.index[1] : false);
            // save out pocket (don't set if it's change or same as input)
            if (inPocket !== _outPocket && !isChange) {
                outPocket = _outPocket;
            }
            addPocketImpact(_outPocket, outPocketType, anOut.value);
        } else {
            if (inMine) {
                txAddr = outAddress.toString();
            }
        }
    }
    if (!txAddr) {
        txAddr = this.getTransferLabel(pocketImpact);
        internal = true;
    }
    // Create a row representing this change (if already referenced will
    // be replaced)
    var newRow = {hash: txHash, tx: txObj, inMine: inMine, outAddresses: outAddresses, myInValue: myInValue, myOutValue: myOutValue, height: height, address: txAddr, isStealth: isStealth, total: myOutValue-myInValue, outPocket: outPocket, inPocket: inPocket, impact: pocketImpact, label: this.identity.txdb.getLabel(txHash), internal: internal, bareid: BtcUtils.getBareTxId(txObj)};
    return newRow;
};

History.prototype.getTransferLabel = function(pocketImpact) {
    var identity = this.identity;
    var keys = Object.keys(pocketImpact);

    // Input pockets
    var inKeys = keys.filter(function(key) { return pocketImpact[key].ins!==0; } );
    inKeys = inKeys.map(function(key) { return identity.wallet.pockets.getPocket(key, pocketImpact[key].type).name; });

    // Output pockets (minus change pockets)
    var outKeys = keys.filter(function(key) { return pocketImpact[key].outs!==0 && pocketImpact[key].ins===0; } );
    outKeys = outKeys.map(function(key) { return identity.wallet.pockets.getPocket(key, pocketImpact[key].type).name; });

    // Compose the final label
    var label = inKeys.join(' ,');
    if (outKeys.length) {
        return label + " to " + outKeys.join(' ,');
    } else {
        return 'internal on ' + label;
    }
}

/**
 * Callback to fill missing input (depending on another transaction)
 * @param {Object} transaction Transaction in serialized form.
 * @param {Object} data        DOCME
 * @private
 */
History.prototype.fillInput = function(transaction, data) {
    var address,
        index = data[0],
        newRow = data[1],
        txObj = Bitcoin.Transaction.fromHex(transaction);
    try {
        address = Bitcoin.Address.fromOutputScript(txObj.outs[index].script, Bitcoin.networks[this.identity.wallet.network]);
    } catch(e) {
    }
    if (address) {
        newRow.address = address.toString();
        this.update();
    } else {
        // TODO: need to handle this better
        console.log("output not recognized:", txObj.outs[index]);
    }
};

/**
 * Callback for fetching a transaction
 * @param {Object} transaction   Transaction in serialized form.
 * @param {Number} height        Height of the block.
 * @return {Object|null}         DOCME
 * @private
 */
History.prototype.txFetched = function(transaction, height) {
    var self = this,
        newRow = this.buildHistoryRow(transaction, height);

    // unknown for now means we need to fill in some extra inputs for now get 1st one
    if (newRow.address == 'unknown') {
        if (newRow.tx.ins[0])
            this.identity.txdb.fetchTransaction(Bitcoin.bufferutils.reverse(newRow.tx.ins[0].hash).toString('hex'),
                                            function(_a, _b) {self.fillInput(_a, _b);},
                                            [newRow.tx.ins[0].index, newRow]);
        else {
            console.log("No input!", newRow.tx);
        }
     }
    if (this.addHistoryRow(newRow) < 2) {
        // It's a relevant event so return the row (initial unspent or initial confirm)
        return newRow;
    }
};

/**
 * Look for transactions from given history records
 * @param {Object} history History array as returned by obelisk
 */
History.prototype.fillHistory = function(history) {
    var self = this,
        txdb = this.identity.txdb;
    history.forEach(function(tx) {
        var outTxHash = tx[0],
            inTxHash = tx[4];
        if (inTxHash) {
            // fetch a row for the spend
            txdb.fetchTransaction(inTxHash, function(_a, _b) {self.txFetched(_a, _b);}, tx[6]);
        }
        // fetch a row for the incoming tx
        txdb.fetchTransaction(outTxHash, function(_a, _b) {self.txFetched(_a, _b);}, tx[2]);
    });
};

/**
 * Update callback, needs to be filled in by the application to register.
 */
History.prototype.update = function() {
    console.log("Program needs to register an update callback!");
};

return History;
});
<commit_msg>Fix bad test.<commit_after>'use strict';

define(['bitcoinjs-lib', 'util/btc'], function(Bitcoin, BtcUtils) {

/**
 * User oriented history view.
 * @param {Object} store Store for the object.
 * @param {Object} identity Parent identity for the object
 * @constructor
 */
function History(store, identity) {
    this.history = [];
    this.identity = identity;
}

/**
 * Add a row into the history.
 * Will make sure it's inserted sorted by its height.
 * @param {Object} newRow row coming from history.buildHistoryRow
 * Will return -1 if row is confirmed, -2 if still not confirmed, -3 if already was confirmed
 * @return {Number} 0 if inserted, 1 confirmed, 2 still not confirmed, 3 was already confirmed
 * @private
 */
History.prototype.addHistoryRow = function(newRow) {
    // Look for repeated element
    for(var idx=this.history.length-1; idx>=0; idx--) {
        var row = this.history[idx];
        if (row.hash == newRow.hash) {
            // replace by new row
            this.history[idx] = newRow;
            // set return code depending on status
            if (!row.height && newRow.height) {
                return 1; // confirmed
            } else if (!row.height && !newRow.height) {
                return 2; // still not confirmed
            } else {
                // keep initial height
                this.history[idx].height = row.height;
                return 3; // already confirmed
            }
        }
    }
    // Not found so insert
    this.history.push(newRow);
    return 0;
};

/**
 * Build a history row from a transaction.
 * Will find out which inputs and outputs correspond to the identity wallet and calculate impact.
 * @param {String} transaction Transaction in serialized form
 * @param {Number} height Height for the transaction
 * @return {Object} The new row
 */
History.prototype.buildHistoryRow = function(transaction, height) {
    var identity = this.identity,
        btcWallet = identity.wallet.wallet,
        inMine = 0,
        outAddresses = [],
        myInValue = 0,
        myOutValue = 0,
        txAddr = "",
        txObj = Bitcoin.Transaction.fromHex(transaction);
    var isStealth = false;
    var txHash = txObj.getId();

    var pocketImpact = {};
    var addPocketImpact = function(pocketId, outPocketType, amount) {
        if (!pocketImpact.hasOwnProperty(pocketId)) {
            pocketImpact[pocketId] = {ins: 0, outs: 0, total: 0, type: outPocketType};
        }
        if (amount > 0) {
            pocketImpact[pocketId].outs += amount;
        } else {
            pocketImpact[pocketId].ins -= amount;
        }
        pocketImpact[pocketId].total += amount;
    };

    var inAddress, inPocket, outPocket, internal;

    // Check inputs
    for(var idx=0; idx<txObj.ins.length; idx++) {
        var anIn = txObj.ins[idx];
        var outIdx = Bitcoin.bufferutils.reverse(anIn.hash).toString('hex')+":"+anIn.index;
        if (btcWallet.outputs[outIdx]) {
            var output = btcWallet.outputs[outIdx];
            // save in pocket
            var inWalletAddress = identity.wallet.getWalletAddress(output.address);
            inPocket = identity.wallet.pockets.getAddressPocketId(inWalletAddress);
            var inPocketType = identity.wallet.pockets.getPocketType(inWalletAddress.type);
            // counters
            inMine += 1;
            myInValue += output.value;
            addPocketImpact(inPocket, inPocketType, -output.value);
        } else {
            try {
                var address = BtcUtils.getInputAddress(anIn, this.identity.wallet.versions);
                inAddress = address || inAddress;
            } catch (e) {
                console.log("error decoding input", anIn);
            }
        }
    }
    if (!inMine) {
        txAddr = inAddress || 'unknown';
    }
    // Check outputs
    for(var idx=0; idx<txObj.outs.length; idx++) {
        var outAddress;
        var anOut = txObj.outs[idx];
        try {
            outAddress = Bitcoin.Address.fromOutputScript(anOut.script, Bitcoin.networks[this.identity.wallet.network]);
        } catch(e) {
            outAddress = "";
        }
        var outWalletAddress = identity.wallet.getWalletAddress(outAddress.toString());
        if (outWalletAddress) {
            var output = btcWallet.outputs[txHash+":"+idx];
            // TODO: mark also when input is mine and output not
            if (output && output.stealth) {
                isStealth = true;
            }
            if (outAddresses.indexOf(outWalletAddress) == -1) {
                outAddresses.push(outWalletAddress);
            }
            myOutValue += anOut.value;
            var _outPocket = identity.wallet.pockets.getAddressPocketId(outWalletAddress);
            var outPocketType = identity.wallet.pockets.getPocketType(outWalletAddress.type);
            // check if this is change, seq[0] for oldstealth and old hd, seq[1] for new hd
            var isChange = (['oldstealth', undefined].indexOf(outWalletAddress.type) > -1) ? (outWalletAddress.index[0]%2) : false;
            isChange = isChange || ((outWalletAddress.type == 'hd') ? outWalletAddress.index[1] : false);
            // save out pocket (don't set if it's change or same as input)
            if (inPocket !== _outPocket && !isChange) {
                outPocket = _outPocket;
            }
            addPocketImpact(_outPocket, outPocketType, anOut.value);
        } else {
            if (inMine) {
                txAddr = outAddress.toString();
            }
        }
    }
    if (!txAddr) {
        txAddr = this.getTransferLabel(pocketImpact);
        internal = true;
    }
    // Create a row representing this change (if already referenced will
    // be replaced)
    var newRow = {hash: txHash, tx: txObj, inMine: inMine, outAddresses: outAddresses, myInValue: myInValue, myOutValue: myOutValue, height: height, address: txAddr, isStealth: isStealth, total: myOutValue-myInValue, outPocket: outPocket, inPocket: inPocket, impact: pocketImpact, label: this.identity.txdb.getLabel(txHash), internal: internal, bareid: BtcUtils.getBareTxId(txObj)};
    return newRow;
};

History.prototype.getTransferLabel = function(pocketImpact) {
    var identity = this.identity;
    var keys = Object.keys(pocketImpact);

    // Input pockets
    var inKeys = keys.filter(function(key) { return pocketImpact[key].ins!==0; } );
    inKeys = inKeys.map(function(key) { return identity.wallet.pockets.getPocket(key, pocketImpact[key].type).name; });

    // Output pockets (minus change pockets)
    var outKeys = keys.filter(function(key) { return pocketImpact[key].outs!==0 && pocketImpact[key].ins===0; } );
    outKeys = outKeys.map(function(key) { return identity.wallet.pockets.getPocket(key, pocketImpact[key].type).name; });

    // Compose the final label
    var label = inKeys.join(' ,');
    if (outKeys.length) {
        return label + " to " + outKeys.join(' ,');
    } else {
        return 'internal on ' + label;
    }
}

/**
 * Callback to fill missing input (depending on another transaction)
 * @param {Object} transaction Transaction in serialized form.
 * @param {Object} data        DOCME
 * @private
 */
History.prototype.fillInput = function(transaction, data) {
    var address,
        index = data[0],
        newRow = data[1],
        txObj = Bitcoin.Transaction.fromHex(transaction);
    try {
        address = Bitcoin.Address.fromOutputScript(txObj.outs[index].script, Bitcoin.networks[this.identity.wallet.network]);
    } catch(e) {
    }
    if (address) {
        newRow.address = address.toString();
        this.update();
    } else {
        // TODO: need to handle this better
        console.log("output not recognized:", txObj.outs[index]);
    }
};

/**
 * Callback for fetching a transaction
 * @param {Object} transaction   Transaction in serialized form.
 * @param {Number} height        Height of the block.
 * @return {Object|null}         DOCME
 * @private
 */
History.prototype.txFetched = function(transaction, height) {
    var self = this,
        newRow = this.buildHistoryRow(transaction, height);

    // unknown for now means we need to fill in some extra inputs for now get 1st one
    if (newRow.address == 'unknown') {
        if (newRow.tx.ins[0])
            this.identity.txdb.fetchTransaction(Bitcoin.bufferutils.reverse(newRow.tx.ins[0].hash).toString('hex'),
                                            function(_a, _b) {self.fillInput(_a, _b);},
                                            [newRow.tx.ins[0].index, newRow]);
        else {
            console.log("No input!", newRow.tx);
        }
     }
    if (this.addHistoryRow(newRow) < 2) {
        // It's a relevant event so return the row (initial unspent or initial confirm)
        return newRow;
    }
};

/**
 * Look for transactions from given history records
 * @param {Object} history History array as returned by obelisk
 */
History.prototype.fillHistory = function(history) {
    var self = this,
        txdb = this.identity.txdb;
    history.forEach(function(tx) {
        var outTxHash = tx[0],
            inTxHash = tx[4];
        if (inTxHash) {
            // fetch a row for the spend
            txdb.fetchTransaction(inTxHash, function(_a, _b) {self.txFetched(_a, _b);}, tx[6]);
        }
        // fetch a row for the incoming tx
        txdb.fetchTransaction(outTxHash, function(_a, _b) {self.txFetched(_a, _b);}, tx[2]);
    });
};

/**
 * Update callback, needs to be filled in by the application to register.
 */
History.prototype.update = function() {
    console.log("Program needs to register an update callback!");
};

return History;
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
 *
 * 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.
 */
'use strict'

const settings = require('./settings')

const EventEmitter = require('events')
const util = require('util')
const fs = require('fs')
const Gpio = require('onoff').Gpio

/**
 * GpioCtrl - GPIO Controller
 *
 * Events
 * - 'pressed' or 'on'
 * - 'released' or 'off'
 *
 * Methods
 * - setOn
 * - setOff
 * - setStatus (status)
 */
function GpioCtrl (pin, isInput) {
  EventEmitter.call(this)

  this.pin = pin
  this.isInput = isInput
  this.handle = null

  if (pin === 0) {
    console.log('GPIO pin(virtual) opened')
    return
  }

  try {
    fs.accessSync('/sys/class/gpio/export', fs.F_OK | fs.W_OK)
  } catch (e) {
    console.error('GPIO pin(' + pin + ') open failed')
    return
  }

  if (isInput === true) {
    this.handle = new Gpio(pin, 'in', 'both')

    const self = this

    this.handle.watch((err, value) => {
      if (err) {
        console.log(err)
      }

      if (value === 0) {
        self.emit('pressed')
        self.emit('on')
      } else {
        self.emit('released')
        self.emit('off')
      }
    })
  } else {
    this.handle = new Gpio(pin, 'out')
  }
}

util.inherits(GpioCtrl, EventEmitter)

GpioCtrl.prototype.setOn = function () {
  if (this.pin === 0) {
    this.emit('on')
    return
  }

  if (this.handle && this.isInput === false) {
    this.handle.writeSync(1)
    this.emit('on')
  } else {
    console.error('setOn() failed')
  }
}

GpioCtrl.prototype.setOff = function () {
  if (this.pin === 0) {
    this.emit('off')
    return
  }

  if (this.handle && this.isInput === false) {
    this.handle.writeSync(0)
    this.emit('off')
  } else {
    console.error('setOff() failed')
  }
}

GpioCtrl.prototype.setStatus = function (status) {
  if (status === 1) {
    this.setOn()
  } else if (status === 0) {
    this.setOff()
  }
}

/**
 * Available GPIO Controls
 *
 */
let ctrls = {
  SW403: null,
  SW404: null,
  LED400: null,
  LED401: null,

  /**
   * Virtual GPIO Controller
   * Only Master-node can control the RemoteGpio module
   */
  RemoteGpio: {
    LED400: new GpioCtrl(0),
    LED401: new GpioCtrl(0)
  }
}

if (settings.config.use.virtual_gpio) {
  ctrls.SW403 = new GpioCtrl(0, true)
  ctrls.SW404 = new GpioCtrl(0, true)
  ctrls.LED400 = new GpioCtrl(0, false)
  ctrls.LED401 = new GpioCtrl(0, false)
} else {
  ctrls.SW403 = new GpioCtrl(settings.config.gpio.sw403, true)
  ctrls.SW404 = new GpioCtrl(settings.config.gpio.sw404, true)
  ctrls.LED400 = new GpioCtrl(settings.config.gpio.led400, false)
  ctrls.LED401 = new GpioCtrl(settings.config.gpio.led401, false)
}

/* link each button to led */
ctrls.SW403.on('pressed', function () {
  ctrls.LED400.setOn()
})
ctrls.SW403.on('released', function () {
  ctrls.LED400.setOff()
})

ctrls.SW404.on('pressed', function () {
  ctrls.LED401.setOn()
})
ctrls.SW404.on('released', function () {
  ctrls.LED401.setOff()
})

module.exports = ctrls
<commit_msg>Update gpioctrl<commit_after>/*
 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
 *
 * 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.
 */
'use strict'

const settings = require('./settings')

const EventEmitter = require('events')
const util = require('util')
const fs = require('fs')
const Gpio = require('onoff').Gpio

/**
 * GpioCtrl - GPIO Controller
 *
 * Events
 * - 'pressed' or 'on'
 * - 'released' or 'off'
 *
 * Methods
 * - setOn
 * - setOff
 * - setStatus (status)
 */
function GpioCtrl (pin, isInput) {
  EventEmitter.call(this)

  this.pin = pin
  this.isInput = isInput
  this.handle = null
  this.prev_value = -1

  if (pin === 0) {
    console.log('GPIO pin(virtual) opened')
    return
  }

  try {
    fs.accessSync('/sys/class/gpio/export', fs.F_OK | fs.W_OK)
  } catch (e) {
    console.error('GPIO pin(' + pin + ') open failed')
    return
  }

  if (isInput === true) {
    this.handle = new Gpio(pin, 'in', 'both')

    const self = this

    this.handle.watch((err, value) => {
      if (err) {
        console.log(err)
      }

      if (self.prev_value === value) {
        console.log('ignore invalid input')
        return
      }

      self.prev_value = value

      if (value === 0) {
        self.emit('pressed')
        self.emit('on')
      } else {
        self.emit('released')
        self.emit('off')
      }
    })
  } else {
    this.handle = new Gpio(pin, 'out')
  }
}

util.inherits(GpioCtrl, EventEmitter)

GpioCtrl.prototype.setOn = function () {
  if (this.pin === 0) {
    this.emit('on')
    return
  }

  if (this.handle && this.isInput === false) {
    this.handle.writeSync(1)
    this.emit('on')
  } else {
    console.error('setOn() failed')
  }
}

GpioCtrl.prototype.setOff = function () {
  if (this.pin === 0) {
    this.emit('off')
    return
  }

  if (this.handle && this.isInput === false) {
    this.handle.writeSync(0)
    this.emit('off')
  } else {
    console.error('setOff() failed')
  }
}

GpioCtrl.prototype.setStatus = function (status) {
  if (status === 1) {
    this.setOn()
  } else if (status === 0) {
    this.setOff()
  }
}

/**
 * Available GPIO Controls
 *
 */
let ctrls = {
  SW403: null,
  SW404: null,
  LED400: null,
  LED401: null,

  /**
   * Virtual GPIO Controller
   * Only Master-node can control the RemoteGpio module
   */
  RemoteGpio: {
    LED400: new GpioCtrl(0),
    LED401: new GpioCtrl(0)
  }
}

if (settings.config.use.virtual_gpio) {
  ctrls.SW403 = new GpioCtrl(0, true)
  ctrls.SW404 = new GpioCtrl(0, true)
  ctrls.LED400 = new GpioCtrl(0, false)
  ctrls.LED401 = new GpioCtrl(0, false)
} else {
  ctrls.SW403 = new GpioCtrl(settings.config.gpio.sw403, true)
  ctrls.SW404 = new GpioCtrl(settings.config.gpio.sw404, true)
  ctrls.LED400 = new GpioCtrl(settings.config.gpio.led400, false)
  ctrls.LED401 = new GpioCtrl(settings.config.gpio.led401, false)
}

/* link each button to led */
ctrls.SW403.on('pressed', function () {
  ctrls.LED400.setOn()
})
ctrls.SW403.on('released', function () {
  ctrls.LED400.setOff()
})

ctrls.SW404.on('pressed', function () {
  ctrls.LED401.setOn()
})
ctrls.SW404.on('released', function () {
  ctrls.LED401.setOff()
})

module.exports = ctrls
<|endoftext|>


<commit_before>// 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.

import {
  getProjectName,
  isPathSubDirOf,
  isProduction,
} from '../../utils'

import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import UglifyJsPlugin from 'uglifyjs-webpack-plugin'
import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer'
import {existsSync} from 'fs'
import {getProjectDir, TOOLBOX_DIR} from '../../paths'
import {getSettings} from '../../settings'
import {join, relative} from 'path'
import {StatsWriterPlugin} from 'webpack-stats-plugin'

// Webpack’s loaders.
import bbHtmlRule from './rules/bb-html'
import cssModuleRule from './rules/css-module'
import cssRule from './rules/css'
import jsRule from './rules/js'
import defaultRule from './rules/default'

const PROJECT_DIR = getProjectDir()
const PROJECT_SRC_DIR = join(PROJECT_DIR, 'src')
const PROJECT_BUILD_DIR = join(PROJECT_DIR, 'build')

let {
  bundleStats = false,
  disableSourceMaps = false,
  configDevServer = false,
  interactiveBundleStats = false,
  externals,
  minify,
  minifyJs,
  port = 9000,
  watch = false,
} = getSettings()

/**
 * Configure the stats generation.
 */
function configureBundleStats(config) {
  // Interactive tree map of the bundle.
  if (interactiveBundleStats)
    config.plugins.push(new BundleAnalyzerPlugin)

  // JSON file containing the bundle stats.
  if (bundleStats) {
    config.plugins.push(new StatsWriterPlugin({
      filename: 'bundle-stats.json',
      // Include everything.
      fields: null,
    }))
  }
}

/**
 * We are only including configuration for the dev server when requested by the
 * CLI.
 */
function configureDevServer(config) {
  if (!configDevServer)
    return
  config.devServer = {
    contentBase: PROJECT_BUILD_DIR,
    headers: {
      'Access-Control-Allow-Headers': '*',
      'Access-Control-Allow-Methods': '*',
      'Access-Control-Allow-Origin': '*',
    },
    index: 'index.html',
    stats: config.stats,
    port,
  }
}

/**
 * Exclude the target module from the bundle and set the global variable that
 * identifies it. This is usually used in conjuction with the HTML plugin to
 * insert CDN links at the “head” tag.
 */
function configureExternals(config) {
  if (!externals)
    return
  config.externals = {}
  for (let {globalIdentifier, importIdentifier} of externals)
    config.externals[importIdentifier] = globalIdentifier
}

/**
 * Configure the minifier to compress the final bundle.
 */
function configureJsMinification(config) {
  if (!(minify || minifyJs))
    return
  config.optimization.minimize = true
  config.optimization.minimizer = [new UglifyJsPlugin({
    sourceMap: !disableSourceMaps,
    uglifyOptions: {
      output: {
        comments: false,
      },
    },
  })]
}

/**
 * By default, the shared configuration look for 2 entries, this is useful when
 * your project act both as an app by running the “main.js” and a library by
 * allowing the “index.js” to be imported.
 */
function getDefaultEntries() {
  let result = {}

  const PROJECT_MAIN = join(PROJECT_SRC_DIR, 'main.js')
  const PROJECT_INDEX = join(PROJECT_SRC_DIR, 'index.js')

  if (existsSync(PROJECT_MAIN))
    result.main = PROJECT_MAIN

  if (existsSync(PROJECT_INDEX))
    result.index = PROJECT_INDEX

  return result
}

/**
 * This function is used in “devtoolModuleFilenameTemplate” to create consistent
 * module paths and simplify the file structure on the browser’s debugger to be
 * as follows:
 *
 *     file://
 *       borela-js-toolbox
 *          entry
 *          node_modules
 *
 *       project-name
 *          node_modules
 *          src
 *
 *     webpack://
 *        ...
 */
function normalizeModulePath(info) {
  let identifier = info.identifier

  if (/^(webpack|\(webpack\))/.test(identifier))
    return `webpack:///${identifier}`

  const PROJECT_NAME = getProjectName()
  let path = info.absoluteResourcePath

  // When hot reloading, path is already correct.
  if (/\w+:\/{3}/.test(path))
    return path

  // Project sources.
  if (isPathSubDirOf(path, PROJECT_DIR)) {
    path = relative(PROJECT_DIR, path)
    path = path.replace(/\\/g, '/')
    return `file:///${PROJECT_NAME}/${path}`
  }

  // Toolbox sources.
  if (isPathSubDirOf(path, TOOLBOX_DIR)) {
    path = relative(TOOLBOX_DIR, path)
    path = path.replace(/\\/g, '/')

    // Simplify entry path.
    path = path.replace(/build\/configs\/webpack\/entries\/\w+/g, 'entry')

    return `file:///borela-js-toolbox/${path}`
  }

  throw new Error(`Invalid resource path “${path}”.`)
}

/**
 * This configuration holds the logic shared across all project types.
 */
export default function () {
  const MODULE_PATHS = [
    join(PROJECT_DIR, 'node_modules'),
    join(TOOLBOX_DIR, 'node_modules'),
    join(PROJECT_DIR, 'src'),
    'node_modules',
    'src',
  ]

  const STATS = {
    // Hide everything first, any property following this one will enable only
    // the features we want.
    all: false,
    // Asset table.
    assets: true,
    // Build date and time.
    builtAt: true,
    // Show errors.
    errors: true,
    // Build hash.
    hash: true,
    // Performance hints.
    performance: isProduction(),
    // Build time.
    timings: true,
    // Webpack version.
    version: true,
  }

  let result = {
    devtool: !disableSourceMaps && 'source-map',
    entry: getDefaultEntries(),
    mode: isProduction() ? 'production' : 'development',
    module: {
      rules: [{
        oneOf: [
          cssModuleRule(),
          cssRule(),
          bbHtmlRule(),
          jsRule(),
          // Fallback, just copy the files if the previous rules didn’t catch
          // the module.
          defaultRule(),
        ],
      }],
    },
    node: {
      // Disable polyfills.
      __dirname: false,
      __filename: false,
    },
    optimization: {
      splitChunks: {
        chunks: 'all',
      },
    },
    output: {
      path: PROJECT_BUILD_DIR,
      filename: '[name].js',
      devtoolModuleFilenameTemplate: normalizeModulePath,
    },
    plugins: [
      new MiniCssExtractPlugin({
        filename: '[name].css',
        chunkFilename: '[id].css',
      }),
    ],
    resolve: {
      extensions: [
        '.js',
        '.jsx',
        '.mjs',
        '.json',
        '.module.css',
      ],
      modules: MODULE_PATHS,
    },
    resolveLoader: {
      modules: [
        // For now we don’t have any custom loaders, but this is here just in
        // case one is needed.
        join(__dirname, 'loaders'),
        // The other loaders will be searched like any other modules.
        ...MODULE_PATHS,
      ],
    },
    stats: STATS,
    watch,
  }

  configureExternals(result)
  configureDevServer(result)
  configureJsMinification(result)
  configureBundleStats(result)

  return result
}
<commit_msg>Watch content base<commit_after>// 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.

import {
  getProjectName,
  isPathSubDirOf,
  isProduction,
} from '../../utils'

import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import UglifyJsPlugin from 'uglifyjs-webpack-plugin'
import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer'
import {existsSync} from 'fs'
import {getProjectDir, TOOLBOX_DIR} from '../../paths'
import {getSettings} from '../../settings'
import {join, relative} from 'path'
import {StatsWriterPlugin} from 'webpack-stats-plugin'

// Webpack’s loaders.
import bbHtmlRule from './rules/bb-html'
import cssModuleRule from './rules/css-module'
import cssRule from './rules/css'
import jsRule from './rules/js'
import defaultRule from './rules/default'

const PROJECT_DIR = getProjectDir()
const PROJECT_SRC_DIR = join(PROJECT_DIR, 'src')
const PROJECT_BUILD_DIR = join(PROJECT_DIR, 'build')

let {
  bundleStats = false,
  disableSourceMaps = false,
  configDevServer = false,
  interactiveBundleStats = false,
  externals,
  minify,
  minifyJs,
  port = 9000,
  watch = false,
} = getSettings()

/**
 * Configure the stats generation.
 */
function configureBundleStats(config) {
  // Interactive tree map of the bundle.
  if (interactiveBundleStats)
    config.plugins.push(new BundleAnalyzerPlugin)

  // JSON file containing the bundle stats.
  if (bundleStats) {
    config.plugins.push(new StatsWriterPlugin({
      filename: 'bundle-stats.json',
      // Include everything.
      fields: null,
    }))
  }
}

/**
 * We are only including configuration for the dev server when requested by the
 * CLI.
 */
function configureDevServer(config) {
  if (!configDevServer)
    return
  config.devServer = {
    contentBase: PROJECT_BUILD_DIR,
    headers: {
      'Access-Control-Allow-Headers': '*',
      'Access-Control-Allow-Methods': '*',
      'Access-Control-Allow-Origin': '*',
    },
    index: 'index.html',
    stats: config.stats,
    watchContentBase: true,
    port,
  }
}

/**
 * Exclude the target module from the bundle and set the global variable that
 * identifies it. This is usually used in conjuction with the HTML plugin to
 * insert CDN links at the “head” tag.
 */
function configureExternals(config) {
  if (!externals)
    return
  config.externals = {}
  for (let {globalIdentifier, importIdentifier} of externals)
    config.externals[importIdentifier] = globalIdentifier
}

/**
 * Configure the minifier to compress the final bundle.
 */
function configureJsMinification(config) {
  if (!(minify || minifyJs))
    return
  config.optimization.minimize = true
  config.optimization.minimizer = [new UglifyJsPlugin({
    sourceMap: !disableSourceMaps,
    uglifyOptions: {
      output: {
        comments: false,
      },
    },
  })]
}

/**
 * By default, the shared configuration look for 2 entries, this is useful when
 * your project act both as an app by running the “main.js” and a library by
 * allowing the “index.js” to be imported.
 */
function getDefaultEntries() {
  let result = {}

  const PROJECT_MAIN = join(PROJECT_SRC_DIR, 'main.js')
  const PROJECT_INDEX = join(PROJECT_SRC_DIR, 'index.js')

  if (existsSync(PROJECT_MAIN))
    result.main = PROJECT_MAIN

  if (existsSync(PROJECT_INDEX))
    result.index = PROJECT_INDEX

  return result
}

/**
 * This function is used in “devtoolModuleFilenameTemplate” to create consistent
 * module paths and simplify the file structure on the browser’s debugger to be
 * as follows:
 *
 *     file://
 *       borela-js-toolbox
 *          entry
 *          node_modules
 *
 *       project-name
 *          node_modules
 *          src
 *
 *     webpack://
 *        ...
 */
function normalizeModulePath(info) {
  let identifier = info.identifier

  if (/^(webpack|\(webpack\))/.test(identifier))
    return `webpack:///${identifier}`

  const PROJECT_NAME = getProjectName()
  let path = info.absoluteResourcePath

  // When hot reloading, path is already correct.
  if (/\w+:\/{3}/.test(path))
    return path

  // Project sources.
  if (isPathSubDirOf(path, PROJECT_DIR)) {
    path = relative(PROJECT_DIR, path)
    path = path.replace(/\\/g, '/')
    return `file:///${PROJECT_NAME}/${path}`
  }

  // Toolbox sources.
  if (isPathSubDirOf(path, TOOLBOX_DIR)) {
    path = relative(TOOLBOX_DIR, path)
    path = path.replace(/\\/g, '/')

    // Simplify entry path.
    path = path.replace(/build\/configs\/webpack\/entries\/\w+/g, 'entry')

    return `file:///borela-js-toolbox/${path}`
  }

  throw new Error(`Invalid resource path “${path}”.`)
}

/**
 * This configuration holds the logic shared across all project types.
 */
export default function () {
  const MODULE_PATHS = [
    join(PROJECT_DIR, 'node_modules'),
    join(TOOLBOX_DIR, 'node_modules'),
    join(PROJECT_DIR, 'src'),
    'node_modules',
    'src',
  ]

  const STATS = {
    // Hide everything first, any property following this one will enable only
    // the features we want.
    all: false,
    // Asset table.
    assets: true,
    // Build date and time.
    builtAt: true,
    // Show errors.
    errors: true,
    // Build hash.
    hash: true,
    // Performance hints.
    performance: isProduction(),
    // Build time.
    timings: true,
    // Webpack version.
    version: true,
  }

  let result = {
    devtool: !disableSourceMaps && 'source-map',
    entry: getDefaultEntries(),
    mode: isProduction() ? 'production' : 'development',
    module: {
      rules: [{
        oneOf: [
          cssModuleRule(),
          cssRule(),
          bbHtmlRule(),
          jsRule(),
          // Fallback, just copy the files if the previous rules didn’t catch
          // the module.
          defaultRule(),
        ],
      }],
    },
    node: {
      // Disable polyfills.
      __dirname: false,
      __filename: false,
    },
    optimization: {
      splitChunks: {
        chunks: 'all',
      },
    },
    output: {
      path: PROJECT_BUILD_DIR,
      filename: '[name].js',
      devtoolModuleFilenameTemplate: normalizeModulePath,
    },
    plugins: [
      new MiniCssExtractPlugin({
        filename: '[name].css',
        chunkFilename: '[id].css',
      }),
    ],
    resolve: {
      extensions: [
        '.js',
        '.jsx',
        '.mjs',
        '.json',
        '.module.css',
      ],
      modules: MODULE_PATHS,
    },
    resolveLoader: {
      modules: [
        // For now we don’t have any custom loaders, but this is here just in
        // case one is needed.
        join(__dirname, 'loaders'),
        // The other loaders will be searched like any other modules.
        ...MODULE_PATHS,
      ],
    },
    stats: STATS,
    watch,
  }

  configureExternals(result)
  configureDevServer(result)
  configureJsMinification(result)
  configureBundleStats(result)

  return result
}
<|endoftext|>


<commit_before>/* globals k:false, jQuery:false, trackEvent:false */
(function($) {
  var searchTimeout;
  var locale = $('html').attr('lang');
  const searchTitle = "Search | Mozilla Support";

  const localStorageUseV2 = {
    "true": true,
    "false": false,
  }[window.localStorage.getItem("enable_search_v2")];

  // the local storage flag overrides everything else:
  if (
    localStorageUseV2 ??
    ($("body").data("readonly") || window.waffle?.flag_is_active("instant_search_v2"))
  ) {
    var search = new k.Search("/" + locale + "/search/v2/");
  } else {
    var search = new k.Search("/" + locale + "/search");
  }

  var cxhr = new k.CachedXHR();
  var aaq_explore_step = $("#question-search-masthead").length > 0;

  const queries = [];
  let renderedQuery;

  function hideContent() {
    $('#main-content').hide();
    $('#main-content').siblings('aside').hide();
    $('body').addClass('search-results-visible');
    $('.home-search-section .mzp-l-content').removeClass('narrow');
    $('.home-search-section').removeClass('extra-pad-bottom');

    // If applicable, close the mobile search field and move the focus to the main field.
    $(".sumo-nav--mobile-search-form").removeClass("mzp-is-open").attr("aria-expanded", "false");

    if (aaq_explore_step) {
      // in aaq explore step we don't want any search to show the default masthead
      $(".hidden-search-masthead").hide();
      $(".question-masthead").show();
      $(".page-heading--logo").css("display", "block");
    } else {
      $(".hidden-search-masthead").show();
    }
  }

  function showContent() {
    $('body').removeClass('search-results-visible');
    $('.home-search-section').addClass('extra-pad-bottom');
    $('.support-search-main').hide();
    $('#main-content').show();
    $('#main-content').siblings('aside').show();
    $('#instant-search-content').remove();
    $('[data-instant-search="form"] input[name="q"]').each(function () {
      $(this).val("");
    });
    $(".page-heading--intro-text").show();
    $(".home-search-section--content .search-results-heading").remove();
    $('.home-search-section .mzp-l-content').addClass('narrow');
    $('.hidden-search-masthead').hide();
  }

  function render(data) {
    var context = $.extend({}, data);

    let query = context.q;
    if (queries.lastIndexOf(query) < queries.lastIndexOf(renderedQuery)) {
      // this query was sent before the query already rendered, don't render it
      return;
    }
    renderedQuery = query;

    let historyState = {
      query,
      params: search.params
    }
    if (history.state?.query) {
      // if a search is already the latest point in history, replace it
      // to avoid filling history with partial searches
      history.replaceState(historyState, searchTitle);
    } else {
      history.pushState(historyState, searchTitle);
    }

    var base_url = search.lastQueryUrl();
    var $searchContent;
    context.base_url = base_url;

    if ($('#instant-search-content').length) {
      $searchContent = $('#instant-search-content');
    } else {
      $searchContent = $('<div />').attr('id', 'instant-search-content');
      $('#main-content').after($searchContent);
    }

    var $searchResults = $(k.nunjucksEnv.render("search-results.html", context));
    if (aaq_explore_step) {
      $searchResults.find('section a').attr('target', '_blank');
    }
    $searchContent.html($searchResults);

    // These two functions are coming from the global scope, but should be proper
    // modules when we replace django-compressor with a FE build process.
    detailsInit(); // fold up sidebar on mobile.
    tabsInit();

    // remove and append search results heading
    $(".page-heading--intro-text").hide();
    $(".home-search-section--content .search-results-heading").remove();
    $(".search-results-heading").appendTo(".home-search-section--content");

    // change aaq link if we're in aaq flow
    if (aaq_explore_step) {
      $("#search-results-aaq-link").attr("href", window.location + "/form");
    }
  }

  window.k.InstantSearchSettings = {
    hideContent: hideContent,
    showContent: showContent,
    render: render,
    searchClient: search
  };

  $(document).on('submit', '[data-instant-search="form"]', function(ev) {
    ev.preventDefault();
    $(this).find('.searchbox').focus();
  });

  $(document).on('keyup', '[data-instant-search="form"] input[type="search"]', function(ev) {
    var $this = $(this);
    var $form = $this.closest('form');
    var formId = $form.attr('id');
    var params = {
      format: 'json'
    };

    if ($this.val().length === 0) {
      if (searchTimeout) {
        window.clearTimeout(searchTimeout);
      }

      window.k.InstantSearchSettings.showContent();

      if (history.state?.query) {
        history.pushState({}, searchTitle);
      }
    } else if ($this.val() !== search.lastQuery) {
      if (searchTimeout) {
        window.clearTimeout(searchTimeout);
      }

      k.InstantSearchSettings.hideContent();

      $form.find('input').each(function () {
        if ($(this).attr('type') === 'submit') {
          return true;
        }
        if ($(this).attr('type') === 'button') {
          return true;
        }
        if ($(this).attr('name') === 'q') {
          var value = $(this).val();
          // update the values in all search forms which aren't the one the user is typing into
          $('[data-instant-search="form"]').not($form).each(function () {
            $(this).find('input[name="q"]').val(value);
          });
          return true;
        }
        params[$(this).attr('name')] = $(this).val();
      });

      searchTimeout = setTimeout(function () {
        if (search.hasLastQuery) {
          trackEvent('Instant Search', 'Exit Search', search.lastQueryUrl());
        }
        search.setParams(params);
        let query = $this.val();
        queries.push(query);
        search.query(query, k.InstantSearchSettings.render);
        trackEvent('Instant Search', 'Search', search.lastQueryUrl());
      }, 200);
    }

    if (formId === "support-search" || formId === "mobile-search-results") {
      window.scrollTo(0, 0);
    }

    if (aaq_explore_step) {
      $(".question-masthead").find("input[name=q]").focus();
    } else if ($(".hidden-search-masthead").length > 0) {
      $(".hidden-search-masthead").find("input[name=q]").focus();
    } else {
      $("#support-search-masthead").find("input[name=q]").focus();
    }
  });

  $(document).on('click', '[data-instant-search="link"]', function(ev) {
    ev.preventDefault();

    var $this = $(this);

    if (search.hasLastQuery) {
      trackEvent('Instant Search', 'Exit Search', search.queryUrl());
    }

    var setParams = $this.data('instant-search-set-params');
    if (setParams) {
      setParams = setParams.split('&');
      $(setParams).each(function() {
        var p = this.split('=');
        search.setParam(p.shift(), p.join('='));
      });
    }

    var unsetParams = $this.data('instant-search-unset-params');
    if (unsetParams) {
      unsetParams = unsetParams.split('&');
      $(unsetParams).each(function() {
        search.unsetParam(this);
      });
    }

    search.query(null, k.InstantSearchSettings.render);
    trackEvent("Instant Search", "Search", search.lastQueryUrl());
  });

  // 'Popular searches' feature
  $(document).on('click', '[data-featured-search]', function(ev) {
    var $mainInput = $('#support-search-masthead input[name=q]');
    var thisLink = $(this).text();
    $('#support-search-masthead input[name=q]').focus().val(thisLink);
    $mainInput.trigger( "keyup" );
    ev.preventDefault();
  });

  $(document).on('click', '[data-mobile-nav-search-button]', function(ev) {
    // If we hijack the layout of the page and the user clicks the button again.
    // assume they want to get rid of the search.
    if ($('.hidden-search-masthead').is(':visible')) {
      window.k.InstantSearchSettings.showContent();
    } else if (aaq_explore_step) {
      // in aaq explore step, we don't want to show the default masthead
      $('#question-search-masthead').find('input[name=q]').focus();
      window.scrollTo(0, 0);
    } else if ($('.hidden-search-masthead').length > 0) {
      $('body').addClass('search-results-visible');
      $('.hidden-search-masthead').show();
      $('.hidden-search-masthead').find('input[name=q]').focus();
      window.scrollTo(0, 0);
    } else {
      // catchall for pages with a searchbox in the masthead
      $('.simple-search-form .searchbox').trigger('keyup').focus();
    }

    ev.preventDefault();
  });

  function loadFromHistory(e) {
    let state = e.type == "popstate" ? e.state : history.state;
    if (state?.query) {
      window.k.InstantSearchSettings.hideContent();
      $('[data-instant-search="form"] input[name="q"]').each(function () {
        $(this).val(state.query);
      });
      search.params = state.params;
      search.query(state.query, k.InstantSearchSettings.render);
    } else {
      window.k.InstantSearchSettings.showContent();
    }
  }

  window.addEventListener("popstate", loadFromHistory);
  window.addEventListener("DOMContentLoaded", loadFromHistory);
})(jQuery);
<commit_msg>instant search: fix closing, starting a second search, then going back to the first (#4701)<commit_after>/* globals k:false, jQuery:false, trackEvent:false */
(function($) {
  var searchTimeout;
  var locale = $('html').attr('lang');
  const searchTitle = "Search | Mozilla Support";

  const localStorageUseV2 = {
    "true": true,
    "false": false,
  }[window.localStorage.getItem("enable_search_v2")];

  // the local storage flag overrides everything else:
  if (
    localStorageUseV2 ??
    ($("body").data("readonly") || window.waffle?.flag_is_active("instant_search_v2"))
  ) {
    var search = new k.Search("/" + locale + "/search/v2/");
  } else {
    var search = new k.Search("/" + locale + "/search");
  }

  var cxhr = new k.CachedXHR();
  var aaq_explore_step = $("#question-search-masthead").length > 0;

  const queries = [];
  let renderedQuery;

  function hideContent() {
    $('#main-content').hide();
    $('#main-content').siblings('aside').hide();
    $('body').addClass('search-results-visible');
    $('.home-search-section .mzp-l-content').removeClass('narrow');
    $('.home-search-section').removeClass('extra-pad-bottom');

    // If applicable, close the mobile search field and move the focus to the main field.
    $(".sumo-nav--mobile-search-form").removeClass("mzp-is-open").attr("aria-expanded", "false");

    if (aaq_explore_step) {
      // in aaq explore step we don't want any search to show the default masthead
      $(".hidden-search-masthead").hide();
      $(".question-masthead").show();
      $(".page-heading--logo").css("display", "block");
    } else {
      $(".hidden-search-masthead").show();
    }
  }

  function showContent() {
    $('body').removeClass('search-results-visible');
    $('.home-search-section').addClass('extra-pad-bottom');
    $('.support-search-main').hide();
    $('#main-content').show();
    $('#main-content').siblings('aside').show();
    $('#instant-search-content').remove();
    $('[data-instant-search="form"] input[name="q"]').each(function () {
      $(this).val("");
    });
    $(".page-heading--intro-text").show();
    $(".home-search-section--content .search-results-heading").remove();
    $('.home-search-section .mzp-l-content').addClass('narrow');
    $('.hidden-search-masthead').hide();
  }

  function render(data) {
    var context = $.extend({}, data);

    let query = context.q;
    if (queries.lastIndexOf(query) < queries.lastIndexOf(renderedQuery)) {
      // this query was sent before the query already rendered, don't render it
      return;
    }
    renderedQuery = query;

    let historyState = {
      query,
      params: search.params
    }
    if (history.state?.query) {
      // if a search is already the latest point in history, replace it
      // to avoid filling history with partial searches
      history.replaceState(historyState, searchTitle);
    } else {
      history.pushState(historyState, searchTitle);
    }

    var base_url = search.lastQueryUrl();
    var $searchContent;
    context.base_url = base_url;

    if ($('#instant-search-content').length) {
      $searchContent = $('#instant-search-content');
    } else {
      $searchContent = $('<div />').attr('id', 'instant-search-content');
      $('#main-content').after($searchContent);
    }

    var $searchResults = $(k.nunjucksEnv.render("search-results.html", context));
    if (aaq_explore_step) {
      $searchResults.find('section a').attr('target', '_blank');
    }
    $searchContent.html($searchResults);

    // These two functions are coming from the global scope, but should be proper
    // modules when we replace django-compressor with a FE build process.
    detailsInit(); // fold up sidebar on mobile.
    tabsInit();

    // remove and append search results heading
    $(".page-heading--intro-text").hide();
    $(".home-search-section--content .search-results-heading").remove();
    $(".search-results-heading").appendTo(".home-search-section--content");

    // change aaq link if we're in aaq flow
    if (aaq_explore_step) {
      $("#search-results-aaq-link").attr("href", window.location + "/form");
    }
  }

  window.k.InstantSearchSettings = {
    hideContent: hideContent,
    showContent: showContent,
    render: render,
    searchClient: search
  };

  $(document).on('submit', '[data-instant-search="form"]', function(ev) {
    ev.preventDefault();
    $(this).find('.searchbox').focus();
  });

  $(document).on('keyup', '[data-instant-search="form"] input[type="search"]', function(ev) {
    var $this = $(this);
    var $form = $this.closest('form');
    var formId = $form.attr('id');
    var params = {
      format: 'json'
    };

    if ($this.val().length === 0) {
      if (searchTimeout) {
        window.clearTimeout(searchTimeout);
      }

      window.k.InstantSearchSettings.showContent();

      if (history.state?.query) {
        history.pushState({}, searchTitle);
      }
    } else if ($this.val() !== search.lastQuery) {
      if (searchTimeout) {
        window.clearTimeout(searchTimeout);
      }

      k.InstantSearchSettings.hideContent();

      $form.find('input').each(function () {
        if ($(this).attr('type') === 'submit') {
          return true;
        }
        if ($(this).attr('type') === 'button') {
          return true;
        }
        if ($(this).attr('name') === 'q') {
          var value = $(this).val();
          // update the values in all search forms which aren't the one the user is typing into
          $('[data-instant-search="form"]').not($form).each(function () {
            $(this).find('input[name="q"]').val(value);
          });
          return true;
        }
        params[$(this).attr('name')] = $(this).val();
      });

      searchTimeout = setTimeout(function () {
        if (search.hasLastQuery) {
          trackEvent('Instant Search', 'Exit Search', search.lastQueryUrl());
        }
        search.setParams(params);
        let query = $this.val();
        queries.push(query);
        search.query(query, k.InstantSearchSettings.render);
        trackEvent('Instant Search', 'Search', search.lastQueryUrl());
      }, 200);
    }

    if (formId === "support-search" || formId === "mobile-search-results") {
      window.scrollTo(0, 0);
    }

    if (aaq_explore_step) {
      $(".question-masthead").find("input[name=q]").focus();
    } else if ($(".hidden-search-masthead").length > 0) {
      $(".hidden-search-masthead").find("input[name=q]").focus();
    } else {
      $("#support-search-masthead").find("input[name=q]").focus();
    }
  });

  $(document).on('click', '[data-instant-search="link"]', function(ev) {
    ev.preventDefault();

    var $this = $(this);

    if (search.hasLastQuery) {
      trackEvent('Instant Search', 'Exit Search', search.queryUrl());
    }

    var setParams = $this.data('instant-search-set-params');
    if (setParams) {
      setParams = setParams.split('&');
      $(setParams).each(function() {
        var p = this.split('=');
        search.setParam(p.shift(), p.join('='));
      });
    }

    var unsetParams = $this.data('instant-search-unset-params');
    if (unsetParams) {
      unsetParams = unsetParams.split('&');
      $(unsetParams).each(function() {
        search.unsetParam(this);
      });
    }

    search.query(null, k.InstantSearchSettings.render);
    trackEvent("Instant Search", "Search", search.lastQueryUrl());
  });

  // 'Popular searches' feature
  $(document).on('click', '[data-featured-search]', function(ev) {
    var $mainInput = $('#support-search-masthead input[name=q]');
    var thisLink = $(this).text();
    $('#support-search-masthead input[name=q]').focus().val(thisLink);
    $mainInput.trigger( "keyup" );
    ev.preventDefault();
  });

  $(document).on('click', '[data-mobile-nav-search-button]', function(ev) {
    // If we hijack the layout of the page and the user clicks the button again.
    // assume they want to get rid of the search.
    if ($('.hidden-search-masthead').is(':visible')) {
      window.k.InstantSearchSettings.showContent();
    } else if (aaq_explore_step) {
      // in aaq explore step, we don't want to show the default masthead
      $('#question-search-masthead').find('input[name=q]').focus();
      window.scrollTo(0, 0);
    } else if ($('.hidden-search-masthead').length > 0) {
      $('body').addClass('search-results-visible');
      $('.hidden-search-masthead').show();
      $('.hidden-search-masthead').find('input[name=q]').focus();
      window.scrollTo(0, 0);
    } else {
      // catchall for pages with a searchbox in the masthead
      $('.simple-search-form .searchbox').trigger('keyup').focus();
    }

    ev.preventDefault();
  });

  function loadFromHistory(e) {
    let state = e.type == "popstate" ? e.state : history.state;
    if (state?.query) {
      window.k.InstantSearchSettings.hideContent();
      $('[data-instant-search="form"] input[name="q"]').each(function () {
        $(this).val(state.query);
      });
      search.params = state.params;
      queries.push(state.query);
      search.query(state.query, k.InstantSearchSettings.render);
    } else {
      window.k.InstantSearchSettings.showContent();
    }
  }

  window.addEventListener("popstate", loadFromHistory);
  window.addEventListener("DOMContentLoaded", loadFromHistory);
})(jQuery);
<|endoftext|>


<commit_before>// takes an IR function object and returns a list of Scratch blocks

module.exports.compileFunction = function(func) {
	console.log("Compiling "+JSON.stringify(func)+"...");

	var blockList = [];

	// add a procdef

	var spec = func.funcName;
	var inputs = [];
	var defaults = [];

	for(var i = 0; i < func.paramList.length; ++i) {
		if(func.paramList[i][1])
			inputs.push(func.paramList[i][1]);
		else
			inputs.push("param"+i);

		defaults.push(defaultForType(func.paramList[i][0]));
		spec += " "+specifierForType(func.paramList[i][0]);
	}

	blockList.push(["procDef", spec, inputs, defaults, false]);

	for(var i = 0; i < func.code.length; ++i) {
		console.log(func.code[i]);
	}

	return blockList;
}

// fixme: stub
function defaultForType(type) {
	console.log(type);
	return 0;
}

// fixme: stub
function specifierForType(type) {
	return "%s";
}<commit_msg>Call code generation stub<commit_after>// takes an IR function object and returns a list of Scratch blocks

module.exports.compileFunction = function(func) {
	console.log("Compiling "+JSON.stringify(func)+"...");

	var blockList = [];

	// add a procdef

	var spec = func.funcName;
	var inputs = [];
	var defaults = [];

	for(var i = 0; i < func.paramList.length; ++i) {
		if(func.paramList[i][1])
			inputs.push(func.paramList[i][1]);
		else
			inputs.push("param"+i);

		defaults.push(defaultForType(func.paramList[i][0]));
		spec += " "+specifierForType(func.paramList[i][0]);
	}

	blockList.push(["procDef", spec, inputs, defaults, false]);

	for(var i = 0; i < func.code.length; ++i) {
		console.log(func.code[i]);
		if(code[i].type == "call") {
			// calling a (potentially foreign) function
			// stub
		}
	}

	return blockList;
}

// fixme: stub
function defaultForType(type) {
	console.log(type);
	return 0;
}

// fixme: stub
function specifierForType(type) {
	return "%s";
}<|endoftext|>